display.c revision b83e0e3eef62f085b7b12e2de856fe3315638484
1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3%                                                                             %
4%                                                                             %
5%                                                                             %
6%               DDDD   IIIII  SSSSS  PPPP   L       AAA   Y   Y               %
7%               D   D    I    SS     P   P  L      A   A   Y Y                %
8%               D   D    I     SSS   PPPP   L      AAAAA    Y                 %
9%               D   D    I       SS  P      L      A   A    Y                 %
10%               DDDD   IIIII  SSSSS  P      LLLLL  A   A    Y                 %
11%                                                                             %
12%                                                                             %
13%        MagickCore Methods to Interactively Display and Edit an Image        %
14%                                                                             %
15%                             Software Design                                 %
16%                                  Cristy                                     %
17%                                July 1992                                    %
18%                                                                             %
19%                                                                             %
20%  Copyright 1999-2016 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/attribute.h"
45#include "MagickCore/blob.h"
46#include "MagickCore/cache.h"
47#include "MagickCore/cache-private.h"
48#include "MagickCore/channel.h"
49#include "MagickCore/client.h"
50#include "MagickCore/color.h"
51#include "MagickCore/colorspace.h"
52#include "MagickCore/composite.h"
53#include "MagickCore/constitute.h"
54#include "MagickCore/decorate.h"
55#include "MagickCore/delegate.h"
56#include "MagickCore/display.h"
57#include "MagickCore/display-private.h"
58#include "MagickCore/distort.h"
59#include "MagickCore/draw.h"
60#include "MagickCore/effect.h"
61#include "MagickCore/enhance.h"
62#include "MagickCore/exception.h"
63#include "MagickCore/exception-private.h"
64#include "MagickCore/fx.h"
65#include "MagickCore/geometry.h"
66#include "MagickCore/image.h"
67#include "MagickCore/image-private.h"
68#include "MagickCore/list.h"
69#include "MagickCore/log.h"
70#include "MagickCore/magick.h"
71#include "MagickCore/memory_.h"
72#include "MagickCore/monitor.h"
73#include "MagickCore/monitor-private.h"
74#include "MagickCore/montage.h"
75#include "MagickCore/nt-base-private.h"
76#include "MagickCore/option.h"
77#include "MagickCore/paint.h"
78#include "MagickCore/pixel.h"
79#include "MagickCore/pixel-accessor.h"
80#include "MagickCore/property.h"
81#include "MagickCore/quantum.h"
82#include "MagickCore/quantum-private.h"
83#include "MagickCore/resize.h"
84#include "MagickCore/resource_.h"
85#include "MagickCore/shear.h"
86#include "MagickCore/segment.h"
87#include "MagickCore/statistic.h"
88#include "MagickCore/string_.h"
89#include "MagickCore/string-private.h"
90#include "MagickCore/transform.h"
91#include "MagickCore/transform-private.h"
92#include "MagickCore/threshold.h"
93#include "MagickCore/utility.h"
94#include "MagickCore/utility-private.h"
95#include "MagickCore/version.h"
96#include "MagickCore/widget.h"
97#include "MagickCore/widget-private.h"
98#include "MagickCore/xwindow.h"
99#include "MagickCore/xwindow-private.h"
100
101#if defined(MAGICKCORE_X11_DELEGATE)
102/*
103  Define declarations.
104*/
105#define MaxColors  MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
106
107/*
108  Constant declarations.
109*/
110static const unsigned char
111  HighlightBitmap[8] =
112  {
113    0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
114  },
115  OpaqueBitmap[8] =
116  {
117    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
118  },
119  ShadowBitmap[8] =
120  {
121    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
122  };
123
124static const char
125  *PageSizes[] =
126  {
127    "Letter",
128    "Tabloid",
129    "Ledger",
130    "Legal",
131    "Statement",
132    "Executive",
133    "A3",
134    "A4",
135    "A5",
136    "B4",
137    "B5",
138    "Folio",
139    "Quarto",
140    "10x14",
141    (char *) NULL
142  };
143
144/*
145  Help widget declarations.
146*/
147static const char
148  *ImageAnnotateHelp[] =
149  {
150    "In annotate mode, the Command widget has these options:",
151    "",
152    "    Font Name",
153    "      fixed",
154    "      variable",
155    "      5x8",
156    "      6x10",
157    "      7x13bold",
158    "      8x13bold",
159    "      9x15bold",
160    "      10x20",
161    "      12x24",
162    "      Browser...",
163    "    Font Color",
164    "      black",
165    "      blue",
166    "      cyan",
167    "      green",
168    "      gray",
169    "      red",
170    "      magenta",
171    "      yellow",
172    "      white",
173    "      transparent",
174    "      Browser...",
175    "    Font Color",
176    "      black",
177    "      blue",
178    "      cyan",
179    "      green",
180    "      gray",
181    "      red",
182    "      magenta",
183    "      yellow",
184    "      white",
185    "      transparent",
186    "      Browser...",
187    "    Rotate Text",
188    "      -90",
189    "      -45",
190    "      -30",
191    "      0",
192    "      30",
193    "      45",
194    "      90",
195    "      180",
196    "      Dialog...",
197    "    Help",
198    "    Dismiss",
199    "",
200    "Choose a font name from the Font Name sub-menu.  Additional",
201    "font names can be specified with the font browser.  You can",
202    "change the menu names by setting the X resources font1",
203    "through font9.",
204    "",
205    "Choose a font color from the Font Color sub-menu.",
206    "Additional font colors can be specified with the color",
207    "browser.  You can change the menu colors by setting the X",
208    "resources pen1 through pen9.",
209    "",
210    "If you select the color browser and press Grab, you can",
211    "choose the font color by moving the pointer to the desired",
212    "color on the screen and press any button.",
213    "",
214    "If you choose to rotate the text, choose Rotate Text from the",
215    "menu and select an angle.  Typically you will only want to",
216    "rotate one line of text at a time.  Depending on the angle you",
217    "choose, subsequent lines may end up overwriting each other.",
218    "",
219    "Choosing a font and its color is optional.  The default font",
220    "is fixed and the default color is black.  However, you must",
221    "choose a location to begin entering text and press button 1.",
222    "An underscore character will appear at the location of the",
223    "pointer.  The cursor changes to a pencil to indicate you are",
224    "in text mode.  To exit immediately, press Dismiss.",
225    "",
226    "In text mode, any key presses will display the character at",
227    "the location of the underscore and advance the underscore",
228    "cursor.  Enter your text and once completed press Apply to",
229    "finish your image annotation.  To correct errors press BACK",
230    "SPACE.  To delete an entire line of text, press DELETE.  Any",
231    "text that exceeds the boundaries of the image window is",
232    "automagically continued onto the next line.",
233    "",
234    "The actual color you request for the font is saved in the",
235    "image.  However, the color that appears in your image window",
236    "may be different.  For example, on a monochrome screen the",
237    "text will appear black or white even if you choose the color",
238    "red as the font color.  However, the image saved to a file",
239    "with -write is written with red lettering.  To assure the",
240    "correct color text in the final image, any PseudoClass image",
241    "is promoted to DirectClass (see miff(5)).  To force a",
242    "PseudoClass image to remain PseudoClass, use -colors.",
243    (char *) NULL,
244  },
245  *ImageChopHelp[] =
246  {
247    "In chop mode, the Command widget has these options:",
248    "",
249    "    Direction",
250    "      horizontal",
251    "      vertical",
252    "    Help",
253    "    Dismiss",
254    "",
255    "If the you choose the horizontal direction (this the",
256    "default), the area of the image between the two horizontal",
257    "endpoints of the chop line is removed.  Otherwise, the area",
258    "of the image between the two vertical endpoints of the chop",
259    "line is removed.",
260    "",
261    "Select a location within the image window to begin your chop,",
262    "press and hold any button.  Next, move the pointer to",
263    "another location in the image.  As you move a line will",
264    "connect the initial location and the pointer.  When you",
265    "release the button, the area within the image to chop is",
266    "determined by which direction you choose from the Command",
267    "widget.",
268    "",
269    "To cancel the image chopping, move the pointer back to the",
270    "starting point of the line and release the button.",
271    (char *) NULL,
272  },
273  *ImageColorEditHelp[] =
274  {
275    "In color edit mode, the Command widget has these options:",
276    "",
277    "    Method",
278    "      point",
279    "      replace",
280    "      floodfill",
281    "      filltoborder",
282    "      reset",
283    "    Pixel Color",
284    "      black",
285    "      blue",
286    "      cyan",
287    "      green",
288    "      gray",
289    "      red",
290    "      magenta",
291    "      yellow",
292    "      white",
293    "      Browser...",
294    "    Border Color",
295    "      black",
296    "      blue",
297    "      cyan",
298    "      green",
299    "      gray",
300    "      red",
301    "      magenta",
302    "      yellow",
303    "      white",
304    "      Browser...",
305    "    Fuzz",
306    "      0%",
307    "      2%",
308    "      5%",
309    "      10%",
310    "      15%",
311    "      Dialog...",
312    "    Undo",
313    "    Help",
314    "    Dismiss",
315    "",
316    "Choose a color editing method from the Method sub-menu",
317    "of the Command widget.  The point method recolors any pixel",
318    "selected with the pointer until the button is released.  The",
319    "replace method recolors any pixel that matches the color of",
320    "the pixel you select with a button press.  Floodfill recolors",
321    "any pixel that matches the color of the pixel you select with",
322    "a button press and is a neighbor.  Whereas filltoborder recolors",
323    "any neighbor pixel that is not the border color.  Finally reset",
324    "changes the entire image to the designated color.",
325    "",
326    "Next, choose a pixel color from the Pixel Color sub-menu.",
327    "Additional pixel colors can be specified with the color",
328    "browser.  You can change the menu colors by setting the X",
329    "resources pen1 through pen9.",
330    "",
331    "Now press button 1 to select a pixel within the image window",
332    "to change its color.  Additional pixels may be recolored as",
333    "prescribed by the method you choose.",
334    "",
335    "If the Magnify widget is mapped, it can be helpful in positioning",
336    "your pointer within the image (refer to button 2).",
337    "",
338    "The actual color you request for the pixels is saved in the",
339    "image.  However, the color that appears in your image window",
340    "may be different.  For example, on a monochrome screen the",
341    "pixel will appear black or white even if you choose the",
342    "color red as the pixel color.  However, the image saved to a",
343    "file with -write is written with red pixels.  To assure the",
344    "correct color text in the final image, any PseudoClass image",
345    "is promoted to DirectClass (see miff(5)).  To force a",
346    "PseudoClass image to remain PseudoClass, use -colors.",
347    (char *) NULL,
348  },
349  *ImageCompositeHelp[] =
350  {
351    "First a widget window is displayed requesting you to enter an",
352    "image name. Press Composite, Grab or type a file name.",
353    "Press Cancel if you choose not to create a composite image.",
354    "When you choose Grab, move the pointer to the desired window",
355    "and press any button.",
356    "",
357    "If the Composite image does not have any matte information,",
358    "you are informed and the file browser is displayed again.",
359    "Enter the name of a mask image.  The image is typically",
360    "grayscale and the same size as the composite image.  If the",
361    "image is not grayscale, it is converted to grayscale and the",
362    "resulting intensities are used as matte information.",
363    "",
364    "A small window appears showing the location of the cursor in",
365    "the image window. You are now in composite mode.  To exit",
366    "immediately, press Dismiss.  In composite mode, the Command",
367    "widget has these options:",
368    "",
369    "    Operators",
370    "      Over",
371    "      In",
372    "      Out",
373    "      Atop",
374    "      Xor",
375    "      Plus",
376    "      Minus",
377    "      Add",
378    "      Subtract",
379    "      Difference",
380    "      Multiply",
381    "      Bumpmap",
382    "      Copy",
383    "      CopyRed",
384    "      CopyGreen",
385    "      CopyBlue",
386    "      CopyOpacity",
387    "      Clear",
388    "    Dissolve",
389    "    Displace",
390    "    Help",
391    "    Dismiss",
392    "",
393    "Choose a composite operation from the Operators sub-menu of",
394    "the Command widget.  How each operator behaves is described",
395    "below.  Image window is the image currently displayed on",
396    "your X server and image is the image obtained with the File",
397    "Browser widget.",
398    "",
399    "Over     The result is the union of the two image shapes,",
400    "         with image obscuring image window in the region of",
401    "         overlap.",
402    "",
403    "In       The result is simply image cut by the shape of",
404    "         image window.  None of the image data of image",
405    "         window is in the result.",
406    "",
407    "Out      The resulting image is image with the shape of",
408    "         image window cut out.",
409    "",
410    "Atop     The result is the same shape as image image window,",
411    "         with image obscuring image window where the image",
412    "         shapes overlap.  Note this differs from over",
413    "         because the portion of image outside image window's",
414    "         shape does not appear in the result.",
415    "",
416    "Xor      The result is the image data from both image and",
417    "         image window that is outside the overlap region.",
418    "         The overlap region is blank.",
419    "",
420    "Plus     The result is just the sum of the image data.",
421    "         Output values are cropped to QuantumRange (no overflow).",
422    "",
423    "Minus    The result of image - image window, with underflow",
424    "         cropped to zero.",
425    "",
426    "Add      The result of image + image window, with overflow",
427    "         wrapping around (mod 256).",
428    "",
429    "Subtract The result of image - image window, with underflow",
430    "         wrapping around (mod 256).  The add and subtract",
431    "         operators can be used to perform reversible",
432    "         transformations.",
433    "",
434    "Difference",
435    "         The result of abs(image - image window).  This",
436    "         useful for comparing two very similar images.",
437    "",
438    "Multiply",
439    "         The result of image * image window.  This",
440    "         useful for the creation of drop-shadows.",
441    "",
442    "Bumpmap  The result of surface normals from image * image",
443    "         window.",
444    "",
445    "Copy     The resulting image is image window replaced with",
446    "         image.  Here the matte information is ignored.",
447    "",
448    "CopyRed  The red layer of the image window is replace with",
449    "         the red layer of the image.  The other layers are",
450    "         untouched.",
451    "",
452    "CopyGreen",
453    "         The green layer of the image window is replace with",
454    "         the green layer of the image.  The other layers are",
455    "         untouched.",
456    "",
457    "CopyBlue The blue layer of the image window is replace with",
458    "         the blue layer of the image.  The other layers are",
459    "         untouched.",
460    "",
461    "CopyOpacity",
462    "         The matte layer of the image window is replace with",
463    "         the matte layer of the image.  The other layers are",
464    "         untouched.",
465    "",
466    "The image compositor requires a matte, or alpha channel in",
467    "the image for some operations.  This extra channel usually",
468    "defines a mask which represents a sort of a cookie-cutter",
469    "for the image.  This the case when matte is opaque (full",
470    "coverage) for pixels inside the shape, zero outside, and",
471    "between 0 and QuantumRange on the boundary.  If image does not",
472    "have a matte channel, it is initialized with 0 for any pixel",
473    "matching in color to pixel location (0,0), otherwise QuantumRange.",
474    "",
475    "If you choose Dissolve, the composite operator becomes Over.  The",
476    "image matte channel percent transparency is initialized to factor.",
477    "The image window is initialized to (100-factor). Where factor is the",
478    "value you specify in the Dialog widget.",
479    "",
480    "Displace shifts the image pixels as defined by a displacement",
481    "map.  With this option, image is used as a displacement map.",
482    "Black, within the displacement map, is a maximum positive",
483    "displacement.  White is a maximum negative displacement and",
484    "middle gray is neutral.  The displacement is scaled to determine",
485    "the pixel shift.  By default, the displacement applies in both the",
486    "horizontal and vertical directions.  However, if you specify a mask,",
487    "image is the horizontal X displacement and mask the vertical Y",
488    "displacement.",
489    "",
490    "Note that matte information for image window is not retained",
491    "for colormapped X server visuals (e.g. StaticColor,",
492    "StaticColor, GrayScale, PseudoColor).  Correct compositing",
493    "behavior may require a TrueColor or DirectColor visual or a",
494    "Standard Colormap.",
495    "",
496    "Choosing a composite operator is optional.  The default",
497    "operator is replace.  However, you must choose a location to",
498    "composite your image and press button 1.  Press and hold the",
499    "button before releasing and an outline of the image will",
500    "appear to help you identify your location.",
501    "",
502    "The actual colors of the composite image is saved.  However,",
503    "the color that appears in image window may be different.",
504    "For example, on a monochrome screen image window will appear",
505    "black or white even though your composited image may have",
506    "many colors.  If the image is saved to a file it is written",
507    "with the correct colors.  To assure the correct colors are",
508    "saved in the final image, any PseudoClass image is promoted",
509    "to DirectClass (see miff(5)).  To force a PseudoClass image",
510    "to remain PseudoClass, use -colors.",
511    (char *) NULL,
512  },
513  *ImageCutHelp[] =
514  {
515    "In cut mode, the Command widget has these options:",
516    "",
517    "    Help",
518    "    Dismiss",
519    "",
520    "To define a cut region, press button 1 and drag.  The",
521    "cut region is defined by a highlighted rectangle that",
522    "expands or contracts as it follows the pointer.  Once you",
523    "are satisfied with the cut region, release the button.",
524    "You are now in rectify mode.  In rectify mode, the Command",
525    "widget has these options:",
526    "",
527    "    Cut",
528    "    Help",
529    "    Dismiss",
530    "",
531    "You can make adjustments by moving the pointer to one of the",
532    "cut rectangle corners, pressing a button, and dragging.",
533    "Finally, press Cut to commit your copy region.  To",
534    "exit without cutting the image, press Dismiss.",
535    (char *) NULL,
536  },
537  *ImageCopyHelp[] =
538  {
539    "In copy mode, the Command widget has these options:",
540    "",
541    "    Help",
542    "    Dismiss",
543    "",
544    "To define a copy region, press button 1 and drag.  The",
545    "copy region is defined by a highlighted rectangle that",
546    "expands or contracts as it follows the pointer.  Once you",
547    "are satisfied with the copy region, release the button.",
548    "You are now in rectify mode.  In rectify mode, the Command",
549    "widget has these options:",
550    "",
551    "    Copy",
552    "    Help",
553    "    Dismiss",
554    "",
555    "You can make adjustments by moving the pointer to one of the",
556    "copy rectangle corners, pressing a button, and dragging.",
557    "Finally, press Copy to commit your copy region.  To",
558    "exit without copying the image, press Dismiss.",
559    (char *) NULL,
560  },
561  *ImageCropHelp[] =
562  {
563    "In crop mode, the Command widget has these options:",
564    "",
565    "    Help",
566    "    Dismiss",
567    "",
568    "To define a cropping region, press button 1 and drag.  The",
569    "cropping region is defined by a highlighted rectangle that",
570    "expands or contracts as it follows the pointer.  Once you",
571    "are satisfied with the cropping region, release the button.",
572    "You are now in rectify mode.  In rectify mode, the Command",
573    "widget has these options:",
574    "",
575    "    Crop",
576    "    Help",
577    "    Dismiss",
578    "",
579    "You can make adjustments by moving the pointer to one of the",
580    "cropping rectangle corners, pressing a button, and dragging.",
581    "Finally, press Crop to commit your cropping region.  To",
582    "exit without cropping the image, press Dismiss.",
583    (char *) NULL,
584  },
585  *ImageDrawHelp[] =
586  {
587    "The cursor changes to a crosshair to indicate you are in",
588    "draw mode.  To exit immediately, press Dismiss.  In draw mode,",
589    "the Command widget has these options:",
590    "",
591    "    Element",
592    "      point",
593    "      line",
594    "      rectangle",
595    "      fill rectangle",
596    "      circle",
597    "      fill circle",
598    "      ellipse",
599    "      fill ellipse",
600    "      polygon",
601    "      fill polygon",
602    "    Color",
603    "      black",
604    "      blue",
605    "      cyan",
606    "      green",
607    "      gray",
608    "      red",
609    "      magenta",
610    "      yellow",
611    "      white",
612    "      transparent",
613    "      Browser...",
614    "    Stipple",
615    "      Brick",
616    "      Diagonal",
617    "      Scales",
618    "      Vertical",
619    "      Wavy",
620    "      Translucent",
621    "      Opaque",
622    "      Open...",
623    "    Width",
624    "      1",
625    "      2",
626    "      4",
627    "      8",
628    "      16",
629    "      Dialog...",
630    "    Undo",
631    "    Help",
632    "    Dismiss",
633    "",
634    "Choose a drawing primitive from the Element sub-menu.",
635    "",
636    "Choose a color from the Color sub-menu.  Additional",
637    "colors can be specified with the color browser.",
638    "",
639    "If you choose the color browser and press Grab, you can",
640    "select the color by moving the pointer to the desired",
641    "color on the screen and press any button.  The transparent",
642    "color updates the image matte channel and is useful for",
643    "image compositing.",
644    "",
645    "Choose a stipple, if appropriate, from the Stipple sub-menu.",
646    "Additional stipples can be specified with the file browser.",
647    "Stipples obtained from the file browser must be on disk in the",
648    "X11 bitmap format.",
649    "",
650    "Choose a width, if appropriate, from the Width sub-menu.  To",
651    "choose a specific width select the Dialog widget.",
652    "",
653    "Choose a point in the Image window and press button 1 and",
654    "hold.  Next, move the pointer to another location in the",
655    "image.  As you move, a line connects the initial location and",
656    "the pointer.  When you release the button, the image is",
657    "updated with the primitive you just drew.  For polygons, the",
658    "image is updated when you press and release the button without",
659    "moving the pointer.",
660    "",
661    "To cancel image drawing, move the pointer back to the",
662    "starting point of the line and release the button.",
663    (char *) NULL,
664  },
665  *DisplayHelp[] =
666  {
667    "BUTTONS",
668    "  The effects of each button press is described below.  Three",
669    "  buttons are required.  If you have a two button mouse,",
670    "  button 1 and 3 are returned.  Press ALT and button 3 to",
671    "  simulate button 2.",
672    "",
673    "  1    Press this button to map or unmap the Command widget.",
674    "",
675    "  2    Press and drag to define a region of the image to",
676    "       magnify.",
677    "",
678    "  3    Press and drag to choose from a select set of commands.",
679    "       This button behaves differently if the image being",
680    "       displayed is a visual image directory.  Here, choose a",
681    "       particular tile of the directory and press this button and",
682    "       drag to select a command from a pop-up menu.  Choose from",
683    "       these menu items:",
684    "",
685    "           Open",
686    "           Next",
687    "           Former",
688    "           Delete",
689    "           Update",
690    "",
691    "       If you choose Open, the image represented by the tile is",
692    "       displayed.  To return to the visual image directory, choose",
693    "       Next from the Command widget.  Next and Former moves to the",
694    "       next or former image respectively.  Choose Delete to delete",
695    "       a particular image tile.  Finally, choose Update to",
696    "       synchronize all the image tiles with their respective",
697    "       images.",
698    "",
699    "COMMAND WIDGET",
700    "  The Command widget lists a number of sub-menus and commands.",
701    "  They are",
702    "",
703    "      File",
704    "        Open...",
705    "        Next",
706    "        Former",
707    "        Select...",
708    "        Save...",
709    "        Print...",
710    "        Delete...",
711    "        New...",
712    "        Visual Directory...",
713    "        Quit",
714    "      Edit",
715    "        Undo",
716    "        Redo",
717    "        Cut",
718    "        Copy",
719    "        Paste",
720    "      View",
721    "        Half Size",
722    "        Original Size",
723    "        Double Size",
724    "        Resize...",
725    "        Apply",
726    "        Refresh",
727    "        Restore",
728    "      Transform",
729    "        Crop",
730    "        Chop",
731    "        Flop",
732    "        Flip",
733    "        Rotate Right",
734    "        Rotate Left",
735    "        Rotate...",
736    "        Shear...",
737    "        Roll...",
738    "        Trim Edges",
739    "      Enhance",
740    "        Brightness...",
741    "        Saturation...",
742    "        Hue...",
743    "        Gamma...",
744    "        Sharpen...",
745    "        Dull",
746    "        Contrast Stretch...",
747    "        Sigmoidal Contrast...",
748    "        Normalize",
749    "        Equalize",
750    "        Negate",
751    "        Grayscale",
752    "        Map...",
753    "        Quantize...",
754    "      Effects",
755    "        Despeckle",
756    "        Emboss",
757    "        Reduce Noise",
758    "        Add Noise",
759    "        Sharpen...",
760    "        Blur...",
761    "        Threshold...",
762    "        Edge Detect...",
763    "        Spread...",
764    "        Shade...",
765    "        Painting...",
766    "        Segment...",
767    "      F/X",
768    "        Solarize...",
769    "        Sepia Tone...",
770    "        Swirl...",
771    "        Implode...",
772    "        Vignette...",
773    "        Wave...",
774    "        Oil Painting...",
775    "        Charcoal Drawing...",
776    "      Image Edit",
777    "        Annotate...",
778    "        Draw...",
779    "        Color...",
780    "        Matte...",
781    "        Composite...",
782    "        Add Border...",
783    "        Add Frame...",
784    "        Comment...",
785    "        Launch...",
786    "        Region of Interest...",
787    "      Miscellany",
788    "        Image Info",
789    "        Zoom Image",
790    "        Show Preview...",
791    "        Show Histogram",
792    "        Show Matte",
793    "        Background...",
794    "        Slide Show",
795    "        Preferences...",
796    "      Help",
797    "        Overview",
798    "        Browse Documentation",
799    "        About Display",
800    "",
801    "  Menu items with a indented triangle have a sub-menu.  They",
802    "  are represented above as the indented items.  To access a",
803    "  sub-menu item, move the pointer to the appropriate menu and",
804    "  press a button and drag.  When you find the desired sub-menu",
805    "  item, release the button and the command is executed.  Move",
806    "  the pointer away from the sub-menu if you decide not to",
807    "  execute a particular command.",
808    "",
809    "KEYBOARD ACCELERATORS",
810    "  Accelerators are one or two key presses that effect a",
811    "  particular command.  The keyboard accelerators that",
812    "  display(1) understands is:",
813    "",
814    "  Ctl+O     Press to open an image from a file.",
815    "",
816    "  space     Press to display the next image.",
817    "",
818    "            If the image is a multi-paged document such as a Postscript",
819    "            document, you can skip ahead several pages by preceding",
820    "            this command with a number.  For example to display the",
821    "            third page beyond the current page, press 3<space>.",
822    "",
823    "  backspace Press to display the former image.",
824    "",
825    "            If the image is a multi-paged document such as a Postscript",
826    "            document, you can skip behind several pages by preceding",
827    "            this command with a number.  For example to display the",
828    "            third page preceding the current page, press 3<backspace>.",
829    "",
830    "  Ctl+S     Press to write the image to a file.",
831    "",
832    "  Ctl+P     Press to print the image to a Postscript printer.",
833    "",
834    "  Ctl+D     Press to delete an image file.",
835    "",
836    "  Ctl+N     Press to create a blank canvas.",
837    "",
838    "  Ctl+Q     Press to discard all images and exit program.",
839    "",
840    "  Ctl+Z     Press to undo last image transformation.",
841    "",
842    "  Ctl+R     Press to redo last image transformation.",
843    "",
844    "  Ctl+X     Press to cut a region of the image.",
845    "",
846    "  Ctl+C     Press to copy a region of the image.",
847    "",
848    "  Ctl+V     Press to paste a region to the image.",
849    "",
850    "  <         Press to half the image size.",
851    "",
852    "  -         Press to return to the original image size.",
853    "",
854    "  >         Press to double the image size.",
855    "",
856    "  %         Press to resize the image to a width and height you",
857    "            specify.",
858    "",
859    "Cmd-A       Press to make any image transformations permanent."
860    "",
861    "            By default, any image size transformations are applied",
862    "            to the original image to create the image displayed on",
863    "            the X server.  However, the transformations are not",
864    "            permanent (i.e. the original image does not change",
865    "            size only the X image does).  For example, if you",
866    "            press > the X image will appear to double in size,",
867    "            but the original image will in fact remain the same size.",
868    "            To force the original image to double in size, press >",
869    "            followed by Cmd-A.",
870    "",
871    "  @         Press to refresh the image window.",
872    "",
873    "  C         Press to cut out a rectangular region of the image.",
874    "",
875    "  [         Press to chop the image.",
876    "",
877    "  H         Press to flop image in the horizontal direction.",
878    "",
879    "  V         Press to flip image in the vertical direction.",
880    "",
881    "  /         Press to rotate the image 90 degrees clockwise.",
882    "",
883    " \\         Press to rotate the image 90 degrees counter-clockwise.",
884    "",
885    "  *         Press to rotate the image the number of degrees you",
886    "            specify.",
887    "",
888    "  S         Press to shear the image the number of degrees you",
889    "            specify.",
890    "",
891    "  R         Press to roll the image.",
892    "",
893    "  T         Press to trim the image edges.",
894    "",
895    "  Shft-H    Press to vary the image hue.",
896    "",
897    "  Shft-S    Press to vary the color saturation.",
898    "",
899    "  Shft-L    Press to vary the color brightness.",
900    "",
901    "  Shft-G    Press to gamma correct the image.",
902    "",
903    "  Shft-C    Press to sharpen the image contrast.",
904    "",
905    "  Shft-Z    Press to dull the image contrast.",
906    "",
907    "  =         Press to perform histogram equalization on the image.",
908    "",
909    "  Shft-N    Press to perform histogram normalization on the image.",
910    "",
911    "  Shft-~    Press to negate the colors of the image.",
912    "",
913    "  .         Press to convert the image colors to gray.",
914    "",
915    "  Shft-#    Press to set the maximum number of unique colors in the",
916    "            image.",
917    "",
918    "  F2        Press to reduce the speckles in an image.",
919    "",
920    "  F3        Press to eliminate peak noise from an image.",
921    "",
922    "  F4        Press to add noise to an image.",
923    "",
924    "  F5        Press to sharpen an image.",
925    "",
926    "  F6        Press to delete an image file.",
927    "",
928    "  F7        Press to threshold the image.",
929    "",
930    "  F8        Press to detect edges within an image.",
931    "",
932    "  F9        Press to emboss an image.",
933    "",
934    "  F10       Press to displace pixels by a random amount.",
935    "",
936    "  F11       Press to negate all pixels above the threshold level.",
937    "",
938    "  F12       Press to shade the image using a distant light source.",
939    "",
940    "  F13       Press to lighten or darken image edges to create a 3-D effect.",
941    "",
942    "  F14       Press to segment the image by color.",
943    "",
944    "  Meta-S    Press to swirl image pixels about the center.",
945    "",
946    "  Meta-I    Press to implode image pixels about the center.",
947    "",
948    "  Meta-W    Press to alter an image along a sine wave.",
949    "",
950    "  Meta-P    Press to simulate an oil painting.",
951    "",
952    "  Meta-C    Press to simulate a charcoal drawing.",
953    "",
954    "  Alt-A     Press to annotate the image with text.",
955    "",
956    "  Alt-D     Press to draw on an image.",
957    "",
958    "  Alt-P     Press to edit an image pixel color.",
959    "",
960    "  Alt-M     Press to edit the image matte information.",
961    "",
962    "  Alt-V     Press to composite the image with another.",
963    "",
964    "  Alt-B     Press to add a border to the image.",
965    "",
966    "  Alt-F     Press to add an ornamental border to the image.",
967    "",
968    "  Alt-Shft-!",
969    "            Press to add an image comment.",
970    "",
971    "  Ctl-A     Press to apply image processing techniques to a region",
972    "            of interest.",
973    "",
974    "  Shft-?    Press to display information about the image.",
975    "",
976    "  Shft-+    Press to map the zoom image window.",
977    "",
978    "  Shft-P    Press to preview an image enhancement, effect, or f/x.",
979    "",
980    "  F1        Press to display helpful information about display(1).",
981    "",
982    "  Find      Press to browse documentation about ImageMagick.",
983    "",
984    "  1-9       Press to change the level of magnification.",
985    "",
986    "  Use the arrow keys to move the image one pixel up, down,",
987    "  left, or right within the magnify window.  Be sure to first",
988    "  map the magnify window by pressing button 2.",
989    "",
990    "  Press ALT and one of the arrow keys to trim off one pixel",
991    "  from any side of the image.",
992    (char *) NULL,
993  },
994  *ImageMatteEditHelp[] =
995  {
996    "Matte information within an image is useful for some",
997    "operations such as image compositing (See IMAGE",
998    "COMPOSITING).  This extra channel usually defines a mask",
999    "which represents a sort of a cookie-cutter for the image.",
1000    "This the case when matte is opaque (full coverage) for",
1001    "pixels inside the shape, zero outside, and between 0 and",
1002    "QuantumRange on the boundary.",
1003    "",
1004    "A small window appears showing the location of the cursor in",
1005    "the image window. You are now in matte edit mode.  To exit",
1006    "immediately, press Dismiss.  In matte edit mode, the Command",
1007    "widget has these options:",
1008    "",
1009    "    Method",
1010    "      point",
1011    "      replace",
1012    "      floodfill",
1013    "      filltoborder",
1014    "      reset",
1015    "    Border Color",
1016    "      black",
1017    "      blue",
1018    "      cyan",
1019    "      green",
1020    "      gray",
1021    "      red",
1022    "      magenta",
1023    "      yellow",
1024    "      white",
1025    "      Browser...",
1026    "    Fuzz",
1027    "      0%",
1028    "      2%",
1029    "      5%",
1030    "      10%",
1031    "      15%",
1032    "      Dialog...",
1033    "    Matte",
1034    "      Opaque",
1035    "      Transparent",
1036    "      Dialog...",
1037    "    Undo",
1038    "    Help",
1039    "    Dismiss",
1040    "",
1041    "Choose a matte editing method from the Method sub-menu of",
1042    "the Command widget.  The point method changes the matte value",
1043    "of any pixel selected with the pointer until the button is",
1044    "is released.  The replace method changes the matte value of",
1045    "any pixel that matches the color of the pixel you select with",
1046    "a button press.  Floodfill changes the matte value of any pixel",
1047    "that matches the color of the pixel you select with a button",
1048    "press and is a neighbor.  Whereas filltoborder changes the matte",
1049    "value any neighbor pixel that is not the border color.  Finally",
1050    "reset changes the entire image to the designated matte value.",
1051    "",
1052    "Choose Matte Value and pick Opaque or Transarent.  For other values",
1053    "select the Dialog entry.  Here a dialog appears requesting a matte",
1054    "value.  The value you select is assigned as the opacity value of the",
1055    "selected pixel or pixels.",
1056    "",
1057    "Now, press any button to select a pixel within the image",
1058    "window to change its matte value.",
1059    "",
1060    "If the Magnify widget is mapped, it can be helpful in positioning",
1061    "your pointer within the image (refer to button 2).",
1062    "",
1063    "Matte information is only valid in a DirectClass image.",
1064    "Therefore, any PseudoClass image is promoted to DirectClass",
1065    "(see miff(5)).  Note that matte information for PseudoClass",
1066    "is not retained for colormapped X server visuals (e.g.",
1067    "StaticColor, StaticColor, GrayScale, PseudoColor) unless you",
1068    "immediately save your image to a file (refer to Write).",
1069    "Correct matte editing behavior may require a TrueColor or",
1070    "DirectColor visual or a Standard Colormap.",
1071    (char *) NULL,
1072  },
1073  *ImagePanHelp[] =
1074  {
1075    "When an image exceeds the width or height of the X server",
1076    "screen, display maps a small panning icon.  The rectangle",
1077    "within the panning icon shows the area that is currently",
1078    "displayed in the image window.  To pan about the image,",
1079    "press any button and drag the pointer within the panning",
1080    "icon.  The pan rectangle moves with the pointer and the",
1081    "image window is updated to reflect the location of the",
1082    "rectangle within the panning icon.  When you have selected",
1083    "the area of the image you wish to view, release the button.",
1084    "",
1085    "Use the arrow keys to pan the image one pixel up, down,",
1086    "left, or right within the image window.",
1087    "",
1088    "The panning icon is withdrawn if the image becomes smaller",
1089    "than the dimensions of the X server screen.",
1090    (char *) NULL,
1091  },
1092  *ImagePasteHelp[] =
1093  {
1094    "A small window appears showing the location of the cursor in",
1095    "the image window. You are now in paste mode.  To exit",
1096    "immediately, press Dismiss.  In paste mode, the Command",
1097    "widget has these options:",
1098    "",
1099    "    Operators",
1100    "      over",
1101    "      in",
1102    "      out",
1103    "      atop",
1104    "      xor",
1105    "      plus",
1106    "      minus",
1107    "      add",
1108    "      subtract",
1109    "      difference",
1110    "      replace",
1111    "    Help",
1112    "    Dismiss",
1113    "",
1114    "Choose a composite operation from the Operators sub-menu of",
1115    "the Command widget.  How each operator behaves is described",
1116    "below.  Image window is the image currently displayed on",
1117    "your X server and image is the image obtained with the File",
1118    "Browser widget.",
1119    "",
1120    "Over     The result is the union of the two image shapes,",
1121    "         with image obscuring image window in the region of",
1122    "         overlap.",
1123    "",
1124    "In       The result is simply image cut by the shape of",
1125    "         image window.  None of the image data of image",
1126    "         window is in the result.",
1127    "",
1128    "Out      The resulting image is image with the shape of",
1129    "         image window cut out.",
1130    "",
1131    "Atop     The result is the same shape as image image window,",
1132    "         with image obscuring image window where the image",
1133    "         shapes overlap.  Note this differs from over",
1134    "         because the portion of image outside image window's",
1135    "         shape does not appear in the result.",
1136    "",
1137    "Xor      The result is the image data from both image and",
1138    "         image window that is outside the overlap region.",
1139    "         The overlap region is blank.",
1140    "",
1141    "Plus     The result is just the sum of the image data.",
1142    "         Output values are cropped to QuantumRange (no overflow).",
1143    "         This operation is independent of the matte",
1144    "         channels.",
1145    "",
1146    "Minus    The result of image - image window, with underflow",
1147    "         cropped to zero.",
1148    "",
1149    "Add      The result of image + image window, with overflow",
1150    "         wrapping around (mod 256).",
1151    "",
1152    "Subtract The result of image - image window, with underflow",
1153    "         wrapping around (mod 256).  The add and subtract",
1154    "         operators can be used to perform reversible",
1155    "         transformations.",
1156    "",
1157    "Difference",
1158    "         The result of abs(image - image window).  This",
1159    "         useful for comparing two very similar images.",
1160    "",
1161    "Copy     The resulting image is image window replaced with",
1162    "         image.  Here the matte information is ignored.",
1163    "",
1164    "CopyRed  The red layer of the image window is replace with",
1165    "         the red layer of the image.  The other layers are",
1166    "         untouched.",
1167    "",
1168    "CopyGreen",
1169    "         The green layer of the image window is replace with",
1170    "         the green layer of the image.  The other layers are",
1171    "         untouched.",
1172    "",
1173    "CopyBlue The blue layer of the image window is replace with",
1174    "         the blue layer of the image.  The other layers are",
1175    "         untouched.",
1176    "",
1177    "CopyOpacity",
1178    "         The matte layer of the image window is replace with",
1179    "         the matte layer of the image.  The other layers are",
1180    "         untouched.",
1181    "",
1182    "The image compositor requires a matte, or alpha channel in",
1183    "the image for some operations.  This extra channel usually",
1184    "defines a mask which represents a sort of a cookie-cutter",
1185    "for the image.  This the case when matte is opaque (full",
1186    "coverage) for pixels inside the shape, zero outside, and",
1187    "between 0 and QuantumRange on the boundary.  If image does not",
1188    "have a matte channel, it is initialized with 0 for any pixel",
1189    "matching in color to pixel location (0,0), otherwise QuantumRange.",
1190    "",
1191    "Note that matte information for image window is not retained",
1192    "for colormapped X server visuals (e.g. StaticColor,",
1193    "StaticColor, GrayScale, PseudoColor).  Correct compositing",
1194    "behavior may require a TrueColor or DirectColor visual or a",
1195    "Standard Colormap.",
1196    "",
1197    "Choosing a composite operator is optional.  The default",
1198    "operator is replace.  However, you must choose a location to",
1199    "paste your image and press button 1.  Press and hold the",
1200    "button before releasing and an outline of the image will",
1201    "appear to help you identify your location.",
1202    "",
1203    "The actual colors of the pasted image is saved.  However,",
1204    "the color that appears in image window may be different.",
1205    "For example, on a monochrome screen image window will appear",
1206    "black or white even though your pasted image may have",
1207    "many colors.  If the image is saved to a file it is written",
1208    "with the correct colors.  To assure the correct colors are",
1209    "saved in the final image, any PseudoClass image is promoted",
1210    "to DirectClass (see miff(5)).  To force a PseudoClass image",
1211    "to remain PseudoClass, use -colors.",
1212    (char *) NULL,
1213  },
1214  *ImageROIHelp[] =
1215  {
1216    "In region of interest mode, the Command widget has these",
1217    "options:",
1218    "",
1219    "    Help",
1220    "    Dismiss",
1221    "",
1222    "To define a region of interest, press button 1 and drag.",
1223    "The region of interest is defined by a highlighted rectangle",
1224    "that expands or contracts as it follows the pointer.  Once",
1225    "you are satisfied with the region of interest, release the",
1226    "button.  You are now in apply mode.  In apply mode the",
1227    "Command widget has these options:",
1228    "",
1229    "      File",
1230    "        Save...",
1231    "        Print...",
1232    "      Edit",
1233    "        Undo",
1234    "        Redo",
1235    "      Transform",
1236    "        Flop",
1237    "        Flip",
1238    "        Rotate Right",
1239    "        Rotate Left",
1240    "      Enhance",
1241    "        Hue...",
1242    "        Saturation...",
1243    "        Brightness...",
1244    "        Gamma...",
1245    "        Spiff",
1246    "        Dull",
1247    "        Contrast Stretch",
1248    "        Sigmoidal Contrast...",
1249    "        Normalize",
1250    "        Equalize",
1251    "        Negate",
1252    "        Grayscale",
1253    "        Map...",
1254    "        Quantize...",
1255    "      Effects",
1256    "        Despeckle",
1257    "        Emboss",
1258    "        Reduce Noise",
1259    "        Sharpen...",
1260    "        Blur...",
1261    "        Threshold...",
1262    "        Edge Detect...",
1263    "        Spread...",
1264    "        Shade...",
1265    "        Raise...",
1266    "        Segment...",
1267    "      F/X",
1268    "        Solarize...",
1269    "        Sepia Tone...",
1270    "        Swirl...",
1271    "        Implode...",
1272    "        Vignette...",
1273    "        Wave...",
1274    "        Oil Painting...",
1275    "        Charcoal Drawing...",
1276    "      Miscellany",
1277    "        Image Info",
1278    "        Zoom Image",
1279    "        Show Preview...",
1280    "        Show Histogram",
1281    "        Show Matte",
1282    "      Help",
1283    "      Dismiss",
1284    "",
1285    "You can make adjustments to the region of interest by moving",
1286    "the pointer to one of the rectangle corners, pressing a",
1287    "button, and dragging.  Finally, choose an image processing",
1288    "technique from the Command widget.  You can choose more than",
1289    "one image processing technique to apply to an area.",
1290    "Alternatively, you can move the region of interest before",
1291    "applying another image processing technique.  To exit, press",
1292    "Dismiss.",
1293    (char *) NULL,
1294  },
1295  *ImageRotateHelp[] =
1296  {
1297    "In rotate mode, the Command widget has these options:",
1298    "",
1299    "    Pixel Color",
1300    "      black",
1301    "      blue",
1302    "      cyan",
1303    "      green",
1304    "      gray",
1305    "      red",
1306    "      magenta",
1307    "      yellow",
1308    "      white",
1309    "      Browser...",
1310    "    Direction",
1311    "      horizontal",
1312    "      vertical",
1313    "    Help",
1314    "    Dismiss",
1315    "",
1316    "Choose a background color from the Pixel Color sub-menu.",
1317    "Additional background colors can be specified with the color",
1318    "browser.  You can change the menu colors by setting the X",
1319    "resources pen1 through pen9.",
1320    "",
1321    "If you choose the color browser and press Grab, you can",
1322    "select the background color by moving the pointer to the",
1323    "desired color on the screen and press any button.",
1324    "",
1325    "Choose a point in the image window and press this button and",
1326    "hold.  Next, move the pointer to another location in the",
1327    "image.  As you move a line connects the initial location and",
1328    "the pointer.  When you release the button, the degree of",
1329    "image rotation is determined by the slope of the line you",
1330    "just drew.  The slope is relative to the direction you",
1331    "choose from the Direction sub-menu of the Command widget.",
1332    "",
1333    "To cancel the image rotation, move the pointer back to the",
1334    "starting point of the line and release the button.",
1335    (char *) NULL,
1336  };
1337
1338/*
1339  Enumeration declarations.
1340*/
1341typedef enum
1342{
1343  CopyMode,
1344  CropMode,
1345  CutMode
1346} ClipboardMode;
1347
1348typedef enum
1349{
1350  OpenCommand,
1351  NextCommand,
1352  FormerCommand,
1353  SelectCommand,
1354  SaveCommand,
1355  PrintCommand,
1356  DeleteCommand,
1357  NewCommand,
1358  VisualDirectoryCommand,
1359  QuitCommand,
1360  UndoCommand,
1361  RedoCommand,
1362  CutCommand,
1363  CopyCommand,
1364  PasteCommand,
1365  HalfSizeCommand,
1366  OriginalSizeCommand,
1367  DoubleSizeCommand,
1368  ResizeCommand,
1369  ApplyCommand,
1370  RefreshCommand,
1371  RestoreCommand,
1372  CropCommand,
1373  ChopCommand,
1374  FlopCommand,
1375  FlipCommand,
1376  RotateRightCommand,
1377  RotateLeftCommand,
1378  RotateCommand,
1379  ShearCommand,
1380  RollCommand,
1381  TrimCommand,
1382  HueCommand,
1383  SaturationCommand,
1384  BrightnessCommand,
1385  GammaCommand,
1386  SpiffCommand,
1387  DullCommand,
1388  ContrastStretchCommand,
1389  SigmoidalContrastCommand,
1390  NormalizeCommand,
1391  EqualizeCommand,
1392  NegateCommand,
1393  GrayscaleCommand,
1394  MapCommand,
1395  QuantizeCommand,
1396  DespeckleCommand,
1397  EmbossCommand,
1398  ReduceNoiseCommand,
1399  AddNoiseCommand,
1400  SharpenCommand,
1401  BlurCommand,
1402  ThresholdCommand,
1403  EdgeDetectCommand,
1404  SpreadCommand,
1405  ShadeCommand,
1406  RaiseCommand,
1407  SegmentCommand,
1408  SolarizeCommand,
1409  SepiaToneCommand,
1410  SwirlCommand,
1411  ImplodeCommand,
1412  VignetteCommand,
1413  WaveCommand,
1414  OilPaintCommand,
1415  CharcoalDrawCommand,
1416  AnnotateCommand,
1417  DrawCommand,
1418  ColorCommand,
1419  MatteCommand,
1420  CompositeCommand,
1421  AddBorderCommand,
1422  AddFrameCommand,
1423  CommentCommand,
1424  LaunchCommand,
1425  RegionofInterestCommand,
1426  ROIHelpCommand,
1427  ROIDismissCommand,
1428  InfoCommand,
1429  ZoomCommand,
1430  ShowPreviewCommand,
1431  ShowHistogramCommand,
1432  ShowMatteCommand,
1433  BackgroundCommand,
1434  SlideShowCommand,
1435  PreferencesCommand,
1436  HelpCommand,
1437  BrowseDocumentationCommand,
1438  VersionCommand,
1439  SaveToUndoBufferCommand,
1440  FreeBuffersCommand,
1441  NullCommand
1442} CommandType;
1443
1444typedef enum
1445{
1446  AnnotateNameCommand,
1447  AnnotateFontColorCommand,
1448  AnnotateBackgroundColorCommand,
1449  AnnotateRotateCommand,
1450  AnnotateHelpCommand,
1451  AnnotateDismissCommand,
1452  TextHelpCommand,
1453  TextApplyCommand,
1454  ChopDirectionCommand,
1455  ChopHelpCommand,
1456  ChopDismissCommand,
1457  HorizontalChopCommand,
1458  VerticalChopCommand,
1459  ColorEditMethodCommand,
1460  ColorEditColorCommand,
1461  ColorEditBorderCommand,
1462  ColorEditFuzzCommand,
1463  ColorEditUndoCommand,
1464  ColorEditHelpCommand,
1465  ColorEditDismissCommand,
1466  CompositeOperatorsCommand,
1467  CompositeDissolveCommand,
1468  CompositeDisplaceCommand,
1469  CompositeHelpCommand,
1470  CompositeDismissCommand,
1471  CropHelpCommand,
1472  CropDismissCommand,
1473  RectifyCopyCommand,
1474  RectifyHelpCommand,
1475  RectifyDismissCommand,
1476  DrawElementCommand,
1477  DrawColorCommand,
1478  DrawStippleCommand,
1479  DrawWidthCommand,
1480  DrawUndoCommand,
1481  DrawHelpCommand,
1482  DrawDismissCommand,
1483  MatteEditMethod,
1484  MatteEditBorderCommand,
1485  MatteEditFuzzCommand,
1486  MatteEditValueCommand,
1487  MatteEditUndoCommand,
1488  MatteEditHelpCommand,
1489  MatteEditDismissCommand,
1490  PasteOperatorsCommand,
1491  PasteHelpCommand,
1492  PasteDismissCommand,
1493  RotateColorCommand,
1494  RotateDirectionCommand,
1495  RotateCropCommand,
1496  RotateSharpenCommand,
1497  RotateHelpCommand,
1498  RotateDismissCommand,
1499  HorizontalRotateCommand,
1500  VerticalRotateCommand,
1501  TileLoadCommand,
1502  TileNextCommand,
1503  TileFormerCommand,
1504  TileDeleteCommand,
1505  TileUpdateCommand
1506} ModeType;
1507
1508/*
1509  Stipples.
1510*/
1511#define BricksWidth  20
1512#define BricksHeight  20
1513#define DiagonalWidth  16
1514#define DiagonalHeight  16
1515#define HighlightWidth  8
1516#define HighlightHeight  8
1517#define OpaqueWidth  8
1518#define OpaqueHeight  8
1519#define ScalesWidth  16
1520#define ScalesHeight  16
1521#define ShadowWidth  8
1522#define ShadowHeight  8
1523#define VerticalWidth  16
1524#define VerticalHeight  16
1525#define WavyWidth  16
1526#define WavyHeight  16
1527
1528/*
1529  Constant declaration.
1530*/
1531static const int
1532  RoiDelta = 8;
1533
1534static const unsigned char
1535  BricksBitmap[] =
1536  {
1537    0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1538    0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1539    0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1540    0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1541    0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1542  },
1543  DiagonalBitmap[] =
1544  {
1545    0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1546    0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1547    0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1548  },
1549  ScalesBitmap[] =
1550  {
1551    0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1552    0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1553    0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1554  },
1555  VerticalBitmap[] =
1556  {
1557    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1558    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1559    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1560  },
1561  WavyBitmap[] =
1562  {
1563    0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1564    0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1565    0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1566  };
1567
1568/*
1569  Function prototypes.
1570*/
1571static CommandType
1572  XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1573    const MagickStatusType,KeySym,Image **,ExceptionInfo *);
1574
1575static Image
1576  *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
1577    Image **,ExceptionInfo *),
1578  *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1579  *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *,
1580    ExceptionInfo *),
1581  *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *,
1582    ExceptionInfo *);
1583
1584static MagickBooleanType
1585  XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *,
1586    ExceptionInfo *),
1587  XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **,
1588    ExceptionInfo *),
1589  XChopImage(Display *,XResourceInfo *,XWindows *,Image **,
1590    ExceptionInfo *),
1591  XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode,
1592    ExceptionInfo *),
1593  XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1594    ExceptionInfo *),
1595  XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *,
1596    ExceptionInfo *),
1597  XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1598  XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1599    ExceptionInfo *),
1600  XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1601    ExceptionInfo *),
1602  XPasteImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1603  XPrintImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1604  XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **,
1605    ExceptionInfo *),
1606  XROIImage(Display *,XResourceInfo *,XWindows *,Image **,ExceptionInfo *),
1607  XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1608  XTrimImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
1609
1610static void
1611  XDrawPanRectangle(Display *,XWindows *),
1612  XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **,
1613    ExceptionInfo *),
1614  XMagnifyImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1615  XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1616  XPanImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1617  XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1618    const KeySym,ExceptionInfo *),
1619  XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1620  XScreenEvent(Display *,XWindows *,XEvent *,ExceptionInfo *),
1621  XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1622
1623/*
1624%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1625%                                                                             %
1626%                                                                             %
1627%                                                                             %
1628%   D i s p l a y I m a g e s                                                 %
1629%                                                                             %
1630%                                                                             %
1631%                                                                             %
1632%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1633%
1634%  DisplayImages() displays an image sequence to any X window screen.  It
1635%  returns a value other than 0 if successful.  Check the exception member
1636%  of image to determine the reason for any failure.
1637%
1638%  The format of the DisplayImages method is:
1639%
1640%      MagickBooleanType DisplayImages(const ImageInfo *image_info,
1641%        Image *images,ExceptionInfo *exception)
1642%
1643%  A description of each parameter follows:
1644%
1645%    o image_info: the image info.
1646%
1647%    o image: the image.
1648%
1649%    o exception: return any errors or warnings in this structure.
1650%
1651*/
1652MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1653  Image *images,ExceptionInfo *exception)
1654{
1655  char
1656    *argv[1];
1657
1658  Display
1659    *display;
1660
1661  Image
1662    *image;
1663
1664  register ssize_t
1665    i;
1666
1667  size_t
1668    state;
1669
1670  XrmDatabase
1671    resource_database;
1672
1673  XResourceInfo
1674    resource_info;
1675
1676  assert(image_info != (const ImageInfo *) NULL);
1677  assert(image_info->signature == MagickCoreSignature);
1678  assert(images != (Image *) NULL);
1679  assert(images->signature == MagickCoreSignature);
1680  if (images->debug != MagickFalse )
1681    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1682  display=XOpenDisplay(image_info->server_name);
1683  if (display == (Display *) NULL)
1684    {
1685      (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1686        "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1687      return(MagickFalse);
1688    }
1689  if (exception->severity != UndefinedException)
1690    CatchException(exception);
1691  (void) XSetErrorHandler(XError);
1692  resource_database=XGetResourceDatabase(display,GetClientName());
1693  (void) ResetMagickMemory(&resource_info,0,sizeof(resource_info));
1694  XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1695  if (image_info->page != (char *) NULL)
1696    resource_info.image_geometry=AcquireString(image_info->page);
1697  resource_info.immutable=MagickTrue;
1698  argv[0]=AcquireString(GetClientName());
1699  state=DefaultState;
1700  for (i=0; (state & ExitState) == 0; i++)
1701  {
1702    if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1703      break;
1704    image=GetImageFromList(images,i % GetImageListLength(images));
1705    (void) XDisplayImage(display,&resource_info,argv,1,&image,&state,exception);
1706  }
1707  (void) SetErrorHandler((ErrorHandler) NULL);
1708  (void) SetWarningHandler((WarningHandler) NULL);
1709  argv[0]=DestroyString(argv[0]);
1710  (void) XCloseDisplay(display);
1711  XDestroyResourceInfo(&resource_info);
1712  if (exception->severity != UndefinedException)
1713    return(MagickFalse);
1714  return(MagickTrue);
1715}
1716
1717/*
1718%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1719%                                                                             %
1720%                                                                             %
1721%                                                                             %
1722%   R e m o t e D i s p l a y C o m m a n d                                   %
1723%                                                                             %
1724%                                                                             %
1725%                                                                             %
1726%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1727%
1728%  RemoteDisplayCommand() encourages a remote display program to display the
1729%  specified image filename.
1730%
1731%  The format of the RemoteDisplayCommand method is:
1732%
1733%      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1734%        const char *window,const char *filename,ExceptionInfo *exception)
1735%
1736%  A description of each parameter follows:
1737%
1738%    o image_info: the image info.
1739%
1740%    o window: Specifies the name or id of an X window.
1741%
1742%    o filename: the name of the image filename to display.
1743%
1744%    o exception: return any errors or warnings in this structure.
1745%
1746*/
1747MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1748  const char *window,const char *filename,ExceptionInfo *exception)
1749{
1750  Display
1751    *display;
1752
1753  MagickStatusType
1754    status;
1755
1756  assert(image_info != (const ImageInfo *) NULL);
1757  assert(image_info->signature == MagickCoreSignature);
1758  assert(filename != (char *) NULL);
1759  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1760  display=XOpenDisplay(image_info->server_name);
1761  if (display == (Display *) NULL)
1762    {
1763      (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1764        "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1765      return(MagickFalse);
1766    }
1767  (void) XSetErrorHandler(XError);
1768  status=XRemoteCommand(display,window,filename);
1769  (void) XCloseDisplay(display);
1770  return(status != 0 ? MagickTrue : MagickFalse);
1771}
1772
1773/*
1774%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1775%                                                                             %
1776%                                                                             %
1777%                                                                             %
1778+   X A n n o t a t e E d i t I m a g e                                       %
1779%                                                                             %
1780%                                                                             %
1781%                                                                             %
1782%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1783%
1784%  XAnnotateEditImage() annotates the image with text.
1785%
1786%  The format of the XAnnotateEditImage method is:
1787%
1788%      MagickBooleanType XAnnotateEditImage(Display *display,
1789%        XResourceInfo *resource_info,XWindows *windows,Image *image,
1790%        ExceptionInfo *exception)
1791%
1792%  A description of each parameter follows:
1793%
1794%    o display: Specifies a connection to an X server;  returned from
1795%      XOpenDisplay.
1796%
1797%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1798%
1799%    o windows: Specifies a pointer to a XWindows structure.
1800%
1801%    o image: the image; returned from ReadImage.
1802%
1803*/
1804
1805static MagickBooleanType XAnnotateEditImage(Display *display,
1806  XResourceInfo *resource_info,XWindows *windows,Image *image,
1807  ExceptionInfo *exception)
1808{
1809  static const char
1810    *AnnotateMenu[] =
1811    {
1812      "Font Name",
1813      "Font Color",
1814      "Box Color",
1815      "Rotate Text",
1816      "Help",
1817      "Dismiss",
1818      (char *) NULL
1819    },
1820    *TextMenu[] =
1821    {
1822      "Help",
1823      "Apply",
1824      (char *) NULL
1825    };
1826
1827  static const ModeType
1828    AnnotateCommands[] =
1829    {
1830      AnnotateNameCommand,
1831      AnnotateFontColorCommand,
1832      AnnotateBackgroundColorCommand,
1833      AnnotateRotateCommand,
1834      AnnotateHelpCommand,
1835      AnnotateDismissCommand
1836    },
1837    TextCommands[] =
1838    {
1839      TextHelpCommand,
1840      TextApplyCommand
1841    };
1842
1843  static MagickBooleanType
1844    transparent_box = MagickTrue,
1845    transparent_pen = MagickFalse;
1846
1847  static double
1848    degrees = 0.0;
1849
1850  static unsigned int
1851    box_id = MaxNumberPens-2,
1852    font_id = 0,
1853    pen_id = 0;
1854
1855  char
1856    command[MagickPathExtent],
1857    text[MagickPathExtent];
1858
1859  const char
1860    *ColorMenu[MaxNumberPens+1];
1861
1862  Cursor
1863    cursor;
1864
1865  GC
1866    annotate_context;
1867
1868  int
1869    id,
1870    pen_number,
1871    status,
1872    x,
1873    y;
1874
1875  KeySym
1876    key_symbol;
1877
1878  register char
1879    *p;
1880
1881  register ssize_t
1882    i;
1883
1884  unsigned int
1885    height,
1886    width;
1887
1888  size_t
1889    state;
1890
1891  XAnnotateInfo
1892    *annotate_info,
1893    *previous_info;
1894
1895  XColor
1896    color;
1897
1898  XFontStruct
1899    *font_info;
1900
1901  XEvent
1902    event,
1903    text_event;
1904
1905  /*
1906    Map Command widget.
1907  */
1908  (void) CloneString(&windows->command.name,"Annotate");
1909  windows->command.data=4;
1910  (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1911  (void) XMapRaised(display,windows->command.id);
1912  XClientMessage(display,windows->image.id,windows->im_protocols,
1913    windows->im_update_widget,CurrentTime);
1914  /*
1915    Track pointer until button 1 is pressed.
1916  */
1917  XQueryPosition(display,windows->image.id,&x,&y);
1918  (void) XSelectInput(display,windows->image.id,
1919    windows->image.attributes.event_mask | PointerMotionMask);
1920  cursor=XCreateFontCursor(display,XC_left_side);
1921  (void) XCheckDefineCursor(display,windows->image.id,cursor);
1922  state=DefaultState;
1923  do
1924  {
1925    if (windows->info.mapped != MagickFalse )
1926      {
1927        /*
1928          Display pointer position.
1929        */
1930        (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
1931          x+windows->image.x,y+windows->image.y);
1932        XInfoWidget(display,windows,text);
1933      }
1934    /*
1935      Wait for next event.
1936    */
1937    XScreenEvent(display,windows,&event,exception);
1938    if (event.xany.window == windows->command.id)
1939      {
1940        /*
1941          Select a command from the Command widget.
1942        */
1943        id=XCommandWidget(display,windows,AnnotateMenu,&event);
1944        (void) XCheckDefineCursor(display,windows->image.id,cursor);
1945        if (id < 0)
1946          continue;
1947        switch (AnnotateCommands[id])
1948        {
1949          case AnnotateNameCommand:
1950          {
1951            const char
1952              *FontMenu[MaxNumberFonts];
1953
1954            int
1955              font_number;
1956
1957            /*
1958              Initialize menu selections.
1959            */
1960            for (i=0; i < MaxNumberFonts; i++)
1961              FontMenu[i]=resource_info->font_name[i];
1962            FontMenu[MaxNumberFonts-2]="Browser...";
1963            FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1964            /*
1965              Select a font name from the pop-up menu.
1966            */
1967            font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1968              (const char **) FontMenu,command);
1969            if (font_number < 0)
1970              break;
1971            if (font_number == (MaxNumberFonts-2))
1972              {
1973                static char
1974                  font_name[MagickPathExtent] = "fixed";
1975
1976                /*
1977                  Select a font name from a browser.
1978                */
1979                resource_info->font_name[font_number]=font_name;
1980                XFontBrowserWidget(display,windows,"Select",font_name);
1981                if (*font_name == '\0')
1982                  break;
1983              }
1984            /*
1985              Initialize font info.
1986            */
1987            font_info=XLoadQueryFont(display,resource_info->font_name[
1988              font_number]);
1989            if (font_info == (XFontStruct *) NULL)
1990              {
1991                XNoticeWidget(display,windows,"Unable to load font:",
1992                  resource_info->font_name[font_number]);
1993                break;
1994              }
1995            font_id=(unsigned int) font_number;
1996            (void) XFreeFont(display,font_info);
1997            break;
1998          }
1999          case AnnotateFontColorCommand:
2000          {
2001            /*
2002              Initialize menu selections.
2003            */
2004            for (i=0; i < (int) (MaxNumberPens-2); i++)
2005              ColorMenu[i]=resource_info->pen_colors[i];
2006            ColorMenu[MaxNumberPens-2]="transparent";
2007            ColorMenu[MaxNumberPens-1]="Browser...";
2008            ColorMenu[MaxNumberPens]=(const char *) NULL;
2009            /*
2010              Select a pen color from the pop-up menu.
2011            */
2012            pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2013              (const char **) ColorMenu,command);
2014            if (pen_number < 0)
2015              break;
2016            transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
2017              MagickFalse;
2018            if (transparent_pen != MagickFalse )
2019              break;
2020            if (pen_number == (MaxNumberPens-1))
2021              {
2022                static char
2023                  color_name[MagickPathExtent] = "gray";
2024
2025                /*
2026                  Select a pen color from a dialog.
2027                */
2028                resource_info->pen_colors[pen_number]=color_name;
2029                XColorBrowserWidget(display,windows,"Select",color_name);
2030                if (*color_name == '\0')
2031                  break;
2032              }
2033            /*
2034              Set pen color.
2035            */
2036            (void) XParseColor(display,windows->map_info->colormap,
2037              resource_info->pen_colors[pen_number],&color);
2038            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2039              (unsigned int) MaxColors,&color);
2040            windows->pixel_info->pen_colors[pen_number]=color;
2041            pen_id=(unsigned int) pen_number;
2042            break;
2043          }
2044          case AnnotateBackgroundColorCommand:
2045          {
2046            /*
2047              Initialize menu selections.
2048            */
2049            for (i=0; i < (int) (MaxNumberPens-2); i++)
2050              ColorMenu[i]=resource_info->pen_colors[i];
2051            ColorMenu[MaxNumberPens-2]="transparent";
2052            ColorMenu[MaxNumberPens-1]="Browser...";
2053            ColorMenu[MaxNumberPens]=(const char *) NULL;
2054            /*
2055              Select a pen color from the pop-up menu.
2056            */
2057            pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2058              (const char **) ColorMenu,command);
2059            if (pen_number < 0)
2060              break;
2061            transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2062              MagickFalse;
2063            if (transparent_box != MagickFalse )
2064              break;
2065            if (pen_number == (MaxNumberPens-1))
2066              {
2067                static char
2068                  color_name[MagickPathExtent] = "gray";
2069
2070                /*
2071                  Select a pen color from a dialog.
2072                */
2073                resource_info->pen_colors[pen_number]=color_name;
2074                XColorBrowserWidget(display,windows,"Select",color_name);
2075                if (*color_name == '\0')
2076                  break;
2077              }
2078            /*
2079              Set pen color.
2080            */
2081            (void) XParseColor(display,windows->map_info->colormap,
2082              resource_info->pen_colors[pen_number],&color);
2083            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2084              (unsigned int) MaxColors,&color);
2085            windows->pixel_info->pen_colors[pen_number]=color;
2086            box_id=(unsigned int) pen_number;
2087            break;
2088          }
2089          case AnnotateRotateCommand:
2090          {
2091            int
2092              entry;
2093
2094            static char
2095              angle[MagickPathExtent] = "30.0";
2096
2097            static const char
2098              *RotateMenu[] =
2099              {
2100                "-90",
2101                "-45",
2102                "-30",
2103                "0",
2104                "30",
2105                "45",
2106                "90",
2107                "180",
2108                "Dialog...",
2109                (char *) NULL,
2110              };
2111
2112            /*
2113              Select a command from the pop-up menu.
2114            */
2115            entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2116              command);
2117            if (entry < 0)
2118              break;
2119            if (entry != 8)
2120              {
2121                degrees=StringToDouble(RotateMenu[entry],(char **) NULL);
2122                break;
2123              }
2124            (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2125              angle);
2126            if (*angle == '\0')
2127              break;
2128            degrees=StringToDouble(angle,(char **) NULL);
2129            break;
2130          }
2131          case AnnotateHelpCommand:
2132          {
2133            XTextViewWidget(display,resource_info,windows,MagickFalse,
2134              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2135            break;
2136          }
2137          case AnnotateDismissCommand:
2138          {
2139            /*
2140              Prematurely exit.
2141            */
2142            state|=EscapeState;
2143            state|=ExitState;
2144            break;
2145          }
2146          default:
2147            break;
2148        }
2149        continue;
2150      }
2151    switch (event.type)
2152    {
2153      case ButtonPress:
2154      {
2155        if (event.xbutton.button != Button1)
2156          break;
2157        if (event.xbutton.window != windows->image.id)
2158          break;
2159        /*
2160          Change to text entering mode.
2161        */
2162        x=event.xbutton.x;
2163        y=event.xbutton.y;
2164        state|=ExitState;
2165        break;
2166      }
2167      case ButtonRelease:
2168        break;
2169      case Expose:
2170        break;
2171      case KeyPress:
2172      {
2173        if (event.xkey.window != windows->image.id)
2174          break;
2175        /*
2176          Respond to a user key press.
2177        */
2178        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2179          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2180        switch ((int) key_symbol)
2181        {
2182          case XK_Escape:
2183          case XK_F20:
2184          {
2185            /*
2186              Prematurely exit.
2187            */
2188            state|=EscapeState;
2189            state|=ExitState;
2190            break;
2191          }
2192          case XK_F1:
2193          case XK_Help:
2194          {
2195            XTextViewWidget(display,resource_info,windows,MagickFalse,
2196              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2197            break;
2198          }
2199          default:
2200          {
2201            (void) XBell(display,0);
2202            break;
2203          }
2204        }
2205        break;
2206      }
2207      case MotionNotify:
2208      {
2209        /*
2210          Map and unmap Info widget as cursor crosses its boundaries.
2211        */
2212        x=event.xmotion.x;
2213        y=event.xmotion.y;
2214        if (windows->info.mapped != MagickFalse )
2215          {
2216            if ((x < (int) (windows->info.x+windows->info.width)) &&
2217                (y < (int) (windows->info.y+windows->info.height)))
2218              (void) XWithdrawWindow(display,windows->info.id,
2219                windows->info.screen);
2220          }
2221        else
2222          if ((x > (int) (windows->info.x+windows->info.width)) ||
2223              (y > (int) (windows->info.y+windows->info.height)))
2224            (void) XMapWindow(display,windows->info.id);
2225        break;
2226      }
2227      default:
2228        break;
2229    }
2230  } while ((state & ExitState) == 0);
2231  (void) XSelectInput(display,windows->image.id,
2232    windows->image.attributes.event_mask);
2233  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2234  if ((state & EscapeState) != 0)
2235    return(MagickTrue);
2236  /*
2237    Set font info and check boundary conditions.
2238  */
2239  font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2240  if (font_info == (XFontStruct *) NULL)
2241    {
2242      XNoticeWidget(display,windows,"Unable to load font:",
2243        resource_info->font_name[font_id]);
2244      font_info=windows->font_info;
2245    }
2246  if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2247    x=(int) windows->image.width-font_info->max_bounds.width;
2248  if (y < (int) (font_info->ascent+font_info->descent))
2249    y=(int) font_info->ascent+font_info->descent;
2250  if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2251      ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2252    return(MagickFalse);
2253  /*
2254    Initialize annotate structure.
2255  */
2256  annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2257  if (annotate_info == (XAnnotateInfo *) NULL)
2258    return(MagickFalse);
2259  XGetAnnotateInfo(annotate_info);
2260  annotate_info->x=x;
2261  annotate_info->y=y;
2262  if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
2263    annotate_info->stencil=OpaqueStencil;
2264  else
2265    if (transparent_box == MagickFalse)
2266      annotate_info->stencil=BackgroundStencil;
2267    else
2268      annotate_info->stencil=ForegroundStencil;
2269  annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2270  annotate_info->degrees=degrees;
2271  annotate_info->font_info=font_info;
2272  annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2273    windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
2274    sizeof(*annotate_info->text));
2275  if (annotate_info->text == (char *) NULL)
2276    return(MagickFalse);
2277  /*
2278    Create cursor and set graphic context.
2279  */
2280  cursor=XCreateFontCursor(display,XC_pencil);
2281  (void) XCheckDefineCursor(display,windows->image.id,cursor);
2282  annotate_context=windows->image.annotate_context;
2283  (void) XSetFont(display,annotate_context,font_info->fid);
2284  (void) XSetBackground(display,annotate_context,
2285    windows->pixel_info->pen_colors[box_id].pixel);
2286  (void) XSetForeground(display,annotate_context,
2287    windows->pixel_info->pen_colors[pen_id].pixel);
2288  /*
2289    Begin annotating the image with text.
2290  */
2291  (void) CloneString(&windows->command.name,"Text");
2292  windows->command.data=0;
2293  (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2294  state=DefaultState;
2295  (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2296  text_event.xexpose.width=(int) font_info->max_bounds.width;
2297  text_event.xexpose.height=font_info->max_bounds.ascent+
2298    font_info->max_bounds.descent;
2299  p=annotate_info->text;
2300  do
2301  {
2302    /*
2303      Display text cursor.
2304    */
2305    *p='\0';
2306    (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2307    /*
2308      Wait for next event.
2309    */
2310    XScreenEvent(display,windows,&event,exception);
2311    if (event.xany.window == windows->command.id)
2312      {
2313        /*
2314          Select a command from the Command widget.
2315        */
2316        (void) XSetBackground(display,annotate_context,
2317          windows->pixel_info->background_color.pixel);
2318        (void) XSetForeground(display,annotate_context,
2319          windows->pixel_info->foreground_color.pixel);
2320        id=XCommandWidget(display,windows,AnnotateMenu,&event);
2321        (void) XSetBackground(display,annotate_context,
2322          windows->pixel_info->pen_colors[box_id].pixel);
2323        (void) XSetForeground(display,annotate_context,
2324          windows->pixel_info->pen_colors[pen_id].pixel);
2325        if (id < 0)
2326          continue;
2327        switch (TextCommands[id])
2328        {
2329          case TextHelpCommand:
2330          {
2331            XTextViewWidget(display,resource_info,windows,MagickFalse,
2332              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2333            (void) XCheckDefineCursor(display,windows->image.id,cursor);
2334            break;
2335          }
2336          case TextApplyCommand:
2337          {
2338            /*
2339              Finished annotating.
2340            */
2341            annotate_info->width=(unsigned int) XTextWidth(font_info,
2342              annotate_info->text,(int) strlen(annotate_info->text));
2343            XRefreshWindow(display,&windows->image,&text_event);
2344            state|=ExitState;
2345            break;
2346          }
2347          default:
2348            break;
2349        }
2350        continue;
2351      }
2352    /*
2353      Erase text cursor.
2354    */
2355    text_event.xexpose.x=x;
2356    text_event.xexpose.y=y-font_info->max_bounds.ascent;
2357    (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2358      (unsigned int) text_event.xexpose.width,(unsigned int)
2359      text_event.xexpose.height,MagickFalse);
2360    XRefreshWindow(display,&windows->image,&text_event);
2361    switch (event.type)
2362    {
2363      case ButtonPress:
2364      {
2365        if (event.xbutton.window != windows->image.id)
2366          break;
2367        if (event.xbutton.button == Button2)
2368          {
2369            /*
2370              Request primary selection.
2371            */
2372            (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2373              windows->image.id,CurrentTime);
2374            break;
2375          }
2376        break;
2377      }
2378      case Expose:
2379      {
2380        if (event.xexpose.count == 0)
2381          {
2382            XAnnotateInfo
2383              *text_info;
2384
2385            /*
2386              Refresh Image window.
2387            */
2388            XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2389            text_info=annotate_info;
2390            while (text_info != (XAnnotateInfo *) NULL)
2391            {
2392              if (annotate_info->stencil == ForegroundStencil)
2393                (void) XDrawString(display,windows->image.id,annotate_context,
2394                  text_info->x,text_info->y,text_info->text,
2395                  (int) strlen(text_info->text));
2396              else
2397                (void) XDrawImageString(display,windows->image.id,
2398                  annotate_context,text_info->x,text_info->y,text_info->text,
2399                  (int) strlen(text_info->text));
2400              text_info=text_info->previous;
2401            }
2402            (void) XDrawString(display,windows->image.id,annotate_context,
2403              x,y,"_",1);
2404          }
2405        break;
2406      }
2407      case KeyPress:
2408      {
2409        int
2410          length;
2411
2412        if (event.xkey.window != windows->image.id)
2413          break;
2414        /*
2415          Respond to a user key press.
2416        */
2417        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2418          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2419        *(command+length)='\0';
2420        if (((event.xkey.state & ControlMask) != 0) ||
2421            ((event.xkey.state & Mod1Mask) != 0))
2422          state|=ModifierState;
2423        if ((state & ModifierState) != 0)
2424          switch ((int) key_symbol)
2425          {
2426            case XK_u:
2427            case XK_U:
2428            {
2429              key_symbol=DeleteCommand;
2430              break;
2431            }
2432            default:
2433              break;
2434          }
2435        switch ((int) key_symbol)
2436        {
2437          case XK_BackSpace:
2438          {
2439            /*
2440              Erase one character.
2441            */
2442            if (p == annotate_info->text)
2443              {
2444                if (annotate_info->previous == (XAnnotateInfo *) NULL)
2445                  break;
2446                else
2447                  {
2448                    /*
2449                      Go to end of the previous line of text.
2450                    */
2451                    annotate_info=annotate_info->previous;
2452                    p=annotate_info->text;
2453                    x=annotate_info->x+annotate_info->width;
2454                    y=annotate_info->y;
2455                    if (annotate_info->width != 0)
2456                      p+=strlen(annotate_info->text);
2457                    break;
2458                  }
2459              }
2460            p--;
2461            x-=XTextWidth(font_info,p,1);
2462            text_event.xexpose.x=x;
2463            text_event.xexpose.y=y-font_info->max_bounds.ascent;
2464            XRefreshWindow(display,&windows->image,&text_event);
2465            break;
2466          }
2467          case XK_bracketleft:
2468          {
2469            key_symbol=XK_Escape;
2470            break;
2471          }
2472          case DeleteCommand:
2473          {
2474            /*
2475              Erase the entire line of text.
2476            */
2477            while (p != annotate_info->text)
2478            {
2479              p--;
2480              x-=XTextWidth(font_info,p,1);
2481              text_event.xexpose.x=x;
2482              XRefreshWindow(display,&windows->image,&text_event);
2483            }
2484            break;
2485          }
2486          case XK_Escape:
2487          case XK_F20:
2488          {
2489            /*
2490              Finished annotating.
2491            */
2492            annotate_info->width=(unsigned int) XTextWidth(font_info,
2493              annotate_info->text,(int) strlen(annotate_info->text));
2494            XRefreshWindow(display,&windows->image,&text_event);
2495            state|=ExitState;
2496            break;
2497          }
2498          default:
2499          {
2500            /*
2501              Draw a single character on the Image window.
2502            */
2503            if ((state & ModifierState) != 0)
2504              break;
2505            if (*command == '\0')
2506              break;
2507            *p=(*command);
2508            if (annotate_info->stencil == ForegroundStencil)
2509              (void) XDrawString(display,windows->image.id,annotate_context,
2510                x,y,p,1);
2511            else
2512              (void) XDrawImageString(display,windows->image.id,
2513                annotate_context,x,y,p,1);
2514            x+=XTextWidth(font_info,p,1);
2515            p++;
2516            if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2517              break;
2518          }
2519          case XK_Return:
2520          case XK_KP_Enter:
2521          {
2522            /*
2523              Advance to the next line of text.
2524            */
2525            *p='\0';
2526            annotate_info->width=(unsigned int) XTextWidth(font_info,
2527              annotate_info->text,(int) strlen(annotate_info->text));
2528            if (annotate_info->next != (XAnnotateInfo *) NULL)
2529              {
2530                /*
2531                  Line of text already exists.
2532                */
2533                annotate_info=annotate_info->next;
2534                x=annotate_info->x;
2535                y=annotate_info->y;
2536                p=annotate_info->text;
2537                break;
2538              }
2539            annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2540              sizeof(*annotate_info->next));
2541            if (annotate_info->next == (XAnnotateInfo *) NULL)
2542              return(MagickFalse);
2543            *annotate_info->next=(*annotate_info);
2544            annotate_info->next->previous=annotate_info;
2545            annotate_info=annotate_info->next;
2546            annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2547              windows->image.width/MagickMax((ssize_t)
2548              font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2549            if (annotate_info->text == (char *) NULL)
2550              return(MagickFalse);
2551            annotate_info->y+=annotate_info->height;
2552            if (annotate_info->y > (int) windows->image.height)
2553              annotate_info->y=(int) annotate_info->height;
2554            annotate_info->next=(XAnnotateInfo *) NULL;
2555            x=annotate_info->x;
2556            y=annotate_info->y;
2557            p=annotate_info->text;
2558            break;
2559          }
2560        }
2561        break;
2562      }
2563      case KeyRelease:
2564      {
2565        /*
2566          Respond to a user key release.
2567        */
2568        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2569          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2570        state&=(~ModifierState);
2571        break;
2572      }
2573      case SelectionNotify:
2574      {
2575        Atom
2576          type;
2577
2578        int
2579          format;
2580
2581        unsigned char
2582          *data;
2583
2584        unsigned long
2585          after,
2586          length;
2587
2588        /*
2589          Obtain response from primary selection.
2590        */
2591        if (event.xselection.property == (Atom) None)
2592          break;
2593        status=XGetWindowProperty(display,event.xselection.requestor,
2594          event.xselection.property,0L,(long) MagickPathExtent,True,XA_STRING,
2595          &type,&format,&length,&after,&data);
2596        if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2597            (length == 0))
2598          break;
2599        /*
2600          Annotate Image window with primary selection.
2601        */
2602        for (i=0; i < (ssize_t) length; i++)
2603        {
2604          if ((char) data[i] != '\n')
2605            {
2606              /*
2607                Draw a single character on the Image window.
2608              */
2609              *p=(char) data[i];
2610              (void) XDrawString(display,windows->image.id,annotate_context,
2611                x,y,p,1);
2612              x+=XTextWidth(font_info,p,1);
2613              p++;
2614              if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2615                continue;
2616            }
2617          /*
2618            Advance to the next line of text.
2619          */
2620          *p='\0';
2621          annotate_info->width=(unsigned int) XTextWidth(font_info,
2622            annotate_info->text,(int) strlen(annotate_info->text));
2623          if (annotate_info->next != (XAnnotateInfo *) NULL)
2624            {
2625              /*
2626                Line of text already exists.
2627              */
2628              annotate_info=annotate_info->next;
2629              x=annotate_info->x;
2630              y=annotate_info->y;
2631              p=annotate_info->text;
2632              continue;
2633            }
2634          annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2635            sizeof(*annotate_info->next));
2636          if (annotate_info->next == (XAnnotateInfo *) NULL)
2637            return(MagickFalse);
2638          *annotate_info->next=(*annotate_info);
2639          annotate_info->next->previous=annotate_info;
2640          annotate_info=annotate_info->next;
2641          annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2642            windows->image.width/MagickMax((ssize_t)
2643            font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2644          if (annotate_info->text == (char *) NULL)
2645            return(MagickFalse);
2646          annotate_info->y+=annotate_info->height;
2647          if (annotate_info->y > (int) windows->image.height)
2648            annotate_info->y=(int) annotate_info->height;
2649          annotate_info->next=(XAnnotateInfo *) NULL;
2650          x=annotate_info->x;
2651          y=annotate_info->y;
2652          p=annotate_info->text;
2653        }
2654        (void) XFree((void *) data);
2655        break;
2656      }
2657      default:
2658        break;
2659    }
2660  } while ((state & ExitState) == 0);
2661  (void) XFreeCursor(display,cursor);
2662  /*
2663    Annotation is relative to image configuration.
2664  */
2665  width=(unsigned int) image->columns;
2666  height=(unsigned int) image->rows;
2667  x=0;
2668  y=0;
2669  if (windows->image.crop_geometry != (char *) NULL)
2670    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2671  /*
2672    Initialize annotated image.
2673  */
2674  XSetCursorState(display,windows,MagickTrue);
2675  XCheckRefreshWindows(display,windows);
2676  while (annotate_info != (XAnnotateInfo *) NULL)
2677  {
2678    if (annotate_info->width == 0)
2679      {
2680        /*
2681          No text on this line--  go to the next line of text.
2682        */
2683        previous_info=annotate_info->previous;
2684        annotate_info->text=(char *)
2685          RelinquishMagickMemory(annotate_info->text);
2686        annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2687        annotate_info=previous_info;
2688        continue;
2689      }
2690    /*
2691      Determine pixel index for box and pen color.
2692    */
2693    windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2694    if (windows->pixel_info->colors != 0)
2695      for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2696        if (windows->pixel_info->pixels[i] ==
2697            windows->pixel_info->pen_colors[box_id].pixel)
2698          {
2699            windows->pixel_info->box_index=(unsigned short) i;
2700            break;
2701          }
2702    windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2703    if (windows->pixel_info->colors != 0)
2704      for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2705        if (windows->pixel_info->pixels[i] ==
2706            windows->pixel_info->pen_colors[pen_id].pixel)
2707          {
2708            windows->pixel_info->pen_index=(unsigned short) i;
2709            break;
2710          }
2711    /*
2712      Define the annotate geometry string.
2713    */
2714    annotate_info->x=(int)
2715      width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2716    annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2717      windows->image.y)/windows->image.ximage->height;
2718    (void) FormatLocaleString(annotate_info->geometry,MagickPathExtent,
2719      "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2720      height*annotate_info->height/windows->image.ximage->height,
2721      annotate_info->x+x,annotate_info->y+y);
2722    /*
2723      Annotate image with text.
2724    */
2725    status=XAnnotateImage(display,windows->pixel_info,annotate_info,image,
2726      exception);
2727    if (status == 0)
2728      return(MagickFalse);
2729    /*
2730      Free up memory.
2731    */
2732    previous_info=annotate_info->previous;
2733    annotate_info->text=DestroyString(annotate_info->text);
2734    annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2735    annotate_info=previous_info;
2736  }
2737  (void) XSetForeground(display,annotate_context,
2738    windows->pixel_info->foreground_color.pixel);
2739  (void) XSetBackground(display,annotate_context,
2740    windows->pixel_info->background_color.pixel);
2741  (void) XSetFont(display,annotate_context,windows->font_info->fid);
2742  XSetCursorState(display,windows,MagickFalse);
2743  (void) XFreeFont(display,font_info);
2744  /*
2745    Update image configuration.
2746  */
2747  XConfigureImageColormap(display,resource_info,windows,image,exception);
2748  (void) XConfigureImage(display,resource_info,windows,image,exception);
2749  return(MagickTrue);
2750}
2751
2752/*
2753%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2754%                                                                             %
2755%                                                                             %
2756%                                                                             %
2757+   X B a c k g r o u n d I m a g e                                           %
2758%                                                                             %
2759%                                                                             %
2760%                                                                             %
2761%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2762%
2763%  XBackgroundImage() displays the image in the background of a window.
2764%
2765%  The format of the XBackgroundImage method is:
2766%
2767%      MagickBooleanType XBackgroundImage(Display *display,
2768%        XResourceInfo *resource_info,XWindows *windows,Image **image,
2769%        ExceptionInfo *exception)
2770%
2771%  A description of each parameter follows:
2772%
2773%    o display: Specifies a connection to an X server; returned from
2774%      XOpenDisplay.
2775%
2776%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2777%
2778%    o windows: Specifies a pointer to a XWindows structure.
2779%
2780%    o image: the image.
2781%
2782%    o exception: return any errors or warnings in this structure.
2783%
2784*/
2785static MagickBooleanType XBackgroundImage(Display *display,
2786  XResourceInfo *resource_info,XWindows *windows,Image **image,
2787  ExceptionInfo *exception)
2788{
2789#define BackgroundImageTag  "Background/Image"
2790
2791  int
2792    status;
2793
2794  static char
2795    window_id[MagickPathExtent] = "root";
2796
2797  XResourceInfo
2798    background_resources;
2799
2800  /*
2801    Put image in background.
2802  */
2803  status=XDialogWidget(display,windows,"Background",
2804    "Enter window id (id 0x00 selects window with pointer):",window_id);
2805  if (*window_id == '\0')
2806    return(MagickFalse);
2807  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
2808    exception);
2809  XInfoWidget(display,windows,BackgroundImageTag);
2810  XSetCursorState(display,windows,MagickTrue);
2811  XCheckRefreshWindows(display,windows);
2812  background_resources=(*resource_info);
2813  background_resources.window_id=window_id;
2814  background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2815  status=XDisplayBackgroundImage(display,&background_resources,*image,
2816    exception);
2817  if (status != MagickFalse)
2818    XClientMessage(display,windows->image.id,windows->im_protocols,
2819      windows->im_retain_colors,CurrentTime);
2820  XSetCursorState(display,windows,MagickFalse);
2821  (void) XMagickCommand(display,resource_info,windows,UndoCommand,image,
2822    exception);
2823  return(MagickTrue);
2824}
2825
2826/*
2827%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2828%                                                                             %
2829%                                                                             %
2830%                                                                             %
2831+   X C h o p I m a g e                                                       %
2832%                                                                             %
2833%                                                                             %
2834%                                                                             %
2835%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2836%
2837%  XChopImage() chops the X image.
2838%
2839%  The format of the XChopImage method is:
2840%
2841%    MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2842%      XWindows *windows,Image **image,ExceptionInfo *exception)
2843%
2844%  A description of each parameter follows:
2845%
2846%    o display: Specifies a connection to an X server; returned from
2847%      XOpenDisplay.
2848%
2849%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2850%
2851%    o windows: Specifies a pointer to a XWindows structure.
2852%
2853%    o image: the image.
2854%
2855%    o exception: return any errors or warnings in this structure.
2856%
2857*/
2858static MagickBooleanType XChopImage(Display *display,
2859  XResourceInfo *resource_info,XWindows *windows,Image **image,
2860  ExceptionInfo *exception)
2861{
2862  static const char
2863    *ChopMenu[] =
2864    {
2865      "Direction",
2866      "Help",
2867      "Dismiss",
2868      (char *) NULL
2869    };
2870
2871  static ModeType
2872    direction = HorizontalChopCommand;
2873
2874  static const ModeType
2875    ChopCommands[] =
2876    {
2877      ChopDirectionCommand,
2878      ChopHelpCommand,
2879      ChopDismissCommand
2880    },
2881    DirectionCommands[] =
2882    {
2883      HorizontalChopCommand,
2884      VerticalChopCommand
2885    };
2886
2887  char
2888    text[MagickPathExtent];
2889
2890  Image
2891    *chop_image;
2892
2893  int
2894    id,
2895    x,
2896    y;
2897
2898  double
2899    scale_factor;
2900
2901  RectangleInfo
2902    chop_info;
2903
2904  unsigned int
2905    distance,
2906    height,
2907    width;
2908
2909  size_t
2910    state;
2911
2912  XEvent
2913    event;
2914
2915  XSegment
2916    segment_info;
2917
2918  /*
2919    Map Command widget.
2920  */
2921  (void) CloneString(&windows->command.name,"Chop");
2922  windows->command.data=1;
2923  (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2924  (void) XMapRaised(display,windows->command.id);
2925  XClientMessage(display,windows->image.id,windows->im_protocols,
2926    windows->im_update_widget,CurrentTime);
2927  /*
2928    Track pointer until button 1 is pressed.
2929  */
2930  XQueryPosition(display,windows->image.id,&x,&y);
2931  (void) XSelectInput(display,windows->image.id,
2932    windows->image.attributes.event_mask | PointerMotionMask);
2933  state=DefaultState;
2934  (void) ResetMagickMemory(&segment_info,0,sizeof(segment_info));
2935  do
2936  {
2937    if (windows->info.mapped != MagickFalse )
2938      {
2939        /*
2940          Display pointer position.
2941        */
2942        (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
2943          x+windows->image.x,y+windows->image.y);
2944        XInfoWidget(display,windows,text);
2945      }
2946    /*
2947      Wait for next event.
2948    */
2949    XScreenEvent(display,windows,&event,exception);
2950    if (event.xany.window == windows->command.id)
2951      {
2952        /*
2953          Select a command from the Command widget.
2954        */
2955        id=XCommandWidget(display,windows,ChopMenu,&event);
2956        if (id < 0)
2957          continue;
2958        switch (ChopCommands[id])
2959        {
2960          case ChopDirectionCommand:
2961          {
2962            char
2963              command[MagickPathExtent];
2964
2965            static const char
2966              *Directions[] =
2967              {
2968                "horizontal",
2969                "vertical",
2970                (char *) NULL,
2971              };
2972
2973            /*
2974              Select a command from the pop-up menu.
2975            */
2976            id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2977            if (id >= 0)
2978              direction=DirectionCommands[id];
2979            break;
2980          }
2981          case ChopHelpCommand:
2982          {
2983            XTextViewWidget(display,resource_info,windows,MagickFalse,
2984              "Help Viewer - Image Chop",ImageChopHelp);
2985            break;
2986          }
2987          case ChopDismissCommand:
2988          {
2989            /*
2990              Prematurely exit.
2991            */
2992            state|=EscapeState;
2993            state|=ExitState;
2994            break;
2995          }
2996          default:
2997            break;
2998        }
2999        continue;
3000      }
3001    switch (event.type)
3002    {
3003      case ButtonPress:
3004      {
3005        if (event.xbutton.button != Button1)
3006          break;
3007        if (event.xbutton.window != windows->image.id)
3008          break;
3009        /*
3010          User has committed to start point of chopping line.
3011        */
3012        segment_info.x1=(short int) event.xbutton.x;
3013        segment_info.x2=(short int) event.xbutton.x;
3014        segment_info.y1=(short int) event.xbutton.y;
3015        segment_info.y2=(short int) event.xbutton.y;
3016        state|=ExitState;
3017        break;
3018      }
3019      case ButtonRelease:
3020        break;
3021      case Expose:
3022        break;
3023      case KeyPress:
3024      {
3025        char
3026          command[MagickPathExtent];
3027
3028        KeySym
3029          key_symbol;
3030
3031        if (event.xkey.window != windows->image.id)
3032          break;
3033        /*
3034          Respond to a user key press.
3035        */
3036        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3037          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3038        switch ((int) key_symbol)
3039        {
3040          case XK_Escape:
3041          case XK_F20:
3042          {
3043            /*
3044              Prematurely exit.
3045            */
3046            state|=EscapeState;
3047            state|=ExitState;
3048            break;
3049          }
3050          case XK_F1:
3051          case XK_Help:
3052          {
3053            (void) XSetFunction(display,windows->image.highlight_context,
3054              GXcopy);
3055            XTextViewWidget(display,resource_info,windows,MagickFalse,
3056              "Help Viewer - Image Chop",ImageChopHelp);
3057            (void) XSetFunction(display,windows->image.highlight_context,
3058              GXinvert);
3059            break;
3060          }
3061          default:
3062          {
3063            (void) XBell(display,0);
3064            break;
3065          }
3066        }
3067        break;
3068      }
3069      case MotionNotify:
3070      {
3071        /*
3072          Map and unmap Info widget as text cursor crosses its boundaries.
3073        */
3074        x=event.xmotion.x;
3075        y=event.xmotion.y;
3076        if (windows->info.mapped != MagickFalse )
3077          {
3078            if ((x < (int) (windows->info.x+windows->info.width)) &&
3079                (y < (int) (windows->info.y+windows->info.height)))
3080              (void) XWithdrawWindow(display,windows->info.id,
3081                windows->info.screen);
3082          }
3083        else
3084          if ((x > (int) (windows->info.x+windows->info.width)) ||
3085              (y > (int) (windows->info.y+windows->info.height)))
3086            (void) XMapWindow(display,windows->info.id);
3087      }
3088    }
3089  } while ((state & ExitState) == 0);
3090  (void) XSelectInput(display,windows->image.id,
3091    windows->image.attributes.event_mask);
3092  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3093  if ((state & EscapeState) != 0)
3094    return(MagickTrue);
3095  /*
3096    Draw line as pointer moves until the mouse button is released.
3097  */
3098  chop_info.width=0;
3099  chop_info.height=0;
3100  chop_info.x=0;
3101  chop_info.y=0;
3102  distance=0;
3103  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3104  state=DefaultState;
3105  do
3106  {
3107    if (distance > 9)
3108      {
3109        /*
3110          Display info and draw chopping line.
3111        */
3112        if (windows->info.mapped == MagickFalse)
3113          (void) XMapWindow(display,windows->info.id);
3114        (void) FormatLocaleString(text,MagickPathExtent,
3115          " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3116          chop_info.height,(double) chop_info.x,(double) chop_info.y);
3117        XInfoWidget(display,windows,text);
3118        XHighlightLine(display,windows->image.id,
3119          windows->image.highlight_context,&segment_info);
3120      }
3121    else
3122      if (windows->info.mapped != MagickFalse )
3123        (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3124    /*
3125      Wait for next event.
3126    */
3127    XScreenEvent(display,windows,&event,exception);
3128    if (distance > 9)
3129      XHighlightLine(display,windows->image.id,
3130        windows->image.highlight_context,&segment_info);
3131    switch (event.type)
3132    {
3133      case ButtonPress:
3134      {
3135        segment_info.x2=(short int) event.xmotion.x;
3136        segment_info.y2=(short int) event.xmotion.y;
3137        break;
3138      }
3139      case ButtonRelease:
3140      {
3141        /*
3142          User has committed to chopping line.
3143        */
3144        segment_info.x2=(short int) event.xbutton.x;
3145        segment_info.y2=(short int) event.xbutton.y;
3146        state|=ExitState;
3147        break;
3148      }
3149      case Expose:
3150        break;
3151      case MotionNotify:
3152      {
3153        segment_info.x2=(short int) event.xmotion.x;
3154        segment_info.y2=(short int) event.xmotion.y;
3155      }
3156      default:
3157        break;
3158    }
3159    /*
3160      Check boundary conditions.
3161    */
3162    if (segment_info.x2 < 0)
3163      segment_info.x2=0;
3164    else
3165      if (segment_info.x2 > windows->image.ximage->width)
3166        segment_info.x2=windows->image.ximage->width;
3167    if (segment_info.y2 < 0)
3168      segment_info.y2=0;
3169    else
3170      if (segment_info.y2 > windows->image.ximage->height)
3171        segment_info.y2=windows->image.ximage->height;
3172    distance=(unsigned int)
3173      (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3174       ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3175    /*
3176      Compute chopping geometry.
3177    */
3178    if (direction == HorizontalChopCommand)
3179      {
3180        chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3181        chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3182        chop_info.height=0;
3183        chop_info.y=0;
3184        if (segment_info.x1 > (int) segment_info.x2)
3185          {
3186            chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3187            chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3188          }
3189      }
3190    else
3191      {
3192        chop_info.width=0;
3193        chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3194        chop_info.x=0;
3195        chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3196        if (segment_info.y1 > segment_info.y2)
3197          {
3198            chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3199            chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3200          }
3201      }
3202  } while ((state & ExitState) == 0);
3203  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3204  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3205  if (distance <= 9)
3206    return(MagickTrue);
3207  /*
3208    Image chopping is relative to image configuration.
3209  */
3210  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
3211    exception);
3212  XSetCursorState(display,windows,MagickTrue);
3213  XCheckRefreshWindows(display,windows);
3214  windows->image.window_changes.width=windows->image.ximage->width-
3215    (unsigned int) chop_info.width;
3216  windows->image.window_changes.height=windows->image.ximage->height-
3217    (unsigned int) chop_info.height;
3218  width=(unsigned int) (*image)->columns;
3219  height=(unsigned int) (*image)->rows;
3220  x=0;
3221  y=0;
3222  if (windows->image.crop_geometry != (char *) NULL)
3223    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3224  scale_factor=(double) width/windows->image.ximage->width;
3225  chop_info.x+=x;
3226  chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3227  chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3228  scale_factor=(double) height/windows->image.ximage->height;
3229  chop_info.y+=y;
3230  chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3231  chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3232  /*
3233    Chop image.
3234  */
3235  chop_image=ChopImage(*image,&chop_info,exception);
3236  XSetCursorState(display,windows,MagickFalse);
3237  if (chop_image == (Image *) NULL)
3238    return(MagickFalse);
3239  *image=DestroyImage(*image);
3240  *image=chop_image;
3241  /*
3242    Update image configuration.
3243  */
3244  XConfigureImageColormap(display,resource_info,windows,*image,exception);
3245  (void) XConfigureImage(display,resource_info,windows,*image,exception);
3246  return(MagickTrue);
3247}
3248
3249/*
3250%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3251%                                                                             %
3252%                                                                             %
3253%                                                                             %
3254+   X C o l o r E d i t I m a g e                                             %
3255%                                                                             %
3256%                                                                             %
3257%                                                                             %
3258%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3259%
3260%  XColorEditImage() allows the user to interactively change the color of one
3261%  pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3262%
3263%  The format of the XColorEditImage method is:
3264%
3265%      MagickBooleanType XColorEditImage(Display *display,
3266%        XResourceInfo *resource_info,XWindows *windows,Image **image,
3267%          ExceptionInfo *exception)
3268%
3269%  A description of each parameter follows:
3270%
3271%    o display: Specifies a connection to an X server;  returned from
3272%      XOpenDisplay.
3273%
3274%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3275%
3276%    o windows: Specifies a pointer to a XWindows structure.
3277%
3278%    o image: the image; returned from ReadImage.
3279%
3280%    o exception: return any errors or warnings in this structure.
3281%
3282*/
3283static MagickBooleanType XColorEditImage(Display *display,
3284  XResourceInfo *resource_info,XWindows *windows,Image **image,
3285  ExceptionInfo *exception)
3286{
3287  static const char
3288    *ColorEditMenu[] =
3289    {
3290      "Method",
3291      "Pixel Color",
3292      "Border Color",
3293      "Fuzz",
3294      "Undo",
3295      "Help",
3296      "Dismiss",
3297      (char *) NULL
3298    };
3299
3300  static const ModeType
3301    ColorEditCommands[] =
3302    {
3303      ColorEditMethodCommand,
3304      ColorEditColorCommand,
3305      ColorEditBorderCommand,
3306      ColorEditFuzzCommand,
3307      ColorEditUndoCommand,
3308      ColorEditHelpCommand,
3309      ColorEditDismissCommand
3310    };
3311
3312  static PaintMethod
3313    method = PointMethod;
3314
3315  static unsigned int
3316    pen_id = 0;
3317
3318  static XColor
3319    border_color = { 0, 0, 0, 0, 0, 0 };
3320
3321  char
3322    command[MagickPathExtent],
3323    text[MagickPathExtent];
3324
3325  Cursor
3326    cursor;
3327
3328  int
3329    entry,
3330    id,
3331    x,
3332    x_offset,
3333    y,
3334    y_offset;
3335
3336  register Quantum
3337    *q;
3338
3339  register ssize_t
3340    i;
3341
3342  unsigned int
3343    height,
3344    width;
3345
3346  size_t
3347    state;
3348
3349  XColor
3350    color;
3351
3352  XEvent
3353    event;
3354
3355  /*
3356    Map Command widget.
3357  */
3358  (void) CloneString(&windows->command.name,"Color Edit");
3359  windows->command.data=4;
3360  (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3361  (void) XMapRaised(display,windows->command.id);
3362  XClientMessage(display,windows->image.id,windows->im_protocols,
3363    windows->im_update_widget,CurrentTime);
3364  /*
3365    Make cursor.
3366  */
3367  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3368    resource_info->background_color,resource_info->foreground_color);
3369  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3370  /*
3371    Track pointer until button 1 is pressed.
3372  */
3373  XQueryPosition(display,windows->image.id,&x,&y);
3374  (void) XSelectInput(display,windows->image.id,
3375    windows->image.attributes.event_mask | PointerMotionMask);
3376  state=DefaultState;
3377  do
3378  {
3379    if (windows->info.mapped != MagickFalse )
3380      {
3381        /*
3382          Display pointer position.
3383        */
3384        (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
3385          x+windows->image.x,y+windows->image.y);
3386        XInfoWidget(display,windows,text);
3387      }
3388    /*
3389      Wait for next event.
3390    */
3391    XScreenEvent(display,windows,&event,exception);
3392    if (event.xany.window == windows->command.id)
3393      {
3394        /*
3395          Select a command from the Command widget.
3396        */
3397        id=XCommandWidget(display,windows,ColorEditMenu,&event);
3398        if (id < 0)
3399          {
3400            (void) XCheckDefineCursor(display,windows->image.id,cursor);
3401            continue;
3402          }
3403        switch (ColorEditCommands[id])
3404        {
3405          case ColorEditMethodCommand:
3406          {
3407            char
3408              **methods;
3409
3410            /*
3411              Select a method from the pop-up menu.
3412            */
3413            methods=(char **) GetCommandOptions(MagickMethodOptions);
3414            if (methods == (char **) NULL)
3415              break;
3416            entry=XMenuWidget(display,windows,ColorEditMenu[id],
3417              (const char **) methods,command);
3418            if (entry >= 0)
3419              method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3420                MagickFalse,methods[entry]);
3421            methods=DestroyStringList(methods);
3422            break;
3423          }
3424          case ColorEditColorCommand:
3425          {
3426            const char
3427              *ColorMenu[MaxNumberPens];
3428
3429            int
3430              pen_number;
3431
3432            /*
3433              Initialize menu selections.
3434            */
3435            for (i=0; i < (int) (MaxNumberPens-2); i++)
3436              ColorMenu[i]=resource_info->pen_colors[i];
3437            ColorMenu[MaxNumberPens-2]="Browser...";
3438            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3439            /*
3440              Select a pen color from the pop-up menu.
3441            */
3442            pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3443              (const char **) ColorMenu,command);
3444            if (pen_number < 0)
3445              break;
3446            if (pen_number == (MaxNumberPens-2))
3447              {
3448                static char
3449                  color_name[MagickPathExtent] = "gray";
3450
3451                /*
3452                  Select a pen color from a dialog.
3453                */
3454                resource_info->pen_colors[pen_number]=color_name;
3455                XColorBrowserWidget(display,windows,"Select",color_name);
3456                if (*color_name == '\0')
3457                  break;
3458              }
3459            /*
3460              Set pen color.
3461            */
3462            (void) XParseColor(display,windows->map_info->colormap,
3463              resource_info->pen_colors[pen_number],&color);
3464            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3465              (unsigned int) MaxColors,&color);
3466            windows->pixel_info->pen_colors[pen_number]=color;
3467            pen_id=(unsigned int) pen_number;
3468            break;
3469          }
3470          case ColorEditBorderCommand:
3471          {
3472            const char
3473              *ColorMenu[MaxNumberPens];
3474
3475            int
3476              pen_number;
3477
3478            /*
3479              Initialize menu selections.
3480            */
3481            for (i=0; i < (int) (MaxNumberPens-2); i++)
3482              ColorMenu[i]=resource_info->pen_colors[i];
3483            ColorMenu[MaxNumberPens-2]="Browser...";
3484            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3485            /*
3486              Select a pen color from the pop-up menu.
3487            */
3488            pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3489              (const char **) ColorMenu,command);
3490            if (pen_number < 0)
3491              break;
3492            if (pen_number == (MaxNumberPens-2))
3493              {
3494                static char
3495                  color_name[MagickPathExtent] = "gray";
3496
3497                /*
3498                  Select a pen color from a dialog.
3499                */
3500                resource_info->pen_colors[pen_number]=color_name;
3501                XColorBrowserWidget(display,windows,"Select",color_name);
3502                if (*color_name == '\0')
3503                  break;
3504              }
3505            /*
3506              Set border color.
3507            */
3508            (void) XParseColor(display,windows->map_info->colormap,
3509              resource_info->pen_colors[pen_number],&border_color);
3510            break;
3511          }
3512          case ColorEditFuzzCommand:
3513          {
3514            static char
3515              fuzz[MagickPathExtent];
3516
3517            static const char
3518              *FuzzMenu[] =
3519              {
3520                "0%",
3521                "2%",
3522                "5%",
3523                "10%",
3524                "15%",
3525                "Dialog...",
3526                (char *) NULL,
3527              };
3528
3529            /*
3530              Select a command from the pop-up menu.
3531            */
3532            entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3533              command);
3534            if (entry < 0)
3535              break;
3536            if (entry != 5)
3537              {
3538                (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
3539                  QuantumRange+1.0);
3540                break;
3541              }
3542            (void) (void) CopyMagickString(fuzz,"20%",MagickPathExtent);
3543            (void) XDialogWidget(display,windows,"Ok",
3544              "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3545            if (*fuzz == '\0')
3546              break;
3547            (void) ConcatenateMagickString(fuzz,"%",MagickPathExtent);
3548            (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
3549              1.0);
3550            break;
3551          }
3552          case ColorEditUndoCommand:
3553          {
3554            (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3555              image,exception);
3556            break;
3557          }
3558          case ColorEditHelpCommand:
3559          default:
3560          {
3561            XTextViewWidget(display,resource_info,windows,MagickFalse,
3562              "Help Viewer - Image Annotation",ImageColorEditHelp);
3563            break;
3564          }
3565          case ColorEditDismissCommand:
3566          {
3567            /*
3568              Prematurely exit.
3569            */
3570            state|=EscapeState;
3571            state|=ExitState;
3572            break;
3573          }
3574        }
3575        (void) XCheckDefineCursor(display,windows->image.id,cursor);
3576        continue;
3577      }
3578    switch (event.type)
3579    {
3580      case ButtonPress:
3581      {
3582        if (event.xbutton.button != Button1)
3583          break;
3584        if ((event.xbutton.window != windows->image.id) &&
3585            (event.xbutton.window != windows->magnify.id))
3586          break;
3587        /*
3588          exit loop.
3589        */
3590        x=event.xbutton.x;
3591        y=event.xbutton.y;
3592        (void) XMagickCommand(display,resource_info,windows,
3593          SaveToUndoBufferCommand,image,exception);
3594        state|=UpdateConfigurationState;
3595        break;
3596      }
3597      case ButtonRelease:
3598      {
3599        if (event.xbutton.button != Button1)
3600          break;
3601        if ((event.xbutton.window != windows->image.id) &&
3602            (event.xbutton.window != windows->magnify.id))
3603          break;
3604        /*
3605          Update colormap information.
3606        */
3607        x=event.xbutton.x;
3608        y=event.xbutton.y;
3609        XConfigureImageColormap(display,resource_info,windows,*image,exception);
3610        (void) XConfigureImage(display,resource_info,windows,*image,exception);
3611        XInfoWidget(display,windows,text);
3612        (void) XCheckDefineCursor(display,windows->image.id,cursor);
3613        state&=(~UpdateConfigurationState);
3614        break;
3615      }
3616      case Expose:
3617        break;
3618      case KeyPress:
3619      {
3620        KeySym
3621          key_symbol;
3622
3623        if (event.xkey.window == windows->magnify.id)
3624          {
3625            Window
3626              window;
3627
3628            window=windows->magnify.id;
3629            while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3630          }
3631        if (event.xkey.window != windows->image.id)
3632          break;
3633        /*
3634          Respond to a user key press.
3635        */
3636        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3637          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3638        switch ((int) key_symbol)
3639        {
3640          case XK_Escape:
3641          case XK_F20:
3642          {
3643            /*
3644              Prematurely exit.
3645            */
3646            state|=ExitState;
3647            break;
3648          }
3649          case XK_F1:
3650          case XK_Help:
3651          {
3652            XTextViewWidget(display,resource_info,windows,MagickFalse,
3653              "Help Viewer - Image Annotation",ImageColorEditHelp);
3654            break;
3655          }
3656          default:
3657          {
3658            (void) XBell(display,0);
3659            break;
3660          }
3661        }
3662        break;
3663      }
3664      case MotionNotify:
3665      {
3666        /*
3667          Map and unmap Info widget as cursor crosses its boundaries.
3668        */
3669        x=event.xmotion.x;
3670        y=event.xmotion.y;
3671        if (windows->info.mapped != MagickFalse )
3672          {
3673            if ((x < (int) (windows->info.x+windows->info.width)) &&
3674                (y < (int) (windows->info.y+windows->info.height)))
3675              (void) XWithdrawWindow(display,windows->info.id,
3676                windows->info.screen);
3677          }
3678        else
3679          if ((x > (int) (windows->info.x+windows->info.width)) ||
3680              (y > (int) (windows->info.y+windows->info.height)))
3681            (void) XMapWindow(display,windows->info.id);
3682        break;
3683      }
3684      default:
3685        break;
3686    }
3687    if (event.xany.window == windows->magnify.id)
3688      {
3689        x=windows->magnify.x-windows->image.x;
3690        y=windows->magnify.y-windows->image.y;
3691      }
3692    x_offset=x;
3693    y_offset=y;
3694    if ((state & UpdateConfigurationState) != 0)
3695      {
3696        CacheView
3697          *image_view;
3698
3699        int
3700          x,
3701          y;
3702
3703        /*
3704          Pixel edit is relative to image configuration.
3705        */
3706        (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3707          MagickTrue);
3708        color=windows->pixel_info->pen_colors[pen_id];
3709        XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3710        width=(unsigned int) (*image)->columns;
3711        height=(unsigned int) (*image)->rows;
3712        x=0;
3713        y=0;
3714        if (windows->image.crop_geometry != (char *) NULL)
3715          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3716            &width,&height);
3717        x_offset=(int)
3718          (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3719        y_offset=(int)
3720          (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3721        if ((x_offset < 0) || (y_offset < 0))
3722          continue;
3723        if ((x_offset >= (int) (*image)->columns) ||
3724            (y_offset >= (int) (*image)->rows))
3725          continue;
3726        image_view=AcquireAuthenticCacheView(*image,exception);
3727        switch (method)
3728        {
3729          case PointMethod:
3730          default:
3731          {
3732            /*
3733              Update color information using point algorithm.
3734            */
3735            if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3736              return(MagickFalse);
3737            q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3738              (ssize_t) y_offset,1,1,exception);
3739            if (q == (Quantum *) NULL)
3740              break;
3741            SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3742            SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3743            SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3744            (void) SyncCacheViewAuthenticPixels(image_view,exception);
3745            break;
3746          }
3747          case ReplaceMethod:
3748          {
3749            PixelInfo
3750              pixel,
3751              target;
3752
3753            /*
3754              Update color information using replace algorithm.
3755            */
3756            (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
3757              x_offset,(ssize_t) y_offset,&target,exception);
3758            if ((*image)->storage_class == DirectClass)
3759              {
3760                for (y=0; y < (int) (*image)->rows; y++)
3761                {
3762                  q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3763                    (*image)->columns,1,exception);
3764                  if (q == (Quantum *) NULL)
3765                    break;
3766                  for (x=0; x < (int) (*image)->columns; x++)
3767                  {
3768                    GetPixelInfoPixel(*image,q,&pixel);
3769                    if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
3770                      {
3771                        SetPixelRed(*image,ScaleShortToQuantum(
3772                          color.red),q);
3773                        SetPixelGreen(*image,ScaleShortToQuantum(
3774                          color.green),q);
3775                        SetPixelBlue(*image,ScaleShortToQuantum(
3776                          color.blue),q);
3777                      }
3778                    q+=GetPixelChannels(*image);
3779                  }
3780                  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3781                    break;
3782                }
3783              }
3784            else
3785              {
3786                for (i=0; i < (ssize_t) (*image)->colors; i++)
3787                  if (IsFuzzyEquivalencePixelInfo((*image)->colormap+i,&target))
3788                    {
3789                      (*image)->colormap[i].red=(double) ScaleShortToQuantum(
3790                        color.red);
3791                      (*image)->colormap[i].green=(double) ScaleShortToQuantum(
3792                        color.green);
3793                      (*image)->colormap[i].blue=(double) ScaleShortToQuantum(
3794                        color.blue);
3795                    }
3796                (void) SyncImage(*image,exception);
3797              }
3798            break;
3799          }
3800          case FloodfillMethod:
3801          case FillToBorderMethod:
3802          {
3803            DrawInfo
3804              *draw_info;
3805
3806            PixelInfo
3807              target;
3808
3809            /*
3810              Update color information using floodfill algorithm.
3811            */
3812            (void) GetOneVirtualPixelInfo(*image,
3813              GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
3814              y_offset,&target,exception);
3815            if (method == FillToBorderMethod)
3816              {
3817                target.red=(double)
3818                  ScaleShortToQuantum(border_color.red);
3819                target.green=(double)
3820                  ScaleShortToQuantum(border_color.green);
3821                target.blue=(double)
3822                  ScaleShortToQuantum(border_color.blue);
3823              }
3824            draw_info=CloneDrawInfo(resource_info->image_info,
3825              (DrawInfo *) NULL);
3826            (void) QueryColorCompliance(resource_info->pen_colors[pen_id],
3827              AllCompliance,&draw_info->fill,exception);
3828            (void) FloodfillPaintImage(*image,draw_info,&target,
3829              (ssize_t)x_offset,(ssize_t)y_offset,
3830              method != FloodfillMethod ? MagickTrue : MagickFalse,exception);
3831            draw_info=DestroyDrawInfo(draw_info);
3832            break;
3833          }
3834          case ResetMethod:
3835          {
3836            /*
3837              Update color information using reset algorithm.
3838            */
3839            if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3840              return(MagickFalse);
3841            for (y=0; y < (int) (*image)->rows; y++)
3842            {
3843              q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3844                (*image)->columns,1,exception);
3845              if (q == (Quantum *) NULL)
3846                break;
3847              for (x=0; x < (int) (*image)->columns; x++)
3848              {
3849                SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3850                SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3851                SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3852                q+=GetPixelChannels(*image);
3853              }
3854              if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3855                break;
3856            }
3857            break;
3858          }
3859        }
3860        image_view=DestroyCacheView(image_view);
3861        state&=(~UpdateConfigurationState);
3862      }
3863  } while ((state & ExitState) == 0);
3864  (void) XSelectInput(display,windows->image.id,
3865    windows->image.attributes.event_mask);
3866  XSetCursorState(display,windows,MagickFalse);
3867  (void) XFreeCursor(display,cursor);
3868  return(MagickTrue);
3869}
3870
3871/*
3872%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3873%                                                                             %
3874%                                                                             %
3875%                                                                             %
3876+   X C o m p o s i t e I m a g e                                             %
3877%                                                                             %
3878%                                                                             %
3879%                                                                             %
3880%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3881%
3882%  XCompositeImage() requests an image name from the user, reads the image and
3883%  composites it with the X window image at a location the user chooses with
3884%  the pointer.
3885%
3886%  The format of the XCompositeImage method is:
3887%
3888%      MagickBooleanType XCompositeImage(Display *display,
3889%        XResourceInfo *resource_info,XWindows *windows,Image *image,
3890%        ExceptionInfo *exception)
3891%
3892%  A description of each parameter follows:
3893%
3894%    o display: Specifies a connection to an X server;  returned from
3895%      XOpenDisplay.
3896%
3897%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3898%
3899%    o windows: Specifies a pointer to a XWindows structure.
3900%
3901%    o image: the image; returned from ReadImage.
3902%
3903%    o exception: return any errors or warnings in this structure.
3904%
3905*/
3906static MagickBooleanType XCompositeImage(Display *display,
3907  XResourceInfo *resource_info,XWindows *windows,Image *image,
3908  ExceptionInfo *exception)
3909{
3910  static char
3911    displacement_geometry[MagickPathExtent] = "30x30",
3912    filename[MagickPathExtent] = "\0";
3913
3914  static const char
3915    *CompositeMenu[] =
3916    {
3917      "Operators",
3918      "Dissolve",
3919      "Displace",
3920      "Help",
3921      "Dismiss",
3922      (char *) NULL
3923    };
3924
3925  static CompositeOperator
3926    compose = CopyCompositeOp;
3927
3928  static const ModeType
3929    CompositeCommands[] =
3930    {
3931      CompositeOperatorsCommand,
3932      CompositeDissolveCommand,
3933      CompositeDisplaceCommand,
3934      CompositeHelpCommand,
3935      CompositeDismissCommand
3936    };
3937
3938  char
3939    text[MagickPathExtent];
3940
3941  Cursor
3942    cursor;
3943
3944  Image
3945    *composite_image;
3946
3947  int
3948    entry,
3949    id,
3950    x,
3951    y;
3952
3953  double
3954    blend,
3955    scale_factor;
3956
3957  RectangleInfo
3958    highlight_info,
3959    composite_info;
3960
3961  unsigned int
3962    height,
3963    width;
3964
3965  size_t
3966    state;
3967
3968  XEvent
3969    event;
3970
3971  /*
3972    Request image file name from user.
3973  */
3974  XFileBrowserWidget(display,windows,"Composite",filename);
3975  if (*filename == '\0')
3976    return(MagickTrue);
3977  /*
3978    Read image.
3979  */
3980  XSetCursorState(display,windows,MagickTrue);
3981  XCheckRefreshWindows(display,windows);
3982  (void) CopyMagickString(resource_info->image_info->filename,filename,
3983    MagickPathExtent);
3984  composite_image=ReadImage(resource_info->image_info,exception);
3985  CatchException(exception);
3986  XSetCursorState(display,windows,MagickFalse);
3987  if (composite_image == (Image *) NULL)
3988    return(MagickFalse);
3989  /*
3990    Map Command widget.
3991  */
3992  (void) CloneString(&windows->command.name,"Composite");
3993  windows->command.data=1;
3994  (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
3995  (void) XMapRaised(display,windows->command.id);
3996  XClientMessage(display,windows->image.id,windows->im_protocols,
3997    windows->im_update_widget,CurrentTime);
3998  /*
3999    Track pointer until button 1 is pressed.
4000  */
4001  XQueryPosition(display,windows->image.id,&x,&y);
4002  (void) XSelectInput(display,windows->image.id,
4003    windows->image.attributes.event_mask | PointerMotionMask);
4004  composite_info.x=(ssize_t) windows->image.x+x;
4005  composite_info.y=(ssize_t) windows->image.y+y;
4006  composite_info.width=0;
4007  composite_info.height=0;
4008  cursor=XCreateFontCursor(display,XC_ul_angle);
4009  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4010  blend=0.0;
4011  state=DefaultState;
4012  do
4013  {
4014    if (windows->info.mapped != MagickFalse )
4015      {
4016        /*
4017          Display pointer position.
4018        */
4019        (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
4020          (long) composite_info.x,(long) composite_info.y);
4021        XInfoWidget(display,windows,text);
4022      }
4023    highlight_info=composite_info;
4024    highlight_info.x=composite_info.x-windows->image.x;
4025    highlight_info.y=composite_info.y-windows->image.y;
4026    XHighlightRectangle(display,windows->image.id,
4027      windows->image.highlight_context,&highlight_info);
4028    /*
4029      Wait for next event.
4030    */
4031    XScreenEvent(display,windows,&event,exception);
4032    XHighlightRectangle(display,windows->image.id,
4033      windows->image.highlight_context,&highlight_info);
4034    if (event.xany.window == windows->command.id)
4035      {
4036        /*
4037          Select a command from the Command widget.
4038        */
4039        id=XCommandWidget(display,windows,CompositeMenu,&event);
4040        if (id < 0)
4041          continue;
4042        switch (CompositeCommands[id])
4043        {
4044          case CompositeOperatorsCommand:
4045          {
4046            char
4047              command[MagickPathExtent],
4048              **operators;
4049
4050            /*
4051              Select a command from the pop-up menu.
4052            */
4053            operators=GetCommandOptions(MagickComposeOptions);
4054            if (operators == (char **) NULL)
4055              break;
4056            entry=XMenuWidget(display,windows,CompositeMenu[id],
4057              (const char **) operators,command);
4058            if (entry >= 0)
4059              compose=(CompositeOperator) ParseCommandOption(
4060                MagickComposeOptions,MagickFalse,operators[entry]);
4061            operators=DestroyStringList(operators);
4062            break;
4063          }
4064          case CompositeDissolveCommand:
4065          {
4066            static char
4067              factor[MagickPathExtent] = "20.0";
4068
4069            /*
4070              Dissolve the two images a given percent.
4071            */
4072            (void) XSetFunction(display,windows->image.highlight_context,
4073              GXcopy);
4074            (void) XDialogWidget(display,windows,"Dissolve",
4075              "Enter the blend factor (0.0 - 99.9%):",factor);
4076            (void) XSetFunction(display,windows->image.highlight_context,
4077              GXinvert);
4078            if (*factor == '\0')
4079              break;
4080            blend=StringToDouble(factor,(char **) NULL);
4081            compose=DissolveCompositeOp;
4082            break;
4083          }
4084          case CompositeDisplaceCommand:
4085          {
4086            /*
4087              Get horizontal and vertical scale displacement geometry.
4088            */
4089            (void) XSetFunction(display,windows->image.highlight_context,
4090              GXcopy);
4091            (void) XDialogWidget(display,windows,"Displace",
4092              "Enter the horizontal and vertical scale:",displacement_geometry);
4093            (void) XSetFunction(display,windows->image.highlight_context,
4094              GXinvert);
4095            if (*displacement_geometry == '\0')
4096              break;
4097            compose=DisplaceCompositeOp;
4098            break;
4099          }
4100          case CompositeHelpCommand:
4101          {
4102            (void) XSetFunction(display,windows->image.highlight_context,
4103              GXcopy);
4104            XTextViewWidget(display,resource_info,windows,MagickFalse,
4105              "Help Viewer - Image Composite",ImageCompositeHelp);
4106            (void) XSetFunction(display,windows->image.highlight_context,
4107              GXinvert);
4108            break;
4109          }
4110          case CompositeDismissCommand:
4111          {
4112            /*
4113              Prematurely exit.
4114            */
4115            state|=EscapeState;
4116            state|=ExitState;
4117            break;
4118          }
4119          default:
4120            break;
4121        }
4122        continue;
4123      }
4124    switch (event.type)
4125    {
4126      case ButtonPress:
4127      {
4128        if (image->debug != MagickFalse )
4129          (void) LogMagickEvent(X11Event,GetMagickModule(),
4130            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4131            event.xbutton.button,event.xbutton.x,event.xbutton.y);
4132        if (event.xbutton.button != Button1)
4133          break;
4134        if (event.xbutton.window != windows->image.id)
4135          break;
4136        /*
4137          Change cursor.
4138        */
4139        composite_info.width=composite_image->columns;
4140        composite_info.height=composite_image->rows;
4141        (void) XCheckDefineCursor(display,windows->image.id,cursor);
4142        composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4143        composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4144        break;
4145      }
4146      case ButtonRelease:
4147      {
4148        if (image->debug != MagickFalse )
4149          (void) LogMagickEvent(X11Event,GetMagickModule(),
4150            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4151            event.xbutton.button,event.xbutton.x,event.xbutton.y);
4152        if (event.xbutton.button != Button1)
4153          break;
4154        if (event.xbutton.window != windows->image.id)
4155          break;
4156        if ((composite_info.width != 0) && (composite_info.height != 0))
4157          {
4158            /*
4159              User has selected the location of the composite image.
4160            */
4161            composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4162            composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4163            state|=ExitState;
4164          }
4165        break;
4166      }
4167      case Expose:
4168        break;
4169      case KeyPress:
4170      {
4171        char
4172          command[MagickPathExtent];
4173
4174        KeySym
4175          key_symbol;
4176
4177        int
4178          length;
4179
4180        if (event.xkey.window != windows->image.id)
4181          break;
4182        /*
4183          Respond to a user key press.
4184        */
4185        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4186          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4187        *(command+length)='\0';
4188        if (image->debug != MagickFalse )
4189          (void) LogMagickEvent(X11Event,GetMagickModule(),
4190            "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4191        switch ((int) key_symbol)
4192        {
4193          case XK_Escape:
4194          case XK_F20:
4195          {
4196            /*
4197              Prematurely exit.
4198            */
4199            composite_image=DestroyImage(composite_image);
4200            state|=EscapeState;
4201            state|=ExitState;
4202            break;
4203          }
4204          case XK_F1:
4205          case XK_Help:
4206          {
4207            (void) XSetFunction(display,windows->image.highlight_context,
4208              GXcopy);
4209            XTextViewWidget(display,resource_info,windows,MagickFalse,
4210              "Help Viewer - Image Composite",ImageCompositeHelp);
4211            (void) XSetFunction(display,windows->image.highlight_context,
4212              GXinvert);
4213            break;
4214          }
4215          default:
4216          {
4217            (void) XBell(display,0);
4218            break;
4219          }
4220        }
4221        break;
4222      }
4223      case MotionNotify:
4224      {
4225        /*
4226          Map and unmap Info widget as text cursor crosses its boundaries.
4227        */
4228        x=event.xmotion.x;
4229        y=event.xmotion.y;
4230        if (windows->info.mapped != MagickFalse )
4231          {
4232            if ((x < (int) (windows->info.x+windows->info.width)) &&
4233                (y < (int) (windows->info.y+windows->info.height)))
4234              (void) XWithdrawWindow(display,windows->info.id,
4235                windows->info.screen);
4236          }
4237        else
4238          if ((x > (int) (windows->info.x+windows->info.width)) ||
4239              (y > (int) (windows->info.y+windows->info.height)))
4240            (void) XMapWindow(display,windows->info.id);
4241        composite_info.x=(ssize_t) windows->image.x+x;
4242        composite_info.y=(ssize_t) windows->image.y+y;
4243        break;
4244      }
4245      default:
4246      {
4247        if (image->debug != MagickFalse )
4248          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4249            event.type);
4250        break;
4251      }
4252    }
4253  } while ((state & ExitState) == 0);
4254  (void) XSelectInput(display,windows->image.id,
4255    windows->image.attributes.event_mask);
4256  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4257  XSetCursorState(display,windows,MagickFalse);
4258  (void) XFreeCursor(display,cursor);
4259  if ((state & EscapeState) != 0)
4260    return(MagickTrue);
4261  /*
4262    Image compositing is relative to image configuration.
4263  */
4264  XSetCursorState(display,windows,MagickTrue);
4265  XCheckRefreshWindows(display,windows);
4266  width=(unsigned int) image->columns;
4267  height=(unsigned int) image->rows;
4268  x=0;
4269  y=0;
4270  if (windows->image.crop_geometry != (char *) NULL)
4271    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4272  scale_factor=(double) width/windows->image.ximage->width;
4273  composite_info.x+=x;
4274  composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4275  composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4276  scale_factor=(double) height/windows->image.ximage->height;
4277  composite_info.y+=y;
4278  composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4279  composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4280  if ((composite_info.width != composite_image->columns) ||
4281      (composite_info.height != composite_image->rows))
4282    {
4283      Image
4284        *resize_image;
4285
4286      /*
4287        Scale composite image.
4288      */
4289      resize_image=ResizeImage(composite_image,composite_info.width,
4290        composite_info.height,composite_image->filter,exception);
4291      composite_image=DestroyImage(composite_image);
4292      if (resize_image == (Image *) NULL)
4293        {
4294          XSetCursorState(display,windows,MagickFalse);
4295          return(MagickFalse);
4296        }
4297      composite_image=resize_image;
4298    }
4299  if (compose == DisplaceCompositeOp)
4300    (void) SetImageArtifact(composite_image,"compose:args",
4301      displacement_geometry);
4302  if (blend != 0.0)
4303    {
4304      CacheView
4305        *image_view;
4306
4307      int
4308        y;
4309
4310      Quantum
4311        opacity;
4312
4313      register int
4314        x;
4315
4316      register Quantum
4317        *q;
4318
4319      /*
4320        Create mattes for blending.
4321      */
4322      (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,exception);
4323      opacity=(Quantum) (ScaleQuantumToChar(QuantumRange)-
4324        ((ssize_t) ScaleQuantumToChar(QuantumRange)*blend)/100);
4325      if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
4326        return(MagickFalse);
4327      image->alpha_trait=BlendPixelTrait;
4328      image_view=AcquireAuthenticCacheView(image,exception);
4329      for (y=0; y < (int) image->rows; y++)
4330      {
4331        q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4332          exception);
4333        if (q == (Quantum *) NULL)
4334          break;
4335        for (x=0; x < (int) image->columns; x++)
4336        {
4337          SetPixelAlpha(image,opacity,q);
4338          q+=GetPixelChannels(image);
4339        }
4340        if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4341          break;
4342      }
4343      image_view=DestroyCacheView(image_view);
4344    }
4345  /*
4346    Composite image with X Image window.
4347  */
4348  (void) CompositeImage(image,composite_image,compose,MagickTrue,
4349    composite_info.x,composite_info.y,exception);
4350  composite_image=DestroyImage(composite_image);
4351  XSetCursorState(display,windows,MagickFalse);
4352  /*
4353    Update image configuration.
4354  */
4355  XConfigureImageColormap(display,resource_info,windows,image,exception);
4356  (void) XConfigureImage(display,resource_info,windows,image,exception);
4357  return(MagickTrue);
4358}
4359
4360/*
4361%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4362%                                                                             %
4363%                                                                             %
4364%                                                                             %
4365+   X C o n f i g u r e I m a g e                                             %
4366%                                                                             %
4367%                                                                             %
4368%                                                                             %
4369%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4370%
4371%  XConfigureImage() creates a new X image.  It also notifies the window
4372%  manager of the new image size and configures the transient widows.
4373%
4374%  The format of the XConfigureImage method is:
4375%
4376%      MagickBooleanType XConfigureImage(Display *display,
4377%        XResourceInfo *resource_info,XWindows *windows,Image *image,
4378%        ExceptionInfo *exception)
4379%
4380%  A description of each parameter follows:
4381%
4382%    o display: Specifies a connection to an X server; returned from
4383%      XOpenDisplay.
4384%
4385%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4386%
4387%    o windows: Specifies a pointer to a XWindows structure.
4388%
4389%    o image: the image.
4390%
4391%    o exception: return any errors or warnings in this structure.
4392%
4393%    o exception: return any errors or warnings in this structure.
4394%
4395*/
4396static MagickBooleanType XConfigureImage(Display *display,
4397  XResourceInfo *resource_info,XWindows *windows,Image *image,
4398  ExceptionInfo *exception)
4399{
4400  char
4401    geometry[MagickPathExtent];
4402
4403  MagickStatusType
4404    status;
4405
4406  size_t
4407    mask,
4408    height,
4409    width;
4410
4411  ssize_t
4412    x,
4413    y;
4414
4415  XSizeHints
4416    *size_hints;
4417
4418  XWindowChanges
4419    window_changes;
4420
4421  /*
4422    Dismiss if window dimensions are zero.
4423  */
4424  width=(unsigned int) windows->image.window_changes.width;
4425  height=(unsigned int) windows->image.window_changes.height;
4426  if (image->debug != MagickFalse )
4427    (void) LogMagickEvent(X11Event,GetMagickModule(),
4428      "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4429      windows->image.ximage->height,(double) width,(double) height);
4430  if ((width*height) == 0)
4431    return(MagickTrue);
4432  x=0;
4433  y=0;
4434  /*
4435    Resize image to fit Image window dimensions.
4436  */
4437  XSetCursorState(display,windows,MagickTrue);
4438  (void) XFlush(display);
4439  if (((int) width != windows->image.ximage->width) ||
4440      ((int) height != windows->image.ximage->height))
4441    image->taint=MagickTrue;
4442  windows->magnify.x=(int)
4443    width*windows->magnify.x/windows->image.ximage->width;
4444  windows->magnify.y=(int)
4445    height*windows->magnify.y/windows->image.ximage->height;
4446  windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4447  windows->image.y=(int)
4448    (height*windows->image.y/windows->image.ximage->height);
4449  status=XMakeImage(display,resource_info,&windows->image,image,
4450    (unsigned int) width,(unsigned int) height,exception);
4451  if (status == MagickFalse)
4452    XNoticeWidget(display,windows,"Unable to configure X image:",
4453      windows->image.name);
4454  /*
4455    Notify window manager of the new configuration.
4456  */
4457  if (resource_info->image_geometry != (char *) NULL)
4458    (void) FormatLocaleString(geometry,MagickPathExtent,"%s>!",
4459      resource_info->image_geometry);
4460  else
4461    (void) FormatLocaleString(geometry,MagickPathExtent,"%ux%u+0+0>!",
4462      XDisplayWidth(display,windows->image.screen),
4463      XDisplayHeight(display,windows->image.screen));
4464  (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4465  window_changes.width=(int) width;
4466  if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4467    window_changes.width=XDisplayWidth(display,windows->image.screen);
4468  window_changes.height=(int) height;
4469  if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4470    window_changes.height=XDisplayHeight(display,windows->image.screen);
4471  mask=(size_t) (CWWidth | CWHeight);
4472  if (resource_info->backdrop)
4473    {
4474      mask|=CWX | CWY;
4475      window_changes.x=(int)
4476        ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4477      window_changes.y=(int)
4478        ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4479    }
4480  (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4481    (unsigned int) mask,&window_changes);
4482  (void) XClearWindow(display,windows->image.id);
4483  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4484  /*
4485    Update Magnify window configuration.
4486  */
4487  if (windows->magnify.mapped != MagickFalse )
4488    XMakeMagnifyImage(display,windows,exception);
4489  windows->pan.crop_geometry=windows->image.crop_geometry;
4490  XBestIconSize(display,&windows->pan,image);
4491  while (((windows->pan.width << 1) < MaxIconSize) &&
4492         ((windows->pan.height << 1) < MaxIconSize))
4493  {
4494    windows->pan.width<<=1;
4495    windows->pan.height<<=1;
4496  }
4497  if (windows->pan.geometry != (char *) NULL)
4498    (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4499      &windows->pan.width,&windows->pan.height);
4500  window_changes.width=(int) windows->pan.width;
4501  window_changes.height=(int) windows->pan.height;
4502  size_hints=XAllocSizeHints();
4503  if (size_hints != (XSizeHints *) NULL)
4504    {
4505      /*
4506        Set new size hints.
4507      */
4508      size_hints->flags=PSize | PMinSize | PMaxSize;
4509      size_hints->width=window_changes.width;
4510      size_hints->height=window_changes.height;
4511      size_hints->min_width=size_hints->width;
4512      size_hints->min_height=size_hints->height;
4513      size_hints->max_width=size_hints->width;
4514      size_hints->max_height=size_hints->height;
4515      (void) XSetNormalHints(display,windows->pan.id,size_hints);
4516      (void) XFree((void *) size_hints);
4517    }
4518  (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4519    (unsigned int) (CWWidth | CWHeight),&window_changes);
4520  /*
4521    Update icon window configuration.
4522  */
4523  windows->icon.crop_geometry=windows->image.crop_geometry;
4524  XBestIconSize(display,&windows->icon,image);
4525  window_changes.width=(int) windows->icon.width;
4526  window_changes.height=(int) windows->icon.height;
4527  (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4528    (unsigned int) (CWWidth | CWHeight),&window_changes);
4529  XSetCursorState(display,windows,MagickFalse);
4530  return(status != 0 ? MagickTrue : MagickFalse);
4531}
4532
4533/*
4534%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4535%                                                                             %
4536%                                                                             %
4537%                                                                             %
4538+   X C r o p I m a g e                                                       %
4539%                                                                             %
4540%                                                                             %
4541%                                                                             %
4542%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4543%
4544%  XCropImage() allows the user to select a region of the image and crop, copy,
4545%  or cut it.  For copy or cut, the image can subsequently be composited onto
4546%  the image with XPasteImage.
4547%
4548%  The format of the XCropImage method is:
4549%
4550%      MagickBooleanType XCropImage(Display *display,
4551%        XResourceInfo *resource_info,XWindows *windows,Image *image,
4552%        const ClipboardMode mode,ExceptionInfo *exception)
4553%
4554%  A description of each parameter follows:
4555%
4556%    o display: Specifies a connection to an X server; returned from
4557%      XOpenDisplay.
4558%
4559%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4560%
4561%    o windows: Specifies a pointer to a XWindows structure.
4562%
4563%    o image: the image; returned from ReadImage.
4564%
4565%    o mode: This unsigned value specified whether the image should be
4566%      cropped, copied, or cut.
4567%
4568%    o exception: return any errors or warnings in this structure.
4569%
4570*/
4571static MagickBooleanType XCropImage(Display *display,
4572  XResourceInfo *resource_info,XWindows *windows,Image *image,
4573  const ClipboardMode mode,ExceptionInfo *exception)
4574{
4575  static const char
4576    *CropModeMenu[] =
4577    {
4578      "Help",
4579      "Dismiss",
4580      (char *) NULL
4581    },
4582    *RectifyModeMenu[] =
4583    {
4584      "Crop",
4585      "Help",
4586      "Dismiss",
4587      (char *) NULL
4588    };
4589
4590  static const ModeType
4591    CropCommands[] =
4592    {
4593      CropHelpCommand,
4594      CropDismissCommand
4595    },
4596    RectifyCommands[] =
4597    {
4598      RectifyCopyCommand,
4599      RectifyHelpCommand,
4600      RectifyDismissCommand
4601    };
4602
4603  CacheView
4604    *image_view;
4605
4606  char
4607    command[MagickPathExtent],
4608    text[MagickPathExtent];
4609
4610  Cursor
4611    cursor;
4612
4613  int
4614    id,
4615    x,
4616    y;
4617
4618  KeySym
4619    key_symbol;
4620
4621  Image
4622    *crop_image;
4623
4624  double
4625    scale_factor;
4626
4627  RectangleInfo
4628    crop_info,
4629    highlight_info;
4630
4631  register Quantum
4632    *q;
4633
4634  unsigned int
4635    height,
4636    width;
4637
4638  size_t
4639    state;
4640
4641  XEvent
4642    event;
4643
4644  /*
4645    Map Command widget.
4646  */
4647  switch (mode)
4648  {
4649    case CopyMode:
4650    {
4651      (void) CloneString(&windows->command.name,"Copy");
4652      break;
4653    }
4654    case CropMode:
4655    {
4656      (void) CloneString(&windows->command.name,"Crop");
4657      break;
4658    }
4659    case CutMode:
4660    {
4661      (void) CloneString(&windows->command.name,"Cut");
4662      break;
4663    }
4664  }
4665  RectifyModeMenu[0]=windows->command.name;
4666  windows->command.data=0;
4667  (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4668  (void) XMapRaised(display,windows->command.id);
4669  XClientMessage(display,windows->image.id,windows->im_protocols,
4670    windows->im_update_widget,CurrentTime);
4671  /*
4672    Track pointer until button 1 is pressed.
4673  */
4674  XQueryPosition(display,windows->image.id,&x,&y);
4675  (void) XSelectInput(display,windows->image.id,
4676    windows->image.attributes.event_mask | PointerMotionMask);
4677  crop_info.x=(ssize_t) windows->image.x+x;
4678  crop_info.y=(ssize_t) windows->image.y+y;
4679  crop_info.width=0;
4680  crop_info.height=0;
4681  cursor=XCreateFontCursor(display,XC_fleur);
4682  state=DefaultState;
4683  do
4684  {
4685    if (windows->info.mapped != MagickFalse )
4686      {
4687        /*
4688          Display pointer position.
4689        */
4690        (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
4691          (long) crop_info.x,(long) crop_info.y);
4692        XInfoWidget(display,windows,text);
4693      }
4694    /*
4695      Wait for next event.
4696    */
4697    XScreenEvent(display,windows,&event,exception);
4698    if (event.xany.window == windows->command.id)
4699      {
4700        /*
4701          Select a command from the Command widget.
4702        */
4703        id=XCommandWidget(display,windows,CropModeMenu,&event);
4704        if (id < 0)
4705          continue;
4706        switch (CropCommands[id])
4707        {
4708          case CropHelpCommand:
4709          {
4710            switch (mode)
4711            {
4712              case CopyMode:
4713              {
4714                XTextViewWidget(display,resource_info,windows,MagickFalse,
4715                  "Help Viewer - Image Copy",ImageCopyHelp);
4716                break;
4717              }
4718              case CropMode:
4719              {
4720                XTextViewWidget(display,resource_info,windows,MagickFalse,
4721                  "Help Viewer - Image Crop",ImageCropHelp);
4722                break;
4723              }
4724              case CutMode:
4725              {
4726                XTextViewWidget(display,resource_info,windows,MagickFalse,
4727                  "Help Viewer - Image Cut",ImageCutHelp);
4728                break;
4729              }
4730            }
4731            break;
4732          }
4733          case CropDismissCommand:
4734          {
4735            /*
4736              Prematurely exit.
4737            */
4738            state|=EscapeState;
4739            state|=ExitState;
4740            break;
4741          }
4742          default:
4743            break;
4744        }
4745        continue;
4746      }
4747    switch (event.type)
4748    {
4749      case ButtonPress:
4750      {
4751        if (event.xbutton.button != Button1)
4752          break;
4753        if (event.xbutton.window != windows->image.id)
4754          break;
4755        /*
4756          Note first corner of cropping rectangle-- exit loop.
4757        */
4758        (void) XCheckDefineCursor(display,windows->image.id,cursor);
4759        crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4760        crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4761        state|=ExitState;
4762        break;
4763      }
4764      case ButtonRelease:
4765        break;
4766      case Expose:
4767        break;
4768      case KeyPress:
4769      {
4770        if (event.xkey.window != windows->image.id)
4771          break;
4772        /*
4773          Respond to a user key press.
4774        */
4775        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4776          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4777        switch ((int) key_symbol)
4778        {
4779          case XK_Escape:
4780          case XK_F20:
4781          {
4782            /*
4783              Prematurely exit.
4784            */
4785            state|=EscapeState;
4786            state|=ExitState;
4787            break;
4788          }
4789          case XK_F1:
4790          case XK_Help:
4791          {
4792            switch (mode)
4793            {
4794              case CopyMode:
4795              {
4796                XTextViewWidget(display,resource_info,windows,MagickFalse,
4797                  "Help Viewer - Image Copy",ImageCopyHelp);
4798                break;
4799              }
4800              case CropMode:
4801              {
4802                XTextViewWidget(display,resource_info,windows,MagickFalse,
4803                  "Help Viewer - Image Crop",ImageCropHelp);
4804                break;
4805              }
4806              case CutMode:
4807              {
4808                XTextViewWidget(display,resource_info,windows,MagickFalse,
4809                  "Help Viewer - Image Cut",ImageCutHelp);
4810                break;
4811              }
4812            }
4813            break;
4814          }
4815          default:
4816          {
4817            (void) XBell(display,0);
4818            break;
4819          }
4820        }
4821        break;
4822      }
4823      case MotionNotify:
4824      {
4825        if (event.xmotion.window != windows->image.id)
4826          break;
4827        /*
4828          Map and unmap Info widget as text cursor crosses its boundaries.
4829        */
4830        x=event.xmotion.x;
4831        y=event.xmotion.y;
4832        if (windows->info.mapped != MagickFalse )
4833          {
4834            if ((x < (int) (windows->info.x+windows->info.width)) &&
4835                (y < (int) (windows->info.y+windows->info.height)))
4836              (void) XWithdrawWindow(display,windows->info.id,
4837                windows->info.screen);
4838          }
4839        else
4840          if ((x > (int) (windows->info.x+windows->info.width)) ||
4841              (y > (int) (windows->info.y+windows->info.height)))
4842            (void) XMapWindow(display,windows->info.id);
4843        crop_info.x=(ssize_t) windows->image.x+x;
4844        crop_info.y=(ssize_t) windows->image.y+y;
4845        break;
4846      }
4847      default:
4848        break;
4849    }
4850  } while ((state & ExitState) == 0);
4851  (void) XSelectInput(display,windows->image.id,
4852    windows->image.attributes.event_mask);
4853  if ((state & EscapeState) != 0)
4854    {
4855      /*
4856        User want to exit without cropping.
4857      */
4858      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4859      (void) XFreeCursor(display,cursor);
4860      return(MagickTrue);
4861    }
4862  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4863  do
4864  {
4865    /*
4866      Size rectangle as pointer moves until the mouse button is released.
4867    */
4868    x=(int) crop_info.x;
4869    y=(int) crop_info.y;
4870    crop_info.width=0;
4871    crop_info.height=0;
4872    state=DefaultState;
4873    do
4874    {
4875      highlight_info=crop_info;
4876      highlight_info.x=crop_info.x-windows->image.x;
4877      highlight_info.y=crop_info.y-windows->image.y;
4878      if ((highlight_info.width > 3) && (highlight_info.height > 3))
4879        {
4880          /*
4881            Display info and draw cropping rectangle.
4882          */
4883          if (windows->info.mapped == MagickFalse)
4884            (void) XMapWindow(display,windows->info.id);
4885          (void) FormatLocaleString(text,MagickPathExtent,
4886            " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4887            crop_info.height,(double) crop_info.x,(double) crop_info.y);
4888          XInfoWidget(display,windows,text);
4889          XHighlightRectangle(display,windows->image.id,
4890            windows->image.highlight_context,&highlight_info);
4891        }
4892      else
4893        if (windows->info.mapped != MagickFalse )
4894          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4895      /*
4896        Wait for next event.
4897      */
4898      XScreenEvent(display,windows,&event,exception);
4899      if ((highlight_info.width > 3) && (highlight_info.height > 3))
4900        XHighlightRectangle(display,windows->image.id,
4901          windows->image.highlight_context,&highlight_info);
4902      switch (event.type)
4903      {
4904        case ButtonPress:
4905        {
4906          crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4907          crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4908          break;
4909        }
4910        case ButtonRelease:
4911        {
4912          /*
4913            User has committed to cropping rectangle.
4914          */
4915          crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4916          crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4917          XSetCursorState(display,windows,MagickFalse);
4918          state|=ExitState;
4919          windows->command.data=0;
4920          (void) XCommandWidget(display,windows,RectifyModeMenu,
4921            (XEvent *) NULL);
4922          break;
4923        }
4924        case Expose:
4925          break;
4926        case MotionNotify:
4927        {
4928          crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4929          crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4930        }
4931        default:
4932          break;
4933      }
4934      if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4935          ((state & ExitState) != 0))
4936        {
4937          /*
4938            Check boundary conditions.
4939          */
4940          if (crop_info.x < 0)
4941            crop_info.x=0;
4942          else
4943            if (crop_info.x > (ssize_t) windows->image.ximage->width)
4944              crop_info.x=(ssize_t) windows->image.ximage->width;
4945          if ((int) crop_info.x < x)
4946            crop_info.width=(unsigned int) (x-crop_info.x);
4947          else
4948            {
4949              crop_info.width=(unsigned int) (crop_info.x-x);
4950              crop_info.x=(ssize_t) x;
4951            }
4952          if (crop_info.y < 0)
4953            crop_info.y=0;
4954          else
4955            if (crop_info.y > (ssize_t) windows->image.ximage->height)
4956              crop_info.y=(ssize_t) windows->image.ximage->height;
4957          if ((int) crop_info.y < y)
4958            crop_info.height=(unsigned int) (y-crop_info.y);
4959          else
4960            {
4961              crop_info.height=(unsigned int) (crop_info.y-y);
4962              crop_info.y=(ssize_t) y;
4963            }
4964        }
4965    } while ((state & ExitState) == 0);
4966    /*
4967      Wait for user to grab a corner of the rectangle or press return.
4968    */
4969    state=DefaultState;
4970    (void) XMapWindow(display,windows->info.id);
4971    do
4972    {
4973      if (windows->info.mapped != MagickFalse )
4974        {
4975          /*
4976            Display pointer position.
4977          */
4978          (void) FormatLocaleString(text,MagickPathExtent,
4979            " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4980            crop_info.height,(double) crop_info.x,(double) crop_info.y);
4981          XInfoWidget(display,windows,text);
4982        }
4983      highlight_info=crop_info;
4984      highlight_info.x=crop_info.x-windows->image.x;
4985      highlight_info.y=crop_info.y-windows->image.y;
4986      if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4987        {
4988          state|=EscapeState;
4989          state|=ExitState;
4990          break;
4991        }
4992      XHighlightRectangle(display,windows->image.id,
4993        windows->image.highlight_context,&highlight_info);
4994      XScreenEvent(display,windows,&event,exception);
4995      if (event.xany.window == windows->command.id)
4996        {
4997          /*
4998            Select a command from the Command widget.
4999          */
5000          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5001          id=XCommandWidget(display,windows,RectifyModeMenu,&event);
5002          (void) XSetFunction(display,windows->image.highlight_context,
5003            GXinvert);
5004          XHighlightRectangle(display,windows->image.id,
5005            windows->image.highlight_context,&highlight_info);
5006          if (id >= 0)
5007            switch (RectifyCommands[id])
5008            {
5009              case RectifyCopyCommand:
5010              {
5011                state|=ExitState;
5012                break;
5013              }
5014              case RectifyHelpCommand:
5015              {
5016                (void) XSetFunction(display,windows->image.highlight_context,
5017                  GXcopy);
5018                switch (mode)
5019                {
5020                  case CopyMode:
5021                  {
5022                    XTextViewWidget(display,resource_info,windows,MagickFalse,
5023                      "Help Viewer - Image Copy",ImageCopyHelp);
5024                    break;
5025                  }
5026                  case CropMode:
5027                  {
5028                    XTextViewWidget(display,resource_info,windows,MagickFalse,
5029                      "Help Viewer - Image Crop",ImageCropHelp);
5030                    break;
5031                  }
5032                  case CutMode:
5033                  {
5034                    XTextViewWidget(display,resource_info,windows,MagickFalse,
5035                      "Help Viewer - Image Cut",ImageCutHelp);
5036                    break;
5037                  }
5038                }
5039                (void) XSetFunction(display,windows->image.highlight_context,
5040                  GXinvert);
5041                break;
5042              }
5043              case RectifyDismissCommand:
5044              {
5045                /*
5046                  Prematurely exit.
5047                */
5048                state|=EscapeState;
5049                state|=ExitState;
5050                break;
5051              }
5052              default:
5053                break;
5054            }
5055          continue;
5056        }
5057      XHighlightRectangle(display,windows->image.id,
5058        windows->image.highlight_context,&highlight_info);
5059      switch (event.type)
5060      {
5061        case ButtonPress:
5062        {
5063          if (event.xbutton.button != Button1)
5064            break;
5065          if (event.xbutton.window != windows->image.id)
5066            break;
5067          x=windows->image.x+event.xbutton.x;
5068          y=windows->image.y+event.xbutton.y;
5069          if ((x < (int) (crop_info.x+RoiDelta)) &&
5070              (x > (int) (crop_info.x-RoiDelta)) &&
5071              (y < (int) (crop_info.y+RoiDelta)) &&
5072              (y > (int) (crop_info.y-RoiDelta)))
5073            {
5074              crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5075              crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5076              state|=UpdateConfigurationState;
5077              break;
5078            }
5079          if ((x < (int) (crop_info.x+RoiDelta)) &&
5080              (x > (int) (crop_info.x-RoiDelta)) &&
5081              (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5082              (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5083            {
5084              crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5085              state|=UpdateConfigurationState;
5086              break;
5087            }
5088          if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5089              (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5090              (y < (int) (crop_info.y+RoiDelta)) &&
5091              (y > (int) (crop_info.y-RoiDelta)))
5092            {
5093              crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5094              state|=UpdateConfigurationState;
5095              break;
5096            }
5097          if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5098              (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5099              (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5100              (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5101            {
5102              state|=UpdateConfigurationState;
5103              break;
5104            }
5105        }
5106        case ButtonRelease:
5107        {
5108          if (event.xbutton.window == windows->pan.id)
5109            if ((highlight_info.x != crop_info.x-windows->image.x) ||
5110                (highlight_info.y != crop_info.y-windows->image.y))
5111              XHighlightRectangle(display,windows->image.id,
5112                windows->image.highlight_context,&highlight_info);
5113          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5114            event.xbutton.time);
5115          break;
5116        }
5117        case Expose:
5118        {
5119          if (event.xexpose.window == windows->image.id)
5120            if (event.xexpose.count == 0)
5121              {
5122                event.xexpose.x=(int) highlight_info.x;
5123                event.xexpose.y=(int) highlight_info.y;
5124                event.xexpose.width=(int) highlight_info.width;
5125                event.xexpose.height=(int) highlight_info.height;
5126                XRefreshWindow(display,&windows->image,&event);
5127              }
5128          if (event.xexpose.window == windows->info.id)
5129            if (event.xexpose.count == 0)
5130              XInfoWidget(display,windows,text);
5131          break;
5132        }
5133        case KeyPress:
5134        {
5135          if (event.xkey.window != windows->image.id)
5136            break;
5137          /*
5138            Respond to a user key press.
5139          */
5140          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5141            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5142          switch ((int) key_symbol)
5143          {
5144            case XK_Escape:
5145            case XK_F20:
5146              state|=EscapeState;
5147            case XK_Return:
5148            {
5149              state|=ExitState;
5150              break;
5151            }
5152            case XK_Home:
5153            case XK_KP_Home:
5154            {
5155              crop_info.x=(ssize_t) (windows->image.width/2L-crop_info.width/
5156                2L);
5157              crop_info.y=(ssize_t) (windows->image.height/2L-crop_info.height/
5158                2L);
5159              break;
5160            }
5161            case XK_Left:
5162            case XK_KP_Left:
5163            {
5164              crop_info.x--;
5165              break;
5166            }
5167            case XK_Up:
5168            case XK_KP_Up:
5169            case XK_Next:
5170            {
5171              crop_info.y--;
5172              break;
5173            }
5174            case XK_Right:
5175            case XK_KP_Right:
5176            {
5177              crop_info.x++;
5178              break;
5179            }
5180            case XK_Prior:
5181            case XK_Down:
5182            case XK_KP_Down:
5183            {
5184              crop_info.y++;
5185              break;
5186            }
5187            case XK_F1:
5188            case XK_Help:
5189            {
5190              (void) XSetFunction(display,windows->image.highlight_context,
5191                GXcopy);
5192              switch (mode)
5193              {
5194                case CopyMode:
5195                {
5196                  XTextViewWidget(display,resource_info,windows,MagickFalse,
5197                    "Help Viewer - Image Copy",ImageCopyHelp);
5198                  break;
5199                }
5200                case CropMode:
5201                {
5202                  XTextViewWidget(display,resource_info,windows,MagickFalse,
5203                    "Help Viewer - Image Cropg",ImageCropHelp);
5204                  break;
5205                }
5206                case CutMode:
5207                {
5208                  XTextViewWidget(display,resource_info,windows,MagickFalse,
5209                    "Help Viewer - Image Cutg",ImageCutHelp);
5210                  break;
5211                }
5212              }
5213              (void) XSetFunction(display,windows->image.highlight_context,
5214                GXinvert);
5215              break;
5216            }
5217            default:
5218            {
5219              (void) XBell(display,0);
5220              break;
5221            }
5222          }
5223          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5224            event.xkey.time);
5225          break;
5226        }
5227        case KeyRelease:
5228          break;
5229        case MotionNotify:
5230        {
5231          if (event.xmotion.window != windows->image.id)
5232            break;
5233          /*
5234            Map and unmap Info widget as text cursor crosses its boundaries.
5235          */
5236          x=event.xmotion.x;
5237          y=event.xmotion.y;
5238          if (windows->info.mapped != MagickFalse )
5239            {
5240              if ((x < (int) (windows->info.x+windows->info.width)) &&
5241                  (y < (int) (windows->info.y+windows->info.height)))
5242                (void) XWithdrawWindow(display,windows->info.id,
5243                  windows->info.screen);
5244            }
5245          else
5246            if ((x > (int) (windows->info.x+windows->info.width)) ||
5247                (y > (int) (windows->info.y+windows->info.height)))
5248              (void) XMapWindow(display,windows->info.id);
5249          crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5250          crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5251          break;
5252        }
5253        case SelectionRequest:
5254        {
5255          XSelectionEvent
5256            notify;
5257
5258          XSelectionRequestEvent
5259            *request;
5260
5261          /*
5262            Set primary selection.
5263          */
5264          (void) FormatLocaleString(text,MagickPathExtent,
5265            "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5266            crop_info.height,(double) crop_info.x,(double) crop_info.y);
5267          request=(&(event.xselectionrequest));
5268          (void) XChangeProperty(request->display,request->requestor,
5269            request->property,request->target,8,PropModeReplace,
5270            (unsigned char *) text,(int) strlen(text));
5271          notify.type=SelectionNotify;
5272          notify.display=request->display;
5273          notify.requestor=request->requestor;
5274          notify.selection=request->selection;
5275          notify.target=request->target;
5276          notify.time=request->time;
5277          if (request->property == None)
5278            notify.property=request->target;
5279          else
5280            notify.property=request->property;
5281          (void) XSendEvent(request->display,request->requestor,False,0,
5282            (XEvent *) &notify);
5283        }
5284        default:
5285          break;
5286      }
5287      if ((state & UpdateConfigurationState) != 0)
5288        {
5289          (void) XPutBackEvent(display,&event);
5290          (void) XCheckDefineCursor(display,windows->image.id,cursor);
5291          break;
5292        }
5293    } while ((state & ExitState) == 0);
5294  } while ((state & ExitState) == 0);
5295  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5296  XSetCursorState(display,windows,MagickFalse);
5297  if ((state & EscapeState) != 0)
5298    return(MagickTrue);
5299  if (mode == CropMode)
5300    if (((int) crop_info.width != windows->image.ximage->width) ||
5301        ((int) crop_info.height != windows->image.ximage->height))
5302      {
5303        /*
5304          Reconfigure Image window as defined by cropping rectangle.
5305        */
5306        XSetCropGeometry(display,windows,&crop_info,image);
5307        windows->image.window_changes.width=(int) crop_info.width;
5308        windows->image.window_changes.height=(int) crop_info.height;
5309        (void) XConfigureImage(display,resource_info,windows,image,exception);
5310        return(MagickTrue);
5311      }
5312  /*
5313    Copy image before applying image transforms.
5314  */
5315  XSetCursorState(display,windows,MagickTrue);
5316  XCheckRefreshWindows(display,windows);
5317  width=(unsigned int) image->columns;
5318  height=(unsigned int) image->rows;
5319  x=0;
5320  y=0;
5321  if (windows->image.crop_geometry != (char *) NULL)
5322    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5323  scale_factor=(double) width/windows->image.ximage->width;
5324  crop_info.x+=x;
5325  crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5326  crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5327  scale_factor=(double) height/windows->image.ximage->height;
5328  crop_info.y+=y;
5329  crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5330  crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5331  crop_image=CropImage(image,&crop_info,exception);
5332  XSetCursorState(display,windows,MagickFalse);
5333  if (crop_image == (Image *) NULL)
5334    return(MagickFalse);
5335  if (resource_info->copy_image != (Image *) NULL)
5336    resource_info->copy_image=DestroyImage(resource_info->copy_image);
5337  resource_info->copy_image=crop_image;
5338  if (mode == CopyMode)
5339    {
5340      (void) XConfigureImage(display,resource_info,windows,image,exception);
5341      return(MagickTrue);
5342    }
5343  /*
5344    Cut image.
5345  */
5346  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
5347    return(MagickFalse);
5348  image->alpha_trait=BlendPixelTrait;
5349  image_view=AcquireAuthenticCacheView(image,exception);
5350  for (y=0; y < (int) crop_info.height; y++)
5351  {
5352    q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5353      crop_info.width,1,exception);
5354    if (q == (Quantum *) NULL)
5355      break;
5356    for (x=0; x < (int) crop_info.width; x++)
5357    {
5358      SetPixelAlpha(image,TransparentAlpha,q);
5359      q+=GetPixelChannels(image);
5360    }
5361    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5362      break;
5363  }
5364  image_view=DestroyCacheView(image_view);
5365  /*
5366    Update image configuration.
5367  */
5368  XConfigureImageColormap(display,resource_info,windows,image,exception);
5369  (void) XConfigureImage(display,resource_info,windows,image,exception);
5370  return(MagickTrue);
5371}
5372
5373/*
5374%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5375%                                                                             %
5376%                                                                             %
5377%                                                                             %
5378+   X D r a w I m a g e                                                       %
5379%                                                                             %
5380%                                                                             %
5381%                                                                             %
5382%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5383%
5384%  XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5385%  the image.
5386%
5387%  The format of the XDrawEditImage method is:
5388%
5389%      MagickBooleanType XDrawEditImage(Display *display,
5390%        XResourceInfo *resource_info,XWindows *windows,Image **image,
5391%        ExceptionInfo *exception)
5392%
5393%  A description of each parameter follows:
5394%
5395%    o display: Specifies a connection to an X server; returned from
5396%      XOpenDisplay.
5397%
5398%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5399%
5400%    o windows: Specifies a pointer to a XWindows structure.
5401%
5402%    o image: the image.
5403%
5404%    o exception: return any errors or warnings in this structure.
5405%
5406*/
5407static MagickBooleanType XDrawEditImage(Display *display,
5408  XResourceInfo *resource_info,XWindows *windows,Image **image,
5409  ExceptionInfo *exception)
5410{
5411  static const char
5412    *DrawMenu[] =
5413    {
5414      "Element",
5415      "Color",
5416      "Stipple",
5417      "Width",
5418      "Undo",
5419      "Help",
5420      "Dismiss",
5421      (char *) NULL
5422    };
5423
5424  static ElementType
5425    element = PointElement;
5426
5427  static const ModeType
5428    DrawCommands[] =
5429    {
5430      DrawElementCommand,
5431      DrawColorCommand,
5432      DrawStippleCommand,
5433      DrawWidthCommand,
5434      DrawUndoCommand,
5435      DrawHelpCommand,
5436      DrawDismissCommand
5437    };
5438
5439  static Pixmap
5440    stipple = (Pixmap) NULL;
5441
5442  static unsigned int
5443    pen_id = 0,
5444    line_width = 1;
5445
5446  char
5447    command[MagickPathExtent],
5448    text[MagickPathExtent];
5449
5450  Cursor
5451    cursor;
5452
5453  int
5454    entry,
5455    id,
5456    number_coordinates,
5457    x,
5458    y;
5459
5460  double
5461    degrees;
5462
5463  MagickStatusType
5464    status;
5465
5466  RectangleInfo
5467    rectangle_info;
5468
5469  register int
5470    i;
5471
5472  unsigned int
5473    distance,
5474    height,
5475    max_coordinates,
5476    width;
5477
5478  size_t
5479    state;
5480
5481  Window
5482    root_window;
5483
5484  XDrawInfo
5485    draw_info;
5486
5487  XEvent
5488    event;
5489
5490  XPoint
5491    *coordinate_info;
5492
5493  XSegment
5494    line_info;
5495
5496  /*
5497    Allocate polygon info.
5498  */
5499  max_coordinates=2048;
5500  coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5501    sizeof(*coordinate_info));
5502  if (coordinate_info == (XPoint *) NULL)
5503    {
5504      (void) ThrowMagickException(exception,GetMagickModule(),
5505        ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5506      return(MagickFalse);
5507    }
5508  /*
5509    Map Command widget.
5510  */
5511  (void) CloneString(&windows->command.name,"Draw");
5512  windows->command.data=4;
5513  (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5514  (void) XMapRaised(display,windows->command.id);
5515  XClientMessage(display,windows->image.id,windows->im_protocols,
5516    windows->im_update_widget,CurrentTime);
5517  /*
5518    Wait for first button press.
5519  */
5520  root_window=XRootWindow(display,XDefaultScreen(display));
5521  draw_info.stencil=OpaqueStencil;
5522  status=MagickTrue;
5523  cursor=XCreateFontCursor(display,XC_tcross);
5524  for ( ; ; )
5525  {
5526    XQueryPosition(display,windows->image.id,&x,&y);
5527    (void) XSelectInput(display,windows->image.id,
5528      windows->image.attributes.event_mask | PointerMotionMask);
5529    (void) XCheckDefineCursor(display,windows->image.id,cursor);
5530    state=DefaultState;
5531    do
5532    {
5533      if (windows->info.mapped != MagickFalse )
5534        {
5535          /*
5536            Display pointer position.
5537          */
5538          (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
5539            x+windows->image.x,y+windows->image.y);
5540          XInfoWidget(display,windows,text);
5541        }
5542      /*
5543        Wait for next event.
5544      */
5545      XScreenEvent(display,windows,&event,exception);
5546      if (event.xany.window == windows->command.id)
5547        {
5548          /*
5549            Select a command from the Command widget.
5550          */
5551          id=XCommandWidget(display,windows,DrawMenu,&event);
5552          if (id < 0)
5553            continue;
5554          switch (DrawCommands[id])
5555          {
5556            case DrawElementCommand:
5557            {
5558              static const char
5559                *Elements[] =
5560                {
5561                  "point",
5562                  "line",
5563                  "rectangle",
5564                  "fill rectangle",
5565                  "circle",
5566                  "fill circle",
5567                  "ellipse",
5568                  "fill ellipse",
5569                  "polygon",
5570                  "fill polygon",
5571                  (char *) NULL,
5572                };
5573
5574              /*
5575                Select a command from the pop-up menu.
5576              */
5577              element=(ElementType) (XMenuWidget(display,windows,
5578                DrawMenu[id],Elements,command)+1);
5579              break;
5580            }
5581            case DrawColorCommand:
5582            {
5583              const char
5584                *ColorMenu[MaxNumberPens+1];
5585
5586              int
5587                pen_number;
5588
5589              MagickBooleanType
5590                transparent;
5591
5592              XColor
5593                color;
5594
5595              /*
5596                Initialize menu selections.
5597              */
5598              for (i=0; i < (int) (MaxNumberPens-2); i++)
5599                ColorMenu[i]=resource_info->pen_colors[i];
5600              ColorMenu[MaxNumberPens-2]="transparent";
5601              ColorMenu[MaxNumberPens-1]="Browser...";
5602              ColorMenu[MaxNumberPens]=(char *) NULL;
5603              /*
5604                Select a pen color from the pop-up menu.
5605              */
5606              pen_number=XMenuWidget(display,windows,DrawMenu[id],
5607                (const char **) ColorMenu,command);
5608              if (pen_number < 0)
5609                break;
5610              transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5611                MagickFalse;
5612              if (transparent != MagickFalse )
5613                {
5614                  draw_info.stencil=TransparentStencil;
5615                  break;
5616                }
5617              if (pen_number == (MaxNumberPens-1))
5618                {
5619                  static char
5620                    color_name[MagickPathExtent] = "gray";
5621
5622                  /*
5623                    Select a pen color from a dialog.
5624                  */
5625                  resource_info->pen_colors[pen_number]=color_name;
5626                  XColorBrowserWidget(display,windows,"Select",color_name);
5627                  if (*color_name == '\0')
5628                    break;
5629                }
5630              /*
5631                Set pen color.
5632              */
5633              (void) XParseColor(display,windows->map_info->colormap,
5634                resource_info->pen_colors[pen_number],&color);
5635              XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5636                (unsigned int) MaxColors,&color);
5637              windows->pixel_info->pen_colors[pen_number]=color;
5638              pen_id=(unsigned int) pen_number;
5639              draw_info.stencil=OpaqueStencil;
5640              break;
5641            }
5642            case DrawStippleCommand:
5643            {
5644              Image
5645                *stipple_image;
5646
5647              ImageInfo
5648                *image_info;
5649
5650              int
5651                status;
5652
5653              static char
5654                filename[MagickPathExtent] = "\0";
5655
5656              static const char
5657                *StipplesMenu[] =
5658                {
5659                  "Brick",
5660                  "Diagonal",
5661                  "Scales",
5662                  "Vertical",
5663                  "Wavy",
5664                  "Translucent",
5665                  "Opaque",
5666                  (char *) NULL,
5667                  (char *) NULL,
5668                };
5669
5670              /*
5671                Select a command from the pop-up menu.
5672              */
5673              StipplesMenu[7]="Open...";
5674              entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5675                command);
5676              if (entry < 0)
5677                break;
5678              if (stipple != (Pixmap) NULL)
5679                (void) XFreePixmap(display,stipple);
5680              stipple=(Pixmap) NULL;
5681              if (entry != 7)
5682                {
5683                  switch (entry)
5684                  {
5685                    case 0:
5686                    {
5687                      stipple=XCreateBitmapFromData(display,root_window,
5688                        (char *) BricksBitmap,BricksWidth,BricksHeight);
5689                      break;
5690                    }
5691                    case 1:
5692                    {
5693                      stipple=XCreateBitmapFromData(display,root_window,
5694                        (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5695                      break;
5696                    }
5697                    case 2:
5698                    {
5699                      stipple=XCreateBitmapFromData(display,root_window,
5700                        (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5701                      break;
5702                    }
5703                    case 3:
5704                    {
5705                      stipple=XCreateBitmapFromData(display,root_window,
5706                        (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5707                      break;
5708                    }
5709                    case 4:
5710                    {
5711                      stipple=XCreateBitmapFromData(display,root_window,
5712                        (char *) WavyBitmap,WavyWidth,WavyHeight);
5713                      break;
5714                    }
5715                    case 5:
5716                    {
5717                      stipple=XCreateBitmapFromData(display,root_window,
5718                        (char *) HighlightBitmap,HighlightWidth,
5719                        HighlightHeight);
5720                      break;
5721                    }
5722                    case 6:
5723                    default:
5724                    {
5725                      stipple=XCreateBitmapFromData(display,root_window,
5726                        (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5727                      break;
5728                    }
5729                  }
5730                  break;
5731                }
5732              XFileBrowserWidget(display,windows,"Stipple",filename);
5733              if (*filename == '\0')
5734                break;
5735              /*
5736                Read image.
5737              */
5738              XSetCursorState(display,windows,MagickTrue);
5739              XCheckRefreshWindows(display,windows);
5740              image_info=AcquireImageInfo();
5741              (void) CopyMagickString(image_info->filename,filename,
5742                MagickPathExtent);
5743              stipple_image=ReadImage(image_info,exception);
5744              CatchException(exception);
5745              XSetCursorState(display,windows,MagickFalse);
5746              if (stipple_image == (Image *) NULL)
5747                break;
5748              (void) AcquireUniqueFileResource(filename);
5749              (void) FormatLocaleString(stipple_image->filename,MagickPathExtent,
5750                "xbm:%s",filename);
5751              (void) WriteImage(image_info,stipple_image,exception);
5752              stipple_image=DestroyImage(stipple_image);
5753              image_info=DestroyImageInfo(image_info);
5754              status=XReadBitmapFile(display,root_window,filename,&width,
5755                &height,&stipple,&x,&y);
5756              (void) RelinquishUniqueFileResource(filename);
5757              if ((status != BitmapSuccess) != 0)
5758                XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5759                  filename);
5760              break;
5761            }
5762            case DrawWidthCommand:
5763            {
5764              static char
5765                width[MagickPathExtent] = "0";
5766
5767              static const char
5768                *WidthsMenu[] =
5769                {
5770                  "1",
5771                  "2",
5772                  "4",
5773                  "8",
5774                  "16",
5775                  "Dialog...",
5776                  (char *) NULL,
5777                };
5778
5779              /*
5780                Select a command from the pop-up menu.
5781              */
5782              entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5783                command);
5784              if (entry < 0)
5785                break;
5786              if (entry != 5)
5787                {
5788                  line_width=(unsigned int) StringToUnsignedLong(
5789                    WidthsMenu[entry]);
5790                  break;
5791                }
5792              (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5793                width);
5794              if (*width == '\0')
5795                break;
5796              line_width=(unsigned int) StringToUnsignedLong(width);
5797              break;
5798            }
5799            case DrawUndoCommand:
5800            {
5801              (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5802                image,exception);
5803              break;
5804            }
5805            case DrawHelpCommand:
5806            {
5807              XTextViewWidget(display,resource_info,windows,MagickFalse,
5808                "Help Viewer - Image Rotation",ImageDrawHelp);
5809              (void) XCheckDefineCursor(display,windows->image.id,cursor);
5810              break;
5811            }
5812            case DrawDismissCommand:
5813            {
5814              /*
5815                Prematurely exit.
5816              */
5817              state|=EscapeState;
5818              state|=ExitState;
5819              break;
5820            }
5821            default:
5822              break;
5823          }
5824          (void) XCheckDefineCursor(display,windows->image.id,cursor);
5825          continue;
5826        }
5827      switch (event.type)
5828      {
5829        case ButtonPress:
5830        {
5831          if (event.xbutton.button != Button1)
5832            break;
5833          if (event.xbutton.window != windows->image.id)
5834            break;
5835          /*
5836            exit loop.
5837          */
5838          x=event.xbutton.x;
5839          y=event.xbutton.y;
5840          state|=ExitState;
5841          break;
5842        }
5843        case ButtonRelease:
5844          break;
5845        case Expose:
5846          break;
5847        case KeyPress:
5848        {
5849          KeySym
5850            key_symbol;
5851
5852          if (event.xkey.window != windows->image.id)
5853            break;
5854          /*
5855            Respond to a user key press.
5856          */
5857          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5858            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5859          switch ((int) key_symbol)
5860          {
5861            case XK_Escape:
5862            case XK_F20:
5863            {
5864              /*
5865                Prematurely exit.
5866              */
5867              state|=EscapeState;
5868              state|=ExitState;
5869              break;
5870            }
5871            case XK_F1:
5872            case XK_Help:
5873            {
5874              XTextViewWidget(display,resource_info,windows,MagickFalse,
5875                "Help Viewer - Image Rotation",ImageDrawHelp);
5876              break;
5877            }
5878            default:
5879            {
5880              (void) XBell(display,0);
5881              break;
5882            }
5883          }
5884          break;
5885        }
5886        case MotionNotify:
5887        {
5888          /*
5889            Map and unmap Info widget as text cursor crosses its boundaries.
5890          */
5891          x=event.xmotion.x;
5892          y=event.xmotion.y;
5893          if (windows->info.mapped != MagickFalse )
5894            {
5895              if ((x < (int) (windows->info.x+windows->info.width)) &&
5896                  (y < (int) (windows->info.y+windows->info.height)))
5897                (void) XWithdrawWindow(display,windows->info.id,
5898                  windows->info.screen);
5899            }
5900          else
5901            if ((x > (int) (windows->info.x+windows->info.width)) ||
5902                (y > (int) (windows->info.y+windows->info.height)))
5903              (void) XMapWindow(display,windows->info.id);
5904          break;
5905        }
5906      }
5907    } while ((state & ExitState) == 0);
5908    (void) XSelectInput(display,windows->image.id,
5909      windows->image.attributes.event_mask);
5910    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5911    if ((state & EscapeState) != 0)
5912      break;
5913    /*
5914      Draw element as pointer moves until the button is released.
5915    */
5916    distance=0;
5917    degrees=0.0;
5918    line_info.x1=x;
5919    line_info.y1=y;
5920    line_info.x2=x;
5921    line_info.y2=y;
5922    rectangle_info.x=(ssize_t) x;
5923    rectangle_info.y=(ssize_t) y;
5924    rectangle_info.width=0;
5925    rectangle_info.height=0;
5926    number_coordinates=1;
5927    coordinate_info->x=x;
5928    coordinate_info->y=y;
5929    (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5930    state=DefaultState;
5931    do
5932    {
5933      switch (element)
5934      {
5935        case PointElement:
5936        default:
5937        {
5938          if (number_coordinates > 1)
5939            {
5940              (void) XDrawLines(display,windows->image.id,
5941                windows->image.highlight_context,coordinate_info,
5942                number_coordinates,CoordModeOrigin);
5943              (void) FormatLocaleString(text,MagickPathExtent," %+d%+d",
5944                coordinate_info[number_coordinates-1].x,
5945                coordinate_info[number_coordinates-1].y);
5946              XInfoWidget(display,windows,text);
5947            }
5948          break;
5949        }
5950        case LineElement:
5951        {
5952          if (distance > 9)
5953            {
5954              /*
5955                Display angle of the line.
5956              */
5957              degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5958                line_info.y1),(double) (line_info.x2-line_info.x1)));
5959              (void) FormatLocaleString(text,MagickPathExtent," %g",
5960                (double) degrees);
5961              XInfoWidget(display,windows,text);
5962              XHighlightLine(display,windows->image.id,
5963                windows->image.highlight_context,&line_info);
5964            }
5965          else
5966            if (windows->info.mapped != MagickFalse )
5967              (void) XWithdrawWindow(display,windows->info.id,
5968                windows->info.screen);
5969          break;
5970        }
5971        case RectangleElement:
5972        case FillRectangleElement:
5973        {
5974          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5975            {
5976              /*
5977                Display info and draw drawing rectangle.
5978              */
5979              (void) FormatLocaleString(text,MagickPathExtent,
5980                " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5981                (double) rectangle_info.height,(double) rectangle_info.x,
5982                (double) rectangle_info.y);
5983              XInfoWidget(display,windows,text);
5984              XHighlightRectangle(display,windows->image.id,
5985                windows->image.highlight_context,&rectangle_info);
5986            }
5987          else
5988            if (windows->info.mapped != MagickFalse )
5989              (void) XWithdrawWindow(display,windows->info.id,
5990                windows->info.screen);
5991          break;
5992        }
5993        case CircleElement:
5994        case FillCircleElement:
5995        case EllipseElement:
5996        case FillEllipseElement:
5997        {
5998          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5999            {
6000              /*
6001                Display info and draw drawing rectangle.
6002              */
6003              (void) FormatLocaleString(text,MagickPathExtent,
6004                " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
6005                (double) rectangle_info.height,(double) rectangle_info.x,
6006                (double) rectangle_info.y);
6007              XInfoWidget(display,windows,text);
6008              XHighlightEllipse(display,windows->image.id,
6009                windows->image.highlight_context,&rectangle_info);
6010            }
6011          else
6012            if (windows->info.mapped != MagickFalse )
6013              (void) XWithdrawWindow(display,windows->info.id,
6014                windows->info.screen);
6015          break;
6016        }
6017        case PolygonElement:
6018        case FillPolygonElement:
6019        {
6020          if (number_coordinates > 1)
6021            (void) XDrawLines(display,windows->image.id,
6022              windows->image.highlight_context,coordinate_info,
6023              number_coordinates,CoordModeOrigin);
6024          if (distance > 9)
6025            {
6026              /*
6027                Display angle of the line.
6028              */
6029              degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
6030                line_info.y1),(double) (line_info.x2-line_info.x1)));
6031              (void) FormatLocaleString(text,MagickPathExtent," %g",
6032                (double) degrees);
6033              XInfoWidget(display,windows,text);
6034              XHighlightLine(display,windows->image.id,
6035                windows->image.highlight_context,&line_info);
6036            }
6037          else
6038            if (windows->info.mapped != MagickFalse )
6039              (void) XWithdrawWindow(display,windows->info.id,
6040                windows->info.screen);
6041          break;
6042        }
6043      }
6044      /*
6045        Wait for next event.
6046      */
6047      XScreenEvent(display,windows,&event,exception);
6048      switch (element)
6049      {
6050        case PointElement:
6051        default:
6052        {
6053          if (number_coordinates > 1)
6054            (void) XDrawLines(display,windows->image.id,
6055              windows->image.highlight_context,coordinate_info,
6056              number_coordinates,CoordModeOrigin);
6057          break;
6058        }
6059        case LineElement:
6060        {
6061          if (distance > 9)
6062            XHighlightLine(display,windows->image.id,
6063              windows->image.highlight_context,&line_info);
6064          break;
6065        }
6066        case RectangleElement:
6067        case FillRectangleElement:
6068        {
6069          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6070            XHighlightRectangle(display,windows->image.id,
6071              windows->image.highlight_context,&rectangle_info);
6072          break;
6073        }
6074        case CircleElement:
6075        case FillCircleElement:
6076        case EllipseElement:
6077        case FillEllipseElement:
6078        {
6079          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6080            XHighlightEllipse(display,windows->image.id,
6081              windows->image.highlight_context,&rectangle_info);
6082          break;
6083        }
6084        case PolygonElement:
6085        case FillPolygonElement:
6086        {
6087          if (number_coordinates > 1)
6088            (void) XDrawLines(display,windows->image.id,
6089              windows->image.highlight_context,coordinate_info,
6090              number_coordinates,CoordModeOrigin);
6091          if (distance > 9)
6092            XHighlightLine(display,windows->image.id,
6093              windows->image.highlight_context,&line_info);
6094          break;
6095        }
6096      }
6097      switch (event.type)
6098      {
6099        case ButtonPress:
6100          break;
6101        case ButtonRelease:
6102        {
6103          /*
6104            User has committed to element.
6105          */
6106          line_info.x2=event.xbutton.x;
6107          line_info.y2=event.xbutton.y;
6108          rectangle_info.x=(ssize_t) event.xbutton.x;
6109          rectangle_info.y=(ssize_t) event.xbutton.y;
6110          coordinate_info[number_coordinates].x=event.xbutton.x;
6111          coordinate_info[number_coordinates].y=event.xbutton.y;
6112          if (((element != PolygonElement) &&
6113               (element != FillPolygonElement)) || (distance <= 9))
6114            {
6115              state|=ExitState;
6116              break;
6117            }
6118          number_coordinates++;
6119          if (number_coordinates < (int) max_coordinates)
6120            {
6121              line_info.x1=event.xbutton.x;
6122              line_info.y1=event.xbutton.y;
6123              break;
6124            }
6125          max_coordinates<<=1;
6126          coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6127            max_coordinates,sizeof(*coordinate_info));
6128          if (coordinate_info == (XPoint *) NULL)
6129            (void) ThrowMagickException(exception,GetMagickModule(),
6130              ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6131          break;
6132        }
6133        case Expose:
6134          break;
6135        case MotionNotify:
6136        {
6137          if (event.xmotion.window != windows->image.id)
6138            break;
6139          if (element != PointElement)
6140            {
6141              line_info.x2=event.xmotion.x;
6142              line_info.y2=event.xmotion.y;
6143              rectangle_info.x=(ssize_t) event.xmotion.x;
6144              rectangle_info.y=(ssize_t) event.xmotion.y;
6145              break;
6146            }
6147          coordinate_info[number_coordinates].x=event.xbutton.x;
6148          coordinate_info[number_coordinates].y=event.xbutton.y;
6149          number_coordinates++;
6150          if (number_coordinates < (int) max_coordinates)
6151            break;
6152          max_coordinates<<=1;
6153          coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6154            max_coordinates,sizeof(*coordinate_info));
6155          if (coordinate_info == (XPoint *) NULL)
6156            (void) ThrowMagickException(exception,GetMagickModule(),
6157              ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6158          break;
6159        }
6160        default:
6161          break;
6162      }
6163      /*
6164        Check boundary conditions.
6165      */
6166      if (line_info.x2 < 0)
6167        line_info.x2=0;
6168      else
6169        if (line_info.x2 > (int) windows->image.width)
6170          line_info.x2=(short) windows->image.width;
6171      if (line_info.y2 < 0)
6172        line_info.y2=0;
6173      else
6174        if (line_info.y2 > (int) windows->image.height)
6175          line_info.y2=(short) windows->image.height;
6176      distance=(unsigned int)
6177        (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6178         ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6179      if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6180          ((state & ExitState) != 0))
6181        {
6182          if (rectangle_info.x < 0)
6183            rectangle_info.x=0;
6184          else
6185            if (rectangle_info.x > (ssize_t) windows->image.width)
6186              rectangle_info.x=(ssize_t) windows->image.width;
6187          if ((int) rectangle_info.x < x)
6188            rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6189          else
6190            {
6191              rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6192              rectangle_info.x=(ssize_t) x;
6193            }
6194          if (rectangle_info.y < 0)
6195            rectangle_info.y=0;
6196          else
6197            if (rectangle_info.y > (ssize_t) windows->image.height)
6198              rectangle_info.y=(ssize_t) windows->image.height;
6199          if ((int) rectangle_info.y < y)
6200            rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6201          else
6202            {
6203              rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6204              rectangle_info.y=(ssize_t) y;
6205            }
6206        }
6207    } while ((state & ExitState) == 0);
6208    (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6209    if ((element == PointElement) || (element == PolygonElement) ||
6210        (element == FillPolygonElement))
6211      {
6212        /*
6213          Determine polygon bounding box.
6214        */
6215        rectangle_info.x=(ssize_t) coordinate_info->x;
6216        rectangle_info.y=(ssize_t) coordinate_info->y;
6217        x=coordinate_info->x;
6218        y=coordinate_info->y;
6219        for (i=1; i < number_coordinates; i++)
6220        {
6221          if (coordinate_info[i].x > x)
6222            x=coordinate_info[i].x;
6223          if (coordinate_info[i].y > y)
6224            y=coordinate_info[i].y;
6225          if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6226            rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6227          if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6228            rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6229        }
6230        rectangle_info.width=(size_t) (x-rectangle_info.x);
6231        rectangle_info.height=(size_t) (y-rectangle_info.y);
6232        for (i=0; i < number_coordinates; i++)
6233        {
6234          coordinate_info[i].x-=rectangle_info.x;
6235          coordinate_info[i].y-=rectangle_info.y;
6236        }
6237      }
6238    else
6239      if (distance <= 9)
6240        continue;
6241      else
6242        if ((element == RectangleElement) ||
6243            (element == CircleElement) || (element == EllipseElement))
6244          {
6245            rectangle_info.width--;
6246            rectangle_info.height--;
6247          }
6248    /*
6249      Drawing is relative to image configuration.
6250    */
6251    draw_info.x=(int) rectangle_info.x;
6252    draw_info.y=(int) rectangle_info.y;
6253    (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6254      image,exception);
6255    width=(unsigned int) (*image)->columns;
6256    height=(unsigned int) (*image)->rows;
6257    x=0;
6258    y=0;
6259    if (windows->image.crop_geometry != (char *) NULL)
6260      (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6261    draw_info.x+=windows->image.x-(line_width/2);
6262    if (draw_info.x < 0)
6263      draw_info.x=0;
6264    draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6265    draw_info.y+=windows->image.y-(line_width/2);
6266    if (draw_info.y < 0)
6267      draw_info.y=0;
6268    draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6269    draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6270    if (draw_info.width > (unsigned int) (*image)->columns)
6271      draw_info.width=(unsigned int) (*image)->columns;
6272    draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6273    if (draw_info.height > (unsigned int) (*image)->rows)
6274      draw_info.height=(unsigned int) (*image)->rows;
6275    (void) FormatLocaleString(draw_info.geometry,MagickPathExtent,"%ux%u%+d%+d",
6276      width*draw_info.width/windows->image.ximage->width,
6277      height*draw_info.height/windows->image.ximage->height,
6278      draw_info.x+x,draw_info.y+y);
6279    /*
6280      Initialize drawing attributes.
6281    */
6282    draw_info.degrees=0.0;
6283    draw_info.element=element;
6284    draw_info.stipple=stipple;
6285    draw_info.line_width=line_width;
6286    draw_info.line_info=line_info;
6287    if (line_info.x1 > (int) (line_width/2))
6288      draw_info.line_info.x1=(short) line_width/2;
6289    if (line_info.y1 > (int) (line_width/2))
6290      draw_info.line_info.y1=(short) line_width/2;
6291    draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6292    draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6293    if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6294      {
6295        draw_info.line_info.x2=(-draw_info.line_info.x2);
6296        draw_info.line_info.y2=(-draw_info.line_info.y2);
6297      }
6298    if (draw_info.line_info.x2 < 0)
6299      {
6300        draw_info.line_info.x2=(-draw_info.line_info.x2);
6301        Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6302      }
6303    if (draw_info.line_info.y2 < 0)
6304      {
6305        draw_info.line_info.y2=(-draw_info.line_info.y2);
6306        Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6307      }
6308    draw_info.rectangle_info=rectangle_info;
6309    if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6310      draw_info.rectangle_info.x=(ssize_t) line_width/2;
6311    if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6312      draw_info.rectangle_info.y=(ssize_t) line_width/2;
6313    draw_info.number_coordinates=(unsigned int) number_coordinates;
6314    draw_info.coordinate_info=coordinate_info;
6315    windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6316    /*
6317      Draw element on image.
6318    */
6319    XSetCursorState(display,windows,MagickTrue);
6320    XCheckRefreshWindows(display,windows);
6321    status=XDrawImage(display,windows->pixel_info,&draw_info,*image,exception);
6322    XSetCursorState(display,windows,MagickFalse);
6323    /*
6324      Update image colormap and return to image drawing.
6325    */
6326    XConfigureImageColormap(display,resource_info,windows,*image,exception);
6327    (void) XConfigureImage(display,resource_info,windows,*image,exception);
6328  }
6329  XSetCursorState(display,windows,MagickFalse);
6330  coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6331  return(status != 0 ? MagickTrue : MagickFalse);
6332}
6333
6334/*
6335%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6336%                                                                             %
6337%                                                                             %
6338%                                                                             %
6339+   X D r a w P a n R e c t a n g l e                                         %
6340%                                                                             %
6341%                                                                             %
6342%                                                                             %
6343%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6344%
6345%  XDrawPanRectangle() draws a rectangle in the pan window.  The pan window
6346%  displays a zoom image and the rectangle shows which portion of the image is
6347%  displayed in the Image window.
6348%
6349%  The format of the XDrawPanRectangle method is:
6350%
6351%      XDrawPanRectangle(Display *display,XWindows *windows)
6352%
6353%  A description of each parameter follows:
6354%
6355%    o display: Specifies a connection to an X server;  returned from
6356%      XOpenDisplay.
6357%
6358%    o windows: Specifies a pointer to a XWindows structure.
6359%
6360*/
6361static void XDrawPanRectangle(Display *display,XWindows *windows)
6362{
6363  double
6364    scale_factor;
6365
6366  RectangleInfo
6367    highlight_info;
6368
6369  /*
6370    Determine dimensions of the panning rectangle.
6371  */
6372  scale_factor=(double) windows->pan.width/windows->image.ximage->width;
6373  highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6374  highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6375  scale_factor=(double)
6376    windows->pan.height/windows->image.ximage->height;
6377  highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6378  highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6379  /*
6380    Display the panning rectangle.
6381  */
6382  (void) XClearWindow(display,windows->pan.id);
6383  XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6384    &highlight_info);
6385}
6386
6387/*
6388%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6389%                                                                             %
6390%                                                                             %
6391%                                                                             %
6392+   X I m a g e C a c h e                                                     %
6393%                                                                             %
6394%                                                                             %
6395%                                                                             %
6396%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6397%
6398%  XImageCache() handles the creation, manipulation, and destruction of the
6399%  image cache (undo and redo buffers).
6400%
6401%  The format of the XImageCache method is:
6402%
6403%      void XImageCache(Display *display,XResourceInfo *resource_info,
6404%        XWindows *windows,const CommandType command,Image **image,
6405%        ExceptionInfo *exception)
6406%
6407%  A description of each parameter follows:
6408%
6409%    o display: Specifies a connection to an X server; returned from
6410%      XOpenDisplay.
6411%
6412%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6413%
6414%    o windows: Specifies a pointer to a XWindows structure.
6415%
6416%    o command: Specifies a command to perform.
6417%
6418%    o image: the image;  XImageCache may transform the image and return a new
6419%      image pointer.
6420%
6421%    o exception: return any errors or warnings in this structure.
6422%
6423*/
6424static void XImageCache(Display *display,XResourceInfo *resource_info,
6425  XWindows *windows,const CommandType command,Image **image,
6426  ExceptionInfo *exception)
6427{
6428  Image
6429    *cache_image;
6430
6431  static Image
6432    *redo_image = (Image *) NULL,
6433    *undo_image = (Image *) NULL;
6434
6435  switch (command)
6436  {
6437    case FreeBuffersCommand:
6438    {
6439      /*
6440        Free memory from the undo and redo cache.
6441      */
6442      while (undo_image != (Image *) NULL)
6443      {
6444        cache_image=undo_image;
6445        undo_image=GetPreviousImageInList(undo_image);
6446        cache_image->list=DestroyImage(cache_image->list);
6447        cache_image=DestroyImage(cache_image);
6448      }
6449      undo_image=NewImageList();
6450      if (redo_image != (Image *) NULL)
6451        redo_image=DestroyImage(redo_image);
6452      redo_image=NewImageList();
6453      return;
6454    }
6455    case UndoCommand:
6456    {
6457      char
6458        image_geometry[MagickPathExtent];
6459
6460      /*
6461        Undo the last image transformation.
6462      */
6463      if (undo_image == (Image *) NULL)
6464        {
6465          (void) XBell(display,0);
6466          return;
6467        }
6468      cache_image=undo_image;
6469      undo_image=GetPreviousImageInList(undo_image);
6470      windows->image.window_changes.width=(int) cache_image->columns;
6471      windows->image.window_changes.height=(int) cache_image->rows;
6472      (void) FormatLocaleString(image_geometry,MagickPathExtent,"%dx%d!",
6473        windows->image.ximage->width,windows->image.ximage->height);
6474      (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
6475        exception);
6476      if (windows->image.crop_geometry != (char *) NULL)
6477        windows->image.crop_geometry=(char *) RelinquishMagickMemory(
6478          windows->image.crop_geometry);
6479      windows->image.crop_geometry=cache_image->geometry;
6480      if (redo_image != (Image *) NULL)
6481        redo_image=DestroyImage(redo_image);
6482      redo_image=(*image);
6483      *image=cache_image->list;
6484      cache_image=DestroyImage(cache_image);
6485      if (windows->image.orphan != MagickFalse )
6486        return;
6487      XConfigureImageColormap(display,resource_info,windows,*image,exception);
6488      (void) XConfigureImage(display,resource_info,windows,*image,exception);
6489      return;
6490    }
6491    case CutCommand:
6492    case PasteCommand:
6493    case ApplyCommand:
6494    case HalfSizeCommand:
6495    case OriginalSizeCommand:
6496    case DoubleSizeCommand:
6497    case ResizeCommand:
6498    case TrimCommand:
6499    case CropCommand:
6500    case ChopCommand:
6501    case FlipCommand:
6502    case FlopCommand:
6503    case RotateRightCommand:
6504    case RotateLeftCommand:
6505    case RotateCommand:
6506    case ShearCommand:
6507    case RollCommand:
6508    case NegateCommand:
6509    case ContrastStretchCommand:
6510    case SigmoidalContrastCommand:
6511    case NormalizeCommand:
6512    case EqualizeCommand:
6513    case HueCommand:
6514    case SaturationCommand:
6515    case BrightnessCommand:
6516    case GammaCommand:
6517    case SpiffCommand:
6518    case DullCommand:
6519    case GrayscaleCommand:
6520    case MapCommand:
6521    case QuantizeCommand:
6522    case DespeckleCommand:
6523    case EmbossCommand:
6524    case ReduceNoiseCommand:
6525    case AddNoiseCommand:
6526    case SharpenCommand:
6527    case BlurCommand:
6528    case ThresholdCommand:
6529    case EdgeDetectCommand:
6530    case SpreadCommand:
6531    case ShadeCommand:
6532    case RaiseCommand:
6533    case SegmentCommand:
6534    case SolarizeCommand:
6535    case SepiaToneCommand:
6536    case SwirlCommand:
6537    case ImplodeCommand:
6538    case VignetteCommand:
6539    case WaveCommand:
6540    case OilPaintCommand:
6541    case CharcoalDrawCommand:
6542    case AnnotateCommand:
6543    case AddBorderCommand:
6544    case AddFrameCommand:
6545    case CompositeCommand:
6546    case CommentCommand:
6547    case LaunchCommand:
6548    case RegionofInterestCommand:
6549    case SaveToUndoBufferCommand:
6550    case RedoCommand:
6551    {
6552      Image
6553        *previous_image;
6554
6555      ssize_t
6556        bytes;
6557
6558      bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelInfo));
6559      if (undo_image != (Image *) NULL)
6560        {
6561          /*
6562            Ensure the undo cache has enough memory available.
6563          */
6564          previous_image=undo_image;
6565          while (previous_image != (Image *) NULL)
6566          {
6567            bytes+=previous_image->list->columns*previous_image->list->rows*
6568              sizeof(PixelInfo);
6569            if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6570              {
6571                previous_image=GetPreviousImageInList(previous_image);
6572                continue;
6573              }
6574            bytes-=previous_image->list->columns*previous_image->list->rows*
6575              sizeof(PixelInfo);
6576            if (previous_image == undo_image)
6577              undo_image=NewImageList();
6578            else
6579              previous_image->next->previous=NewImageList();
6580            break;
6581          }
6582          while (previous_image != (Image *) NULL)
6583          {
6584            /*
6585              Delete any excess memory from undo cache.
6586            */
6587            cache_image=previous_image;
6588            previous_image=GetPreviousImageInList(previous_image);
6589            cache_image->list=DestroyImage(cache_image->list);
6590            cache_image=DestroyImage(cache_image);
6591          }
6592        }
6593      if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6594        break;
6595      /*
6596        Save image before transformations are applied.
6597      */
6598      cache_image=AcquireImage((ImageInfo *) NULL,exception);
6599      if (cache_image == (Image *) NULL)
6600        break;
6601      XSetCursorState(display,windows,MagickTrue);
6602      XCheckRefreshWindows(display,windows);
6603      cache_image->list=CloneImage(*image,0,0,MagickTrue,exception);
6604      XSetCursorState(display,windows,MagickFalse);
6605      if (cache_image->list == (Image *) NULL)
6606        {
6607          cache_image=DestroyImage(cache_image);
6608          break;
6609        }
6610      cache_image->columns=(size_t) windows->image.ximage->width;
6611      cache_image->rows=(size_t) windows->image.ximage->height;
6612      cache_image->geometry=windows->image.crop_geometry;
6613      if (windows->image.crop_geometry != (char *) NULL)
6614        {
6615          cache_image->geometry=AcquireString((char *) NULL);
6616          (void) CopyMagickString(cache_image->geometry,
6617            windows->image.crop_geometry,MagickPathExtent);
6618        }
6619      if (undo_image == (Image *) NULL)
6620        {
6621          undo_image=cache_image;
6622          break;
6623        }
6624      undo_image->next=cache_image;
6625      undo_image->next->previous=undo_image;
6626      undo_image=undo_image->next;
6627      break;
6628    }
6629    default:
6630      break;
6631  }
6632  if (command == RedoCommand)
6633    {
6634      /*
6635        Redo the last image transformation.
6636      */
6637      if (redo_image == (Image *) NULL)
6638        {
6639          (void) XBell(display,0);
6640          return;
6641        }
6642      windows->image.window_changes.width=(int) redo_image->columns;
6643      windows->image.window_changes.height=(int) redo_image->rows;
6644      if (windows->image.crop_geometry != (char *) NULL)
6645        windows->image.crop_geometry=(char *)
6646          RelinquishMagickMemory(windows->image.crop_geometry);
6647      windows->image.crop_geometry=redo_image->geometry;
6648      *image=DestroyImage(*image);
6649      *image=redo_image;
6650      redo_image=NewImageList();
6651      if (windows->image.orphan != MagickFalse )
6652        return;
6653      XConfigureImageColormap(display,resource_info,windows,*image,exception);
6654      (void) XConfigureImage(display,resource_info,windows,*image,exception);
6655      return;
6656    }
6657  if (command != InfoCommand)
6658    return;
6659  /*
6660    Display image info.
6661  */
6662  XSetCursorState(display,windows,MagickTrue);
6663  XCheckRefreshWindows(display,windows);
6664  XDisplayImageInfo(display,resource_info,windows,undo_image,*image,exception);
6665  XSetCursorState(display,windows,MagickFalse);
6666}
6667
6668/*
6669%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6670%                                                                             %
6671%                                                                             %
6672%                                                                             %
6673+   X I m a g e W i n d o w C o m m a n d                                     %
6674%                                                                             %
6675%                                                                             %
6676%                                                                             %
6677%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6678%
6679%  XImageWindowCommand() makes a transform to the image or Image window as
6680%  specified by a user menu button or keyboard command.
6681%
6682%  The format of the XImageWindowCommand method is:
6683%
6684%      CommandType XImageWindowCommand(Display *display,
6685%        XResourceInfo *resource_info,XWindows *windows,
6686%        const MagickStatusType state,KeySym key_symbol,Image **image,
6687%        ExceptionInfo *exception)
6688%
6689%  A description of each parameter follows:
6690%
6691%    o nexus:  Method XImageWindowCommand returns an image when the
6692%      user chooses 'Open Image' from the command menu.  Otherwise a null
6693%      image is returned.
6694%
6695%    o display: Specifies a connection to an X server; returned from
6696%      XOpenDisplay.
6697%
6698%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6699%
6700%    o windows: Specifies a pointer to a XWindows structure.
6701%
6702%    o state: key mask.
6703%
6704%    o key_symbol: Specifies a command to perform.
6705%
6706%    o image: the image;  XImageWIndowCommand may transform the image and
6707%      return a new image pointer.
6708%
6709%    o exception: return any errors or warnings in this structure.
6710%
6711*/
6712static CommandType XImageWindowCommand(Display *display,
6713  XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6714  KeySym key_symbol,Image **image,ExceptionInfo *exception)
6715{
6716  static char
6717    delta[MagickPathExtent] = "";
6718
6719  static const char
6720    Digits[] = "01234567890";
6721
6722  static KeySym
6723    last_symbol = XK_0;
6724
6725  if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6726    {
6727      if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6728        {
6729          *delta='\0';
6730          resource_info->quantum=1;
6731        }
6732      last_symbol=key_symbol;
6733      delta[strlen(delta)+1]='\0';
6734      delta[strlen(delta)]=Digits[key_symbol-XK_0];
6735      resource_info->quantum=StringToLong(delta);
6736      return(NullCommand);
6737    }
6738  last_symbol=key_symbol;
6739  if (resource_info->immutable)
6740    {
6741      /*
6742        Virtual image window has a restricted command set.
6743      */
6744      switch (key_symbol)
6745      {
6746        case XK_question:
6747          return(InfoCommand);
6748        case XK_p:
6749        case XK_Print:
6750          return(PrintCommand);
6751        case XK_space:
6752          return(NextCommand);
6753        case XK_q:
6754        case XK_Escape:
6755          return(QuitCommand);
6756        default:
6757          break;
6758      }
6759      return(NullCommand);
6760    }
6761  switch ((int) key_symbol)
6762  {
6763    case XK_o:
6764    {
6765      if ((state & ControlMask) == 0)
6766        break;
6767      return(OpenCommand);
6768    }
6769    case XK_space:
6770      return(NextCommand);
6771    case XK_BackSpace:
6772      return(FormerCommand);
6773    case XK_s:
6774    {
6775      if ((state & Mod1Mask) != 0)
6776        return(SwirlCommand);
6777      if ((state & ControlMask) == 0)
6778        return(ShearCommand);
6779      return(SaveCommand);
6780    }
6781    case XK_p:
6782    case XK_Print:
6783    {
6784      if ((state & Mod1Mask) != 0)
6785        return(OilPaintCommand);
6786      if ((state & Mod4Mask) != 0)
6787        return(ColorCommand);
6788      if ((state & ControlMask) == 0)
6789        return(NullCommand);
6790      return(PrintCommand);
6791    }
6792    case XK_d:
6793    {
6794      if ((state & Mod4Mask) != 0)
6795        return(DrawCommand);
6796      if ((state & ControlMask) == 0)
6797        return(NullCommand);
6798      return(DeleteCommand);
6799    }
6800    case XK_Select:
6801    {
6802      if ((state & ControlMask) == 0)
6803        return(NullCommand);
6804      return(SelectCommand);
6805    }
6806    case XK_n:
6807    {
6808      if ((state & ControlMask) == 0)
6809        return(NullCommand);
6810      return(NewCommand);
6811    }
6812    case XK_q:
6813    case XK_Escape:
6814      return(QuitCommand);
6815    case XK_z:
6816    case XK_Undo:
6817    {
6818      if ((state & ControlMask) == 0)
6819        return(NullCommand);
6820      return(UndoCommand);
6821    }
6822    case XK_r:
6823    case XK_Redo:
6824    {
6825      if ((state & ControlMask) == 0)
6826        return(RollCommand);
6827      return(RedoCommand);
6828    }
6829    case XK_x:
6830    {
6831      if ((state & ControlMask) == 0)
6832        return(NullCommand);
6833      return(CutCommand);
6834    }
6835    case XK_c:
6836    {
6837      if ((state & Mod1Mask) != 0)
6838        return(CharcoalDrawCommand);
6839      if ((state & ControlMask) == 0)
6840        return(CropCommand);
6841      return(CopyCommand);
6842    }
6843    case XK_v:
6844    case XK_Insert:
6845    {
6846      if ((state & Mod4Mask) != 0)
6847        return(CompositeCommand);
6848      if ((state & ControlMask) == 0)
6849        return(FlipCommand);
6850      return(PasteCommand);
6851    }
6852    case XK_less:
6853      return(HalfSizeCommand);
6854    case XK_minus:
6855      return(OriginalSizeCommand);
6856    case XK_greater:
6857      return(DoubleSizeCommand);
6858    case XK_percent:
6859      return(ResizeCommand);
6860    case XK_at:
6861      return(RefreshCommand);
6862    case XK_bracketleft:
6863      return(ChopCommand);
6864    case XK_h:
6865      return(FlopCommand);
6866    case XK_slash:
6867      return(RotateRightCommand);
6868    case XK_backslash:
6869      return(RotateLeftCommand);
6870    case XK_asterisk:
6871      return(RotateCommand);
6872    case XK_t:
6873      return(TrimCommand);
6874    case XK_H:
6875      return(HueCommand);
6876    case XK_S:
6877      return(SaturationCommand);
6878    case XK_L:
6879      return(BrightnessCommand);
6880    case XK_G:
6881      return(GammaCommand);
6882    case XK_C:
6883      return(SpiffCommand);
6884    case XK_Z:
6885      return(DullCommand);
6886    case XK_N:
6887      return(NormalizeCommand);
6888    case XK_equal:
6889      return(EqualizeCommand);
6890    case XK_asciitilde:
6891      return(NegateCommand);
6892    case XK_period:
6893      return(GrayscaleCommand);
6894    case XK_numbersign:
6895      return(QuantizeCommand);
6896    case XK_F2:
6897      return(DespeckleCommand);
6898    case XK_F3:
6899      return(EmbossCommand);
6900    case XK_F4:
6901      return(ReduceNoiseCommand);
6902    case XK_F5:
6903      return(AddNoiseCommand);
6904    case XK_F6:
6905      return(SharpenCommand);
6906    case XK_F7:
6907      return(BlurCommand);
6908    case XK_F8:
6909      return(ThresholdCommand);
6910    case XK_F9:
6911      return(EdgeDetectCommand);
6912    case XK_F10:
6913      return(SpreadCommand);
6914    case XK_F11:
6915      return(ShadeCommand);
6916    case XK_F12:
6917      return(RaiseCommand);
6918    case XK_F13:
6919      return(SegmentCommand);
6920    case XK_i:
6921    {
6922      if ((state & Mod1Mask) == 0)
6923        return(NullCommand);
6924      return(ImplodeCommand);
6925    }
6926    case XK_w:
6927    {
6928      if ((state & Mod1Mask) == 0)
6929        return(NullCommand);
6930      return(WaveCommand);
6931    }
6932    case XK_m:
6933    {
6934      if ((state & Mod4Mask) == 0)
6935        return(NullCommand);
6936      return(MatteCommand);
6937    }
6938    case XK_b:
6939    {
6940      if ((state & Mod4Mask) == 0)
6941        return(NullCommand);
6942      return(AddBorderCommand);
6943    }
6944    case XK_f:
6945    {
6946      if ((state & Mod4Mask) == 0)
6947        return(NullCommand);
6948      return(AddFrameCommand);
6949    }
6950    case XK_exclam:
6951    {
6952      if ((state & Mod4Mask) == 0)
6953        return(NullCommand);
6954      return(CommentCommand);
6955    }
6956    case XK_a:
6957    {
6958      if ((state & Mod1Mask) != 0)
6959        return(ApplyCommand);
6960      if ((state & Mod4Mask) != 0)
6961        return(AnnotateCommand);
6962      if ((state & ControlMask) == 0)
6963        return(NullCommand);
6964      return(RegionofInterestCommand);
6965    }
6966    case XK_question:
6967      return(InfoCommand);
6968    case XK_plus:
6969      return(ZoomCommand);
6970    case XK_P:
6971    {
6972      if ((state & ShiftMask) == 0)
6973        return(NullCommand);
6974      return(ShowPreviewCommand);
6975    }
6976    case XK_Execute:
6977      return(LaunchCommand);
6978    case XK_F1:
6979      return(HelpCommand);
6980    case XK_Find:
6981      return(BrowseDocumentationCommand);
6982    case XK_Menu:
6983    {
6984      (void) XMapRaised(display,windows->command.id);
6985      return(NullCommand);
6986    }
6987    case XK_Next:
6988    case XK_Prior:
6989    case XK_Home:
6990    case XK_KP_Home:
6991    {
6992      XTranslateImage(display,windows,*image,key_symbol);
6993      return(NullCommand);
6994    }
6995    case XK_Up:
6996    case XK_KP_Up:
6997    case XK_Down:
6998    case XK_KP_Down:
6999    case XK_Left:
7000    case XK_KP_Left:
7001    case XK_Right:
7002    case XK_KP_Right:
7003    {
7004      if ((state & Mod1Mask) != 0)
7005        {
7006          RectangleInfo
7007            crop_info;
7008
7009          /*
7010            Trim one pixel from edge of image.
7011          */
7012          crop_info.x=0;
7013          crop_info.y=0;
7014          crop_info.width=(size_t) windows->image.ximage->width;
7015          crop_info.height=(size_t) windows->image.ximage->height;
7016          if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
7017            {
7018              if (resource_info->quantum >= (int) crop_info.height)
7019                resource_info->quantum=(int) crop_info.height-1;
7020              crop_info.height-=resource_info->quantum;
7021            }
7022          if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
7023            {
7024              if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
7025                resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
7026              crop_info.y+=resource_info->quantum;
7027              crop_info.height-=resource_info->quantum;
7028            }
7029          if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
7030            {
7031              if (resource_info->quantum >= (int) crop_info.width)
7032                resource_info->quantum=(int) crop_info.width-1;
7033              crop_info.width-=resource_info->quantum;
7034            }
7035          if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
7036            {
7037              if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
7038                resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
7039              crop_info.x+=resource_info->quantum;
7040              crop_info.width-=resource_info->quantum;
7041            }
7042          if ((int) (windows->image.x+windows->image.width) >
7043              (int) crop_info.width)
7044            windows->image.x=(int) (crop_info.width-windows->image.width);
7045          if ((int) (windows->image.y+windows->image.height) >
7046              (int) crop_info.height)
7047            windows->image.y=(int) (crop_info.height-windows->image.height);
7048          XSetCropGeometry(display,windows,&crop_info,*image);
7049          windows->image.window_changes.width=(int) crop_info.width;
7050          windows->image.window_changes.height=(int) crop_info.height;
7051          (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
7052          (void) XConfigureImage(display,resource_info,windows,*image,
7053            exception);
7054          return(NullCommand);
7055        }
7056      XTranslateImage(display,windows,*image,key_symbol);
7057      return(NullCommand);
7058    }
7059    default:
7060      return(NullCommand);
7061  }
7062  return(NullCommand);
7063}
7064
7065/*
7066%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7067%                                                                             %
7068%                                                                             %
7069%                                                                             %
7070+   X M a g i c k C o m m a n d                                               %
7071%                                                                             %
7072%                                                                             %
7073%                                                                             %
7074%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7075%
7076%  XMagickCommand() makes a transform to the image or Image window as
7077%  specified by a user menu button or keyboard command.
7078%
7079%  The format of the XMagickCommand method is:
7080%
7081%      Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7082%        XWindows *windows,const CommandType command,Image **image,
7083%        ExceptionInfo *exception)
7084%
7085%  A description of each parameter follows:
7086%
7087%    o display: Specifies a connection to an X server; returned from
7088%      XOpenDisplay.
7089%
7090%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7091%
7092%    o windows: Specifies a pointer to a XWindows structure.
7093%
7094%    o command: Specifies a command to perform.
7095%
7096%    o image: the image;  XMagickCommand may transform the image and return a
7097%      new image pointer.
7098%
7099%    o exception: return any errors or warnings in this structure.
7100%
7101*/
7102static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7103  XWindows *windows,const CommandType command,Image **image,
7104  ExceptionInfo *exception)
7105{
7106  char
7107    filename[MagickPathExtent],
7108    geometry[MagickPathExtent],
7109    modulate_factors[MagickPathExtent];
7110
7111  GeometryInfo
7112    geometry_info;
7113
7114  Image
7115    *nexus;
7116
7117  ImageInfo
7118    *image_info;
7119
7120  int
7121    x,
7122    y;
7123
7124  MagickStatusType
7125    flags,
7126    status;
7127
7128  QuantizeInfo
7129    quantize_info;
7130
7131  RectangleInfo
7132    page_geometry;
7133
7134  register int
7135    i;
7136
7137  static char
7138    color[MagickPathExtent] = "gray";
7139
7140  unsigned int
7141    height,
7142    width;
7143
7144  /*
7145    Process user command.
7146  */
7147  XCheckRefreshWindows(display,windows);
7148  XImageCache(display,resource_info,windows,command,image,exception);
7149  nexus=NewImageList();
7150  windows->image.window_changes.width=windows->image.ximage->width;
7151  windows->image.window_changes.height=windows->image.ximage->height;
7152  image_info=CloneImageInfo(resource_info->image_info);
7153  SetGeometryInfo(&geometry_info);
7154  GetQuantizeInfo(&quantize_info);
7155  switch (command)
7156  {
7157    case OpenCommand:
7158    {
7159      /*
7160        Load image.
7161      */
7162      nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7163      break;
7164    }
7165    case NextCommand:
7166    {
7167      /*
7168        Display next image.
7169      */
7170      for (i=0; i < resource_info->quantum; i++)
7171        XClientMessage(display,windows->image.id,windows->im_protocols,
7172          windows->im_next_image,CurrentTime);
7173      break;
7174    }
7175    case FormerCommand:
7176    {
7177      /*
7178        Display former image.
7179      */
7180      for (i=0; i < resource_info->quantum; i++)
7181        XClientMessage(display,windows->image.id,windows->im_protocols,
7182          windows->im_former_image,CurrentTime);
7183      break;
7184    }
7185    case SelectCommand:
7186    {
7187      int
7188        status;
7189
7190      /*
7191        Select image.
7192      */
7193      if (*resource_info->home_directory == '\0')
7194        (void) CopyMagickString(resource_info->home_directory,".",
7195          MagickPathExtent);
7196      status=chdir(resource_info->home_directory);
7197      if (status == -1)
7198        (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
7199          "UnableToOpenFile","%s",resource_info->home_directory);
7200      nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7201      break;
7202    }
7203    case SaveCommand:
7204    {
7205      /*
7206        Save image.
7207      */
7208      status=XSaveImage(display,resource_info,windows,*image,exception);
7209      if (status == MagickFalse)
7210        {
7211          char
7212            message[MagickPathExtent];
7213
7214          (void) FormatLocaleString(message,MagickPathExtent,"%s:%s",
7215            exception->reason != (char *) NULL ? exception->reason : "",
7216            exception->description != (char *) NULL ? exception->description :
7217            "");
7218          XNoticeWidget(display,windows,"Unable to save file:",message);
7219          break;
7220        }
7221      break;
7222    }
7223    case PrintCommand:
7224    {
7225      /*
7226        Print image.
7227      */
7228      status=XPrintImage(display,resource_info,windows,*image,exception);
7229      if (status == MagickFalse)
7230        {
7231          char
7232            message[MagickPathExtent];
7233
7234          (void) FormatLocaleString(message,MagickPathExtent,"%s:%s",
7235            exception->reason != (char *) NULL ? exception->reason : "",
7236            exception->description != (char *) NULL ? exception->description :
7237            "");
7238          XNoticeWidget(display,windows,"Unable to print file:",message);
7239          break;
7240        }
7241      break;
7242    }
7243    case DeleteCommand:
7244    {
7245      static char
7246        filename[MagickPathExtent] = "\0";
7247
7248      /*
7249        Delete image file.
7250      */
7251      XFileBrowserWidget(display,windows,"Delete",filename);
7252      if (*filename == '\0')
7253        break;
7254      status=ShredFile(filename);
7255      if (status != MagickFalse )
7256        XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7257      break;
7258    }
7259    case NewCommand:
7260    {
7261      int
7262        status;
7263
7264      static char
7265        color[MagickPathExtent] = "gray",
7266        geometry[MagickPathExtent] = "640x480";
7267
7268      static const char
7269        *format = "gradient";
7270
7271      /*
7272        Query user for canvas geometry.
7273      */
7274      status=XDialogWidget(display,windows,"New","Enter image geometry:",
7275        geometry);
7276      if (*geometry == '\0')
7277        break;
7278      if (status == 0)
7279        format="xc";
7280      XColorBrowserWidget(display,windows,"Select",color);
7281      if (*color == '\0')
7282        break;
7283      /*
7284        Create canvas.
7285      */
7286      (void) FormatLocaleString(image_info->filename,MagickPathExtent,
7287        "%s:%s",format,color);
7288      (void) CloneString(&image_info->size,geometry);
7289      nexus=ReadImage(image_info,exception);
7290      CatchException(exception);
7291      XClientMessage(display,windows->image.id,windows->im_protocols,
7292        windows->im_next_image,CurrentTime);
7293      break;
7294    }
7295    case VisualDirectoryCommand:
7296    {
7297      /*
7298        Visual Image directory.
7299      */
7300      nexus=XVisualDirectoryImage(display,resource_info,windows,exception);
7301      break;
7302    }
7303    case QuitCommand:
7304    {
7305      /*
7306        exit program.
7307      */
7308      if (resource_info->confirm_exit == MagickFalse)
7309        XClientMessage(display,windows->image.id,windows->im_protocols,
7310          windows->im_exit,CurrentTime);
7311      else
7312        {
7313          int
7314            status;
7315
7316          /*
7317            Confirm program exit.
7318          */
7319          status=XConfirmWidget(display,windows,"Do you really want to exit",
7320            resource_info->client_name);
7321          if (status > 0)
7322            XClientMessage(display,windows->image.id,windows->im_protocols,
7323              windows->im_exit,CurrentTime);
7324        }
7325      break;
7326    }
7327    case CutCommand:
7328    {
7329      /*
7330        Cut image.
7331      */
7332      (void) XCropImage(display,resource_info,windows,*image,CutMode,exception);
7333      break;
7334    }
7335    case CopyCommand:
7336    {
7337      /*
7338        Copy image.
7339      */
7340      (void) XCropImage(display,resource_info,windows,*image,CopyMode,
7341        exception);
7342      break;
7343    }
7344    case PasteCommand:
7345    {
7346      /*
7347        Paste image.
7348      */
7349      status=XPasteImage(display,resource_info,windows,*image,exception);
7350      if (status == MagickFalse)
7351        {
7352          XNoticeWidget(display,windows,"Unable to paste X image",
7353            (*image)->filename);
7354          break;
7355        }
7356      break;
7357    }
7358    case HalfSizeCommand:
7359    {
7360      /*
7361        Half image size.
7362      */
7363      windows->image.window_changes.width=windows->image.ximage->width/2;
7364      windows->image.window_changes.height=windows->image.ximage->height/2;
7365      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7366      break;
7367    }
7368    case OriginalSizeCommand:
7369    {
7370      /*
7371        Original image size.
7372      */
7373      windows->image.window_changes.width=(int) (*image)->columns;
7374      windows->image.window_changes.height=(int) (*image)->rows;
7375      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7376      break;
7377    }
7378    case DoubleSizeCommand:
7379    {
7380      /*
7381        Double the image size.
7382      */
7383      windows->image.window_changes.width=windows->image.ximage->width << 1;
7384      windows->image.window_changes.height=windows->image.ximage->height << 1;
7385      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7386      break;
7387    }
7388    case ResizeCommand:
7389    {
7390      int
7391        status;
7392
7393      size_t
7394        height,
7395        width;
7396
7397      ssize_t
7398        x,
7399        y;
7400
7401      /*
7402        Resize image.
7403      */
7404      width=(size_t) windows->image.ximage->width;
7405      height=(size_t) windows->image.ximage->height;
7406      x=0;
7407      y=0;
7408      (void) FormatLocaleString(geometry,MagickPathExtent,"%.20gx%.20g+0+0",
7409        (double) width,(double) height);
7410      status=XDialogWidget(display,windows,"Resize",
7411        "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7412      if (*geometry == '\0')
7413        break;
7414      if (status == 0)
7415        (void) ConcatenateMagickString(geometry,"!",MagickPathExtent);
7416      (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7417      windows->image.window_changes.width=(int) width;
7418      windows->image.window_changes.height=(int) height;
7419      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7420      break;
7421    }
7422    case ApplyCommand:
7423    {
7424      char
7425        image_geometry[MagickPathExtent];
7426
7427      if ((windows->image.crop_geometry == (char *) NULL) &&
7428          ((int) (*image)->columns == windows->image.ximage->width) &&
7429          ((int) (*image)->rows == windows->image.ximage->height))
7430        break;
7431      /*
7432        Apply size transforms to image.
7433      */
7434      XSetCursorState(display,windows,MagickTrue);
7435      XCheckRefreshWindows(display,windows);
7436      /*
7437        Crop and/or scale displayed image.
7438      */
7439      (void) FormatLocaleString(image_geometry,MagickPathExtent,"%dx%d!",
7440        windows->image.ximage->width,windows->image.ximage->height);
7441      (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
7442        exception);
7443      if (windows->image.crop_geometry != (char *) NULL)
7444        windows->image.crop_geometry=(char *) RelinquishMagickMemory(
7445          windows->image.crop_geometry);
7446      windows->image.x=0;
7447      windows->image.y=0;
7448      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7449      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7450      break;
7451    }
7452    case RefreshCommand:
7453    {
7454      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7455      break;
7456    }
7457    case RestoreCommand:
7458    {
7459      /*
7460        Restore Image window to its original size.
7461      */
7462      if ((windows->image.width == (unsigned int) (*image)->columns) &&
7463          (windows->image.height == (unsigned int) (*image)->rows) &&
7464          (windows->image.crop_geometry == (char *) NULL))
7465        {
7466          (void) XBell(display,0);
7467          break;
7468        }
7469      windows->image.window_changes.width=(int) (*image)->columns;
7470      windows->image.window_changes.height=(int) (*image)->rows;
7471      if (windows->image.crop_geometry != (char *) NULL)
7472        {
7473          windows->image.crop_geometry=(char *)
7474            RelinquishMagickMemory(windows->image.crop_geometry);
7475          windows->image.crop_geometry=(char *) NULL;
7476          windows->image.x=0;
7477          windows->image.y=0;
7478        }
7479      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7480      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7481      break;
7482    }
7483    case CropCommand:
7484    {
7485      /*
7486        Crop image.
7487      */
7488      (void) XCropImage(display,resource_info,windows,*image,CropMode,
7489        exception);
7490      break;
7491    }
7492    case ChopCommand:
7493    {
7494      /*
7495        Chop image.
7496      */
7497      status=XChopImage(display,resource_info,windows,image,exception);
7498      if (status == MagickFalse)
7499        {
7500          XNoticeWidget(display,windows,"Unable to cut X image",
7501            (*image)->filename);
7502          break;
7503        }
7504      break;
7505    }
7506    case FlopCommand:
7507    {
7508      Image
7509        *flop_image;
7510
7511      /*
7512        Flop image scanlines.
7513      */
7514      XSetCursorState(display,windows,MagickTrue);
7515      XCheckRefreshWindows(display,windows);
7516      flop_image=FlopImage(*image,exception);
7517      if (flop_image != (Image *) NULL)
7518        {
7519          *image=DestroyImage(*image);
7520          *image=flop_image;
7521        }
7522      CatchException(exception);
7523      XSetCursorState(display,windows,MagickFalse);
7524      if (windows->image.crop_geometry != (char *) NULL)
7525        {
7526          /*
7527            Flop crop geometry.
7528          */
7529          width=(unsigned int) (*image)->columns;
7530          height=(unsigned int) (*image)->rows;
7531          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7532            &width,&height);
7533          (void) FormatLocaleString(windows->image.crop_geometry,MagickPathExtent,
7534            "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7535        }
7536      if (windows->image.orphan != MagickFalse )
7537        break;
7538      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7539      break;
7540    }
7541    case FlipCommand:
7542    {
7543      Image
7544        *flip_image;
7545
7546      /*
7547        Flip image scanlines.
7548      */
7549      XSetCursorState(display,windows,MagickTrue);
7550      XCheckRefreshWindows(display,windows);
7551      flip_image=FlipImage(*image,exception);
7552      if (flip_image != (Image *) NULL)
7553        {
7554          *image=DestroyImage(*image);
7555          *image=flip_image;
7556        }
7557      CatchException(exception);
7558      XSetCursorState(display,windows,MagickFalse);
7559      if (windows->image.crop_geometry != (char *) NULL)
7560        {
7561          /*
7562            Flip crop geometry.
7563          */
7564          width=(unsigned int) (*image)->columns;
7565          height=(unsigned int) (*image)->rows;
7566          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7567            &width,&height);
7568          (void) FormatLocaleString(windows->image.crop_geometry,MagickPathExtent,
7569            "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7570        }
7571      if (windows->image.orphan != MagickFalse )
7572        break;
7573      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7574      break;
7575    }
7576    case RotateRightCommand:
7577    {
7578      /*
7579        Rotate image 90 degrees clockwise.
7580      */
7581      status=XRotateImage(display,resource_info,windows,90.0,image,exception);
7582      if (status == MagickFalse)
7583        {
7584          XNoticeWidget(display,windows,"Unable to rotate X image",
7585            (*image)->filename);
7586          break;
7587        }
7588      break;
7589    }
7590    case RotateLeftCommand:
7591    {
7592      /*
7593        Rotate image 90 degrees counter-clockwise.
7594      */
7595      status=XRotateImage(display,resource_info,windows,-90.0,image,exception);
7596      if (status == MagickFalse)
7597        {
7598          XNoticeWidget(display,windows,"Unable to rotate X image",
7599            (*image)->filename);
7600          break;
7601        }
7602      break;
7603    }
7604    case RotateCommand:
7605    {
7606      /*
7607        Rotate image.
7608      */
7609      status=XRotateImage(display,resource_info,windows,0.0,image,exception);
7610      if (status == MagickFalse)
7611        {
7612          XNoticeWidget(display,windows,"Unable to rotate X image",
7613            (*image)->filename);
7614          break;
7615        }
7616      break;
7617    }
7618    case ShearCommand:
7619    {
7620      Image
7621        *shear_image;
7622
7623      static char
7624        geometry[MagickPathExtent] = "45.0x45.0";
7625
7626      /*
7627        Query user for shear color and geometry.
7628      */
7629      XColorBrowserWidget(display,windows,"Select",color);
7630      if (*color == '\0')
7631        break;
7632      (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7633        geometry);
7634      if (*geometry == '\0')
7635        break;
7636      /*
7637        Shear image.
7638      */
7639      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7640        exception);
7641      XSetCursorState(display,windows,MagickTrue);
7642      XCheckRefreshWindows(display,windows);
7643      (void) QueryColorCompliance(color,AllCompliance,
7644        &(*image)->background_color,exception);
7645      flags=ParseGeometry(geometry,&geometry_info);
7646      if ((flags & SigmaValue) == 0)
7647        geometry_info.sigma=geometry_info.rho;
7648      shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7649        exception);
7650      if (shear_image != (Image *) NULL)
7651        {
7652          *image=DestroyImage(*image);
7653          *image=shear_image;
7654        }
7655      CatchException(exception);
7656      XSetCursorState(display,windows,MagickFalse);
7657      if (windows->image.orphan != MagickFalse )
7658        break;
7659      windows->image.window_changes.width=(int) (*image)->columns;
7660      windows->image.window_changes.height=(int) (*image)->rows;
7661      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7662      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7663      break;
7664    }
7665    case RollCommand:
7666    {
7667      Image
7668        *roll_image;
7669
7670      static char
7671        geometry[MagickPathExtent] = "+2+2";
7672
7673      /*
7674        Query user for the roll geometry.
7675      */
7676      (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7677        geometry);
7678      if (*geometry == '\0')
7679        break;
7680      /*
7681        Roll image.
7682      */
7683      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7684        exception);
7685      XSetCursorState(display,windows,MagickTrue);
7686      XCheckRefreshWindows(display,windows);
7687      (void) ParsePageGeometry(*image,geometry,&page_geometry,
7688        exception);
7689      roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7690        exception);
7691      if (roll_image != (Image *) NULL)
7692        {
7693          *image=DestroyImage(*image);
7694          *image=roll_image;
7695        }
7696      CatchException(exception);
7697      XSetCursorState(display,windows,MagickFalse);
7698      if (windows->image.orphan != MagickFalse )
7699        break;
7700      windows->image.window_changes.width=(int) (*image)->columns;
7701      windows->image.window_changes.height=(int) (*image)->rows;
7702      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7703      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7704      break;
7705    }
7706    case TrimCommand:
7707    {
7708      static char
7709        fuzz[MagickPathExtent];
7710
7711      /*
7712        Query user for the fuzz factor.
7713      */
7714      (void) FormatLocaleString(fuzz,MagickPathExtent,"%g%%",100.0*
7715        (*image)->fuzz/(QuantumRange+1.0));
7716      (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7717      if (*fuzz == '\0')
7718        break;
7719      (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+1.0);
7720      /*
7721        Trim image.
7722      */
7723      status=XTrimImage(display,resource_info,windows,*image,exception);
7724      if (status == MagickFalse)
7725        {
7726          XNoticeWidget(display,windows,"Unable to trim X image",
7727            (*image)->filename);
7728          break;
7729        }
7730      break;
7731    }
7732    case HueCommand:
7733    {
7734      static char
7735        hue_percent[MagickPathExtent] = "110";
7736
7737      /*
7738        Query user for percent hue change.
7739      */
7740      (void) XDialogWidget(display,windows,"Apply",
7741        "Enter percent change in image hue (0-200):",hue_percent);
7742      if (*hue_percent == '\0')
7743        break;
7744      /*
7745        Vary the image hue.
7746      */
7747      XSetCursorState(display,windows,MagickTrue);
7748      XCheckRefreshWindows(display,windows);
7749      (void) CopyMagickString(modulate_factors,"100.0/100.0/",MagickPathExtent);
7750      (void) ConcatenateMagickString(modulate_factors,hue_percent,
7751        MagickPathExtent);
7752      (void) ModulateImage(*image,modulate_factors,exception);
7753      XSetCursorState(display,windows,MagickFalse);
7754      if (windows->image.orphan != MagickFalse )
7755        break;
7756      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7757      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7758      break;
7759    }
7760    case SaturationCommand:
7761    {
7762      static char
7763        saturation_percent[MagickPathExtent] = "110";
7764
7765      /*
7766        Query user for percent saturation change.
7767      */
7768      (void) XDialogWidget(display,windows,"Apply",
7769        "Enter percent change in color saturation (0-200):",saturation_percent);
7770      if (*saturation_percent == '\0')
7771        break;
7772      /*
7773        Vary color saturation.
7774      */
7775      XSetCursorState(display,windows,MagickTrue);
7776      XCheckRefreshWindows(display,windows);
7777      (void) CopyMagickString(modulate_factors,"100.0/",MagickPathExtent);
7778      (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7779        MagickPathExtent);
7780      (void) ModulateImage(*image,modulate_factors,exception);
7781      XSetCursorState(display,windows,MagickFalse);
7782      if (windows->image.orphan != MagickFalse )
7783        break;
7784      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7785      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7786      break;
7787    }
7788    case BrightnessCommand:
7789    {
7790      static char
7791        brightness_percent[MagickPathExtent] = "110";
7792
7793      /*
7794        Query user for percent brightness change.
7795      */
7796      (void) XDialogWidget(display,windows,"Apply",
7797        "Enter percent change in color brightness (0-200):",brightness_percent);
7798      if (*brightness_percent == '\0')
7799        break;
7800      /*
7801        Vary the color brightness.
7802      */
7803      XSetCursorState(display,windows,MagickTrue);
7804      XCheckRefreshWindows(display,windows);
7805      (void) CopyMagickString(modulate_factors,brightness_percent,
7806        MagickPathExtent);
7807      (void) ModulateImage(*image,modulate_factors,exception);
7808      XSetCursorState(display,windows,MagickFalse);
7809      if (windows->image.orphan != MagickFalse )
7810        break;
7811      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7812      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7813      break;
7814    }
7815    case GammaCommand:
7816    {
7817      static char
7818        factor[MagickPathExtent] = "1.6";
7819
7820      /*
7821        Query user for gamma value.
7822      */
7823      (void) XDialogWidget(display,windows,"Gamma",
7824        "Enter gamma value (e.g. 1.2):",factor);
7825      if (*factor == '\0')
7826        break;
7827      /*
7828        Gamma correct image.
7829      */
7830      XSetCursorState(display,windows,MagickTrue);
7831      XCheckRefreshWindows(display,windows);
7832      (void) GammaImage(*image,strtod(factor,(char **) NULL),exception);
7833      XSetCursorState(display,windows,MagickFalse);
7834      if (windows->image.orphan != MagickFalse )
7835        break;
7836      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7837      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7838      break;
7839    }
7840    case SpiffCommand:
7841    {
7842      /*
7843        Sharpen the image contrast.
7844      */
7845      XSetCursorState(display,windows,MagickTrue);
7846      XCheckRefreshWindows(display,windows);
7847      (void) ContrastImage(*image,MagickTrue,exception);
7848      XSetCursorState(display,windows,MagickFalse);
7849      if (windows->image.orphan != MagickFalse )
7850        break;
7851      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7852      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7853      break;
7854    }
7855    case DullCommand:
7856    {
7857      /*
7858        Dull the image contrast.
7859      */
7860      XSetCursorState(display,windows,MagickTrue);
7861      XCheckRefreshWindows(display,windows);
7862      (void) ContrastImage(*image,MagickFalse,exception);
7863      XSetCursorState(display,windows,MagickFalse);
7864      if (windows->image.orphan != MagickFalse )
7865        break;
7866      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7867      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7868      break;
7869    }
7870    case ContrastStretchCommand:
7871    {
7872      double
7873        black_point,
7874        white_point;
7875
7876      static char
7877        levels[MagickPathExtent] = "1%";
7878
7879      /*
7880        Query user for gamma value.
7881      */
7882      (void) XDialogWidget(display,windows,"Contrast Stretch",
7883        "Enter black and white points:",levels);
7884      if (*levels == '\0')
7885        break;
7886      /*
7887        Contrast stretch image.
7888      */
7889      XSetCursorState(display,windows,MagickTrue);
7890      XCheckRefreshWindows(display,windows);
7891      flags=ParseGeometry(levels,&geometry_info);
7892      black_point=geometry_info.rho;
7893      white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7894      if ((flags & PercentValue) != 0)
7895        {
7896          black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7897          white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7898        }
7899      white_point=(double) (*image)->columns*(*image)->rows-white_point;
7900      (void) ContrastStretchImage(*image,black_point,white_point,
7901        exception);
7902      XSetCursorState(display,windows,MagickFalse);
7903      if (windows->image.orphan != MagickFalse )
7904        break;
7905      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7906      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7907      break;
7908    }
7909    case SigmoidalContrastCommand:
7910    {
7911      GeometryInfo
7912        geometry_info;
7913
7914      MagickStatusType
7915        flags;
7916
7917      static char
7918        levels[MagickPathExtent] = "3x50%";
7919
7920      /*
7921        Query user for gamma value.
7922      */
7923      (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7924        "Enter contrast and midpoint:",levels);
7925      if (*levels == '\0')
7926        break;
7927      /*
7928        Contrast stretch image.
7929      */
7930      XSetCursorState(display,windows,MagickTrue);
7931      XCheckRefreshWindows(display,windows);
7932      flags=ParseGeometry(levels,&geometry_info);
7933      if ((flags & SigmaValue) == 0)
7934        geometry_info.sigma=1.0*QuantumRange/2.0;
7935      if ((flags & PercentValue) != 0)
7936        geometry_info.sigma=1.0*QuantumRange*geometry_info.sigma/100.0;
7937      (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
7938        geometry_info.sigma,exception);
7939      XSetCursorState(display,windows,MagickFalse);
7940      if (windows->image.orphan != MagickFalse )
7941        break;
7942      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7943      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7944      break;
7945    }
7946    case NormalizeCommand:
7947    {
7948      /*
7949        Perform histogram normalization on the image.
7950      */
7951      XSetCursorState(display,windows,MagickTrue);
7952      XCheckRefreshWindows(display,windows);
7953      (void) NormalizeImage(*image,exception);
7954      XSetCursorState(display,windows,MagickFalse);
7955      if (windows->image.orphan != MagickFalse )
7956        break;
7957      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7958      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7959      break;
7960    }
7961    case EqualizeCommand:
7962    {
7963      /*
7964        Perform histogram equalization on the image.
7965      */
7966      XSetCursorState(display,windows,MagickTrue);
7967      XCheckRefreshWindows(display,windows);
7968      (void) EqualizeImage(*image,exception);
7969      XSetCursorState(display,windows,MagickFalse);
7970      if (windows->image.orphan != MagickFalse )
7971        break;
7972      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7973      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7974      break;
7975    }
7976    case NegateCommand:
7977    {
7978      /*
7979        Negate colors in image.
7980      */
7981      XSetCursorState(display,windows,MagickTrue);
7982      XCheckRefreshWindows(display,windows);
7983      (void) NegateImage(*image,MagickFalse,exception);
7984      XSetCursorState(display,windows,MagickFalse);
7985      if (windows->image.orphan != MagickFalse )
7986        break;
7987      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7988      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7989      break;
7990    }
7991    case GrayscaleCommand:
7992    {
7993      /*
7994        Convert image to grayscale.
7995      */
7996      XSetCursorState(display,windows,MagickTrue);
7997      XCheckRefreshWindows(display,windows);
7998      (void) SetImageType(*image,(*image)->alpha_trait == UndefinedPixelTrait ?
7999        GrayscaleType : GrayscaleAlphaType,exception);
8000      XSetCursorState(display,windows,MagickFalse);
8001      if (windows->image.orphan != MagickFalse )
8002        break;
8003      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8004      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8005      break;
8006    }
8007    case MapCommand:
8008    {
8009      Image
8010        *affinity_image;
8011
8012      static char
8013        filename[MagickPathExtent] = "\0";
8014
8015      /*
8016        Request image file name from user.
8017      */
8018      XFileBrowserWidget(display,windows,"Map",filename);
8019      if (*filename == '\0')
8020        break;
8021      /*
8022        Map image.
8023      */
8024      XSetCursorState(display,windows,MagickTrue);
8025      XCheckRefreshWindows(display,windows);
8026      (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
8027      affinity_image=ReadImage(image_info,exception);
8028      if (affinity_image != (Image *) NULL)
8029        {
8030          (void) RemapImage(&quantize_info,*image,affinity_image,exception);
8031          affinity_image=DestroyImage(affinity_image);
8032        }
8033      CatchException(exception);
8034      XSetCursorState(display,windows,MagickFalse);
8035      if (windows->image.orphan != MagickFalse )
8036        break;
8037      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8038      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8039      break;
8040    }
8041    case QuantizeCommand:
8042    {
8043      int
8044        status;
8045
8046      static char
8047        colors[MagickPathExtent] = "256";
8048
8049      /*
8050        Query user for maximum number of colors.
8051      */
8052      status=XDialogWidget(display,windows,"Quantize",
8053        "Maximum number of colors:",colors);
8054      if (*colors == '\0')
8055        break;
8056      /*
8057        Color reduce the image.
8058      */
8059      XSetCursorState(display,windows,MagickTrue);
8060      XCheckRefreshWindows(display,windows);
8061      quantize_info.number_colors=StringToUnsignedLong(colors);
8062      quantize_info.dither_method=status != 0 ? RiemersmaDitherMethod :
8063        NoDitherMethod;
8064      (void) QuantizeImage(&quantize_info,*image,exception);
8065      XSetCursorState(display,windows,MagickFalse);
8066      if (windows->image.orphan != MagickFalse )
8067        break;
8068      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8069      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8070      break;
8071    }
8072    case DespeckleCommand:
8073    {
8074      Image
8075        *despeckle_image;
8076
8077      /*
8078        Despeckle image.
8079      */
8080      XSetCursorState(display,windows,MagickTrue);
8081      XCheckRefreshWindows(display,windows);
8082      despeckle_image=DespeckleImage(*image,exception);
8083      if (despeckle_image != (Image *) NULL)
8084        {
8085          *image=DestroyImage(*image);
8086          *image=despeckle_image;
8087        }
8088      CatchException(exception);
8089      XSetCursorState(display,windows,MagickFalse);
8090      if (windows->image.orphan != MagickFalse )
8091        break;
8092      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8093      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8094      break;
8095    }
8096    case EmbossCommand:
8097    {
8098      Image
8099        *emboss_image;
8100
8101      static char
8102        radius[MagickPathExtent] = "0.0x1.0";
8103
8104      /*
8105        Query user for emboss radius.
8106      */
8107      (void) XDialogWidget(display,windows,"Emboss",
8108        "Enter the emboss radius and standard deviation:",radius);
8109      if (*radius == '\0')
8110        break;
8111      /*
8112        Reduce noise in the image.
8113      */
8114      XSetCursorState(display,windows,MagickTrue);
8115      XCheckRefreshWindows(display,windows);
8116      flags=ParseGeometry(radius,&geometry_info);
8117      if ((flags & SigmaValue) == 0)
8118        geometry_info.sigma=1.0;
8119      emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8120        exception);
8121      if (emboss_image != (Image *) NULL)
8122        {
8123          *image=DestroyImage(*image);
8124          *image=emboss_image;
8125        }
8126      CatchException(exception);
8127      XSetCursorState(display,windows,MagickFalse);
8128      if (windows->image.orphan != MagickFalse )
8129        break;
8130      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8131      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8132      break;
8133    }
8134    case ReduceNoiseCommand:
8135    {
8136      Image
8137        *noise_image;
8138
8139      static char
8140        radius[MagickPathExtent] = "0";
8141
8142      /*
8143        Query user for noise radius.
8144      */
8145      (void) XDialogWidget(display,windows,"Reduce Noise",
8146        "Enter the noise radius:",radius);
8147      if (*radius == '\0')
8148        break;
8149      /*
8150        Reduce noise in the image.
8151      */
8152      XSetCursorState(display,windows,MagickTrue);
8153      XCheckRefreshWindows(display,windows);
8154      flags=ParseGeometry(radius,&geometry_info);
8155      noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8156        geometry_info.rho,(size_t) geometry_info.rho,exception);
8157      if (noise_image != (Image *) NULL)
8158        {
8159          *image=DestroyImage(*image);
8160          *image=noise_image;
8161        }
8162      CatchException(exception);
8163      XSetCursorState(display,windows,MagickFalse);
8164      if (windows->image.orphan != MagickFalse )
8165        break;
8166      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8167      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8168      break;
8169    }
8170    case AddNoiseCommand:
8171    {
8172      char
8173        **noises;
8174
8175      Image
8176        *noise_image;
8177
8178      static char
8179        noise_type[MagickPathExtent] = "Gaussian";
8180
8181      /*
8182        Add noise to the image.
8183      */
8184      noises=GetCommandOptions(MagickNoiseOptions);
8185      if (noises == (char **) NULL)
8186        break;
8187      XListBrowserWidget(display,windows,&windows->widget,
8188        (const char **) noises,"Add Noise",
8189        "Select a type of noise to add to your image:",noise_type);
8190      noises=DestroyStringList(noises);
8191      if (*noise_type == '\0')
8192        break;
8193      XSetCursorState(display,windows,MagickTrue);
8194      XCheckRefreshWindows(display,windows);
8195      noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8196        MagickNoiseOptions,MagickFalse,noise_type),1.0,exception);
8197      if (noise_image != (Image *) NULL)
8198        {
8199          *image=DestroyImage(*image);
8200          *image=noise_image;
8201        }
8202      CatchException(exception);
8203      XSetCursorState(display,windows,MagickFalse);
8204      if (windows->image.orphan != MagickFalse )
8205        break;
8206      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8207      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8208      break;
8209    }
8210    case SharpenCommand:
8211    {
8212      Image
8213        *sharp_image;
8214
8215      static char
8216        radius[MagickPathExtent] = "0.0x1.0";
8217
8218      /*
8219        Query user for sharpen radius.
8220      */
8221      (void) XDialogWidget(display,windows,"Sharpen",
8222        "Enter the sharpen radius and standard deviation:",radius);
8223      if (*radius == '\0')
8224        break;
8225      /*
8226        Sharpen image scanlines.
8227      */
8228      XSetCursorState(display,windows,MagickTrue);
8229      XCheckRefreshWindows(display,windows);
8230      flags=ParseGeometry(radius,&geometry_info);
8231      sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8232        exception);
8233      if (sharp_image != (Image *) NULL)
8234        {
8235          *image=DestroyImage(*image);
8236          *image=sharp_image;
8237        }
8238      CatchException(exception);
8239      XSetCursorState(display,windows,MagickFalse);
8240      if (windows->image.orphan != MagickFalse )
8241        break;
8242      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8243      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8244      break;
8245    }
8246    case BlurCommand:
8247    {
8248      Image
8249        *blur_image;
8250
8251      static char
8252        radius[MagickPathExtent] = "0.0x1.0";
8253
8254      /*
8255        Query user for blur radius.
8256      */
8257      (void) XDialogWidget(display,windows,"Blur",
8258        "Enter the blur radius and standard deviation:",radius);
8259      if (*radius == '\0')
8260        break;
8261      /*
8262        Blur an image.
8263      */
8264      XSetCursorState(display,windows,MagickTrue);
8265      XCheckRefreshWindows(display,windows);
8266      flags=ParseGeometry(radius,&geometry_info);
8267      blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8268        exception);
8269      if (blur_image != (Image *) NULL)
8270        {
8271          *image=DestroyImage(*image);
8272          *image=blur_image;
8273        }
8274      CatchException(exception);
8275      XSetCursorState(display,windows,MagickFalse);
8276      if (windows->image.orphan != MagickFalse )
8277        break;
8278      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8279      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8280      break;
8281    }
8282    case ThresholdCommand:
8283    {
8284      double
8285        threshold;
8286
8287      static char
8288        factor[MagickPathExtent] = "128";
8289
8290      /*
8291        Query user for threshold value.
8292      */
8293      (void) XDialogWidget(display,windows,"Threshold",
8294        "Enter threshold value:",factor);
8295      if (*factor == '\0')
8296        break;
8297      /*
8298        Gamma correct image.
8299      */
8300      XSetCursorState(display,windows,MagickTrue);
8301      XCheckRefreshWindows(display,windows);
8302      threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8303      (void) BilevelImage(*image,threshold,exception);
8304      XSetCursorState(display,windows,MagickFalse);
8305      if (windows->image.orphan != MagickFalse )
8306        break;
8307      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8308      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8309      break;
8310    }
8311    case EdgeDetectCommand:
8312    {
8313      Image
8314        *edge_image;
8315
8316      static char
8317        radius[MagickPathExtent] = "0";
8318
8319      /*
8320        Query user for edge factor.
8321      */
8322      (void) XDialogWidget(display,windows,"Detect Edges",
8323        "Enter the edge detect radius:",radius);
8324      if (*radius == '\0')
8325        break;
8326      /*
8327        Detect edge in image.
8328      */
8329      XSetCursorState(display,windows,MagickTrue);
8330      XCheckRefreshWindows(display,windows);
8331      flags=ParseGeometry(radius,&geometry_info);
8332      edge_image=EdgeImage(*image,geometry_info.rho,exception);
8333      if (edge_image != (Image *) NULL)
8334        {
8335          *image=DestroyImage(*image);
8336          *image=edge_image;
8337        }
8338      CatchException(exception);
8339      XSetCursorState(display,windows,MagickFalse);
8340      if (windows->image.orphan != MagickFalse )
8341        break;
8342      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8343      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8344      break;
8345    }
8346    case SpreadCommand:
8347    {
8348      Image
8349        *spread_image;
8350
8351      static char
8352        amount[MagickPathExtent] = "2";
8353
8354      /*
8355        Query user for spread amount.
8356      */
8357      (void) XDialogWidget(display,windows,"Spread",
8358        "Enter the displacement amount:",amount);
8359      if (*amount == '\0')
8360        break;
8361      /*
8362        Displace image pixels by a random amount.
8363      */
8364      XSetCursorState(display,windows,MagickTrue);
8365      XCheckRefreshWindows(display,windows);
8366      flags=ParseGeometry(amount,&geometry_info);
8367      spread_image=EdgeImage(*image,geometry_info.rho,exception);
8368      if (spread_image != (Image *) NULL)
8369        {
8370          *image=DestroyImage(*image);
8371          *image=spread_image;
8372        }
8373      CatchException(exception);
8374      XSetCursorState(display,windows,MagickFalse);
8375      if (windows->image.orphan != MagickFalse )
8376        break;
8377      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8378      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8379      break;
8380    }
8381    case ShadeCommand:
8382    {
8383      Image
8384        *shade_image;
8385
8386      int
8387        status;
8388
8389      static char
8390        geometry[MagickPathExtent] = "30x30";
8391
8392      /*
8393        Query user for the shade geometry.
8394      */
8395      status=XDialogWidget(display,windows,"Shade",
8396        "Enter the azimuth and elevation of the light source:",geometry);
8397      if (*geometry == '\0')
8398        break;
8399      /*
8400        Shade image pixels.
8401      */
8402      XSetCursorState(display,windows,MagickTrue);
8403      XCheckRefreshWindows(display,windows);
8404      flags=ParseGeometry(geometry,&geometry_info);
8405      if ((flags & SigmaValue) == 0)
8406        geometry_info.sigma=1.0;
8407      shade_image=ShadeImage(*image,status != 0 ? MagickTrue : MagickFalse,
8408        geometry_info.rho,geometry_info.sigma,exception);
8409      if (shade_image != (Image *) NULL)
8410        {
8411          *image=DestroyImage(*image);
8412          *image=shade_image;
8413        }
8414      CatchException(exception);
8415      XSetCursorState(display,windows,MagickFalse);
8416      if (windows->image.orphan != MagickFalse )
8417        break;
8418      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8419      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8420      break;
8421    }
8422    case RaiseCommand:
8423    {
8424      static char
8425        bevel_width[MagickPathExtent] = "10";
8426
8427      /*
8428        Query user for bevel width.
8429      */
8430      (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8431      if (*bevel_width == '\0')
8432        break;
8433      /*
8434        Raise an image.
8435      */
8436      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8437        exception);
8438      XSetCursorState(display,windows,MagickTrue);
8439      XCheckRefreshWindows(display,windows);
8440      (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8441        exception);
8442      (void) RaiseImage(*image,&page_geometry,MagickTrue,exception);
8443      XSetCursorState(display,windows,MagickFalse);
8444      if (windows->image.orphan != MagickFalse )
8445        break;
8446      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8447      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8448      break;
8449    }
8450    case SegmentCommand:
8451    {
8452      static char
8453        threshold[MagickPathExtent] = "1.0x1.5";
8454
8455      /*
8456        Query user for smoothing threshold.
8457      */
8458      (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8459        threshold);
8460      if (*threshold == '\0')
8461        break;
8462      /*
8463        Segment an image.
8464      */
8465      XSetCursorState(display,windows,MagickTrue);
8466      XCheckRefreshWindows(display,windows);
8467      flags=ParseGeometry(threshold,&geometry_info);
8468      if ((flags & SigmaValue) == 0)
8469        geometry_info.sigma=1.0;
8470      (void) SegmentImage(*image,sRGBColorspace,MagickFalse,geometry_info.rho,
8471        geometry_info.sigma,exception);
8472      XSetCursorState(display,windows,MagickFalse);
8473      if (windows->image.orphan != MagickFalse )
8474        break;
8475      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8476      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8477      break;
8478    }
8479    case SepiaToneCommand:
8480    {
8481      double
8482        threshold;
8483
8484      Image
8485        *sepia_image;
8486
8487      static char
8488        factor[MagickPathExtent] = "80%";
8489
8490      /*
8491        Query user for sepia-tone factor.
8492      */
8493      (void) XDialogWidget(display,windows,"Sepia Tone",
8494        "Enter the sepia tone factor (0 - 99.9%):",factor);
8495      if (*factor == '\0')
8496        break;
8497      /*
8498        Sepia tone image pixels.
8499      */
8500      XSetCursorState(display,windows,MagickTrue);
8501      XCheckRefreshWindows(display,windows);
8502      threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8503      sepia_image=SepiaToneImage(*image,threshold,exception);
8504      if (sepia_image != (Image *) NULL)
8505        {
8506          *image=DestroyImage(*image);
8507          *image=sepia_image;
8508        }
8509      CatchException(exception);
8510      XSetCursorState(display,windows,MagickFalse);
8511      if (windows->image.orphan != MagickFalse )
8512        break;
8513      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8514      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8515      break;
8516    }
8517    case SolarizeCommand:
8518    {
8519      double
8520        threshold;
8521
8522      static char
8523        factor[MagickPathExtent] = "60%";
8524
8525      /*
8526        Query user for solarize factor.
8527      */
8528      (void) XDialogWidget(display,windows,"Solarize",
8529        "Enter the solarize factor (0 - 99.9%):",factor);
8530      if (*factor == '\0')
8531        break;
8532      /*
8533        Solarize image pixels.
8534      */
8535      XSetCursorState(display,windows,MagickTrue);
8536      XCheckRefreshWindows(display,windows);
8537      threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8538      (void) SolarizeImage(*image,threshold,exception);
8539      XSetCursorState(display,windows,MagickFalse);
8540      if (windows->image.orphan != MagickFalse )
8541        break;
8542      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8543      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8544      break;
8545    }
8546    case SwirlCommand:
8547    {
8548      Image
8549        *swirl_image;
8550
8551      static char
8552        degrees[MagickPathExtent] = "60";
8553
8554      /*
8555        Query user for swirl angle.
8556      */
8557      (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8558        degrees);
8559      if (*degrees == '\0')
8560        break;
8561      /*
8562        Swirl image pixels about the center.
8563      */
8564      XSetCursorState(display,windows,MagickTrue);
8565      XCheckRefreshWindows(display,windows);
8566      flags=ParseGeometry(degrees,&geometry_info);
8567      swirl_image=SwirlImage(*image,geometry_info.rho,(*image)->interpolate,
8568        exception);
8569      if (swirl_image != (Image *) NULL)
8570        {
8571          *image=DestroyImage(*image);
8572          *image=swirl_image;
8573        }
8574      CatchException(exception);
8575      XSetCursorState(display,windows,MagickFalse);
8576      if (windows->image.orphan != MagickFalse )
8577        break;
8578      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8579      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8580      break;
8581    }
8582    case ImplodeCommand:
8583    {
8584      Image
8585        *implode_image;
8586
8587      static char
8588        factor[MagickPathExtent] = "0.3";
8589
8590      /*
8591        Query user for implode factor.
8592      */
8593      (void) XDialogWidget(display,windows,"Implode",
8594        "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8595      if (*factor == '\0')
8596        break;
8597      /*
8598        Implode image pixels about the center.
8599      */
8600      XSetCursorState(display,windows,MagickTrue);
8601      XCheckRefreshWindows(display,windows);
8602      flags=ParseGeometry(factor,&geometry_info);
8603      implode_image=ImplodeImage(*image,geometry_info.rho,(*image)->interpolate,
8604        exception);
8605      if (implode_image != (Image *) NULL)
8606        {
8607          *image=DestroyImage(*image);
8608          *image=implode_image;
8609        }
8610      CatchException(exception);
8611      XSetCursorState(display,windows,MagickFalse);
8612      if (windows->image.orphan != MagickFalse )
8613        break;
8614      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8615      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8616      break;
8617    }
8618    case VignetteCommand:
8619    {
8620      Image
8621        *vignette_image;
8622
8623      static char
8624        geometry[MagickPathExtent] = "0x20";
8625
8626      /*
8627        Query user for the vignette geometry.
8628      */
8629      (void) XDialogWidget(display,windows,"Vignette",
8630        "Enter the radius, sigma, and x and y offsets:",geometry);
8631      if (*geometry == '\0')
8632        break;
8633      /*
8634        Soften the edges of the image in vignette style
8635      */
8636      XSetCursorState(display,windows,MagickTrue);
8637      XCheckRefreshWindows(display,windows);
8638      flags=ParseGeometry(geometry,&geometry_info);
8639      if ((flags & SigmaValue) == 0)
8640        geometry_info.sigma=1.0;
8641      if ((flags & XiValue) == 0)
8642        geometry_info.xi=0.1*(*image)->columns;
8643      if ((flags & PsiValue) == 0)
8644        geometry_info.psi=0.1*(*image)->rows;
8645      vignette_image=VignetteImage(*image,geometry_info.rho,0.0,(ssize_t)
8646        ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-0.5),
8647        exception);
8648      if (vignette_image != (Image *) NULL)
8649        {
8650          *image=DestroyImage(*image);
8651          *image=vignette_image;
8652        }
8653      CatchException(exception);
8654      XSetCursorState(display,windows,MagickFalse);
8655      if (windows->image.orphan != MagickFalse )
8656        break;
8657      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8658      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8659      break;
8660    }
8661    case WaveCommand:
8662    {
8663      Image
8664        *wave_image;
8665
8666      static char
8667        geometry[MagickPathExtent] = "25x150";
8668
8669      /*
8670        Query user for the wave geometry.
8671      */
8672      (void) XDialogWidget(display,windows,"Wave",
8673        "Enter the amplitude and length of the wave:",geometry);
8674      if (*geometry == '\0')
8675        break;
8676      /*
8677        Alter an image along a sine wave.
8678      */
8679      XSetCursorState(display,windows,MagickTrue);
8680      XCheckRefreshWindows(display,windows);
8681      flags=ParseGeometry(geometry,&geometry_info);
8682      if ((flags & SigmaValue) == 0)
8683        geometry_info.sigma=1.0;
8684      wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8685        (*image)->interpolate,exception);
8686      if (wave_image != (Image *) NULL)
8687        {
8688          *image=DestroyImage(*image);
8689          *image=wave_image;
8690        }
8691      CatchException(exception);
8692      XSetCursorState(display,windows,MagickFalse);
8693      if (windows->image.orphan != MagickFalse )
8694        break;
8695      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8696      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8697      break;
8698    }
8699    case OilPaintCommand:
8700    {
8701      Image
8702        *paint_image;
8703
8704      static char
8705        radius[MagickPathExtent] = "0";
8706
8707      /*
8708        Query user for circular neighborhood radius.
8709      */
8710      (void) XDialogWidget(display,windows,"Oil Paint",
8711        "Enter the mask radius:",radius);
8712      if (*radius == '\0')
8713        break;
8714      /*
8715        OilPaint image scanlines.
8716      */
8717      XSetCursorState(display,windows,MagickTrue);
8718      XCheckRefreshWindows(display,windows);
8719      flags=ParseGeometry(radius,&geometry_info);
8720      paint_image=OilPaintImage(*image,geometry_info.rho,geometry_info.sigma,
8721        exception);
8722      if (paint_image != (Image *) NULL)
8723        {
8724          *image=DestroyImage(*image);
8725          *image=paint_image;
8726        }
8727      CatchException(exception);
8728      XSetCursorState(display,windows,MagickFalse);
8729      if (windows->image.orphan != MagickFalse )
8730        break;
8731      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8732      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8733      break;
8734    }
8735    case CharcoalDrawCommand:
8736    {
8737      Image
8738        *charcoal_image;
8739
8740      static char
8741        radius[MagickPathExtent] = "0x1";
8742
8743      /*
8744        Query user for charcoal radius.
8745      */
8746      (void) XDialogWidget(display,windows,"Charcoal Draw",
8747        "Enter the charcoal radius and sigma:",radius);
8748      if (*radius == '\0')
8749        break;
8750      /*
8751        Charcoal the image.
8752      */
8753      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8754        exception);
8755      XSetCursorState(display,windows,MagickTrue);
8756      XCheckRefreshWindows(display,windows);
8757      flags=ParseGeometry(radius,&geometry_info);
8758      if ((flags & SigmaValue) == 0)
8759        geometry_info.sigma=geometry_info.rho;
8760      charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8761        exception);
8762      if (charcoal_image != (Image *) NULL)
8763        {
8764          *image=DestroyImage(*image);
8765          *image=charcoal_image;
8766        }
8767      CatchException(exception);
8768      XSetCursorState(display,windows,MagickFalse);
8769      if (windows->image.orphan != MagickFalse )
8770        break;
8771      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8772      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8773      break;
8774    }
8775    case AnnotateCommand:
8776    {
8777      /*
8778        Annotate the image with text.
8779      */
8780      status=XAnnotateEditImage(display,resource_info,windows,*image,exception);
8781      if (status == MagickFalse)
8782        {
8783          XNoticeWidget(display,windows,"Unable to annotate X image",
8784            (*image)->filename);
8785          break;
8786        }
8787      break;
8788    }
8789    case DrawCommand:
8790    {
8791      /*
8792        Draw image.
8793      */
8794      status=XDrawEditImage(display,resource_info,windows,image,exception);
8795      if (status == MagickFalse)
8796        {
8797          XNoticeWidget(display,windows,"Unable to draw on the X image",
8798            (*image)->filename);
8799          break;
8800        }
8801      break;
8802    }
8803    case ColorCommand:
8804    {
8805      /*
8806        Color edit.
8807      */
8808      status=XColorEditImage(display,resource_info,windows,image,exception);
8809      if (status == MagickFalse)
8810        {
8811          XNoticeWidget(display,windows,"Unable to pixel edit X image",
8812            (*image)->filename);
8813          break;
8814        }
8815      break;
8816    }
8817    case MatteCommand:
8818    {
8819      /*
8820        Matte edit.
8821      */
8822      status=XMatteEditImage(display,resource_info,windows,image,exception);
8823      if (status == MagickFalse)
8824        {
8825          XNoticeWidget(display,windows,"Unable to matte edit X image",
8826            (*image)->filename);
8827          break;
8828        }
8829      break;
8830    }
8831    case CompositeCommand:
8832    {
8833      /*
8834        Composite image.
8835      */
8836      status=XCompositeImage(display,resource_info,windows,*image,
8837        exception);
8838      if (status == MagickFalse)
8839        {
8840          XNoticeWidget(display,windows,"Unable to composite X image",
8841            (*image)->filename);
8842          break;
8843        }
8844      break;
8845    }
8846    case AddBorderCommand:
8847    {
8848      Image
8849        *border_image;
8850
8851      static char
8852        geometry[MagickPathExtent] = "6x6";
8853
8854      /*
8855        Query user for border color and geometry.
8856      */
8857      XColorBrowserWidget(display,windows,"Select",color);
8858      if (*color == '\0')
8859        break;
8860      (void) XDialogWidget(display,windows,"Add Border",
8861        "Enter border geometry:",geometry);
8862      if (*geometry == '\0')
8863        break;
8864      /*
8865        Add a border to the image.
8866      */
8867      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8868        exception);
8869      XSetCursorState(display,windows,MagickTrue);
8870      XCheckRefreshWindows(display,windows);
8871      (void) QueryColorCompliance(color,AllCompliance,&(*image)->border_color,
8872        exception);
8873      (void) ParsePageGeometry(*image,geometry,&page_geometry,
8874        exception);
8875      border_image=BorderImage(*image,&page_geometry,(*image)->compose,
8876        exception);
8877      if (border_image != (Image *) NULL)
8878        {
8879          *image=DestroyImage(*image);
8880          *image=border_image;
8881        }
8882      CatchException(exception);
8883      XSetCursorState(display,windows,MagickFalse);
8884      if (windows->image.orphan != MagickFalse )
8885        break;
8886      windows->image.window_changes.width=(int) (*image)->columns;
8887      windows->image.window_changes.height=(int) (*image)->rows;
8888      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8889      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8890      break;
8891    }
8892    case AddFrameCommand:
8893    {
8894      FrameInfo
8895        frame_info;
8896
8897      Image
8898        *frame_image;
8899
8900      static char
8901        geometry[MagickPathExtent] = "6x6";
8902
8903      /*
8904        Query user for frame color and geometry.
8905      */
8906      XColorBrowserWidget(display,windows,"Select",color);
8907      if (*color == '\0')
8908        break;
8909      (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8910        geometry);
8911      if (*geometry == '\0')
8912        break;
8913      /*
8914        Surround image with an ornamental border.
8915      */
8916      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8917        exception);
8918      XSetCursorState(display,windows,MagickTrue);
8919      XCheckRefreshWindows(display,windows);
8920      (void) QueryColorCompliance(color,AllCompliance,&(*image)->alpha_color,
8921        exception);
8922      (void) ParsePageGeometry(*image,geometry,&page_geometry,
8923        exception);
8924      frame_info.width=page_geometry.width;
8925      frame_info.height=page_geometry.height;
8926      frame_info.outer_bevel=page_geometry.x;
8927      frame_info.inner_bevel=page_geometry.y;
8928      frame_info.x=(ssize_t) frame_info.width;
8929      frame_info.y=(ssize_t) frame_info.height;
8930      frame_info.width=(*image)->columns+2*frame_info.width;
8931      frame_info.height=(*image)->rows+2*frame_info.height;
8932      frame_image=FrameImage(*image,&frame_info,(*image)->compose,exception);
8933      if (frame_image != (Image *) NULL)
8934        {
8935          *image=DestroyImage(*image);
8936          *image=frame_image;
8937        }
8938      CatchException(exception);
8939      XSetCursorState(display,windows,MagickFalse);
8940      if (windows->image.orphan != MagickFalse )
8941        break;
8942      windows->image.window_changes.width=(int) (*image)->columns;
8943      windows->image.window_changes.height=(int) (*image)->rows;
8944      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8945      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8946      break;
8947    }
8948    case CommentCommand:
8949    {
8950      const char
8951        *value;
8952
8953      FILE
8954        *file;
8955
8956      int
8957        unique_file;
8958
8959      /*
8960        Edit image comment.
8961      */
8962      unique_file=AcquireUniqueFileResource(image_info->filename);
8963      if (unique_file == -1)
8964        XNoticeWidget(display,windows,"Unable to edit image comment",
8965          image_info->filename);
8966      value=GetImageProperty(*image,"comment",exception);
8967      if (value == (char *) NULL)
8968        unique_file=close(unique_file)-1;
8969      else
8970        {
8971          register const char
8972            *p;
8973
8974          file=fdopen(unique_file,"w");
8975          if (file == (FILE *) NULL)
8976            {
8977              XNoticeWidget(display,windows,"Unable to edit image comment",
8978                image_info->filename);
8979              break;
8980            }
8981          for (p=value; *p != '\0'; p++)
8982            (void) fputc((int) *p,file);
8983          (void) fputc('\n',file);
8984          (void) fclose(file);
8985        }
8986      XSetCursorState(display,windows,MagickTrue);
8987      XCheckRefreshWindows(display,windows);
8988      status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
8989        exception);
8990      if (status == MagickFalse)
8991        XNoticeWidget(display,windows,"Unable to edit image comment",
8992          (char *) NULL);
8993      else
8994        {
8995          char
8996            *comment;
8997
8998          comment=FileToString(image_info->filename,~0UL,exception);
8999          if (comment != (char *) NULL)
9000            {
9001              (void) SetImageProperty(*image,"comment",comment,exception);
9002              (*image)->taint=MagickTrue;
9003            }
9004        }
9005      (void) RelinquishUniqueFileResource(image_info->filename);
9006      XSetCursorState(display,windows,MagickFalse);
9007      break;
9008    }
9009    case LaunchCommand:
9010    {
9011      /*
9012        Launch program.
9013      */
9014      XSetCursorState(display,windows,MagickTrue);
9015      XCheckRefreshWindows(display,windows);
9016      (void) AcquireUniqueFilename(filename);
9017      (void) FormatLocaleString((*image)->filename,MagickPathExtent,"launch:%s",
9018        filename);
9019      status=WriteImage(image_info,*image,exception);
9020      if (status == MagickFalse)
9021        XNoticeWidget(display,windows,"Unable to launch image editor",
9022          (char *) NULL);
9023      else
9024        {
9025          nexus=ReadImage(resource_info->image_info,exception);
9026          CatchException(exception);
9027          XClientMessage(display,windows->image.id,windows->im_protocols,
9028            windows->im_next_image,CurrentTime);
9029        }
9030      (void) RelinquishUniqueFileResource(filename);
9031      XSetCursorState(display,windows,MagickFalse);
9032      break;
9033    }
9034    case RegionofInterestCommand:
9035    {
9036      /*
9037        Apply an image processing technique to a region of interest.
9038      */
9039      (void) XROIImage(display,resource_info,windows,image,exception);
9040      break;
9041    }
9042    case InfoCommand:
9043      break;
9044    case ZoomCommand:
9045    {
9046      /*
9047        Zoom image.
9048      */
9049      if (windows->magnify.mapped != MagickFalse )
9050        (void) XRaiseWindow(display,windows->magnify.id);
9051      else
9052        {
9053          /*
9054            Make magnify image.
9055          */
9056          XSetCursorState(display,windows,MagickTrue);
9057          (void) XMapRaised(display,windows->magnify.id);
9058          XSetCursorState(display,windows,MagickFalse);
9059        }
9060      break;
9061    }
9062    case ShowPreviewCommand:
9063    {
9064      char
9065        **previews,
9066        value[MagickPathExtent];
9067
9068      Image
9069        *preview_image;
9070
9071      static char
9072        preview_type[MagickPathExtent] = "Gamma";
9073
9074      /*
9075        Select preview type from menu.
9076      */
9077      previews=GetCommandOptions(MagickPreviewOptions);
9078      if (previews == (char **) NULL)
9079        break;
9080      XListBrowserWidget(display,windows,&windows->widget,
9081        (const char **) previews,"Preview",
9082        "Select an enhancement, effect, or F/X:",preview_type);
9083      previews=DestroyStringList(previews);
9084      if (*preview_type == '\0')
9085        break;
9086      /*
9087        Show image preview.
9088      */
9089      XSetCursorState(display,windows,MagickTrue);
9090      XCheckRefreshWindows(display,windows);
9091      image_info->preview_type=(PreviewType)
9092        ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
9093      (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
9094        windows->image.id);
9095      (void) SetImageProperty(*image,"group",value,exception);
9096      (void) DeleteImageProperty(*image,"label");
9097      (void) SetImageProperty(*image,"label","Preview",exception);
9098      (void) AcquireUniqueFilename(filename);
9099      (void) FormatLocaleString((*image)->filename,MagickPathExtent,"preview:%s",
9100        filename);
9101      status=WriteImage(image_info,*image,exception);
9102      (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
9103      preview_image=ReadImage(image_info,exception);
9104      (void) RelinquishUniqueFileResource(filename);
9105      if (preview_image == (Image *) NULL)
9106        break;
9107      (void) FormatLocaleString(preview_image->filename,MagickPathExtent,"show:%s",
9108        filename);
9109      status=WriteImage(image_info,preview_image,exception);
9110      preview_image=DestroyImage(preview_image);
9111      if (status == MagickFalse)
9112        XNoticeWidget(display,windows,"Unable to show image preview",
9113          (*image)->filename);
9114      XDelay(display,1500);
9115      XSetCursorState(display,windows,MagickFalse);
9116      break;
9117    }
9118    case ShowHistogramCommand:
9119    {
9120      char
9121        value[MagickPathExtent];
9122
9123      Image
9124        *histogram_image;
9125
9126      /*
9127        Show image histogram.
9128      */
9129      XSetCursorState(display,windows,MagickTrue);
9130      XCheckRefreshWindows(display,windows);
9131      (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
9132        windows->image.id);
9133      (void) SetImageProperty(*image,"group",value,exception);
9134      (void) DeleteImageProperty(*image,"label");
9135      (void) SetImageProperty(*image,"label","Histogram",exception);
9136      (void) AcquireUniqueFilename(filename);
9137      (void) FormatLocaleString((*image)->filename,MagickPathExtent,"histogram:%s",
9138        filename);
9139      status=WriteImage(image_info,*image,exception);
9140      (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
9141      histogram_image=ReadImage(image_info,exception);
9142      (void) RelinquishUniqueFileResource(filename);
9143      if (histogram_image == (Image *) NULL)
9144        break;
9145      (void) FormatLocaleString(histogram_image->filename,MagickPathExtent,
9146        "show:%s",filename);
9147      status=WriteImage(image_info,histogram_image,exception);
9148      histogram_image=DestroyImage(histogram_image);
9149      if (status == MagickFalse)
9150        XNoticeWidget(display,windows,"Unable to show histogram",
9151          (*image)->filename);
9152      XDelay(display,1500);
9153      XSetCursorState(display,windows,MagickFalse);
9154      break;
9155    }
9156    case ShowMatteCommand:
9157    {
9158      char
9159        value[MagickPathExtent];
9160
9161      Image
9162        *matte_image;
9163
9164      if ((*image)->alpha_trait == UndefinedPixelTrait)
9165        {
9166          XNoticeWidget(display,windows,
9167            "Image does not have any matte information",(*image)->filename);
9168          break;
9169        }
9170      /*
9171        Show image matte.
9172      */
9173      XSetCursorState(display,windows,MagickTrue);
9174      XCheckRefreshWindows(display,windows);
9175      (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
9176        windows->image.id);
9177      (void) SetImageProperty(*image,"group",value,exception);
9178      (void) DeleteImageProperty(*image,"label");
9179      (void) SetImageProperty(*image,"label","Matte",exception);
9180      (void) AcquireUniqueFilename(filename);
9181      (void) FormatLocaleString((*image)->filename,MagickPathExtent,"matte:%s",
9182        filename);
9183      status=WriteImage(image_info,*image,exception);
9184      (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
9185      matte_image=ReadImage(image_info,exception);
9186      (void) RelinquishUniqueFileResource(filename);
9187      if (matte_image == (Image *) NULL)
9188        break;
9189      (void) FormatLocaleString(matte_image->filename,MagickPathExtent,"show:%s",
9190        filename);
9191      status=WriteImage(image_info,matte_image,exception);
9192      matte_image=DestroyImage(matte_image);
9193      if (status == MagickFalse)
9194        XNoticeWidget(display,windows,"Unable to show matte",
9195          (*image)->filename);
9196      XDelay(display,1500);
9197      XSetCursorState(display,windows,MagickFalse);
9198      break;
9199    }
9200    case BackgroundCommand:
9201    {
9202      /*
9203        Background image.
9204      */
9205      status=XBackgroundImage(display,resource_info,windows,image,exception);
9206      if (status == MagickFalse)
9207        break;
9208      nexus=CloneImage(*image,0,0,MagickTrue,exception);
9209      if (nexus != (Image *) NULL)
9210        XClientMessage(display,windows->image.id,windows->im_protocols,
9211          windows->im_next_image,CurrentTime);
9212      break;
9213    }
9214    case SlideShowCommand:
9215    {
9216      static char
9217        delay[MagickPathExtent] = "5";
9218
9219      /*
9220        Display next image after pausing.
9221      */
9222      (void) XDialogWidget(display,windows,"Slide Show",
9223        "Pause how many 1/100ths of a second between images:",delay);
9224      if (*delay == '\0')
9225        break;
9226      resource_info->delay=StringToUnsignedLong(delay);
9227      XClientMessage(display,windows->image.id,windows->im_protocols,
9228        windows->im_next_image,CurrentTime);
9229      break;
9230    }
9231    case PreferencesCommand:
9232    {
9233      /*
9234        Set user preferences.
9235      */
9236      status=XPreferencesWidget(display,resource_info,windows);
9237      if (status == MagickFalse)
9238        break;
9239      nexus=CloneImage(*image,0,0,MagickTrue,exception);
9240      if (nexus != (Image *) NULL)
9241        XClientMessage(display,windows->image.id,windows->im_protocols,
9242          windows->im_next_image,CurrentTime);
9243      break;
9244    }
9245    case HelpCommand:
9246    {
9247      /*
9248        User requested help.
9249      */
9250      XTextViewWidget(display,resource_info,windows,MagickFalse,
9251        "Help Viewer - Display",DisplayHelp);
9252      break;
9253    }
9254    case BrowseDocumentationCommand:
9255    {
9256      Atom
9257        mozilla_atom;
9258
9259      Window
9260        mozilla_window,
9261        root_window;
9262
9263      /*
9264        Browse the ImageMagick documentation.
9265      */
9266      root_window=XRootWindow(display,XDefaultScreen(display));
9267      mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9268      mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9269      if (mozilla_window != (Window) NULL)
9270        {
9271          char
9272            command[MagickPathExtent],
9273            *url;
9274
9275          /*
9276            Display documentation using Netscape remote control.
9277          */
9278          url=GetMagickHomeURL();
9279          (void) FormatLocaleString(command,MagickPathExtent,
9280            "openurl(%s,new-tab)",url);
9281          url=DestroyString(url);
9282          mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9283          (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9284            8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9285          XSetCursorState(display,windows,MagickFalse);
9286          break;
9287        }
9288      XSetCursorState(display,windows,MagickTrue);
9289      XCheckRefreshWindows(display,windows);
9290      status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9291        exception);
9292      if (status == MagickFalse)
9293        XNoticeWidget(display,windows,"Unable to browse documentation",
9294          (char *) NULL);
9295      XDelay(display,1500);
9296      XSetCursorState(display,windows,MagickFalse);
9297      break;
9298    }
9299    case VersionCommand:
9300    {
9301      XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9302        GetMagickCopyright());
9303      break;
9304    }
9305    case SaveToUndoBufferCommand:
9306      break;
9307    default:
9308    {
9309      (void) XBell(display,0);
9310      break;
9311    }
9312  }
9313  image_info=DestroyImageInfo(image_info);
9314  return(nexus);
9315}
9316
9317/*
9318%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9319%                                                                             %
9320%                                                                             %
9321%                                                                             %
9322+   X M a g n i f y I m a g e                                                 %
9323%                                                                             %
9324%                                                                             %
9325%                                                                             %
9326%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9327%
9328%  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9329%  The magnified portion is displayed in a separate window.
9330%
9331%  The format of the XMagnifyImage method is:
9332%
9333%      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9334%        ExceptionInfo *exception)
9335%
9336%  A description of each parameter follows:
9337%
9338%    o display: Specifies a connection to an X server;  returned from
9339%      XOpenDisplay.
9340%
9341%    o windows: Specifies a pointer to a XWindows structure.
9342%
9343%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
9344%      the entire image is refreshed.
9345%
9346%    o exception: return any errors or warnings in this structure.
9347%
9348*/
9349static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9350  ExceptionInfo *exception)
9351{
9352  char
9353    text[MagickPathExtent];
9354
9355  register int
9356    x,
9357    y;
9358
9359  size_t
9360    state;
9361
9362  /*
9363    Update magnified image until the mouse button is released.
9364  */
9365  (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9366  state=DefaultState;
9367  x=event->xbutton.x;
9368  y=event->xbutton.y;
9369  windows->magnify.x=(int) windows->image.x+x;
9370  windows->magnify.y=(int) windows->image.y+y;
9371  do
9372  {
9373    /*
9374      Map and unmap Info widget as text cursor crosses its boundaries.
9375    */
9376    if (windows->info.mapped != MagickFalse )
9377      {
9378        if ((x < (int) (windows->info.x+windows->info.width)) &&
9379            (y < (int) (windows->info.y+windows->info.height)))
9380          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9381      }
9382    else
9383      if ((x > (int) (windows->info.x+windows->info.width)) ||
9384          (y > (int) (windows->info.y+windows->info.height)))
9385        (void) XMapWindow(display,windows->info.id);
9386    if (windows->info.mapped != MagickFalse )
9387      {
9388        /*
9389          Display pointer position.
9390        */
9391        (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
9392          windows->magnify.x,windows->magnify.y);
9393        XInfoWidget(display,windows,text);
9394      }
9395    /*
9396      Wait for next event.
9397    */
9398    XScreenEvent(display,windows,event,exception);
9399    switch (event->type)
9400    {
9401      case ButtonPress:
9402        break;
9403      case ButtonRelease:
9404      {
9405        /*
9406          User has finished magnifying image.
9407        */
9408        x=event->xbutton.x;
9409        y=event->xbutton.y;
9410        state|=ExitState;
9411        break;
9412      }
9413      case Expose:
9414        break;
9415      case MotionNotify:
9416      {
9417        x=event->xmotion.x;
9418        y=event->xmotion.y;
9419        break;
9420      }
9421      default:
9422        break;
9423    }
9424    /*
9425      Check boundary conditions.
9426    */
9427    if (x < 0)
9428      x=0;
9429    else
9430      if (x >= (int) windows->image.width)
9431        x=(int) windows->image.width-1;
9432    if (y < 0)
9433      y=0;
9434    else
9435     if (y >= (int) windows->image.height)
9436       y=(int) windows->image.height-1;
9437  } while ((state & ExitState) == 0);
9438  /*
9439    Display magnified image.
9440  */
9441  XSetCursorState(display,windows,MagickFalse);
9442}
9443
9444/*
9445%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9446%                                                                             %
9447%                                                                             %
9448%                                                                             %
9449+   X M a g n i f y W i n d o w C o m m a n d                                 %
9450%                                                                             %
9451%                                                                             %
9452%                                                                             %
9453%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9454%
9455%  XMagnifyWindowCommand() moves the image within an Magnify window by one
9456%  pixel as specified by the key symbol.
9457%
9458%  The format of the XMagnifyWindowCommand method is:
9459%
9460%      void XMagnifyWindowCommand(Display *display,XWindows *windows,
9461%        const MagickStatusType state,const KeySym key_symbol,
9462%        ExceptionInfo *exception)
9463%
9464%  A description of each parameter follows:
9465%
9466%    o display: Specifies a connection to an X server; returned from
9467%      XOpenDisplay.
9468%
9469%    o windows: Specifies a pointer to a XWindows structure.
9470%
9471%    o state: key mask.
9472%
9473%    o key_symbol: Specifies a KeySym which indicates which side of the image
9474%      to trim.
9475%
9476%    o exception: return any errors or warnings in this structure.
9477%
9478*/
9479static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9480  const MagickStatusType state,const KeySym key_symbol,ExceptionInfo *exception)
9481{
9482  unsigned int
9483    quantum;
9484
9485  /*
9486    User specified a magnify factor or position.
9487  */
9488  quantum=1;
9489  if ((state & Mod1Mask) != 0)
9490    quantum=10;
9491  switch ((int) key_symbol)
9492  {
9493    case QuitCommand:
9494    {
9495      (void) XWithdrawWindow(display,windows->magnify.id,
9496        windows->magnify.screen);
9497      break;
9498    }
9499    case XK_Home:
9500    case XK_KP_Home:
9501    {
9502      windows->magnify.x=(int) windows->image.width/2;
9503      windows->magnify.y=(int) windows->image.height/2;
9504      break;
9505    }
9506    case XK_Left:
9507    case XK_KP_Left:
9508    {
9509      if (windows->magnify.x > 0)
9510        windows->magnify.x-=quantum;
9511      break;
9512    }
9513    case XK_Up:
9514    case XK_KP_Up:
9515    {
9516      if (windows->magnify.y > 0)
9517        windows->magnify.y-=quantum;
9518      break;
9519    }
9520    case XK_Right:
9521    case XK_KP_Right:
9522    {
9523      if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9524        windows->magnify.x+=quantum;
9525      break;
9526    }
9527    case XK_Down:
9528    case XK_KP_Down:
9529    {
9530      if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9531        windows->magnify.y+=quantum;
9532      break;
9533    }
9534    case XK_0:
9535    case XK_1:
9536    case XK_2:
9537    case XK_3:
9538    case XK_4:
9539    case XK_5:
9540    case XK_6:
9541    case XK_7:
9542    case XK_8:
9543    case XK_9:
9544    {
9545      windows->magnify.data=(key_symbol-XK_0);
9546      break;
9547    }
9548    case XK_KP_0:
9549    case XK_KP_1:
9550    case XK_KP_2:
9551    case XK_KP_3:
9552    case XK_KP_4:
9553    case XK_KP_5:
9554    case XK_KP_6:
9555    case XK_KP_7:
9556    case XK_KP_8:
9557    case XK_KP_9:
9558    {
9559      windows->magnify.data=(key_symbol-XK_KP_0);
9560      break;
9561    }
9562    default:
9563      break;
9564  }
9565  XMakeMagnifyImage(display,windows,exception);
9566}
9567
9568/*
9569%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9570%                                                                             %
9571%                                                                             %
9572%                                                                             %
9573+   X M a k e P a n I m a g e                                                 %
9574%                                                                             %
9575%                                                                             %
9576%                                                                             %
9577%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9578%
9579%  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9580%  icon window.
9581%
9582%  The format of the XMakePanImage method is:
9583%
9584%        void XMakePanImage(Display *display,XResourceInfo *resource_info,
9585%          XWindows *windows,Image *image,ExceptionInfo *exception)
9586%
9587%  A description of each parameter follows:
9588%
9589%    o display: Specifies a connection to an X server;  returned from
9590%      XOpenDisplay.
9591%
9592%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9593%
9594%    o windows: Specifies a pointer to a XWindows structure.
9595%
9596%    o image: the image.
9597%
9598%    o exception: return any errors or warnings in this structure.
9599%
9600*/
9601static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9602  XWindows *windows,Image *image,ExceptionInfo *exception)
9603{
9604  MagickStatusType
9605    status;
9606
9607  /*
9608    Create and display image for panning icon.
9609  */
9610  XSetCursorState(display,windows,MagickTrue);
9611  XCheckRefreshWindows(display,windows);
9612  windows->pan.x=(int) windows->image.x;
9613  windows->pan.y=(int) windows->image.y;
9614  status=XMakeImage(display,resource_info,&windows->pan,image,
9615    windows->pan.width,windows->pan.height,exception);
9616  if (status == MagickFalse)
9617    ThrowXWindowException(ResourceLimitError,
9618     "MemoryAllocationFailed",image->filename);
9619  (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9620    windows->pan.pixmap);
9621  (void) XClearWindow(display,windows->pan.id);
9622  XDrawPanRectangle(display,windows);
9623  XSetCursorState(display,windows,MagickFalse);
9624}
9625
9626/*
9627%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9628%                                                                             %
9629%                                                                             %
9630%                                                                             %
9631+   X M a t t a E d i t I m a g e                                             %
9632%                                                                             %
9633%                                                                             %
9634%                                                                             %
9635%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9636%
9637%  XMatteEditImage() allows the user to interactively change the Matte channel
9638%  of an image.  If the image is PseudoClass it is promoted to DirectClass
9639%  before the matte information is stored.
9640%
9641%  The format of the XMatteEditImage method is:
9642%
9643%      MagickBooleanType XMatteEditImage(Display *display,
9644%        XResourceInfo *resource_info,XWindows *windows,Image **image,
9645%        ExceptionInfo *exception)
9646%
9647%  A description of each parameter follows:
9648%
9649%    o display: Specifies a connection to an X server;  returned from
9650%      XOpenDisplay.
9651%
9652%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9653%
9654%    o windows: Specifies a pointer to a XWindows structure.
9655%
9656%    o image: the image; returned from ReadImage.
9657%
9658%    o exception: return any errors or warnings in this structure.
9659%
9660*/
9661static MagickBooleanType XMatteEditImage(Display *display,
9662  XResourceInfo *resource_info,XWindows *windows,Image **image,
9663  ExceptionInfo *exception)
9664{
9665  static char
9666    matte[MagickPathExtent] = "0";
9667
9668  static const char
9669    *MatteEditMenu[] =
9670    {
9671      "Method",
9672      "Border Color",
9673      "Fuzz",
9674      "Matte Value",
9675      "Undo",
9676      "Help",
9677      "Dismiss",
9678      (char *) NULL
9679    };
9680
9681  static const ModeType
9682    MatteEditCommands[] =
9683    {
9684      MatteEditMethod,
9685      MatteEditBorderCommand,
9686      MatteEditFuzzCommand,
9687      MatteEditValueCommand,
9688      MatteEditUndoCommand,
9689      MatteEditHelpCommand,
9690      MatteEditDismissCommand
9691    };
9692
9693  static PaintMethod
9694    method = PointMethod;
9695
9696  static XColor
9697    border_color = { 0, 0, 0, 0, 0, 0 };
9698
9699  char
9700    command[MagickPathExtent],
9701    text[MagickPathExtent];
9702
9703  Cursor
9704    cursor;
9705
9706  int
9707    entry,
9708    id,
9709    x,
9710    x_offset,
9711    y,
9712    y_offset;
9713
9714  register int
9715    i;
9716
9717  register Quantum
9718    *q;
9719
9720  unsigned int
9721    height,
9722    width;
9723
9724  size_t
9725    state;
9726
9727  XEvent
9728    event;
9729
9730  /*
9731    Map Command widget.
9732  */
9733  (void) CloneString(&windows->command.name,"Matte Edit");
9734  windows->command.data=4;
9735  (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9736  (void) XMapRaised(display,windows->command.id);
9737  XClientMessage(display,windows->image.id,windows->im_protocols,
9738    windows->im_update_widget,CurrentTime);
9739  /*
9740    Make cursor.
9741  */
9742  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9743    resource_info->background_color,resource_info->foreground_color);
9744  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9745  /*
9746    Track pointer until button 1 is pressed.
9747  */
9748  XQueryPosition(display,windows->image.id,&x,&y);
9749  (void) XSelectInput(display,windows->image.id,
9750    windows->image.attributes.event_mask | PointerMotionMask);
9751  state=DefaultState;
9752  do
9753  {
9754    if (windows->info.mapped != MagickFalse )
9755      {
9756        /*
9757          Display pointer position.
9758        */
9759        (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
9760          x+windows->image.x,y+windows->image.y);
9761        XInfoWidget(display,windows,text);
9762      }
9763    /*
9764      Wait for next event.
9765    */
9766    XScreenEvent(display,windows,&event,exception);
9767    if (event.xany.window == windows->command.id)
9768      {
9769        /*
9770          Select a command from the Command widget.
9771        */
9772        id=XCommandWidget(display,windows,MatteEditMenu,&event);
9773        if (id < 0)
9774          {
9775            (void) XCheckDefineCursor(display,windows->image.id,cursor);
9776            continue;
9777          }
9778        switch (MatteEditCommands[id])
9779        {
9780          case MatteEditMethod:
9781          {
9782            char
9783              **methods;
9784
9785            /*
9786              Select a method from the pop-up menu.
9787            */
9788            methods=GetCommandOptions(MagickMethodOptions);
9789            if (methods == (char **) NULL)
9790              break;
9791            entry=XMenuWidget(display,windows,MatteEditMenu[id],
9792              (const char **) methods,command);
9793            if (entry >= 0)
9794              method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9795                MagickFalse,methods[entry]);
9796            methods=DestroyStringList(methods);
9797            break;
9798          }
9799          case MatteEditBorderCommand:
9800          {
9801            const char
9802              *ColorMenu[MaxNumberPens];
9803
9804            int
9805              pen_number;
9806
9807            /*
9808              Initialize menu selections.
9809            */
9810            for (i=0; i < (int) (MaxNumberPens-2); i++)
9811              ColorMenu[i]=resource_info->pen_colors[i];
9812            ColorMenu[MaxNumberPens-2]="Browser...";
9813            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9814            /*
9815              Select a pen color from the pop-up menu.
9816            */
9817            pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9818              (const char **) ColorMenu,command);
9819            if (pen_number < 0)
9820              break;
9821            if (pen_number == (MaxNumberPens-2))
9822              {
9823                static char
9824                  color_name[MagickPathExtent] = "gray";
9825
9826                /*
9827                  Select a pen color from a dialog.
9828                */
9829                resource_info->pen_colors[pen_number]=color_name;
9830                XColorBrowserWidget(display,windows,"Select",color_name);
9831                if (*color_name == '\0')
9832                  break;
9833              }
9834            /*
9835              Set border color.
9836            */
9837            (void) XParseColor(display,windows->map_info->colormap,
9838              resource_info->pen_colors[pen_number],&border_color);
9839            break;
9840          }
9841          case MatteEditFuzzCommand:
9842          {
9843            static char
9844              fuzz[MagickPathExtent];
9845
9846            static const char
9847              *FuzzMenu[] =
9848              {
9849                "0%",
9850                "2%",
9851                "5%",
9852                "10%",
9853                "15%",
9854                "Dialog...",
9855                (char *) NULL,
9856              };
9857
9858            /*
9859              Select a command from the pop-up menu.
9860            */
9861            entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9862              command);
9863            if (entry < 0)
9864              break;
9865            if (entry != 5)
9866              {
9867                (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
9868                  QuantumRange+1.0);
9869                break;
9870              }
9871            (void) CopyMagickString(fuzz,"20%",MagickPathExtent);
9872            (void) XDialogWidget(display,windows,"Ok",
9873              "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9874            if (*fuzz == '\0')
9875              break;
9876            (void) ConcatenateMagickString(fuzz,"%",MagickPathExtent);
9877            (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
9878              1.0);
9879            break;
9880          }
9881          case MatteEditValueCommand:
9882          {
9883            static char
9884              message[MagickPathExtent];
9885
9886            static const char
9887              *MatteMenu[] =
9888              {
9889                "Opaque",
9890                "Transparent",
9891                "Dialog...",
9892                (char *) NULL,
9893              };
9894
9895            /*
9896              Select a command from the pop-up menu.
9897            */
9898            entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9899              command);
9900            if (entry < 0)
9901              break;
9902            if (entry != 2)
9903              {
9904                (void) FormatLocaleString(matte,MagickPathExtent,QuantumFormat,
9905                  OpaqueAlpha);
9906                if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9907                  (void) FormatLocaleString(matte,MagickPathExtent,QuantumFormat,
9908                    (Quantum) TransparentAlpha);
9909                break;
9910              }
9911            (void) FormatLocaleString(message,MagickPathExtent,
9912              "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9913              QuantumRange);
9914            (void) XDialogWidget(display,windows,"Matte",message,matte);
9915            if (*matte == '\0')
9916              break;
9917            break;
9918          }
9919          case MatteEditUndoCommand:
9920          {
9921            (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9922              image,exception);
9923            break;
9924          }
9925          case MatteEditHelpCommand:
9926          {
9927            XTextViewWidget(display,resource_info,windows,MagickFalse,
9928              "Help Viewer - Matte Edit",ImageMatteEditHelp);
9929            break;
9930          }
9931          case MatteEditDismissCommand:
9932          {
9933            /*
9934              Prematurely exit.
9935            */
9936            state|=EscapeState;
9937            state|=ExitState;
9938            break;
9939          }
9940          default:
9941            break;
9942        }
9943        (void) XCheckDefineCursor(display,windows->image.id,cursor);
9944        continue;
9945      }
9946    switch (event.type)
9947    {
9948      case ButtonPress:
9949      {
9950        if (event.xbutton.button != Button1)
9951          break;
9952        if ((event.xbutton.window != windows->image.id) &&
9953            (event.xbutton.window != windows->magnify.id))
9954          break;
9955        /*
9956          Update matte data.
9957        */
9958        x=event.xbutton.x;
9959        y=event.xbutton.y;
9960        (void) XMagickCommand(display,resource_info,windows,
9961          SaveToUndoBufferCommand,image,exception);
9962        state|=UpdateConfigurationState;
9963        break;
9964      }
9965      case ButtonRelease:
9966      {
9967        if (event.xbutton.button != Button1)
9968          break;
9969        if ((event.xbutton.window != windows->image.id) &&
9970            (event.xbutton.window != windows->magnify.id))
9971          break;
9972        /*
9973          Update colormap information.
9974        */
9975        x=event.xbutton.x;
9976        y=event.xbutton.y;
9977        XConfigureImageColormap(display,resource_info,windows,*image,exception);
9978        (void) XConfigureImage(display,resource_info,windows,*image,exception);
9979        XInfoWidget(display,windows,text);
9980        (void) XCheckDefineCursor(display,windows->image.id,cursor);
9981        state&=(~UpdateConfigurationState);
9982        break;
9983      }
9984      case Expose:
9985        break;
9986      case KeyPress:
9987      {
9988        char
9989          command[MagickPathExtent];
9990
9991        KeySym
9992          key_symbol;
9993
9994        if (event.xkey.window == windows->magnify.id)
9995          {
9996            Window
9997              window;
9998
9999            window=windows->magnify.id;
10000            while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
10001          }
10002        if (event.xkey.window != windows->image.id)
10003          break;
10004        /*
10005          Respond to a user key press.
10006        */
10007        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
10008          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10009        switch ((int) key_symbol)
10010        {
10011          case XK_Escape:
10012          case XK_F20:
10013          {
10014            /*
10015              Prematurely exit.
10016            */
10017            state|=ExitState;
10018            break;
10019          }
10020          case XK_F1:
10021          case XK_Help:
10022          {
10023            XTextViewWidget(display,resource_info,windows,MagickFalse,
10024              "Help Viewer - Matte Edit",ImageMatteEditHelp);
10025            break;
10026          }
10027          default:
10028          {
10029            (void) XBell(display,0);
10030            break;
10031          }
10032        }
10033        break;
10034      }
10035      case MotionNotify:
10036      {
10037        /*
10038          Map and unmap Info widget as cursor crosses its boundaries.
10039        */
10040        x=event.xmotion.x;
10041        y=event.xmotion.y;
10042        if (windows->info.mapped != MagickFalse )
10043          {
10044            if ((x < (int) (windows->info.x+windows->info.width)) &&
10045                (y < (int) (windows->info.y+windows->info.height)))
10046              (void) XWithdrawWindow(display,windows->info.id,
10047                windows->info.screen);
10048          }
10049        else
10050          if ((x > (int) (windows->info.x+windows->info.width)) ||
10051              (y > (int) (windows->info.y+windows->info.height)))
10052            (void) XMapWindow(display,windows->info.id);
10053        break;
10054      }
10055      default:
10056        break;
10057    }
10058    if (event.xany.window == windows->magnify.id)
10059      {
10060        x=windows->magnify.x-windows->image.x;
10061        y=windows->magnify.y-windows->image.y;
10062      }
10063    x_offset=x;
10064    y_offset=y;
10065    if ((state & UpdateConfigurationState) != 0)
10066      {
10067        CacheView
10068          *image_view;
10069
10070        int
10071          x,
10072          y;
10073
10074        /*
10075          Matte edit is relative to image configuration.
10076        */
10077        (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
10078          MagickTrue);
10079        XPutPixel(windows->image.ximage,x_offset,y_offset,
10080          windows->pixel_info->background_color.pixel);
10081        width=(unsigned int) (*image)->columns;
10082        height=(unsigned int) (*image)->rows;
10083        x=0;
10084        y=0;
10085        if (windows->image.crop_geometry != (char *) NULL)
10086          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
10087            &height);
10088        x_offset=(int) (width*(windows->image.x+x_offset)/
10089          windows->image.ximage->width+x);
10090        y_offset=(int) (height*(windows->image.y+y_offset)/
10091          windows->image.ximage->height+y);
10092        if ((x_offset < 0) || (y_offset < 0))
10093          continue;
10094        if ((x_offset >= (int) (*image)->columns) ||
10095            (y_offset >= (int) (*image)->rows))
10096          continue;
10097        if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10098          return(MagickFalse);
10099        if ((*image)->alpha_trait == UndefinedPixelTrait)
10100          (void) SetImageAlphaChannel(*image,OpaqueAlphaChannel,exception);
10101        image_view=AcquireAuthenticCacheView(*image,exception);
10102        switch (method)
10103        {
10104          case PointMethod:
10105          default:
10106          {
10107            /*
10108              Update matte information using point algorithm.
10109            */
10110            q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10111              (ssize_t) y_offset,1,1,exception);
10112            if (q == (Quantum *) NULL)
10113              break;
10114            SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10115            (void) SyncCacheViewAuthenticPixels(image_view,exception);
10116            break;
10117          }
10118          case ReplaceMethod:
10119          {
10120            PixelInfo
10121              pixel,
10122              target;
10123
10124            /*
10125              Update matte information using replace algorithm.
10126            */
10127            (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
10128              x_offset,(ssize_t) y_offset,&target,exception);
10129            for (y=0; y < (int) (*image)->rows; y++)
10130            {
10131              q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10132                (*image)->columns,1,exception);
10133              if (q == (Quantum *) NULL)
10134                break;
10135              for (x=0; x < (int) (*image)->columns; x++)
10136              {
10137                GetPixelInfoPixel(*image,q,&pixel);
10138                if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
10139                  SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10140                q+=GetPixelChannels(*image);
10141              }
10142              if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10143                break;
10144            }
10145            break;
10146          }
10147          case FloodfillMethod:
10148          case FillToBorderMethod:
10149          {
10150            ChannelType
10151              channel_mask;
10152
10153            DrawInfo
10154              *draw_info;
10155
10156            PixelInfo
10157              target;
10158
10159            /*
10160              Update matte information using floodfill algorithm.
10161            */
10162            (void) GetOneVirtualPixelInfo(*image,
10163              GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
10164              y_offset,&target,exception);
10165            if (method == FillToBorderMethod)
10166              {
10167                target.red=(double) ScaleShortToQuantum(
10168                  border_color.red);
10169                target.green=(double) ScaleShortToQuantum(
10170                  border_color.green);
10171                target.blue=(double) ScaleShortToQuantum(
10172                  border_color.blue);
10173              }
10174            draw_info=CloneDrawInfo(resource_info->image_info,
10175              (DrawInfo *) NULL);
10176            draw_info->fill.alpha=(double) ClampToQuantum(
10177              StringToDouble(matte,(char **) NULL));
10178            channel_mask=SetImageChannelMask(*image,AlphaChannel);
10179            (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10180              x_offset,(ssize_t) y_offset,
10181              method != FloodfillMethod ? MagickTrue : MagickFalse,exception);
10182            (void) SetPixelChannelMask(*image,channel_mask);
10183            draw_info=DestroyDrawInfo(draw_info);
10184            break;
10185          }
10186          case ResetMethod:
10187          {
10188            /*
10189              Update matte information using reset algorithm.
10190            */
10191            if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10192              return(MagickFalse);
10193            for (y=0; y < (int) (*image)->rows; y++)
10194            {
10195              q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10196                (*image)->columns,1,exception);
10197              if (q == (Quantum *) NULL)
10198                break;
10199              for (x=0; x < (int) (*image)->columns; x++)
10200              {
10201                SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10202                q+=GetPixelChannels(*image);
10203              }
10204              if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10205                break;
10206            }
10207            if (StringToLong(matte) == (long) OpaqueAlpha)
10208              (*image)->alpha_trait=UndefinedPixelTrait;
10209            break;
10210          }
10211        }
10212        image_view=DestroyCacheView(image_view);
10213        state&=(~UpdateConfigurationState);
10214      }
10215  } while ((state & ExitState) == 0);
10216  (void) XSelectInput(display,windows->image.id,
10217    windows->image.attributes.event_mask);
10218  XSetCursorState(display,windows,MagickFalse);
10219  (void) XFreeCursor(display,cursor);
10220  return(MagickTrue);
10221}
10222
10223/*
10224%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10225%                                                                             %
10226%                                                                             %
10227%                                                                             %
10228+   X O p e n I m a g e                                                       %
10229%                                                                             %
10230%                                                                             %
10231%                                                                             %
10232%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10233%
10234%  XOpenImage() loads an image from a file.
10235%
10236%  The format of the XOpenImage method is:
10237%
10238%     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10239%       XWindows *windows,const unsigned int command)
10240%
10241%  A description of each parameter follows:
10242%
10243%    o display: Specifies a connection to an X server; returned from
10244%      XOpenDisplay.
10245%
10246%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10247%
10248%    o windows: Specifies a pointer to a XWindows structure.
10249%
10250%    o command: A value other than zero indicates that the file is selected
10251%      from the command line argument list.
10252%
10253*/
10254static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10255  XWindows *windows,const MagickBooleanType command)
10256{
10257  const MagickInfo
10258    *magick_info;
10259
10260  ExceptionInfo
10261    *exception;
10262
10263  Image
10264    *nexus;
10265
10266  ImageInfo
10267    *image_info;
10268
10269  static char
10270    filename[MagickPathExtent] = "\0";
10271
10272  /*
10273    Request file name from user.
10274  */
10275  if (command == MagickFalse)
10276    XFileBrowserWidget(display,windows,"Open",filename);
10277  else
10278    {
10279      char
10280        **filelist,
10281        **files;
10282
10283      int
10284        count,
10285        status;
10286
10287      register int
10288        i,
10289        j;
10290
10291      /*
10292        Select next image from the command line.
10293      */
10294      status=XGetCommand(display,windows->image.id,&files,&count);
10295      if (status == 0)
10296        {
10297          ThrowXWindowException(XServerError,"UnableToGetProperty","...");
10298          return((Image *) NULL);
10299        }
10300      filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10301      if (filelist == (char **) NULL)
10302        {
10303          ThrowXWindowException(ResourceLimitError,
10304            "MemoryAllocationFailed","...");
10305          (void) XFreeStringList(files);
10306          return((Image *) NULL);
10307        }
10308      j=0;
10309      for (i=1; i < count; i++)
10310        if (*files[i] != '-')
10311          filelist[j++]=files[i];
10312      filelist[j]=(char *) NULL;
10313      XListBrowserWidget(display,windows,&windows->widget,
10314        (const char **) filelist,"Load","Select Image to Load:",filename);
10315      filelist=(char **) RelinquishMagickMemory(filelist);
10316      (void) XFreeStringList(files);
10317    }
10318  if (*filename == '\0')
10319    return((Image *) NULL);
10320  image_info=CloneImageInfo(resource_info->image_info);
10321  (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10322    (void *) NULL);
10323  (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
10324  exception=AcquireExceptionInfo();
10325  (void) SetImageInfo(image_info,0,exception);
10326  if (LocaleCompare(image_info->magick,"X") == 0)
10327    {
10328      char
10329        seconds[MagickPathExtent];
10330
10331      /*
10332        User may want to delay the X server screen grab.
10333      */
10334      (void) CopyMagickString(seconds,"0",MagickPathExtent);
10335      (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10336        seconds);
10337      if (*seconds == '\0')
10338        return((Image *) NULL);
10339      XDelay(display,(size_t) (1000*StringToLong(seconds)));
10340    }
10341  magick_info=GetMagickInfo(image_info->magick,exception);
10342  if ((magick_info != (const MagickInfo *) NULL) &&
10343      GetMagickRawSupport(magick_info) == MagickTrue)
10344    {
10345      char
10346        geometry[MagickPathExtent];
10347
10348      /*
10349        Request image size from the user.
10350      */
10351      (void) CopyMagickString(geometry,"512x512",MagickPathExtent);
10352      if (image_info->size != (char *) NULL)
10353        (void) CopyMagickString(geometry,image_info->size,MagickPathExtent);
10354      (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10355        geometry);
10356      (void) CloneString(&image_info->size,geometry);
10357    }
10358  /*
10359    Load the image.
10360  */
10361  XSetCursorState(display,windows,MagickTrue);
10362  XCheckRefreshWindows(display,windows);
10363  (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
10364  nexus=ReadImage(image_info,exception);
10365  CatchException(exception);
10366  XSetCursorState(display,windows,MagickFalse);
10367  if (nexus != (Image *) NULL)
10368    XClientMessage(display,windows->image.id,windows->im_protocols,
10369      windows->im_next_image,CurrentTime);
10370  else
10371    {
10372      char
10373        *text,
10374        **textlist;
10375
10376      /*
10377        Unknown image format.
10378      */
10379      text=FileToString(filename,~0UL,exception);
10380      if (text == (char *) NULL)
10381        return((Image *) NULL);
10382      textlist=StringToList(text);
10383      if (textlist != (char **) NULL)
10384        {
10385          char
10386            title[MagickPathExtent];
10387
10388          register int
10389            i;
10390
10391          (void) FormatLocaleString(title,MagickPathExtent,
10392            "Unknown format: %s",filename);
10393          XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10394            (const char **) textlist);
10395          for (i=0; textlist[i] != (char *) NULL; i++)
10396            textlist[i]=DestroyString(textlist[i]);
10397          textlist=(char **) RelinquishMagickMemory(textlist);
10398        }
10399      text=DestroyString(text);
10400    }
10401  exception=DestroyExceptionInfo(exception);
10402  image_info=DestroyImageInfo(image_info);
10403  return(nexus);
10404}
10405
10406/*
10407%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10408%                                                                             %
10409%                                                                             %
10410%                                                                             %
10411+   X P a n I m a g e                                                         %
10412%                                                                             %
10413%                                                                             %
10414%                                                                             %
10415%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10416%
10417%  XPanImage() pans the image until the mouse button is released.
10418%
10419%  The format of the XPanImage method is:
10420%
10421%      void XPanImage(Display *display,XWindows *windows,XEvent *event,
10422%        ExceptionInfo *exception)
10423%
10424%  A description of each parameter follows:
10425%
10426%    o display: Specifies a connection to an X server;  returned from
10427%      XOpenDisplay.
10428%
10429%    o windows: Specifies a pointer to a XWindows structure.
10430%
10431%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10432%      the entire image is refreshed.
10433%
10434%    o exception: return any errors or warnings in this structure.
10435%
10436*/
10437static void XPanImage(Display *display,XWindows *windows,XEvent *event,
10438  ExceptionInfo *exception)
10439{
10440  char
10441    text[MagickPathExtent];
10442
10443  Cursor
10444    cursor;
10445
10446  double
10447    x_factor,
10448    y_factor;
10449
10450  RectangleInfo
10451    pan_info;
10452
10453  size_t
10454    state;
10455
10456  /*
10457    Define cursor.
10458  */
10459  if ((windows->image.ximage->width > (int) windows->image.width) &&
10460      (windows->image.ximage->height > (int) windows->image.height))
10461    cursor=XCreateFontCursor(display,XC_fleur);
10462  else
10463    if (windows->image.ximage->width > (int) windows->image.width)
10464      cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10465    else
10466      if (windows->image.ximage->height > (int) windows->image.height)
10467        cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10468      else
10469        cursor=XCreateFontCursor(display,XC_arrow);
10470  (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10471  /*
10472    Pan image as pointer moves until the mouse button is released.
10473  */
10474  x_factor=(double) windows->image.ximage->width/windows->pan.width;
10475  y_factor=(double) windows->image.ximage->height/windows->pan.height;
10476  pan_info.width=windows->pan.width*windows->image.width/
10477    windows->image.ximage->width;
10478  pan_info.height=windows->pan.height*windows->image.height/
10479    windows->image.ximage->height;
10480  pan_info.x=0;
10481  pan_info.y=0;
10482  state=UpdateConfigurationState;
10483  do
10484  {
10485    switch (event->type)
10486    {
10487      case ButtonPress:
10488      {
10489        /*
10490          User choose an initial pan location.
10491        */
10492        pan_info.x=(ssize_t) event->xbutton.x;
10493        pan_info.y=(ssize_t) event->xbutton.y;
10494        state|=UpdateConfigurationState;
10495        break;
10496      }
10497      case ButtonRelease:
10498      {
10499        /*
10500          User has finished panning the image.
10501        */
10502        pan_info.x=(ssize_t) event->xbutton.x;
10503        pan_info.y=(ssize_t) event->xbutton.y;
10504        state|=UpdateConfigurationState | ExitState;
10505        break;
10506      }
10507      case MotionNotify:
10508      {
10509        pan_info.x=(ssize_t) event->xmotion.x;
10510        pan_info.y=(ssize_t) event->xmotion.y;
10511        state|=UpdateConfigurationState;
10512      }
10513      default:
10514        break;
10515    }
10516    if ((state & UpdateConfigurationState) != 0)
10517      {
10518        /*
10519          Check boundary conditions.
10520        */
10521        if (pan_info.x < (ssize_t) (pan_info.width/2))
10522          pan_info.x=0;
10523        else
10524          pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10525        if (pan_info.x < 0)
10526          pan_info.x=0;
10527        else
10528          if ((int) (pan_info.x+windows->image.width) >
10529              windows->image.ximage->width)
10530            pan_info.x=(ssize_t)
10531              (windows->image.ximage->width-windows->image.width);
10532        if (pan_info.y < (ssize_t) (pan_info.height/2))
10533          pan_info.y=0;
10534        else
10535          pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10536        if (pan_info.y < 0)
10537          pan_info.y=0;
10538        else
10539          if ((int) (pan_info.y+windows->image.height) >
10540              windows->image.ximage->height)
10541            pan_info.y=(ssize_t)
10542              (windows->image.ximage->height-windows->image.height);
10543        if ((windows->image.x != (int) pan_info.x) ||
10544            (windows->image.y != (int) pan_info.y))
10545          {
10546            /*
10547              Display image pan offset.
10548            */
10549            windows->image.x=(int) pan_info.x;
10550            windows->image.y=(int) pan_info.y;
10551            (void) FormatLocaleString(text,MagickPathExtent," %ux%u%+d%+d ",
10552              windows->image.width,windows->image.height,windows->image.x,
10553              windows->image.y);
10554            XInfoWidget(display,windows,text);
10555            /*
10556              Refresh Image window.
10557            */
10558            XDrawPanRectangle(display,windows);
10559            XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10560          }
10561        state&=(~UpdateConfigurationState);
10562      }
10563    /*
10564      Wait for next event.
10565    */
10566    if ((state & ExitState) == 0)
10567      XScreenEvent(display,windows,event,exception);
10568  } while ((state & ExitState) == 0);
10569  /*
10570    Restore cursor.
10571  */
10572  (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10573  (void) XFreeCursor(display,cursor);
10574  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10575}
10576
10577/*
10578%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10579%                                                                             %
10580%                                                                             %
10581%                                                                             %
10582+   X P a s t e I m a g e                                                     %
10583%                                                                             %
10584%                                                                             %
10585%                                                                             %
10586%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10587%
10588%  XPasteImage() pastes an image previously saved with XCropImage in the X
10589%  window image at a location the user chooses with the pointer.
10590%
10591%  The format of the XPasteImage method is:
10592%
10593%      MagickBooleanType XPasteImage(Display *display,
10594%        XResourceInfo *resource_info,XWindows *windows,Image *image,
10595%        ExceptionInfo *exception)
10596%
10597%  A description of each parameter follows:
10598%
10599%    o display: Specifies a connection to an X server;  returned from
10600%      XOpenDisplay.
10601%
10602%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10603%
10604%    o windows: Specifies a pointer to a XWindows structure.
10605%
10606%    o image: the image; returned from ReadImage.
10607%
10608%    o exception: return any errors or warnings in this structure.
10609%
10610*/
10611static MagickBooleanType XPasteImage(Display *display,
10612  XResourceInfo *resource_info,XWindows *windows,Image *image,
10613  ExceptionInfo *exception)
10614{
10615  static const char
10616    *PasteMenu[] =
10617    {
10618      "Operator",
10619      "Help",
10620      "Dismiss",
10621      (char *) NULL
10622    };
10623
10624  static const ModeType
10625    PasteCommands[] =
10626    {
10627      PasteOperatorsCommand,
10628      PasteHelpCommand,
10629      PasteDismissCommand
10630    };
10631
10632  static CompositeOperator
10633    compose = CopyCompositeOp;
10634
10635  char
10636    text[MagickPathExtent];
10637
10638  Cursor
10639    cursor;
10640
10641  Image
10642    *paste_image;
10643
10644  int
10645    entry,
10646    id,
10647    x,
10648    y;
10649
10650  double
10651    scale_factor;
10652
10653  RectangleInfo
10654    highlight_info,
10655    paste_info;
10656
10657  unsigned int
10658    height,
10659    width;
10660
10661  size_t
10662    state;
10663
10664  XEvent
10665    event;
10666
10667  /*
10668    Copy image.
10669  */
10670  if (resource_info->copy_image == (Image *) NULL)
10671    return(MagickFalse);
10672  paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,exception);
10673  /*
10674    Map Command widget.
10675  */
10676  (void) CloneString(&windows->command.name,"Paste");
10677  windows->command.data=1;
10678  (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10679  (void) XMapRaised(display,windows->command.id);
10680  XClientMessage(display,windows->image.id,windows->im_protocols,
10681    windows->im_update_widget,CurrentTime);
10682  /*
10683    Track pointer until button 1 is pressed.
10684  */
10685  XSetCursorState(display,windows,MagickFalse);
10686  XQueryPosition(display,windows->image.id,&x,&y);
10687  (void) XSelectInput(display,windows->image.id,
10688    windows->image.attributes.event_mask | PointerMotionMask);
10689  paste_info.x=(ssize_t) windows->image.x+x;
10690  paste_info.y=(ssize_t) windows->image.y+y;
10691  paste_info.width=0;
10692  paste_info.height=0;
10693  cursor=XCreateFontCursor(display,XC_ul_angle);
10694  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10695  state=DefaultState;
10696  do
10697  {
10698    if (windows->info.mapped != MagickFalse )
10699      {
10700        /*
10701          Display pointer position.
10702        */
10703        (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
10704          (long) paste_info.x,(long) paste_info.y);
10705        XInfoWidget(display,windows,text);
10706      }
10707    highlight_info=paste_info;
10708    highlight_info.x=paste_info.x-windows->image.x;
10709    highlight_info.y=paste_info.y-windows->image.y;
10710    XHighlightRectangle(display,windows->image.id,
10711      windows->image.highlight_context,&highlight_info);
10712    /*
10713      Wait for next event.
10714    */
10715    XScreenEvent(display,windows,&event,exception);
10716    XHighlightRectangle(display,windows->image.id,
10717      windows->image.highlight_context,&highlight_info);
10718    if (event.xany.window == windows->command.id)
10719      {
10720        /*
10721          Select a command from the Command widget.
10722        */
10723        id=XCommandWidget(display,windows,PasteMenu,&event);
10724        if (id < 0)
10725          continue;
10726        switch (PasteCommands[id])
10727        {
10728          case PasteOperatorsCommand:
10729          {
10730            char
10731              command[MagickPathExtent],
10732              **operators;
10733
10734            /*
10735              Select a command from the pop-up menu.
10736            */
10737            operators=GetCommandOptions(MagickComposeOptions);
10738            if (operators == (char **) NULL)
10739              break;
10740            entry=XMenuWidget(display,windows,PasteMenu[id],
10741              (const char **) operators,command);
10742            if (entry >= 0)
10743              compose=(CompositeOperator) ParseCommandOption(
10744                MagickComposeOptions,MagickFalse,operators[entry]);
10745            operators=DestroyStringList(operators);
10746            break;
10747          }
10748          case PasteHelpCommand:
10749          {
10750            XTextViewWidget(display,resource_info,windows,MagickFalse,
10751              "Help Viewer - Image Composite",ImagePasteHelp);
10752            break;
10753          }
10754          case PasteDismissCommand:
10755          {
10756            /*
10757              Prematurely exit.
10758            */
10759            state|=EscapeState;
10760            state|=ExitState;
10761            break;
10762          }
10763          default:
10764            break;
10765        }
10766        continue;
10767      }
10768    switch (event.type)
10769    {
10770      case ButtonPress:
10771      {
10772        if (image->debug != MagickFalse )
10773          (void) LogMagickEvent(X11Event,GetMagickModule(),
10774            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10775            event.xbutton.button,event.xbutton.x,event.xbutton.y);
10776        if (event.xbutton.button != Button1)
10777          break;
10778        if (event.xbutton.window != windows->image.id)
10779          break;
10780        /*
10781          Paste rectangle is relative to image configuration.
10782        */
10783        width=(unsigned int) image->columns;
10784        height=(unsigned int) image->rows;
10785        x=0;
10786        y=0;
10787        if (windows->image.crop_geometry != (char *) NULL)
10788          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10789            &width,&height);
10790        scale_factor=(double) windows->image.ximage->width/width;
10791        paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10792        scale_factor=(double) windows->image.ximage->height/height;
10793        paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10794        (void) XCheckDefineCursor(display,windows->image.id,cursor);
10795        paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10796        paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10797        break;
10798      }
10799      case ButtonRelease:
10800      {
10801        if (image->debug != MagickFalse )
10802          (void) LogMagickEvent(X11Event,GetMagickModule(),
10803            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10804            event.xbutton.button,event.xbutton.x,event.xbutton.y);
10805        if (event.xbutton.button != Button1)
10806          break;
10807        if (event.xbutton.window != windows->image.id)
10808          break;
10809        if ((paste_info.width != 0) && (paste_info.height != 0))
10810          {
10811            /*
10812              User has selected the location of the paste image.
10813            */
10814            paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10815            paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10816            state|=ExitState;
10817          }
10818        break;
10819      }
10820      case Expose:
10821        break;
10822      case KeyPress:
10823      {
10824        char
10825          command[MagickPathExtent];
10826
10827        KeySym
10828          key_symbol;
10829
10830        int
10831          length;
10832
10833        if (event.xkey.window != windows->image.id)
10834          break;
10835        /*
10836          Respond to a user key press.
10837        */
10838        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10839          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10840        *(command+length)='\0';
10841        if (image->debug != MagickFalse )
10842          (void) LogMagickEvent(X11Event,GetMagickModule(),
10843            "Key press: 0x%lx (%s)",(long) key_symbol,command);
10844        switch ((int) key_symbol)
10845        {
10846          case XK_Escape:
10847          case XK_F20:
10848          {
10849            /*
10850              Prematurely exit.
10851            */
10852            paste_image=DestroyImage(paste_image);
10853            state|=EscapeState;
10854            state|=ExitState;
10855            break;
10856          }
10857          case XK_F1:
10858          case XK_Help:
10859          {
10860            (void) XSetFunction(display,windows->image.highlight_context,
10861              GXcopy);
10862            XTextViewWidget(display,resource_info,windows,MagickFalse,
10863              "Help Viewer - Image Composite",ImagePasteHelp);
10864            (void) XSetFunction(display,windows->image.highlight_context,
10865              GXinvert);
10866            break;
10867          }
10868          default:
10869          {
10870            (void) XBell(display,0);
10871            break;
10872          }
10873        }
10874        break;
10875      }
10876      case MotionNotify:
10877      {
10878        /*
10879          Map and unmap Info widget as text cursor crosses its boundaries.
10880        */
10881        x=event.xmotion.x;
10882        y=event.xmotion.y;
10883        if (windows->info.mapped != MagickFalse )
10884          {
10885            if ((x < (int) (windows->info.x+windows->info.width)) &&
10886                (y < (int) (windows->info.y+windows->info.height)))
10887              (void) XWithdrawWindow(display,windows->info.id,
10888                windows->info.screen);
10889          }
10890        else
10891          if ((x > (int) (windows->info.x+windows->info.width)) ||
10892              (y > (int) (windows->info.y+windows->info.height)))
10893            (void) XMapWindow(display,windows->info.id);
10894        paste_info.x=(ssize_t) windows->image.x+x;
10895        paste_info.y=(ssize_t) windows->image.y+y;
10896        break;
10897      }
10898      default:
10899      {
10900        if (image->debug != MagickFalse )
10901          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10902            event.type);
10903        break;
10904      }
10905    }
10906  } while ((state & ExitState) == 0);
10907  (void) XSelectInput(display,windows->image.id,
10908    windows->image.attributes.event_mask);
10909  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10910  XSetCursorState(display,windows,MagickFalse);
10911  (void) XFreeCursor(display,cursor);
10912  if ((state & EscapeState) != 0)
10913    return(MagickTrue);
10914  /*
10915    Image pasting is relative to image configuration.
10916  */
10917  XSetCursorState(display,windows,MagickTrue);
10918  XCheckRefreshWindows(display,windows);
10919  width=(unsigned int) image->columns;
10920  height=(unsigned int) image->rows;
10921  x=0;
10922  y=0;
10923  if (windows->image.crop_geometry != (char *) NULL)
10924    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10925  scale_factor=(double) width/windows->image.ximage->width;
10926  paste_info.x+=x;
10927  paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10928  paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10929  scale_factor=(double) height/windows->image.ximage->height;
10930  paste_info.y+=y;
10931  paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10932  paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10933  /*
10934    Paste image with X Image window.
10935  */
10936  (void) CompositeImage(image,paste_image,compose,MagickTrue,paste_info.x,
10937    paste_info.y,exception);
10938  paste_image=DestroyImage(paste_image);
10939  XSetCursorState(display,windows,MagickFalse);
10940  /*
10941    Update image colormap.
10942  */
10943  XConfigureImageColormap(display,resource_info,windows,image,exception);
10944  (void) XConfigureImage(display,resource_info,windows,image,exception);
10945  return(MagickTrue);
10946}
10947
10948/*
10949%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10950%                                                                             %
10951%                                                                             %
10952%                                                                             %
10953+   X P r i n t I m a g e                                                     %
10954%                                                                             %
10955%                                                                             %
10956%                                                                             %
10957%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10958%
10959%  XPrintImage() prints an image to a Postscript printer.
10960%
10961%  The format of the XPrintImage method is:
10962%
10963%      MagickBooleanType XPrintImage(Display *display,
10964%        XResourceInfo *resource_info,XWindows *windows,Image *image,
10965%        ExceptionInfo *exception)
10966%
10967%  A description of each parameter follows:
10968%
10969%    o display: Specifies a connection to an X server; returned from
10970%      XOpenDisplay.
10971%
10972%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10973%
10974%    o windows: Specifies a pointer to a XWindows structure.
10975%
10976%    o image: the image.
10977%
10978%    o exception: return any errors or warnings in this structure.
10979%
10980*/
10981static MagickBooleanType XPrintImage(Display *display,
10982  XResourceInfo *resource_info,XWindows *windows,Image *image,
10983  ExceptionInfo *exception)
10984{
10985  char
10986    filename[MagickPathExtent],
10987    geometry[MagickPathExtent];
10988
10989  Image
10990    *print_image;
10991
10992  ImageInfo
10993    *image_info;
10994
10995  MagickStatusType
10996    status;
10997
10998  /*
10999    Request Postscript page geometry from user.
11000  */
11001  image_info=CloneImageInfo(resource_info->image_info);
11002  (void) FormatLocaleString(geometry,MagickPathExtent,"Letter");
11003  if (image_info->page != (char *) NULL)
11004    (void) CopyMagickString(geometry,image_info->page,MagickPathExtent);
11005  XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
11006    "Select Postscript Page Geometry:",geometry);
11007  if (*geometry == '\0')
11008    return(MagickTrue);
11009  image_info->page=GetPageGeometry(geometry);
11010  /*
11011    Apply image transforms.
11012  */
11013  XSetCursorState(display,windows,MagickTrue);
11014  XCheckRefreshWindows(display,windows);
11015  print_image=CloneImage(image,0,0,MagickTrue,exception);
11016  if (print_image == (Image *) NULL)
11017    return(MagickFalse);
11018  (void) FormatLocaleString(geometry,MagickPathExtent,"%dx%d!",
11019    windows->image.ximage->width,windows->image.ximage->height);
11020  (void) TransformImage(&print_image,windows->image.crop_geometry,geometry,
11021    exception);
11022  /*
11023    Print image.
11024  */
11025  (void) AcquireUniqueFilename(filename);
11026  (void) FormatLocaleString(print_image->filename,MagickPathExtent,"print:%s",
11027    filename);
11028  status=WriteImage(image_info,print_image,exception);
11029  (void) RelinquishUniqueFileResource(filename);
11030  print_image=DestroyImage(print_image);
11031  image_info=DestroyImageInfo(image_info);
11032  XSetCursorState(display,windows,MagickFalse);
11033  return(status != 0 ? MagickTrue : MagickFalse);
11034}
11035
11036/*
11037%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11038%                                                                             %
11039%                                                                             %
11040%                                                                             %
11041+   X R O I I m a g e                                                         %
11042%                                                                             %
11043%                                                                             %
11044%                                                                             %
11045%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11046%
11047%  XROIImage() applies an image processing technique to a region of interest.
11048%
11049%  The format of the XROIImage method is:
11050%
11051%      MagickBooleanType XROIImage(Display *display,
11052%        XResourceInfo *resource_info,XWindows *windows,Image **image,
11053%        ExceptionInfo *exception)
11054%
11055%  A description of each parameter follows:
11056%
11057%    o display: Specifies a connection to an X server; returned from
11058%      XOpenDisplay.
11059%
11060%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11061%
11062%    o windows: Specifies a pointer to a XWindows structure.
11063%
11064%    o image: the image; returned from ReadImage.
11065%
11066%    o exception: return any errors or warnings in this structure.
11067%
11068*/
11069static MagickBooleanType XROIImage(Display *display,
11070  XResourceInfo *resource_info,XWindows *windows,Image **image,
11071  ExceptionInfo *exception)
11072{
11073#define ApplyMenus  7
11074
11075  static const char
11076    *ROIMenu[] =
11077    {
11078      "Help",
11079      "Dismiss",
11080      (char *) NULL
11081    },
11082    *ApplyMenu[] =
11083    {
11084      "File",
11085      "Edit",
11086      "Transform",
11087      "Enhance",
11088      "Effects",
11089      "F/X",
11090      "Miscellany",
11091      "Help",
11092      "Dismiss",
11093      (char *) NULL
11094    },
11095    *FileMenu[] =
11096    {
11097      "Save...",
11098      "Print...",
11099      (char *) NULL
11100    },
11101    *EditMenu[] =
11102    {
11103      "Undo",
11104      "Redo",
11105      (char *) NULL
11106    },
11107    *TransformMenu[] =
11108    {
11109      "Flop",
11110      "Flip",
11111      "Rotate Right",
11112      "Rotate Left",
11113      (char *) NULL
11114    },
11115    *EnhanceMenu[] =
11116    {
11117      "Hue...",
11118      "Saturation...",
11119      "Brightness...",
11120      "Gamma...",
11121      "Spiff",
11122      "Dull",
11123      "Contrast Stretch...",
11124      "Sigmoidal Contrast...",
11125      "Normalize",
11126      "Equalize",
11127      "Negate",
11128      "Grayscale",
11129      "Map...",
11130      "Quantize...",
11131      (char *) NULL
11132    },
11133    *EffectsMenu[] =
11134    {
11135      "Despeckle",
11136      "Emboss",
11137      "Reduce Noise",
11138      "Add Noise",
11139      "Sharpen...",
11140      "Blur...",
11141      "Threshold...",
11142      "Edge Detect...",
11143      "Spread...",
11144      "Shade...",
11145      "Raise...",
11146      "Segment...",
11147      (char *) NULL
11148    },
11149    *FXMenu[] =
11150    {
11151      "Solarize...",
11152      "Sepia Tone...",
11153      "Swirl...",
11154      "Implode...",
11155      "Vignette...",
11156      "Wave...",
11157      "Oil Paint...",
11158      "Charcoal Draw...",
11159      (char *) NULL
11160    },
11161    *MiscellanyMenu[] =
11162    {
11163      "Image Info",
11164      "Zoom Image",
11165      "Show Preview...",
11166      "Show Histogram",
11167      "Show Matte",
11168      (char *) NULL
11169    };
11170
11171  static const char
11172    **Menus[ApplyMenus] =
11173    {
11174      FileMenu,
11175      EditMenu,
11176      TransformMenu,
11177      EnhanceMenu,
11178      EffectsMenu,
11179      FXMenu,
11180      MiscellanyMenu
11181    };
11182
11183  static const CommandType
11184    ApplyCommands[] =
11185    {
11186      NullCommand,
11187      NullCommand,
11188      NullCommand,
11189      NullCommand,
11190      NullCommand,
11191      NullCommand,
11192      NullCommand,
11193      HelpCommand,
11194      QuitCommand
11195    },
11196    FileCommands[] =
11197    {
11198      SaveCommand,
11199      PrintCommand
11200    },
11201    EditCommands[] =
11202    {
11203      UndoCommand,
11204      RedoCommand
11205    },
11206    TransformCommands[] =
11207    {
11208      FlopCommand,
11209      FlipCommand,
11210      RotateRightCommand,
11211      RotateLeftCommand
11212    },
11213    EnhanceCommands[] =
11214    {
11215      HueCommand,
11216      SaturationCommand,
11217      BrightnessCommand,
11218      GammaCommand,
11219      SpiffCommand,
11220      DullCommand,
11221      ContrastStretchCommand,
11222      SigmoidalContrastCommand,
11223      NormalizeCommand,
11224      EqualizeCommand,
11225      NegateCommand,
11226      GrayscaleCommand,
11227      MapCommand,
11228      QuantizeCommand
11229    },
11230    EffectsCommands[] =
11231    {
11232      DespeckleCommand,
11233      EmbossCommand,
11234      ReduceNoiseCommand,
11235      AddNoiseCommand,
11236      SharpenCommand,
11237      BlurCommand,
11238      EdgeDetectCommand,
11239      SpreadCommand,
11240      ShadeCommand,
11241      RaiseCommand,
11242      SegmentCommand
11243    },
11244    FXCommands[] =
11245    {
11246      SolarizeCommand,
11247      SepiaToneCommand,
11248      SwirlCommand,
11249      ImplodeCommand,
11250      VignetteCommand,
11251      WaveCommand,
11252      OilPaintCommand,
11253      CharcoalDrawCommand
11254    },
11255    MiscellanyCommands[] =
11256    {
11257      InfoCommand,
11258      ZoomCommand,
11259      ShowPreviewCommand,
11260      ShowHistogramCommand,
11261      ShowMatteCommand
11262    },
11263    ROICommands[] =
11264    {
11265      ROIHelpCommand,
11266      ROIDismissCommand
11267    };
11268
11269  static const CommandType
11270    *Commands[ApplyMenus] =
11271    {
11272      FileCommands,
11273      EditCommands,
11274      TransformCommands,
11275      EnhanceCommands,
11276      EffectsCommands,
11277      FXCommands,
11278      MiscellanyCommands
11279    };
11280
11281  char
11282    command[MagickPathExtent],
11283    text[MagickPathExtent];
11284
11285  CommandType
11286    command_type;
11287
11288  Cursor
11289    cursor;
11290
11291  Image
11292    *roi_image;
11293
11294  int
11295    entry,
11296    id,
11297    x,
11298    y;
11299
11300  double
11301    scale_factor;
11302
11303  MagickProgressMonitor
11304    progress_monitor;
11305
11306  RectangleInfo
11307    crop_info,
11308    highlight_info,
11309    roi_info;
11310
11311  unsigned int
11312    height,
11313    width;
11314
11315  size_t
11316    state;
11317
11318  XEvent
11319    event;
11320
11321  /*
11322    Map Command widget.
11323  */
11324  (void) CloneString(&windows->command.name,"ROI");
11325  windows->command.data=0;
11326  (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11327  (void) XMapRaised(display,windows->command.id);
11328  XClientMessage(display,windows->image.id,windows->im_protocols,
11329    windows->im_update_widget,CurrentTime);
11330  /*
11331    Track pointer until button 1 is pressed.
11332  */
11333  XQueryPosition(display,windows->image.id,&x,&y);
11334  (void) XSelectInput(display,windows->image.id,
11335    windows->image.attributes.event_mask | PointerMotionMask);
11336  roi_info.x=(ssize_t) windows->image.x+x;
11337  roi_info.y=(ssize_t) windows->image.y+y;
11338  roi_info.width=0;
11339  roi_info.height=0;
11340  cursor=XCreateFontCursor(display,XC_fleur);
11341  state=DefaultState;
11342  do
11343  {
11344    if (windows->info.mapped != MagickFalse )
11345      {
11346        /*
11347          Display pointer position.
11348        */
11349        (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
11350          (long) roi_info.x,(long) roi_info.y);
11351        XInfoWidget(display,windows,text);
11352      }
11353    /*
11354      Wait for next event.
11355    */
11356    XScreenEvent(display,windows,&event,exception);
11357    if (event.xany.window == windows->command.id)
11358      {
11359        /*
11360          Select a command from the Command widget.
11361        */
11362        id=XCommandWidget(display,windows,ROIMenu,&event);
11363        if (id < 0)
11364          continue;
11365        switch (ROICommands[id])
11366        {
11367          case ROIHelpCommand:
11368          {
11369            XTextViewWidget(display,resource_info,windows,MagickFalse,
11370              "Help Viewer - Region of Interest",ImageROIHelp);
11371            break;
11372          }
11373          case ROIDismissCommand:
11374          {
11375            /*
11376              Prematurely exit.
11377            */
11378            state|=EscapeState;
11379            state|=ExitState;
11380            break;
11381          }
11382          default:
11383            break;
11384        }
11385        continue;
11386      }
11387    switch (event.type)
11388    {
11389      case ButtonPress:
11390      {
11391        if (event.xbutton.button != Button1)
11392          break;
11393        if (event.xbutton.window != windows->image.id)
11394          break;
11395        /*
11396          Note first corner of region of interest rectangle-- exit loop.
11397        */
11398        (void) XCheckDefineCursor(display,windows->image.id,cursor);
11399        roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11400        roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11401        state|=ExitState;
11402        break;
11403      }
11404      case ButtonRelease:
11405        break;
11406      case Expose:
11407        break;
11408      case KeyPress:
11409      {
11410        KeySym
11411          key_symbol;
11412
11413        if (event.xkey.window != windows->image.id)
11414          break;
11415        /*
11416          Respond to a user key press.
11417        */
11418        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11419          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11420        switch ((int) key_symbol)
11421        {
11422          case XK_Escape:
11423          case XK_F20:
11424          {
11425            /*
11426              Prematurely exit.
11427            */
11428            state|=EscapeState;
11429            state|=ExitState;
11430            break;
11431          }
11432          case XK_F1:
11433          case XK_Help:
11434          {
11435            XTextViewWidget(display,resource_info,windows,MagickFalse,
11436              "Help Viewer - Region of Interest",ImageROIHelp);
11437            break;
11438          }
11439          default:
11440          {
11441            (void) XBell(display,0);
11442            break;
11443          }
11444        }
11445        break;
11446      }
11447      case MotionNotify:
11448      {
11449        /*
11450          Map and unmap Info widget as text cursor crosses its boundaries.
11451        */
11452        x=event.xmotion.x;
11453        y=event.xmotion.y;
11454        if (windows->info.mapped != MagickFalse )
11455          {
11456            if ((x < (int) (windows->info.x+windows->info.width)) &&
11457                (y < (int) (windows->info.y+windows->info.height)))
11458              (void) XWithdrawWindow(display,windows->info.id,
11459                windows->info.screen);
11460          }
11461        else
11462          if ((x > (int) (windows->info.x+windows->info.width)) ||
11463              (y > (int) (windows->info.y+windows->info.height)))
11464            (void) XMapWindow(display,windows->info.id);
11465        roi_info.x=(ssize_t) windows->image.x+x;
11466        roi_info.y=(ssize_t) windows->image.y+y;
11467        break;
11468      }
11469      default:
11470        break;
11471    }
11472  } while ((state & ExitState) == 0);
11473  (void) XSelectInput(display,windows->image.id,
11474    windows->image.attributes.event_mask);
11475  if ((state & EscapeState) != 0)
11476    {
11477      /*
11478        User want to exit without region of interest.
11479      */
11480      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11481      (void) XFreeCursor(display,cursor);
11482      return(MagickTrue);
11483    }
11484  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11485  do
11486  {
11487    /*
11488      Size rectangle as pointer moves until the mouse button is released.
11489    */
11490    x=(int) roi_info.x;
11491    y=(int) roi_info.y;
11492    roi_info.width=0;
11493    roi_info.height=0;
11494    state=DefaultState;
11495    do
11496    {
11497      highlight_info=roi_info;
11498      highlight_info.x=roi_info.x-windows->image.x;
11499      highlight_info.y=roi_info.y-windows->image.y;
11500      if ((highlight_info.width > 3) && (highlight_info.height > 3))
11501        {
11502          /*
11503            Display info and draw region of interest rectangle.
11504          */
11505          if (windows->info.mapped == MagickFalse)
11506            (void) XMapWindow(display,windows->info.id);
11507          (void) FormatLocaleString(text,MagickPathExtent,
11508            " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11509            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11510          XInfoWidget(display,windows,text);
11511          XHighlightRectangle(display,windows->image.id,
11512            windows->image.highlight_context,&highlight_info);
11513        }
11514      else
11515        if (windows->info.mapped != MagickFalse )
11516          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11517      /*
11518        Wait for next event.
11519      */
11520      XScreenEvent(display,windows,&event,exception);
11521      if ((highlight_info.width > 3) && (highlight_info.height > 3))
11522        XHighlightRectangle(display,windows->image.id,
11523          windows->image.highlight_context,&highlight_info);
11524      switch (event.type)
11525      {
11526        case ButtonPress:
11527        {
11528          roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11529          roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11530          break;
11531        }
11532        case ButtonRelease:
11533        {
11534          /*
11535            User has committed to region of interest rectangle.
11536          */
11537          roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11538          roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11539          XSetCursorState(display,windows,MagickFalse);
11540          state|=ExitState;
11541          if (LocaleCompare(windows->command.name,"Apply") == 0)
11542            break;
11543          (void) CloneString(&windows->command.name,"Apply");
11544          windows->command.data=ApplyMenus;
11545          (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11546          break;
11547        }
11548        case Expose:
11549          break;
11550        case MotionNotify:
11551        {
11552          roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11553          roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11554        }
11555        default:
11556          break;
11557      }
11558      if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11559          ((state & ExitState) != 0))
11560        {
11561          /*
11562            Check boundary conditions.
11563          */
11564          if (roi_info.x < 0)
11565            roi_info.x=0;
11566          else
11567            if (roi_info.x > (ssize_t) windows->image.ximage->width)
11568              roi_info.x=(ssize_t) windows->image.ximage->width;
11569          if ((int) roi_info.x < x)
11570            roi_info.width=(unsigned int) (x-roi_info.x);
11571          else
11572            {
11573              roi_info.width=(unsigned int) (roi_info.x-x);
11574              roi_info.x=(ssize_t) x;
11575            }
11576          if (roi_info.y < 0)
11577            roi_info.y=0;
11578          else
11579            if (roi_info.y > (ssize_t) windows->image.ximage->height)
11580              roi_info.y=(ssize_t) windows->image.ximage->height;
11581          if ((int) roi_info.y < y)
11582            roi_info.height=(unsigned int) (y-roi_info.y);
11583          else
11584            {
11585              roi_info.height=(unsigned int) (roi_info.y-y);
11586              roi_info.y=(ssize_t) y;
11587            }
11588        }
11589    } while ((state & ExitState) == 0);
11590    /*
11591      Wait for user to grab a corner of the rectangle or press return.
11592    */
11593    state=DefaultState;
11594    command_type=NullCommand;
11595    crop_info.x=0;
11596    crop_info.y=0;
11597    (void) XMapWindow(display,windows->info.id);
11598    do
11599    {
11600      if (windows->info.mapped != MagickFalse )
11601        {
11602          /*
11603            Display pointer position.
11604          */
11605          (void) FormatLocaleString(text,MagickPathExtent,
11606            " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11607            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11608          XInfoWidget(display,windows,text);
11609        }
11610      highlight_info=roi_info;
11611      highlight_info.x=roi_info.x-windows->image.x;
11612      highlight_info.y=roi_info.y-windows->image.y;
11613      if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11614        {
11615          state|=EscapeState;
11616          state|=ExitState;
11617          break;
11618        }
11619      if ((state & UpdateRegionState) != 0)
11620        {
11621          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11622          switch (command_type)
11623          {
11624            case UndoCommand:
11625            case RedoCommand:
11626            {
11627              (void) XMagickCommand(display,resource_info,windows,command_type,
11628                image,exception);
11629              break;
11630            }
11631            default:
11632            {
11633              /*
11634                Region of interest is relative to image configuration.
11635              */
11636              progress_monitor=SetImageProgressMonitor(*image,
11637                (MagickProgressMonitor) NULL,(*image)->client_data);
11638              crop_info=roi_info;
11639              width=(unsigned int) (*image)->columns;
11640              height=(unsigned int) (*image)->rows;
11641              x=0;
11642              y=0;
11643              if (windows->image.crop_geometry != (char *) NULL)
11644                (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11645                  &width,&height);
11646              scale_factor=(double) width/windows->image.ximage->width;
11647              crop_info.x+=x;
11648              crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11649              crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11650              scale_factor=(double)
11651                height/windows->image.ximage->height;
11652              crop_info.y+=y;
11653              crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11654              crop_info.height=(unsigned int)
11655                (scale_factor*crop_info.height+0.5);
11656              roi_image=CropImage(*image,&crop_info,exception);
11657              (void) SetImageProgressMonitor(*image,progress_monitor,
11658                (*image)->client_data);
11659              if (roi_image == (Image *) NULL)
11660                continue;
11661              /*
11662                Apply image processing technique to the region of interest.
11663              */
11664              windows->image.orphan=MagickTrue;
11665              (void) XMagickCommand(display,resource_info,windows,command_type,
11666                &roi_image,exception);
11667              progress_monitor=SetImageProgressMonitor(*image,
11668                (MagickProgressMonitor) NULL,(*image)->client_data);
11669              (void) XMagickCommand(display,resource_info,windows,
11670                SaveToUndoBufferCommand,image,exception);
11671              windows->image.orphan=MagickFalse;
11672              (void) CompositeImage(*image,roi_image,CopyCompositeOp,
11673                MagickTrue,crop_info.x,crop_info.y,exception);
11674              roi_image=DestroyImage(roi_image);
11675              (void) SetImageProgressMonitor(*image,progress_monitor,
11676                (*image)->client_data);
11677              break;
11678            }
11679          }
11680          if (command_type != InfoCommand)
11681            {
11682              XConfigureImageColormap(display,resource_info,windows,*image,
11683                exception);
11684              (void) XConfigureImage(display,resource_info,windows,*image,
11685                exception);
11686            }
11687          XCheckRefreshWindows(display,windows);
11688          XInfoWidget(display,windows,text);
11689          (void) XSetFunction(display,windows->image.highlight_context,
11690            GXinvert);
11691          state&=(~UpdateRegionState);
11692        }
11693      XHighlightRectangle(display,windows->image.id,
11694        windows->image.highlight_context,&highlight_info);
11695      XScreenEvent(display,windows,&event,exception);
11696      if (event.xany.window == windows->command.id)
11697        {
11698          /*
11699            Select a command from the Command widget.
11700          */
11701          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11702          command_type=NullCommand;
11703          id=XCommandWidget(display,windows,ApplyMenu,&event);
11704          if (id >= 0)
11705            {
11706              (void) CopyMagickString(command,ApplyMenu[id],MagickPathExtent);
11707              command_type=ApplyCommands[id];
11708              if (id < ApplyMenus)
11709                {
11710                  /*
11711                    Select a command from a pop-up menu.
11712                  */
11713                  entry=XMenuWidget(display,windows,ApplyMenu[id],
11714                    (const char **) Menus[id],command);
11715                  if (entry >= 0)
11716                    {
11717                      (void) CopyMagickString(command,Menus[id][entry],
11718                        MagickPathExtent);
11719                      command_type=Commands[id][entry];
11720                    }
11721                }
11722            }
11723          (void) XSetFunction(display,windows->image.highlight_context,
11724            GXinvert);
11725          XHighlightRectangle(display,windows->image.id,
11726            windows->image.highlight_context,&highlight_info);
11727          if (command_type == HelpCommand)
11728            {
11729              (void) XSetFunction(display,windows->image.highlight_context,
11730                GXcopy);
11731              XTextViewWidget(display,resource_info,windows,MagickFalse,
11732                "Help Viewer - Region of Interest",ImageROIHelp);
11733              (void) XSetFunction(display,windows->image.highlight_context,
11734                GXinvert);
11735              continue;
11736            }
11737          if (command_type == QuitCommand)
11738            {
11739              /*
11740                exit.
11741              */
11742              state|=EscapeState;
11743              state|=ExitState;
11744              continue;
11745            }
11746          if (command_type != NullCommand)
11747            state|=UpdateRegionState;
11748          continue;
11749        }
11750      XHighlightRectangle(display,windows->image.id,
11751        windows->image.highlight_context,&highlight_info);
11752      switch (event.type)
11753      {
11754        case ButtonPress:
11755        {
11756          x=windows->image.x;
11757          y=windows->image.y;
11758          if (event.xbutton.button != Button1)
11759            break;
11760          if (event.xbutton.window != windows->image.id)
11761            break;
11762          x=windows->image.x+event.xbutton.x;
11763          y=windows->image.y+event.xbutton.y;
11764          if ((x < (int) (roi_info.x+RoiDelta)) &&
11765              (x > (int) (roi_info.x-RoiDelta)) &&
11766              (y < (int) (roi_info.y+RoiDelta)) &&
11767              (y > (int) (roi_info.y-RoiDelta)))
11768            {
11769              roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11770              roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11771              state|=UpdateConfigurationState;
11772              break;
11773            }
11774          if ((x < (int) (roi_info.x+RoiDelta)) &&
11775              (x > (int) (roi_info.x-RoiDelta)) &&
11776              (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11777              (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11778            {
11779              roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11780              state|=UpdateConfigurationState;
11781              break;
11782            }
11783          if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11784              (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11785              (y < (int) (roi_info.y+RoiDelta)) &&
11786              (y > (int) (roi_info.y-RoiDelta)))
11787            {
11788              roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11789              state|=UpdateConfigurationState;
11790              break;
11791            }
11792          if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11793              (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11794              (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11795              (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11796            {
11797              state|=UpdateConfigurationState;
11798              break;
11799            }
11800        }
11801        case ButtonRelease:
11802        {
11803          if (event.xbutton.window == windows->pan.id)
11804            if ((highlight_info.x != crop_info.x-windows->image.x) ||
11805                (highlight_info.y != crop_info.y-windows->image.y))
11806              XHighlightRectangle(display,windows->image.id,
11807                windows->image.highlight_context,&highlight_info);
11808          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11809            event.xbutton.time);
11810          break;
11811        }
11812        case Expose:
11813        {
11814          if (event.xexpose.window == windows->image.id)
11815            if (event.xexpose.count == 0)
11816              {
11817                event.xexpose.x=(int) highlight_info.x;
11818                event.xexpose.y=(int) highlight_info.y;
11819                event.xexpose.width=(int) highlight_info.width;
11820                event.xexpose.height=(int) highlight_info.height;
11821                XRefreshWindow(display,&windows->image,&event);
11822              }
11823          if (event.xexpose.window == windows->info.id)
11824            if (event.xexpose.count == 0)
11825              XInfoWidget(display,windows,text);
11826          break;
11827        }
11828        case KeyPress:
11829        {
11830          KeySym
11831            key_symbol;
11832
11833          if (event.xkey.window != windows->image.id)
11834            break;
11835          /*
11836            Respond to a user key press.
11837          */
11838          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11839            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11840          switch ((int) key_symbol)
11841          {
11842            case XK_Shift_L:
11843            case XK_Shift_R:
11844              break;
11845            case XK_Escape:
11846            case XK_F20:
11847              state|=EscapeState;
11848            case XK_Return:
11849            {
11850              state|=ExitState;
11851              break;
11852            }
11853            case XK_Home:
11854            case XK_KP_Home:
11855            {
11856              roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11857              roi_info.y=(ssize_t) (windows->image.height/2L-
11858                roi_info.height/2L);
11859              break;
11860            }
11861            case XK_Left:
11862            case XK_KP_Left:
11863            {
11864              roi_info.x--;
11865              break;
11866            }
11867            case XK_Up:
11868            case XK_KP_Up:
11869            case XK_Next:
11870            {
11871              roi_info.y--;
11872              break;
11873            }
11874            case XK_Right:
11875            case XK_KP_Right:
11876            {
11877              roi_info.x++;
11878              break;
11879            }
11880            case XK_Prior:
11881            case XK_Down:
11882            case XK_KP_Down:
11883            {
11884              roi_info.y++;
11885              break;
11886            }
11887            case XK_F1:
11888            case XK_Help:
11889            {
11890              (void) XSetFunction(display,windows->image.highlight_context,
11891                GXcopy);
11892              XTextViewWidget(display,resource_info,windows,MagickFalse,
11893                "Help Viewer - Region of Interest",ImageROIHelp);
11894              (void) XSetFunction(display,windows->image.highlight_context,
11895                GXinvert);
11896              break;
11897            }
11898            default:
11899            {
11900              command_type=XImageWindowCommand(display,resource_info,windows,
11901                event.xkey.state,key_symbol,image,exception);
11902              if (command_type != NullCommand)
11903                state|=UpdateRegionState;
11904              break;
11905            }
11906          }
11907          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11908            event.xkey.time);
11909          break;
11910        }
11911        case KeyRelease:
11912          break;
11913        case MotionNotify:
11914        {
11915          if (event.xbutton.window != windows->image.id)
11916            break;
11917          /*
11918            Map and unmap Info widget as text cursor crosses its boundaries.
11919          */
11920          x=event.xmotion.x;
11921          y=event.xmotion.y;
11922          if (windows->info.mapped != MagickFalse )
11923            {
11924              if ((x < (int) (windows->info.x+windows->info.width)) &&
11925                  (y < (int) (windows->info.y+windows->info.height)))
11926                (void) XWithdrawWindow(display,windows->info.id,
11927                  windows->info.screen);
11928            }
11929          else
11930            if ((x > (int) (windows->info.x+windows->info.width)) ||
11931                (y > (int) (windows->info.y+windows->info.height)))
11932              (void) XMapWindow(display,windows->info.id);
11933          roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11934          roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11935          break;
11936        }
11937        case SelectionRequest:
11938        {
11939          XSelectionEvent
11940            notify;
11941
11942          XSelectionRequestEvent
11943            *request;
11944
11945          /*
11946            Set primary selection.
11947          */
11948          (void) FormatLocaleString(text,MagickPathExtent,
11949            "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11950            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11951          request=(&(event.xselectionrequest));
11952          (void) XChangeProperty(request->display,request->requestor,
11953            request->property,request->target,8,PropModeReplace,
11954            (unsigned char *) text,(int) strlen(text));
11955          notify.type=SelectionNotify;
11956          notify.display=request->display;
11957          notify.requestor=request->requestor;
11958          notify.selection=request->selection;
11959          notify.target=request->target;
11960          notify.time=request->time;
11961          if (request->property == None)
11962            notify.property=request->target;
11963          else
11964            notify.property=request->property;
11965          (void) XSendEvent(request->display,request->requestor,False,0,
11966            (XEvent *) &notify);
11967        }
11968        default:
11969          break;
11970      }
11971      if ((state & UpdateConfigurationState) != 0)
11972        {
11973          (void) XPutBackEvent(display,&event);
11974          (void) XCheckDefineCursor(display,windows->image.id,cursor);
11975          break;
11976        }
11977    } while ((state & ExitState) == 0);
11978  } while ((state & ExitState) == 0);
11979  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11980  XSetCursorState(display,windows,MagickFalse);
11981  if ((state & EscapeState) != 0)
11982    return(MagickTrue);
11983  return(MagickTrue);
11984}
11985
11986/*
11987%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11988%                                                                             %
11989%                                                                             %
11990%                                                                             %
11991+   X R o t a t e I m a g e                                                   %
11992%                                                                             %
11993%                                                                             %
11994%                                                                             %
11995%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11996%
11997%  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
11998%  rotation angle is computed from the slope of a line drawn by the user.
11999%
12000%  The format of the XRotateImage method is:
12001%
12002%      MagickBooleanType XRotateImage(Display *display,
12003%        XResourceInfo *resource_info,XWindows *windows,double degrees,
12004%        Image **image,ExceptionInfo *exception)
12005%
12006%  A description of each parameter follows:
12007%
12008%    o display: Specifies a connection to an X server; returned from
12009%      XOpenDisplay.
12010%
12011%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12012%
12013%    o windows: Specifies a pointer to a XWindows structure.
12014%
12015%    o degrees: Specifies the number of degrees to rotate the image.
12016%
12017%    o image: the image.
12018%
12019%    o exception: return any errors or warnings in this structure.
12020%
12021*/
12022static MagickBooleanType XRotateImage(Display *display,
12023  XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
12024  ExceptionInfo *exception)
12025{
12026  static const char
12027    *RotateMenu[] =
12028    {
12029      "Pixel Color",
12030      "Direction",
12031      "Help",
12032      "Dismiss",
12033      (char *) NULL
12034    };
12035
12036  static ModeType
12037    direction = HorizontalRotateCommand;
12038
12039  static const ModeType
12040    DirectionCommands[] =
12041    {
12042      HorizontalRotateCommand,
12043      VerticalRotateCommand
12044    },
12045    RotateCommands[] =
12046    {
12047      RotateColorCommand,
12048      RotateDirectionCommand,
12049      RotateHelpCommand,
12050      RotateDismissCommand
12051    };
12052
12053  static unsigned int
12054    pen_id = 0;
12055
12056  char
12057    command[MagickPathExtent],
12058    text[MagickPathExtent];
12059
12060  Image
12061    *rotate_image;
12062
12063  int
12064    id,
12065    x,
12066    y;
12067
12068  double
12069    normalized_degrees;
12070
12071  register int
12072    i;
12073
12074  unsigned int
12075    height,
12076    rotations,
12077    width;
12078
12079  if (degrees == 0.0)
12080    {
12081      unsigned int
12082        distance;
12083
12084      size_t
12085        state;
12086
12087      XEvent
12088        event;
12089
12090      XSegment
12091        rotate_info;
12092
12093      /*
12094        Map Command widget.
12095      */
12096      (void) CloneString(&windows->command.name,"Rotate");
12097      windows->command.data=2;
12098      (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12099      (void) XMapRaised(display,windows->command.id);
12100      XClientMessage(display,windows->image.id,windows->im_protocols,
12101        windows->im_update_widget,CurrentTime);
12102      /*
12103        Wait for first button press.
12104      */
12105      (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12106      XQueryPosition(display,windows->image.id,&x,&y);
12107      rotate_info.x1=x;
12108      rotate_info.y1=y;
12109      rotate_info.x2=x;
12110      rotate_info.y2=y;
12111      state=DefaultState;
12112      do
12113      {
12114        XHighlightLine(display,windows->image.id,
12115          windows->image.highlight_context,&rotate_info);
12116        /*
12117          Wait for next event.
12118        */
12119        XScreenEvent(display,windows,&event,exception);
12120        XHighlightLine(display,windows->image.id,
12121          windows->image.highlight_context,&rotate_info);
12122        if (event.xany.window == windows->command.id)
12123          {
12124            /*
12125              Select a command from the Command widget.
12126            */
12127            id=XCommandWidget(display,windows,RotateMenu,&event);
12128            if (id < 0)
12129              continue;
12130            (void) XSetFunction(display,windows->image.highlight_context,
12131              GXcopy);
12132            switch (RotateCommands[id])
12133            {
12134              case RotateColorCommand:
12135              {
12136                const char
12137                  *ColorMenu[MaxNumberPens];
12138
12139                int
12140                  pen_number;
12141
12142                XColor
12143                  color;
12144
12145                /*
12146                  Initialize menu selections.
12147                */
12148                for (i=0; i < (int) (MaxNumberPens-2); i++)
12149                  ColorMenu[i]=resource_info->pen_colors[i];
12150                ColorMenu[MaxNumberPens-2]="Browser...";
12151                ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12152                /*
12153                  Select a pen color from the pop-up menu.
12154                */
12155                pen_number=XMenuWidget(display,windows,RotateMenu[id],
12156                  (const char **) ColorMenu,command);
12157                if (pen_number < 0)
12158                  break;
12159                if (pen_number == (MaxNumberPens-2))
12160                  {
12161                    static char
12162                      color_name[MagickPathExtent] = "gray";
12163
12164                    /*
12165                      Select a pen color from a dialog.
12166                    */
12167                    resource_info->pen_colors[pen_number]=color_name;
12168                    XColorBrowserWidget(display,windows,"Select",color_name);
12169                    if (*color_name == '\0')
12170                      break;
12171                  }
12172                /*
12173                  Set pen color.
12174                */
12175                (void) XParseColor(display,windows->map_info->colormap,
12176                  resource_info->pen_colors[pen_number],&color);
12177                XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12178                  (unsigned int) MaxColors,&color);
12179                windows->pixel_info->pen_colors[pen_number]=color;
12180                pen_id=(unsigned int) pen_number;
12181                break;
12182              }
12183              case RotateDirectionCommand:
12184              {
12185                static const char
12186                  *Directions[] =
12187                  {
12188                    "horizontal",
12189                    "vertical",
12190                    (char *) NULL,
12191                  };
12192
12193                /*
12194                  Select a command from the pop-up menu.
12195                */
12196                id=XMenuWidget(display,windows,RotateMenu[id],
12197                  Directions,command);
12198                if (id >= 0)
12199                  direction=DirectionCommands[id];
12200                break;
12201              }
12202              case RotateHelpCommand:
12203              {
12204                XTextViewWidget(display,resource_info,windows,MagickFalse,
12205                  "Help Viewer - Image Rotation",ImageRotateHelp);
12206                break;
12207              }
12208              case RotateDismissCommand:
12209              {
12210                /*
12211                  Prematurely exit.
12212                */
12213                state|=EscapeState;
12214                state|=ExitState;
12215                break;
12216              }
12217              default:
12218                break;
12219            }
12220            (void) XSetFunction(display,windows->image.highlight_context,
12221              GXinvert);
12222            continue;
12223          }
12224        switch (event.type)
12225        {
12226          case ButtonPress:
12227          {
12228            if (event.xbutton.button != Button1)
12229              break;
12230            if (event.xbutton.window != windows->image.id)
12231              break;
12232            /*
12233              exit loop.
12234            */
12235            (void) XSetFunction(display,windows->image.highlight_context,
12236              GXcopy);
12237            rotate_info.x1=event.xbutton.x;
12238            rotate_info.y1=event.xbutton.y;
12239            state|=ExitState;
12240            break;
12241          }
12242          case ButtonRelease:
12243            break;
12244          case Expose:
12245            break;
12246          case KeyPress:
12247          {
12248            char
12249              command[MagickPathExtent];
12250
12251            KeySym
12252              key_symbol;
12253
12254            if (event.xkey.window != windows->image.id)
12255              break;
12256            /*
12257              Respond to a user key press.
12258            */
12259            (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12260              sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12261            switch ((int) key_symbol)
12262            {
12263              case XK_Escape:
12264              case XK_F20:
12265              {
12266                /*
12267                  Prematurely exit.
12268                */
12269                state|=EscapeState;
12270                state|=ExitState;
12271                break;
12272              }
12273              case XK_F1:
12274              case XK_Help:
12275              {
12276                (void) XSetFunction(display,windows->image.highlight_context,
12277                  GXcopy);
12278                XTextViewWidget(display,resource_info,windows,MagickFalse,
12279                  "Help Viewer - Image Rotation",ImageRotateHelp);
12280                (void) XSetFunction(display,windows->image.highlight_context,
12281                  GXinvert);
12282                break;
12283              }
12284              default:
12285              {
12286                (void) XBell(display,0);
12287                break;
12288              }
12289            }
12290            break;
12291          }
12292          case MotionNotify:
12293          {
12294            rotate_info.x1=event.xmotion.x;
12295            rotate_info.y1=event.xmotion.y;
12296          }
12297        }
12298        rotate_info.x2=rotate_info.x1;
12299        rotate_info.y2=rotate_info.y1;
12300        if (direction == HorizontalRotateCommand)
12301          rotate_info.x2+=32;
12302        else
12303          rotate_info.y2-=32;
12304      } while ((state & ExitState) == 0);
12305      (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12306      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12307      if ((state & EscapeState) != 0)
12308        return(MagickTrue);
12309      /*
12310        Draw line as pointer moves until the mouse button is released.
12311      */
12312      distance=0;
12313      (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12314      state=DefaultState;
12315      do
12316      {
12317        if (distance > 9)
12318          {
12319            /*
12320              Display info and draw rotation line.
12321            */
12322            if (windows->info.mapped == MagickFalse)
12323              (void) XMapWindow(display,windows->info.id);
12324            (void) FormatLocaleString(text,MagickPathExtent," %g",
12325              direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12326            XInfoWidget(display,windows,text);
12327            XHighlightLine(display,windows->image.id,
12328              windows->image.highlight_context,&rotate_info);
12329          }
12330        else
12331          if (windows->info.mapped != MagickFalse )
12332            (void) XWithdrawWindow(display,windows->info.id,
12333              windows->info.screen);
12334        /*
12335          Wait for next event.
12336        */
12337        XScreenEvent(display,windows,&event,exception);
12338        if (distance > 9)
12339          XHighlightLine(display,windows->image.id,
12340            windows->image.highlight_context,&rotate_info);
12341        switch (event.type)
12342        {
12343          case ButtonPress:
12344            break;
12345          case ButtonRelease:
12346          {
12347            /*
12348              User has committed to rotation line.
12349            */
12350            rotate_info.x2=event.xbutton.x;
12351            rotate_info.y2=event.xbutton.y;
12352            state|=ExitState;
12353            break;
12354          }
12355          case Expose:
12356            break;
12357          case MotionNotify:
12358          {
12359            rotate_info.x2=event.xmotion.x;
12360            rotate_info.y2=event.xmotion.y;
12361          }
12362          default:
12363            break;
12364        }
12365        /*
12366          Check boundary conditions.
12367        */
12368        if (rotate_info.x2 < 0)
12369          rotate_info.x2=0;
12370        else
12371          if (rotate_info.x2 > (int) windows->image.width)
12372            rotate_info.x2=(short) windows->image.width;
12373        if (rotate_info.y2 < 0)
12374          rotate_info.y2=0;
12375        else
12376          if (rotate_info.y2 > (int) windows->image.height)
12377            rotate_info.y2=(short) windows->image.height;
12378        /*
12379          Compute rotation angle from the slope of the line.
12380        */
12381        degrees=0.0;
12382        distance=(unsigned int)
12383          ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12384          ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12385        if (distance > 9)
12386          degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12387            rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12388      } while ((state & ExitState) == 0);
12389      (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12390      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12391      if (distance <= 9)
12392        return(MagickTrue);
12393    }
12394  if (direction == VerticalRotateCommand)
12395    degrees-=90.0;
12396  if (degrees == 0.0)
12397    return(MagickTrue);
12398  /*
12399    Rotate image.
12400  */
12401  normalized_degrees=degrees;
12402  while (normalized_degrees < -45.0)
12403    normalized_degrees+=360.0;
12404  for (rotations=0; normalized_degrees > 45.0; rotations++)
12405    normalized_degrees-=90.0;
12406  if (normalized_degrees != 0.0)
12407    (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12408      exception);
12409  XSetCursorState(display,windows,MagickTrue);
12410  XCheckRefreshWindows(display,windows);
12411  (*image)->background_color.red=(double) ScaleShortToQuantum(
12412    windows->pixel_info->pen_colors[pen_id].red);
12413  (*image)->background_color.green=(double) ScaleShortToQuantum(
12414    windows->pixel_info->pen_colors[pen_id].green);
12415  (*image)->background_color.blue=(double) ScaleShortToQuantum(
12416    windows->pixel_info->pen_colors[pen_id].blue);
12417  rotate_image=RotateImage(*image,degrees,exception);
12418  XSetCursorState(display,windows,MagickFalse);
12419  if (rotate_image == (Image *) NULL)
12420    return(MagickFalse);
12421  *image=DestroyImage(*image);
12422  *image=rotate_image;
12423  if (windows->image.crop_geometry != (char *) NULL)
12424    {
12425      /*
12426        Rotate crop geometry.
12427      */
12428      width=(unsigned int) (*image)->columns;
12429      height=(unsigned int) (*image)->rows;
12430      (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12431      switch (rotations % 4)
12432      {
12433        default:
12434        case 0:
12435          break;
12436        case 1:
12437        {
12438          /*
12439            Rotate 90 degrees.
12440          */
12441          (void) FormatLocaleString(windows->image.crop_geometry,MagickPathExtent,
12442            "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12443            (int) height-y,x);
12444          break;
12445        }
12446        case 2:
12447        {
12448          /*
12449            Rotate 180 degrees.
12450          */
12451          (void) FormatLocaleString(windows->image.crop_geometry,MagickPathExtent,
12452            "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12453          break;
12454        }
12455        case 3:
12456        {
12457          /*
12458            Rotate 270 degrees.
12459          */
12460          (void) FormatLocaleString(windows->image.crop_geometry,MagickPathExtent,
12461            "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12462          break;
12463        }
12464      }
12465    }
12466  if (windows->image.orphan != MagickFalse )
12467    return(MagickTrue);
12468  if (normalized_degrees != 0.0)
12469    {
12470      /*
12471        Update image colormap.
12472      */
12473      windows->image.window_changes.width=(int) (*image)->columns;
12474      windows->image.window_changes.height=(int) (*image)->rows;
12475      if (windows->image.crop_geometry != (char *) NULL)
12476        {
12477          /*
12478            Obtain dimensions of image from crop geometry.
12479          */
12480          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12481            &width,&height);
12482          windows->image.window_changes.width=(int) width;
12483          windows->image.window_changes.height=(int) height;
12484        }
12485      XConfigureImageColormap(display,resource_info,windows,*image,exception);
12486    }
12487  else
12488    if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12489      {
12490        windows->image.window_changes.width=windows->image.ximage->height;
12491        windows->image.window_changes.height=windows->image.ximage->width;
12492      }
12493  /*
12494    Update image configuration.
12495  */
12496  (void) XConfigureImage(display,resource_info,windows,*image,exception);
12497  return(MagickTrue);
12498}
12499
12500/*
12501%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12502%                                                                             %
12503%                                                                             %
12504%                                                                             %
12505+   X S a v e I m a g e                                                       %
12506%                                                                             %
12507%                                                                             %
12508%                                                                             %
12509%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12510%
12511%  XSaveImage() saves an image to a file.
12512%
12513%  The format of the XSaveImage method is:
12514%
12515%      MagickBooleanType XSaveImage(Display *display,
12516%        XResourceInfo *resource_info,XWindows *windows,Image *image,
12517%        ExceptionInfo *exception)
12518%
12519%  A description of each parameter follows:
12520%
12521%    o display: Specifies a connection to an X server; returned from
12522%      XOpenDisplay.
12523%
12524%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12525%
12526%    o windows: Specifies a pointer to a XWindows structure.
12527%
12528%    o image: the image.
12529%
12530%    o exception: return any errors or warnings in this structure.
12531%
12532*/
12533static MagickBooleanType XSaveImage(Display *display,
12534  XResourceInfo *resource_info,XWindows *windows,Image *image,
12535  ExceptionInfo *exception)
12536{
12537  char
12538    filename[MagickPathExtent],
12539    geometry[MagickPathExtent];
12540
12541  Image
12542    *save_image;
12543
12544  ImageInfo
12545    *image_info;
12546
12547  MagickStatusType
12548    status;
12549
12550  /*
12551    Request file name from user.
12552  */
12553  if (resource_info->write_filename != (char *) NULL)
12554    (void) CopyMagickString(filename,resource_info->write_filename,
12555      MagickPathExtent);
12556  else
12557    {
12558      char
12559        path[MagickPathExtent];
12560
12561      int
12562        status;
12563
12564      GetPathComponent(image->filename,HeadPath,path);
12565      GetPathComponent(image->filename,TailPath,filename);
12566      if (*path != '\0')
12567        {
12568          status=chdir(path);
12569          if (status == -1)
12570            (void) ThrowMagickException(exception,GetMagickModule(),
12571              FileOpenError,"UnableToOpenFile","%s",path);
12572        }
12573    }
12574  XFileBrowserWidget(display,windows,"Save",filename);
12575  if (*filename == '\0')
12576    return(MagickTrue);
12577  if (IsPathAccessible(filename) != MagickFalse )
12578    {
12579      int
12580        status;
12581
12582      /*
12583        File exists-- seek user's permission before overwriting.
12584      */
12585      status=XConfirmWidget(display,windows,"Overwrite",filename);
12586      if (status <= 0)
12587        return(MagickTrue);
12588    }
12589  image_info=CloneImageInfo(resource_info->image_info);
12590  (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
12591  (void) SetImageInfo(image_info,1,exception);
12592  if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12593      (LocaleCompare(image_info->magick,"JPG") == 0))
12594    {
12595      char
12596        quality[MagickPathExtent];
12597
12598      int
12599        status;
12600
12601      /*
12602        Request JPEG quality from user.
12603      */
12604      (void) FormatLocaleString(quality,MagickPathExtent,"%.20g",(double)
12605        image->quality);
12606      status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12607        quality);
12608      if (*quality == '\0')
12609        return(MagickTrue);
12610      image->quality=StringToUnsignedLong(quality);
12611      image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12612    }
12613  if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12614      (LocaleCompare(image_info->magick,"PDF") == 0) ||
12615      (LocaleCompare(image_info->magick,"PS") == 0) ||
12616      (LocaleCompare(image_info->magick,"PS2") == 0))
12617    {
12618      char
12619        geometry[MagickPathExtent];
12620
12621      /*
12622        Request page geometry from user.
12623      */
12624      (void) CopyMagickString(geometry,PSPageGeometry,MagickPathExtent);
12625      if (LocaleCompare(image_info->magick,"PDF") == 0)
12626        (void) CopyMagickString(geometry,PSPageGeometry,MagickPathExtent);
12627      if (image_info->page != (char *) NULL)
12628        (void) CopyMagickString(geometry,image_info->page,MagickPathExtent);
12629      XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12630        "Select page geometry:",geometry);
12631      if (*geometry != '\0')
12632        image_info->page=GetPageGeometry(geometry);
12633    }
12634  /*
12635    Apply image transforms.
12636  */
12637  XSetCursorState(display,windows,MagickTrue);
12638  XCheckRefreshWindows(display,windows);
12639  save_image=CloneImage(image,0,0,MagickTrue,exception);
12640  if (save_image == (Image *) NULL)
12641    return(MagickFalse);
12642  (void) FormatLocaleString(geometry,MagickPathExtent,"%dx%d!",
12643    windows->image.ximage->width,windows->image.ximage->height);
12644  (void) TransformImage(&save_image,windows->image.crop_geometry,geometry,
12645    exception);
12646  /*
12647    Write image.
12648  */
12649  (void) CopyMagickString(save_image->filename,filename,MagickPathExtent);
12650  status=WriteImage(image_info,save_image,exception);
12651  if (status != MagickFalse )
12652    image->taint=MagickFalse;
12653  save_image=DestroyImage(save_image);
12654  image_info=DestroyImageInfo(image_info);
12655  XSetCursorState(display,windows,MagickFalse);
12656  return(status != 0 ? MagickTrue : MagickFalse);
12657}
12658
12659/*
12660%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12661%                                                                             %
12662%                                                                             %
12663%                                                                             %
12664+   X S c r e e n E v e n t                                                   %
12665%                                                                             %
12666%                                                                             %
12667%                                                                             %
12668%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12669%
12670%  XScreenEvent() handles global events associated with the Pan and Magnify
12671%  windows.
12672%
12673%  The format of the XScreenEvent function is:
12674%
12675%      void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12676%        ExceptionInfo *exception)
12677%
12678%  A description of each parameter follows:
12679%
12680%    o display: Specifies a pointer to the Display structure;  returned from
12681%      XOpenDisplay.
12682%
12683%    o windows: Specifies a pointer to a XWindows structure.
12684%
12685%    o event: Specifies a pointer to a X11 XEvent structure.
12686%
12687%    o exception: return any errors or warnings in this structure.
12688%
12689*/
12690
12691#if defined(__cplusplus) || defined(c_plusplus)
12692extern "C" {
12693#endif
12694
12695static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12696{
12697  register XWindows
12698    *windows;
12699
12700  windows=(XWindows *) data;
12701  if ((event->type == ClientMessage) &&
12702      (event->xclient.window == windows->image.id))
12703    return(MagickFalse);
12704  return(MagickTrue);
12705}
12706
12707#if defined(__cplusplus) || defined(c_plusplus)
12708}
12709#endif
12710
12711static void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12712  ExceptionInfo *exception)
12713{
12714  register int
12715    x,
12716    y;
12717
12718  (void) XIfEvent(display,event,XPredicate,(char *) windows);
12719  if (event->xany.window == windows->command.id)
12720    return;
12721  switch (event->type)
12722  {
12723    case ButtonPress:
12724    case ButtonRelease:
12725    {
12726      if ((event->xbutton.button == Button3) &&
12727          (event->xbutton.state & Mod1Mask))
12728        {
12729          /*
12730            Convert Alt-Button3 to Button2.
12731          */
12732          event->xbutton.button=Button2;
12733          event->xbutton.state&=(~Mod1Mask);
12734        }
12735      if (event->xbutton.window == windows->backdrop.id)
12736        {
12737          (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12738            event->xbutton.time);
12739          break;
12740        }
12741      if (event->xbutton.window == windows->pan.id)
12742        {
12743          XPanImage(display,windows,event,exception);
12744          break;
12745        }
12746      if (event->xbutton.window == windows->image.id)
12747        if (event->xbutton.button == Button2)
12748          {
12749            /*
12750              Update magnified image.
12751            */
12752            x=event->xbutton.x;
12753            y=event->xbutton.y;
12754            if (x < 0)
12755              x=0;
12756            else
12757              if (x >= (int) windows->image.width)
12758                x=(int) (windows->image.width-1);
12759            windows->magnify.x=(int) windows->image.x+x;
12760            if (y < 0)
12761              y=0;
12762            else
12763             if (y >= (int) windows->image.height)
12764               y=(int) (windows->image.height-1);
12765            windows->magnify.y=windows->image.y+y;
12766            if (windows->magnify.mapped == MagickFalse)
12767              (void) XMapRaised(display,windows->magnify.id);
12768            XMakeMagnifyImage(display,windows,exception);
12769            if (event->type == ButtonRelease)
12770              (void) XWithdrawWindow(display,windows->info.id,
12771                windows->info.screen);
12772            break;
12773          }
12774      break;
12775    }
12776    case ClientMessage:
12777    {
12778      /*
12779        If client window delete message, exit.
12780      */
12781      if (event->xclient.message_type != windows->wm_protocols)
12782        break;
12783      if (*event->xclient.data.l != (long) windows->wm_delete_window)
12784        break;
12785      if (event->xclient.window == windows->magnify.id)
12786        {
12787          (void) XWithdrawWindow(display,windows->magnify.id,
12788            windows->magnify.screen);
12789          break;
12790        }
12791      break;
12792    }
12793    case ConfigureNotify:
12794    {
12795      if (event->xconfigure.window == windows->magnify.id)
12796        {
12797          unsigned int
12798            magnify;
12799
12800          /*
12801            Magnify window has a new configuration.
12802          */
12803          windows->magnify.width=(unsigned int) event->xconfigure.width;
12804          windows->magnify.height=(unsigned int) event->xconfigure.height;
12805          if (windows->magnify.mapped == MagickFalse)
12806            break;
12807          magnify=1;
12808          while ((int) magnify <= event->xconfigure.width)
12809            magnify<<=1;
12810          while ((int) magnify <= event->xconfigure.height)
12811            magnify<<=1;
12812          magnify>>=1;
12813          if (((int) magnify != event->xconfigure.width) ||
12814              ((int) magnify != event->xconfigure.height))
12815            {
12816              XWindowChanges
12817                window_changes;
12818
12819              window_changes.width=(int) magnify;
12820              window_changes.height=(int) magnify;
12821              (void) XReconfigureWMWindow(display,windows->magnify.id,
12822                windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12823                &window_changes);
12824              break;
12825            }
12826          XMakeMagnifyImage(display,windows,exception);
12827          break;
12828        }
12829      break;
12830    }
12831    case Expose:
12832    {
12833      if (event->xexpose.window == windows->image.id)
12834        {
12835          XRefreshWindow(display,&windows->image,event);
12836          break;
12837        }
12838      if (event->xexpose.window == windows->pan.id)
12839        if (event->xexpose.count == 0)
12840          {
12841            XDrawPanRectangle(display,windows);
12842            break;
12843          }
12844      if (event->xexpose.window == windows->magnify.id)
12845        if (event->xexpose.count == 0)
12846          {
12847            XMakeMagnifyImage(display,windows,exception);
12848            break;
12849          }
12850      break;
12851    }
12852    case KeyPress:
12853    {
12854      char
12855        command[MagickPathExtent];
12856
12857      KeySym
12858        key_symbol;
12859
12860      if (event->xkey.window != windows->magnify.id)
12861        break;
12862      /*
12863        Respond to a user key press.
12864      */
12865      (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12866        sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12867      XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol,
12868        exception);
12869      break;
12870    }
12871    case MapNotify:
12872    {
12873      if (event->xmap.window == windows->magnify.id)
12874        {
12875          windows->magnify.mapped=MagickTrue;
12876          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12877          break;
12878        }
12879      if (event->xmap.window == windows->info.id)
12880        {
12881          windows->info.mapped=MagickTrue;
12882          break;
12883        }
12884      break;
12885    }
12886    case MotionNotify:
12887    {
12888      while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12889      if (event->xmotion.window == windows->image.id)
12890        if (windows->magnify.mapped != MagickFalse )
12891          {
12892            /*
12893              Update magnified image.
12894            */
12895            x=event->xmotion.x;
12896            y=event->xmotion.y;
12897            if (x < 0)
12898              x=0;
12899            else
12900              if (x >= (int) windows->image.width)
12901                x=(int) (windows->image.width-1);
12902            windows->magnify.x=(int) windows->image.x+x;
12903            if (y < 0)
12904              y=0;
12905            else
12906             if (y >= (int) windows->image.height)
12907               y=(int) (windows->image.height-1);
12908            windows->magnify.y=windows->image.y+y;
12909            XMakeMagnifyImage(display,windows,exception);
12910          }
12911      break;
12912    }
12913    case UnmapNotify:
12914    {
12915      if (event->xunmap.window == windows->magnify.id)
12916        {
12917          windows->magnify.mapped=MagickFalse;
12918          break;
12919        }
12920      if (event->xunmap.window == windows->info.id)
12921        {
12922          windows->info.mapped=MagickFalse;
12923          break;
12924        }
12925      break;
12926    }
12927    default:
12928      break;
12929  }
12930}
12931
12932/*
12933%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12934%                                                                             %
12935%                                                                             %
12936%                                                                             %
12937+   X S e t C r o p G e o m e t r y                                           %
12938%                                                                             %
12939%                                                                             %
12940%                                                                             %
12941%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12942%
12943%  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12944%  and translates it to a cropping geometry relative to the image.
12945%
12946%  The format of the XSetCropGeometry method is:
12947%
12948%      void XSetCropGeometry(Display *display,XWindows *windows,
12949%        RectangleInfo *crop_info,Image *image)
12950%
12951%  A description of each parameter follows:
12952%
12953%    o display: Specifies a connection to an X server; returned from
12954%      XOpenDisplay.
12955%
12956%    o windows: Specifies a pointer to a XWindows structure.
12957%
12958%    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12959%      Image window to crop.
12960%
12961%    o image: the image.
12962%
12963*/
12964static void XSetCropGeometry(Display *display,XWindows *windows,
12965  RectangleInfo *crop_info,Image *image)
12966{
12967  char
12968    text[MagickPathExtent];
12969
12970  int
12971    x,
12972    y;
12973
12974  double
12975    scale_factor;
12976
12977  unsigned int
12978    height,
12979    width;
12980
12981  if (windows->info.mapped != MagickFalse )
12982    {
12983      /*
12984        Display info on cropping rectangle.
12985      */
12986      (void) FormatLocaleString(text,MagickPathExtent," %.20gx%.20g%+.20g%+.20g",
12987        (double) crop_info->width,(double) crop_info->height,(double)
12988        crop_info->x,(double) crop_info->y);
12989      XInfoWidget(display,windows,text);
12990    }
12991  /*
12992    Cropping geometry is relative to any previous crop geometry.
12993  */
12994  x=0;
12995  y=0;
12996  width=(unsigned int) image->columns;
12997  height=(unsigned int) image->rows;
12998  if (windows->image.crop_geometry != (char *) NULL)
12999    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13000  else
13001    windows->image.crop_geometry=AcquireString((char *) NULL);
13002  /*
13003    Define the crop geometry string from the cropping rectangle.
13004  */
13005  scale_factor=(double) width/windows->image.ximage->width;
13006  if (crop_info->x > 0)
13007    x+=(int) (scale_factor*crop_info->x+0.5);
13008  width=(unsigned int) (scale_factor*crop_info->width+0.5);
13009  if (width == 0)
13010    width=1;
13011  scale_factor=(double) height/windows->image.ximage->height;
13012  if (crop_info->y > 0)
13013    y+=(int) (scale_factor*crop_info->y+0.5);
13014  height=(unsigned int) (scale_factor*crop_info->height+0.5);
13015  if (height == 0)
13016    height=1;
13017  (void) FormatLocaleString(windows->image.crop_geometry,MagickPathExtent,
13018    "%ux%u%+d%+d",width,height,x,y);
13019}
13020
13021/*
13022%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13023%                                                                             %
13024%                                                                             %
13025%                                                                             %
13026+   X T i l e I m a g e                                                       %
13027%                                                                             %
13028%                                                                             %
13029%                                                                             %
13030%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13031%
13032%  XTileImage() loads or deletes a selected tile from a visual image directory.
13033%  The load or delete command is chosen from a menu.
13034%
13035%  The format of the XTileImage method is:
13036%
13037%      Image *XTileImage(Display *display,XResourceInfo *resource_info,
13038%        XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13039%
13040%  A description of each parameter follows:
13041%
13042%    o tile_image:  XTileImage reads or deletes the tile image
13043%      and returns it.  A null image is returned if an error occurs.
13044%
13045%    o display: Specifies a connection to an X server;  returned from
13046%      XOpenDisplay.
13047%
13048%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13049%
13050%    o windows: Specifies a pointer to a XWindows structure.
13051%
13052%    o image: the image; returned from ReadImage.
13053%
13054%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
13055%      the entire image is refreshed.
13056%
13057%    o exception: return any errors or warnings in this structure.
13058%
13059*/
13060static Image *XTileImage(Display *display,XResourceInfo *resource_info,
13061  XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13062{
13063  static const char
13064    *VerbMenu[] =
13065    {
13066      "Load",
13067      "Next",
13068      "Former",
13069      "Delete",
13070      "Update",
13071      (char *) NULL,
13072    };
13073
13074  static const ModeType
13075    TileCommands[] =
13076    {
13077      TileLoadCommand,
13078      TileNextCommand,
13079      TileFormerCommand,
13080      TileDeleteCommand,
13081      TileUpdateCommand
13082    };
13083
13084  char
13085    command[MagickPathExtent],
13086    filename[MagickPathExtent];
13087
13088  Image
13089    *tile_image;
13090
13091  int
13092    id,
13093    status,
13094    tile,
13095    x,
13096    y;
13097
13098  double
13099    scale_factor;
13100
13101  register char
13102    *p,
13103    *q;
13104
13105  register int
13106    i;
13107
13108  unsigned int
13109    height,
13110    width;
13111
13112  /*
13113    Tile image is relative to montage image configuration.
13114  */
13115  x=0;
13116  y=0;
13117  width=(unsigned int) image->columns;
13118  height=(unsigned int) image->rows;
13119  if (windows->image.crop_geometry != (char *) NULL)
13120    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13121  scale_factor=(double) width/windows->image.ximage->width;
13122  event->xbutton.x+=windows->image.x;
13123  event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13124  scale_factor=(double) height/windows->image.ximage->height;
13125  event->xbutton.y+=windows->image.y;
13126  event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13127  /*
13128    Determine size and location of each tile in the visual image directory.
13129  */
13130  width=(unsigned int) image->columns;
13131  height=(unsigned int) image->rows;
13132  x=0;
13133  y=0;
13134  (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13135  tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13136    (event->xbutton.x-x)/width;
13137  if (tile < 0)
13138    {
13139      /*
13140        Button press is outside any tile.
13141      */
13142      (void) XBell(display,0);
13143      return((Image *) NULL);
13144    }
13145  /*
13146    Determine file name from the tile directory.
13147  */
13148  p=image->directory;
13149  for (i=tile; (i != 0) && (*p != '\0'); )
13150  {
13151    if (*p == '\n')
13152      i--;
13153    p++;
13154  }
13155  if (*p == '\0')
13156    {
13157      /*
13158        Button press is outside any tile.
13159      */
13160      (void) XBell(display,0);
13161      return((Image *) NULL);
13162    }
13163  /*
13164    Select a command from the pop-up menu.
13165  */
13166  id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13167  if (id < 0)
13168    return((Image *) NULL);
13169  q=p;
13170  while ((*q != '\n') && (*q != '\0'))
13171    q++;
13172  (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13173  /*
13174    Perform command for the selected tile.
13175  */
13176  XSetCursorState(display,windows,MagickTrue);
13177  XCheckRefreshWindows(display,windows);
13178  tile_image=NewImageList();
13179  switch (TileCommands[id])
13180  {
13181    case TileLoadCommand:
13182    {
13183      /*
13184        Load tile image.
13185      */
13186      XCheckRefreshWindows(display,windows);
13187      (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13188        MagickPathExtent);
13189      (void) CopyMagickString(resource_info->image_info->filename,filename,
13190        MagickPathExtent);
13191      tile_image=ReadImage(resource_info->image_info,exception);
13192      CatchException(exception);
13193      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13194      break;
13195    }
13196    case TileNextCommand:
13197    {
13198      /*
13199        Display next image.
13200      */
13201      XClientMessage(display,windows->image.id,windows->im_protocols,
13202        windows->im_next_image,CurrentTime);
13203      break;
13204    }
13205    case TileFormerCommand:
13206    {
13207      /*
13208        Display former image.
13209      */
13210      XClientMessage(display,windows->image.id,windows->im_protocols,
13211        windows->im_former_image,CurrentTime);
13212      break;
13213    }
13214    case TileDeleteCommand:
13215    {
13216      /*
13217        Delete tile image.
13218      */
13219      if (IsPathAccessible(filename) == MagickFalse)
13220        {
13221          XNoticeWidget(display,windows,"Image file does not exist:",filename);
13222          break;
13223        }
13224      status=XConfirmWidget(display,windows,"Really delete tile",filename);
13225      if (status <= 0)
13226        break;
13227      status=ShredFile(filename);
13228      if (status != MagickFalse )
13229        {
13230          XNoticeWidget(display,windows,"Unable to delete image file:",
13231            filename);
13232          break;
13233        }
13234    }
13235    case TileUpdateCommand:
13236    {
13237      int
13238        x_offset,
13239        y_offset;
13240
13241      PixelInfo
13242        pixel;
13243
13244      register int
13245        j;
13246
13247      register Quantum
13248        *s;
13249
13250      /*
13251        Ensure all the images exist.
13252      */
13253      tile=0;
13254      GetPixelInfo(image,&pixel);
13255      for (p=image->directory; *p != '\0'; p++)
13256      {
13257        CacheView
13258          *image_view;
13259
13260        q=p;
13261        while ((*q != '\n') && (*q != '\0'))
13262          q++;
13263        (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13264        p=q;
13265        if (IsPathAccessible(filename) != MagickFalse )
13266          {
13267            tile++;
13268            continue;
13269          }
13270        /*
13271          Overwrite tile with background color.
13272        */
13273        x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13274        y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13275        image_view=AcquireAuthenticCacheView(image,exception);
13276        (void) GetOneCacheViewVirtualPixelInfo(image_view,0,0,&pixel,exception);
13277        for (i=0; i < (int) height; i++)
13278        {
13279          s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13280            y_offset+i,width,1,exception);
13281          if (s == (Quantum *) NULL)
13282            break;
13283          for (j=0; j < (int) width; j++)
13284          {
13285            SetPixelViaPixelInfo(image,&pixel,s);
13286            s+=GetPixelChannels(image);
13287          }
13288          if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13289            break;
13290        }
13291        image_view=DestroyCacheView(image_view);
13292        tile++;
13293      }
13294      windows->image.window_changes.width=(int) image->columns;
13295      windows->image.window_changes.height=(int) image->rows;
13296      XConfigureImageColormap(display,resource_info,windows,image,exception);
13297      (void) XConfigureImage(display,resource_info,windows,image,exception);
13298      break;
13299    }
13300    default:
13301      break;
13302  }
13303  XSetCursorState(display,windows,MagickFalse);
13304  return(tile_image);
13305}
13306
13307/*
13308%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13309%                                                                             %
13310%                                                                             %
13311%                                                                             %
13312+   X T r a n s l a t e I m a g e                                             %
13313%                                                                             %
13314%                                                                             %
13315%                                                                             %
13316%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13317%
13318%  XTranslateImage() translates the image within an Image window by one pixel
13319%  as specified by the key symbol.  If the image has a montage string the
13320%  translation is respect to the width and height contained within the string.
13321%
13322%  The format of the XTranslateImage method is:
13323%
13324%      void XTranslateImage(Display *display,XWindows *windows,
13325%        Image *image,const KeySym key_symbol)
13326%
13327%  A description of each parameter follows:
13328%
13329%    o display: Specifies a connection to an X server; returned from
13330%      XOpenDisplay.
13331%
13332%    o windows: Specifies a pointer to a XWindows structure.
13333%
13334%    o image: the image.
13335%
13336%    o key_symbol: Specifies a KeySym which indicates which side of the image
13337%      to trim.
13338%
13339*/
13340static void XTranslateImage(Display *display,XWindows *windows,
13341  Image *image,const KeySym key_symbol)
13342{
13343  char
13344    text[MagickPathExtent];
13345
13346  int
13347    x,
13348    y;
13349
13350  unsigned int
13351    x_offset,
13352    y_offset;
13353
13354  /*
13355    User specified a pan position offset.
13356  */
13357  x_offset=windows->image.width;
13358  y_offset=windows->image.height;
13359  if (image->montage != (char *) NULL)
13360    (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13361  switch ((int) key_symbol)
13362  {
13363    case XK_Home:
13364    case XK_KP_Home:
13365    {
13366      windows->image.x=(int) windows->image.width/2;
13367      windows->image.y=(int) windows->image.height/2;
13368      break;
13369    }
13370    case XK_Left:
13371    case XK_KP_Left:
13372    {
13373      windows->image.x-=x_offset;
13374      break;
13375    }
13376    case XK_Next:
13377    case XK_Up:
13378    case XK_KP_Up:
13379    {
13380      windows->image.y-=y_offset;
13381      break;
13382    }
13383    case XK_Right:
13384    case XK_KP_Right:
13385    {
13386      windows->image.x+=x_offset;
13387      break;
13388    }
13389    case XK_Prior:
13390    case XK_Down:
13391    case XK_KP_Down:
13392    {
13393      windows->image.y+=y_offset;
13394      break;
13395    }
13396    default:
13397      return;
13398  }
13399  /*
13400    Check boundary conditions.
13401  */
13402  if (windows->image.x < 0)
13403    windows->image.x=0;
13404  else
13405    if ((int) (windows->image.x+windows->image.width) >
13406        windows->image.ximage->width)
13407      windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13408  if (windows->image.y < 0)
13409    windows->image.y=0;
13410  else
13411    if ((int) (windows->image.y+windows->image.height) >
13412        windows->image.ximage->height)
13413      windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13414  /*
13415    Refresh Image window.
13416  */
13417  (void) FormatLocaleString(text,MagickPathExtent," %ux%u%+d%+d ",
13418    windows->image.width,windows->image.height,windows->image.x,
13419    windows->image.y);
13420  XInfoWidget(display,windows,text);
13421  XCheckRefreshWindows(display,windows);
13422  XDrawPanRectangle(display,windows);
13423  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13424  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13425}
13426
13427/*
13428%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13429%                                                                             %
13430%                                                                             %
13431%                                                                             %
13432+   X T r i m I m a g e                                                       %
13433%                                                                             %
13434%                                                                             %
13435%                                                                             %
13436%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13437%
13438%  XTrimImage() trims the edges from the Image window.
13439%
13440%  The format of the XTrimImage method is:
13441%
13442%      MagickBooleanType XTrimImage(Display *display,
13443%        XResourceInfo *resource_info,XWindows *windows,Image *image,
13444%        ExceptionInfo *exception)
13445%
13446%  A description of each parameter follows:
13447%
13448%    o display: Specifies a connection to an X server; returned from
13449%      XOpenDisplay.
13450%
13451%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13452%
13453%    o windows: Specifies a pointer to a XWindows structure.
13454%
13455%    o image: the image.
13456%
13457%    o exception: return any errors or warnings in this structure.
13458%
13459*/
13460static MagickBooleanType XTrimImage(Display *display,
13461  XResourceInfo *resource_info,XWindows *windows,Image *image,
13462  ExceptionInfo *exception)
13463{
13464  RectangleInfo
13465    trim_info;
13466
13467  register int
13468    x,
13469    y;
13470
13471  size_t
13472    background,
13473    pixel;
13474
13475  /*
13476    Trim edges from image.
13477  */
13478  XSetCursorState(display,windows,MagickTrue);
13479  XCheckRefreshWindows(display,windows);
13480  /*
13481    Crop the left edge.
13482  */
13483  background=XGetPixel(windows->image.ximage,0,0);
13484  trim_info.width=(size_t) windows->image.ximage->width;
13485  for (x=0; x < windows->image.ximage->width; x++)
13486  {
13487    for (y=0; y < windows->image.ximage->height; y++)
13488    {
13489      pixel=XGetPixel(windows->image.ximage,x,y);
13490      if (pixel != background)
13491        break;
13492    }
13493    if (y < windows->image.ximage->height)
13494      break;
13495  }
13496  trim_info.x=(ssize_t) x;
13497  if (trim_info.x == (ssize_t) windows->image.ximage->width)
13498    {
13499      XSetCursorState(display,windows,MagickFalse);
13500      return(MagickFalse);
13501    }
13502  /*
13503    Crop the right edge.
13504  */
13505  background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13506  for (x=windows->image.ximage->width-1; x != 0; x--)
13507  {
13508    for (y=0; y < windows->image.ximage->height; y++)
13509    {
13510      pixel=XGetPixel(windows->image.ximage,x,y);
13511      if (pixel != background)
13512        break;
13513    }
13514    if (y < windows->image.ximage->height)
13515      break;
13516  }
13517  trim_info.width=(size_t) (x-trim_info.x+1);
13518  /*
13519    Crop the top edge.
13520  */
13521  background=XGetPixel(windows->image.ximage,0,0);
13522  trim_info.height=(size_t) windows->image.ximage->height;
13523  for (y=0; y < windows->image.ximage->height; y++)
13524  {
13525    for (x=0; x < windows->image.ximage->width; x++)
13526    {
13527      pixel=XGetPixel(windows->image.ximage,x,y);
13528      if (pixel != background)
13529        break;
13530    }
13531    if (x < windows->image.ximage->width)
13532      break;
13533  }
13534  trim_info.y=(ssize_t) y;
13535  /*
13536    Crop the bottom edge.
13537  */
13538  background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13539  for (y=windows->image.ximage->height-1; y != 0; y--)
13540  {
13541    for (x=0; x < windows->image.ximage->width; x++)
13542    {
13543      pixel=XGetPixel(windows->image.ximage,x,y);
13544      if (pixel != background)
13545        break;
13546    }
13547    if (x < windows->image.ximage->width)
13548      break;
13549  }
13550  trim_info.height=(size_t) y-trim_info.y+1;
13551  if (((unsigned int) trim_info.width != windows->image.width) ||
13552      ((unsigned int) trim_info.height != windows->image.height))
13553    {
13554      /*
13555        Reconfigure Image window as defined by the trimming rectangle.
13556      */
13557      XSetCropGeometry(display,windows,&trim_info,image);
13558      windows->image.window_changes.width=(int) trim_info.width;
13559      windows->image.window_changes.height=(int) trim_info.height;
13560      (void) XConfigureImage(display,resource_info,windows,image,exception);
13561    }
13562  XSetCursorState(display,windows,MagickFalse);
13563  return(MagickTrue);
13564}
13565
13566/*
13567%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13568%                                                                             %
13569%                                                                             %
13570%                                                                             %
13571+   X V i s u a l D i r e c t o r y I m a g e                                 %
13572%                                                                             %
13573%                                                                             %
13574%                                                                             %
13575%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13576%
13577%  XVisualDirectoryImage() creates a Visual Image Directory.
13578%
13579%  The format of the XVisualDirectoryImage method is:
13580%
13581%      Image *XVisualDirectoryImage(Display *display,
13582%        XResourceInfo *resource_info,XWindows *windows,
13583%        ExceptionInfo *exception)
13584%
13585%  A description of each parameter follows:
13586%
13587%    o display: Specifies a connection to an X server; returned from
13588%      XOpenDisplay.
13589%
13590%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13591%
13592%    o windows: Specifies a pointer to a XWindows structure.
13593%
13594%    o exception: return any errors or warnings in this structure.
13595%
13596*/
13597static Image *XVisualDirectoryImage(Display *display,
13598  XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
13599{
13600#define TileImageTag  "Scale/Image"
13601#define XClientName  "montage"
13602
13603  char
13604    **filelist;
13605
13606  Image
13607    *images,
13608    *montage_image,
13609    *next_image,
13610    *thumbnail_image;
13611
13612  ImageInfo
13613    *read_info;
13614
13615  int
13616    number_files;
13617
13618  MagickBooleanType
13619    backdrop;
13620
13621  MagickStatusType
13622    status;
13623
13624  MontageInfo
13625    *montage_info;
13626
13627  RectangleInfo
13628    geometry;
13629
13630  register int
13631    i;
13632
13633  static char
13634    filename[MagickPathExtent] = "\0",
13635    filenames[MagickPathExtent] = "*";
13636
13637  XResourceInfo
13638    background_resources;
13639
13640  /*
13641    Request file name from user.
13642  */
13643  XFileBrowserWidget(display,windows,"Directory",filenames);
13644  if (*filenames == '\0')
13645    return((Image *) NULL);
13646  /*
13647    Expand the filenames.
13648  */
13649  filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13650  if (filelist == (char **) NULL)
13651    {
13652      ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13653        filenames);
13654      return((Image *) NULL);
13655    }
13656  number_files=1;
13657  filelist[0]=filenames;
13658  status=ExpandFilenames(&number_files,&filelist);
13659  if ((status == MagickFalse) || (number_files == 0))
13660    {
13661      if (number_files == 0)
13662        ThrowXWindowException(ImageError,"NoImagesWereFound",filenames)
13663      else
13664        ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13665          filenames);
13666      return((Image *) NULL);
13667    }
13668  /*
13669    Set image background resources.
13670  */
13671  background_resources=(*resource_info);
13672  background_resources.window_id=AcquireString("");
13673  (void) FormatLocaleString(background_resources.window_id,MagickPathExtent,
13674    "0x%lx",windows->image.id);
13675  background_resources.backdrop=MagickTrue;
13676  /*
13677    Read each image and convert them to a tile.
13678  */
13679  backdrop=((windows->visual_info->klass == TrueColor) ||
13680    (windows->visual_info->klass == DirectColor)) ? MagickTrue : MagickFalse;
13681  read_info=CloneImageInfo(resource_info->image_info);
13682  (void) SetImageOption(read_info,"jpeg:size","120x120");
13683  (void) CloneString(&read_info->size,DefaultTileGeometry);
13684  (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13685    (void *) NULL);
13686  images=NewImageList();
13687  XSetCursorState(display,windows,MagickTrue);
13688  XCheckRefreshWindows(display,windows);
13689  for (i=0; i < (int) number_files; i++)
13690  {
13691    (void) CopyMagickString(read_info->filename,filelist[i],MagickPathExtent);
13692    filelist[i]=DestroyString(filelist[i]);
13693    *read_info->magick='\0';
13694    next_image=ReadImage(read_info,exception);
13695    CatchException(exception);
13696    if (next_image != (Image *) NULL)
13697      {
13698        (void) DeleteImageProperty(next_image,"label");
13699        (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13700          read_info,next_image,DefaultTileLabel,exception),exception);
13701        (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13702          exception);
13703        thumbnail_image=ThumbnailImage(next_image,geometry.width,
13704          geometry.height,exception);
13705        if (thumbnail_image != (Image *) NULL)
13706          {
13707            next_image=DestroyImage(next_image);
13708            next_image=thumbnail_image;
13709          }
13710        if (backdrop)
13711          {
13712            (void) XDisplayBackgroundImage(display,&background_resources,
13713              next_image,exception);
13714            XSetCursorState(display,windows,MagickTrue);
13715          }
13716        AppendImageToList(&images,next_image);
13717        if (images->progress_monitor != (MagickProgressMonitor) NULL)
13718          {
13719            MagickBooleanType
13720              proceed;
13721
13722            proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13723              (MagickSizeType) number_files);
13724            if (proceed == MagickFalse)
13725              break;
13726          }
13727      }
13728  }
13729  filelist=(char **) RelinquishMagickMemory(filelist);
13730  if (images == (Image *) NULL)
13731    {
13732      read_info=DestroyImageInfo(read_info);
13733      XSetCursorState(display,windows,MagickFalse);
13734      ThrowXWindowException(ImageError,"NoImagesWereLoaded",filenames);
13735      return((Image *) NULL);
13736    }
13737  /*
13738    Create the Visual Image Directory.
13739  */
13740  montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13741  montage_info->pointsize=10;
13742  if (resource_info->font != (char *) NULL)
13743    (void) CloneString(&montage_info->font,resource_info->font);
13744  (void) CopyMagickString(montage_info->filename,filename,MagickPathExtent);
13745  montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13746    images),exception);
13747  images=DestroyImageList(images);
13748  montage_info=DestroyMontageInfo(montage_info);
13749  read_info=DestroyImageInfo(read_info);
13750  XSetCursorState(display,windows,MagickFalse);
13751  if (montage_image == (Image *) NULL)
13752    return(montage_image);
13753  XClientMessage(display,windows->image.id,windows->im_protocols,
13754    windows->im_next_image,CurrentTime);
13755  return(montage_image);
13756}
13757
13758/*
13759%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13760%                                                                             %
13761%                                                                             %
13762%                                                                             %
13763%   X D i s p l a y B a c k g r o u n d I m a g e                             %
13764%                                                                             %
13765%                                                                             %
13766%                                                                             %
13767%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13768%
13769%  XDisplayBackgroundImage() displays an image in the background of a window.
13770%
13771%  The format of the XDisplayBackgroundImage method is:
13772%
13773%      MagickBooleanType XDisplayBackgroundImage(Display *display,
13774%        XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13775%
13776%  A description of each parameter follows:
13777%
13778%    o display: Specifies a connection to an X server;  returned from
13779%      XOpenDisplay.
13780%
13781%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13782%
13783%    o image: the image.
13784%
13785%    o exception: return any errors or warnings in this structure.
13786%
13787*/
13788MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13789  XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13790{
13791  char
13792    geometry[MagickPathExtent],
13793    visual_type[MagickPathExtent];
13794
13795  int
13796    height,
13797    status,
13798    width;
13799
13800  RectangleInfo
13801    geometry_info;
13802
13803  static XPixelInfo
13804    pixel;
13805
13806  static XStandardColormap
13807    *map_info;
13808
13809  static XVisualInfo
13810    *visual_info = (XVisualInfo *) NULL;
13811
13812  static XWindowInfo
13813    window_info;
13814
13815  size_t
13816    delay;
13817
13818  Window
13819    root_window;
13820
13821  XGCValues
13822    context_values;
13823
13824  XResourceInfo
13825    resources;
13826
13827  XWindowAttributes
13828    window_attributes;
13829
13830  /*
13831    Determine target window.
13832  */
13833  assert(image != (Image *) NULL);
13834  assert(image->signature == MagickCoreSignature);
13835  if (image->debug != MagickFalse )
13836    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13837  resources=(*resource_info);
13838  window_info.id=(Window) NULL;
13839  root_window=XRootWindow(display,XDefaultScreen(display));
13840  if (LocaleCompare(resources.window_id,"root") == 0)
13841    window_info.id=root_window;
13842  else
13843    {
13844      if (isdigit((int) ((unsigned char) *resources.window_id)) != 0)
13845        window_info.id=XWindowByID(display,root_window,
13846          (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13847      if (window_info.id == (Window) NULL)
13848        window_info.id=XWindowByName(display,root_window,resources.window_id);
13849    }
13850  if (window_info.id == (Window) NULL)
13851    {
13852      ThrowXWindowException(XServerError,"NoWindowWithSpecifiedIDExists",
13853        resources.window_id);
13854      return(MagickFalse);
13855    }
13856  /*
13857    Determine window visual id.
13858  */
13859  window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13860  window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13861  (void) CopyMagickString(visual_type,"default",MagickPathExtent);
13862  status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13863  if (status != 0)
13864    (void) FormatLocaleString(visual_type,MagickPathExtent,"0x%lx",
13865      XVisualIDFromVisual(window_attributes.visual));
13866  if (visual_info == (XVisualInfo *) NULL)
13867    {
13868      /*
13869        Allocate standard colormap.
13870      */
13871      map_info=XAllocStandardColormap();
13872      if (map_info == (XStandardColormap *) NULL)
13873        ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13874          image->filename);
13875      map_info->colormap=(Colormap) NULL;
13876      pixel.pixels=(unsigned long *) NULL;
13877      /*
13878        Initialize visual info.
13879      */
13880      resources.map_type=(char *) NULL;
13881      resources.visual_type=visual_type;
13882      visual_info=XBestVisualInfo(display,map_info,&resources);
13883      if (visual_info == (XVisualInfo *) NULL)
13884        ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13885          resources.visual_type);
13886      /*
13887        Initialize window info.
13888      */
13889      window_info.ximage=(XImage *) NULL;
13890      window_info.matte_image=(XImage *) NULL;
13891      window_info.pixmap=(Pixmap) NULL;
13892      window_info.matte_pixmap=(Pixmap) NULL;
13893    }
13894  /*
13895    Free previous root colors.
13896  */
13897  if (window_info.id == root_window)
13898    (void) XDestroyWindowColors(display,root_window);
13899  /*
13900    Initialize Standard Colormap.
13901  */
13902  resources.colormap=SharedColormap;
13903  XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel,
13904    exception);
13905  /*
13906    Graphic context superclass.
13907  */
13908  context_values.background=pixel.background_color.pixel;
13909  context_values.foreground=pixel.foreground_color.pixel;
13910  pixel.annotate_context=XCreateGC(display,window_info.id,
13911    (size_t) (GCBackground | GCForeground),&context_values);
13912  if (pixel.annotate_context == (GC) NULL)
13913    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13914      image->filename);
13915  /*
13916    Initialize Image window attributes.
13917  */
13918  window_info.name=AcquireString("\0");
13919  window_info.icon_name=AcquireString("\0");
13920  XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13921    &resources,&window_info);
13922  /*
13923    Create the X image.
13924  */
13925  window_info.width=(unsigned int) image->columns;
13926  window_info.height=(unsigned int) image->rows;
13927  if ((image->columns != window_info.width) ||
13928      (image->rows != window_info.height))
13929    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13930      image->filename);
13931  (void) FormatLocaleString(geometry,MagickPathExtent,"%ux%u+0+0>",
13932    window_attributes.width,window_attributes.height);
13933  geometry_info.width=window_info.width;
13934  geometry_info.height=window_info.height;
13935  geometry_info.x=(ssize_t) window_info.x;
13936  geometry_info.y=(ssize_t) window_info.y;
13937  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13938    &geometry_info.width,&geometry_info.height);
13939  window_info.width=(unsigned int) geometry_info.width;
13940  window_info.height=(unsigned int) geometry_info.height;
13941  window_info.x=(int) geometry_info.x;
13942  window_info.y=(int) geometry_info.y;
13943  status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13944    window_info.height,exception);
13945  if (status == MagickFalse)
13946    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13947      image->filename);
13948  window_info.x=0;
13949  window_info.y=0;
13950  if (image->debug != MagickFalse )
13951    {
13952      (void) LogMagickEvent(X11Event,GetMagickModule(),
13953        "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13954        (double) image->columns,(double) image->rows);
13955      if (image->colors != 0)
13956        (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13957          image->colors);
13958      (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13959    }
13960  /*
13961    Adjust image dimensions as specified by backdrop or geometry options.
13962  */
13963  width=(int) window_info.width;
13964  height=(int) window_info.height;
13965  if (resources.backdrop != MagickFalse )
13966    {
13967      /*
13968        Center image on window.
13969      */
13970      window_info.x=(window_attributes.width/2)-
13971        (window_info.ximage->width/2);
13972      window_info.y=(window_attributes.height/2)-
13973        (window_info.ximage->height/2);
13974      width=window_attributes.width;
13975      height=window_attributes.height;
13976    }
13977  if ((resources.image_geometry != (char *) NULL) &&
13978      (*resources.image_geometry != '\0'))
13979    {
13980      char
13981        default_geometry[MagickPathExtent];
13982
13983      int
13984        flags,
13985        gravity;
13986
13987      XSizeHints
13988        *size_hints;
13989
13990      /*
13991        User specified geometry.
13992      */
13993      size_hints=XAllocSizeHints();
13994      if (size_hints == (XSizeHints *) NULL)
13995        ThrowXWindowFatalException(ResourceLimitFatalError,
13996          "MemoryAllocationFailed",image->filename);
13997      size_hints->flags=0L;
13998      (void) FormatLocaleString(default_geometry,MagickPathExtent,"%dx%d",
13999        width,height);
14000      flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
14001        default_geometry,window_info.border_width,size_hints,&window_info.x,
14002        &window_info.y,&width,&height,&gravity);
14003      if (flags & (XValue | YValue))
14004        {
14005          width=window_attributes.width;
14006          height=window_attributes.height;
14007        }
14008      (void) XFree((void *) size_hints);
14009    }
14010  /*
14011    Create the X pixmap.
14012  */
14013  window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
14014    (unsigned int) height,window_info.depth);
14015  if (window_info.pixmap == (Pixmap) NULL)
14016    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
14017      image->filename);
14018  /*
14019    Display pixmap on the window.
14020  */
14021  if (((unsigned int) width > window_info.width) ||
14022      ((unsigned int) height > window_info.height))
14023    (void) XFillRectangle(display,window_info.pixmap,
14024      window_info.annotate_context,0,0,(unsigned int) width,
14025      (unsigned int) height);
14026  (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
14027    window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
14028    window_info.width,(unsigned int) window_info.height);
14029  (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
14030  (void) XClearWindow(display,window_info.id);
14031  delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
14032  XDelay(display,delay == 0UL ? 10UL : delay);
14033  (void) XSync(display,MagickFalse);
14034  return(window_info.id == root_window ? MagickTrue : MagickFalse);
14035}
14036
14037/*
14038%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14039%                                                                             %
14040%                                                                             %
14041%                                                                             %
14042+   X D i s p l a y I m a g e                                                 %
14043%                                                                             %
14044%                                                                             %
14045%                                                                             %
14046%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14047%
14048%  XDisplayImage() displays an image via X11.  A new image is created and
14049%  returned if the user interactively transforms the displayed image.
14050%
14051%  The format of the XDisplayImage method is:
14052%
14053%      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14054%        char **argv,int argc,Image **image,size_t *state,
14055%        ExceptionInfo *exception)
14056%
14057%  A description of each parameter follows:
14058%
14059%    o nexus:  Method XDisplayImage returns an image when the
14060%      user chooses 'Open Image' from the command menu or picks a tile
14061%      from the image directory.  Otherwise a null image is returned.
14062%
14063%    o display: Specifies a connection to an X server;  returned from
14064%      XOpenDisplay.
14065%
14066%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14067%
14068%    o argv: Specifies the application's argument list.
14069%
14070%    o argc: Specifies the number of arguments.
14071%
14072%    o image: Specifies an address to an address of an Image structure;
14073%
14074%    o exception: return any errors or warnings in this structure.
14075%
14076*/
14077MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14078  char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
14079{
14080#define MagnifySize  256  /* must be a power of 2 */
14081#define MagickMenus  10
14082#define MagickTitle  "Commands"
14083
14084  static const char
14085    *CommandMenu[] =
14086    {
14087      "File",
14088      "Edit",
14089      "View",
14090      "Transform",
14091      "Enhance",
14092      "Effects",
14093      "F/X",
14094      "Image Edit",
14095      "Miscellany",
14096      "Help",
14097      (char *) NULL
14098    },
14099    *FileMenu[] =
14100    {
14101      "Open...",
14102      "Next",
14103      "Former",
14104      "Select...",
14105      "Save...",
14106      "Print...",
14107      "Delete...",
14108      "New...",
14109      "Visual Directory...",
14110      "Quit",
14111      (char *) NULL
14112    },
14113    *EditMenu[] =
14114    {
14115      "Undo",
14116      "Redo",
14117      "Cut",
14118      "Copy",
14119      "Paste",
14120      (char *) NULL
14121    },
14122    *ViewMenu[] =
14123    {
14124      "Half Size",
14125      "Original Size",
14126      "Double Size",
14127      "Resize...",
14128      "Apply",
14129      "Refresh",
14130      "Restore",
14131      (char *) NULL
14132    },
14133    *TransformMenu[] =
14134    {
14135      "Crop",
14136      "Chop",
14137      "Flop",
14138      "Flip",
14139      "Rotate Right",
14140      "Rotate Left",
14141      "Rotate...",
14142      "Shear...",
14143      "Roll...",
14144      "Trim Edges",
14145      (char *) NULL
14146    },
14147    *EnhanceMenu[] =
14148    {
14149      "Hue...",
14150      "Saturation...",
14151      "Brightness...",
14152      "Gamma...",
14153      "Spiff",
14154      "Dull",
14155      "Contrast Stretch...",
14156      "Sigmoidal Contrast...",
14157      "Normalize",
14158      "Equalize",
14159      "Negate",
14160      "Grayscale",
14161      "Map...",
14162      "Quantize...",
14163      (char *) NULL
14164    },
14165    *EffectsMenu[] =
14166    {
14167      "Despeckle",
14168      "Emboss",
14169      "Reduce Noise",
14170      "Add Noise...",
14171      "Sharpen...",
14172      "Blur...",
14173      "Threshold...",
14174      "Edge Detect...",
14175      "Spread...",
14176      "Shade...",
14177      "Raise...",
14178      "Segment...",
14179      (char *) NULL
14180    },
14181    *FXMenu[] =
14182    {
14183      "Solarize...",
14184      "Sepia Tone...",
14185      "Swirl...",
14186      "Implode...",
14187      "Vignette...",
14188      "Wave...",
14189      "Oil Paint...",
14190      "Charcoal Draw...",
14191      (char *) NULL
14192    },
14193    *ImageEditMenu[] =
14194    {
14195      "Annotate...",
14196      "Draw...",
14197      "Color...",
14198      "Matte...",
14199      "Composite...",
14200      "Add Border...",
14201      "Add Frame...",
14202      "Comment...",
14203      "Launch...",
14204      "Region of Interest...",
14205      (char *) NULL
14206    },
14207    *MiscellanyMenu[] =
14208    {
14209      "Image Info",
14210      "Zoom Image",
14211      "Show Preview...",
14212      "Show Histogram",
14213      "Show Matte",
14214      "Background...",
14215      "Slide Show...",
14216      "Preferences...",
14217      (char *) NULL
14218    },
14219    *HelpMenu[] =
14220    {
14221      "Overview",
14222      "Browse Documentation",
14223      "About Display",
14224      (char *) NULL
14225    },
14226    *ShortCutsMenu[] =
14227    {
14228      "Next",
14229      "Former",
14230      "Open...",
14231      "Save...",
14232      "Print...",
14233      "Undo",
14234      "Restore",
14235      "Image Info",
14236      "Quit",
14237      (char *) NULL
14238    },
14239    *VirtualMenu[] =
14240    {
14241      "Image Info",
14242      "Print",
14243      "Next",
14244      "Quit",
14245      (char *) NULL
14246    };
14247
14248  static const char
14249    **Menus[MagickMenus] =
14250    {
14251      FileMenu,
14252      EditMenu,
14253      ViewMenu,
14254      TransformMenu,
14255      EnhanceMenu,
14256      EffectsMenu,
14257      FXMenu,
14258      ImageEditMenu,
14259      MiscellanyMenu,
14260      HelpMenu
14261    };
14262
14263  static CommandType
14264    CommandMenus[] =
14265    {
14266      NullCommand,
14267      NullCommand,
14268      NullCommand,
14269      NullCommand,
14270      NullCommand,
14271      NullCommand,
14272      NullCommand,
14273      NullCommand,
14274      NullCommand,
14275      NullCommand,
14276    },
14277    FileCommands[] =
14278    {
14279      OpenCommand,
14280      NextCommand,
14281      FormerCommand,
14282      SelectCommand,
14283      SaveCommand,
14284      PrintCommand,
14285      DeleteCommand,
14286      NewCommand,
14287      VisualDirectoryCommand,
14288      QuitCommand
14289    },
14290    EditCommands[] =
14291    {
14292      UndoCommand,
14293      RedoCommand,
14294      CutCommand,
14295      CopyCommand,
14296      PasteCommand
14297    },
14298    ViewCommands[] =
14299    {
14300      HalfSizeCommand,
14301      OriginalSizeCommand,
14302      DoubleSizeCommand,
14303      ResizeCommand,
14304      ApplyCommand,
14305      RefreshCommand,
14306      RestoreCommand
14307    },
14308    TransformCommands[] =
14309    {
14310      CropCommand,
14311      ChopCommand,
14312      FlopCommand,
14313      FlipCommand,
14314      RotateRightCommand,
14315      RotateLeftCommand,
14316      RotateCommand,
14317      ShearCommand,
14318      RollCommand,
14319      TrimCommand
14320    },
14321    EnhanceCommands[] =
14322    {
14323      HueCommand,
14324      SaturationCommand,
14325      BrightnessCommand,
14326      GammaCommand,
14327      SpiffCommand,
14328      DullCommand,
14329      ContrastStretchCommand,
14330      SigmoidalContrastCommand,
14331      NormalizeCommand,
14332      EqualizeCommand,
14333      NegateCommand,
14334      GrayscaleCommand,
14335      MapCommand,
14336      QuantizeCommand
14337    },
14338    EffectsCommands[] =
14339    {
14340      DespeckleCommand,
14341      EmbossCommand,
14342      ReduceNoiseCommand,
14343      AddNoiseCommand,
14344      SharpenCommand,
14345      BlurCommand,
14346      ThresholdCommand,
14347      EdgeDetectCommand,
14348      SpreadCommand,
14349      ShadeCommand,
14350      RaiseCommand,
14351      SegmentCommand
14352    },
14353    FXCommands[] =
14354    {
14355      SolarizeCommand,
14356      SepiaToneCommand,
14357      SwirlCommand,
14358      ImplodeCommand,
14359      VignetteCommand,
14360      WaveCommand,
14361      OilPaintCommand,
14362      CharcoalDrawCommand
14363    },
14364    ImageEditCommands[] =
14365    {
14366      AnnotateCommand,
14367      DrawCommand,
14368      ColorCommand,
14369      MatteCommand,
14370      CompositeCommand,
14371      AddBorderCommand,
14372      AddFrameCommand,
14373      CommentCommand,
14374      LaunchCommand,
14375      RegionofInterestCommand
14376    },
14377    MiscellanyCommands[] =
14378    {
14379      InfoCommand,
14380      ZoomCommand,
14381      ShowPreviewCommand,
14382      ShowHistogramCommand,
14383      ShowMatteCommand,
14384      BackgroundCommand,
14385      SlideShowCommand,
14386      PreferencesCommand
14387    },
14388    HelpCommands[] =
14389    {
14390      HelpCommand,
14391      BrowseDocumentationCommand,
14392      VersionCommand
14393    },
14394    ShortCutsCommands[] =
14395    {
14396      NextCommand,
14397      FormerCommand,
14398      OpenCommand,
14399      SaveCommand,
14400      PrintCommand,
14401      UndoCommand,
14402      RestoreCommand,
14403      InfoCommand,
14404      QuitCommand
14405    },
14406    VirtualCommands[] =
14407    {
14408      InfoCommand,
14409      PrintCommand,
14410      NextCommand,
14411      QuitCommand
14412    };
14413
14414  static CommandType
14415    *Commands[MagickMenus] =
14416    {
14417      FileCommands,
14418      EditCommands,
14419      ViewCommands,
14420      TransformCommands,
14421      EnhanceCommands,
14422      EffectsCommands,
14423      FXCommands,
14424      ImageEditCommands,
14425      MiscellanyCommands,
14426      HelpCommands
14427    };
14428
14429  char
14430    command[MagickPathExtent],
14431    *directory,
14432    geometry[MagickPathExtent],
14433    resource_name[MagickPathExtent];
14434
14435  CommandType
14436    command_type;
14437
14438  Image
14439    *display_image,
14440    *nexus;
14441
14442  int
14443    entry,
14444    id;
14445
14446  KeySym
14447    key_symbol;
14448
14449  MagickStatusType
14450    context_mask,
14451    status;
14452
14453  RectangleInfo
14454    geometry_info;
14455
14456  register int
14457    i;
14458
14459  static char
14460    working_directory[MagickPathExtent];
14461
14462  static XPoint
14463    vid_info;
14464
14465  static XWindowInfo
14466    *magick_windows[MaxXWindows];
14467
14468  static unsigned int
14469    number_windows;
14470
14471  struct stat
14472    attributes;
14473
14474  time_t
14475    timer,
14476    timestamp,
14477    update_time;
14478
14479  unsigned int
14480    height,
14481    width;
14482
14483  size_t
14484    delay;
14485
14486  WarningHandler
14487    warning_handler;
14488
14489  Window
14490    root_window;
14491
14492  XClassHint
14493    *class_hints;
14494
14495  XEvent
14496    event;
14497
14498  XFontStruct
14499    *font_info;
14500
14501  XGCValues
14502    context_values;
14503
14504  XPixelInfo
14505    *icon_pixel,
14506    *pixel;
14507
14508  XResourceInfo
14509    *icon_resources;
14510
14511  XStandardColormap
14512    *icon_map,
14513    *map_info;
14514
14515  XVisualInfo
14516    *icon_visual,
14517    *visual_info;
14518
14519  XWindowChanges
14520    window_changes;
14521
14522  XWindows
14523    *windows;
14524
14525  XWMHints
14526    *manager_hints;
14527
14528  assert(image != (Image **) NULL);
14529  assert((*image)->signature == MagickCoreSignature);
14530  if ((*image)->debug != MagickFalse )
14531    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14532  display_image=(*image);
14533  warning_handler=(WarningHandler) NULL;
14534  windows=XSetWindows((XWindows *) ~0);
14535  if (windows != (XWindows *) NULL)
14536    {
14537      int
14538        status;
14539
14540      if (*working_directory == '\0')
14541        (void) CopyMagickString(working_directory,".",MagickPathExtent);
14542      status=chdir(working_directory);
14543      if (status == -1)
14544        (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14545          "UnableToOpenFile","%s",working_directory);
14546      warning_handler=resource_info->display_warnings ?
14547        SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14548      warning_handler=resource_info->display_warnings ?
14549        SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14550    }
14551  else
14552    {
14553      /*
14554        Allocate windows structure.
14555      */
14556      resource_info->colors=display_image->colors;
14557      windows=XSetWindows(XInitializeWindows(display,resource_info));
14558      if (windows == (XWindows *) NULL)
14559        ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14560          (*image)->filename);
14561      /*
14562        Initialize window id's.
14563      */
14564      number_windows=0;
14565      magick_windows[number_windows++]=(&windows->icon);
14566      magick_windows[number_windows++]=(&windows->backdrop);
14567      magick_windows[number_windows++]=(&windows->image);
14568      magick_windows[number_windows++]=(&windows->info);
14569      magick_windows[number_windows++]=(&windows->command);
14570      magick_windows[number_windows++]=(&windows->widget);
14571      magick_windows[number_windows++]=(&windows->popup);
14572      magick_windows[number_windows++]=(&windows->magnify);
14573      magick_windows[number_windows++]=(&windows->pan);
14574      for (i=0; i < (int) number_windows; i++)
14575        magick_windows[i]->id=(Window) NULL;
14576      vid_info.x=0;
14577      vid_info.y=0;
14578    }
14579  /*
14580    Initialize font info.
14581  */
14582  if (windows->font_info != (XFontStruct *) NULL)
14583    (void) XFreeFont(display,windows->font_info);
14584  windows->font_info=XBestFont(display,resource_info,MagickFalse);
14585  if (windows->font_info == (XFontStruct *) NULL)
14586    ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14587      resource_info->font);
14588  /*
14589    Initialize Standard Colormap.
14590  */
14591  map_info=windows->map_info;
14592  icon_map=windows->icon_map;
14593  visual_info=windows->visual_info;
14594  icon_visual=windows->icon_visual;
14595  pixel=windows->pixel_info;
14596  icon_pixel=windows->icon_pixel;
14597  font_info=windows->font_info;
14598  icon_resources=windows->icon_resources;
14599  class_hints=windows->class_hints;
14600  manager_hints=windows->manager_hints;
14601  root_window=XRootWindow(display,visual_info->screen);
14602  nexus=NewImageList();
14603  if (display_image->debug != MagickFalse )
14604    {
14605      (void) LogMagickEvent(X11Event,GetMagickModule(),
14606        "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14607        (double) display_image->scene,(double) display_image->columns,
14608        (double) display_image->rows);
14609      if (display_image->colors != 0)
14610        (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14611          display_image->colors);
14612      (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14613        display_image->magick);
14614    }
14615  XMakeStandardColormap(display,visual_info,resource_info,display_image,
14616    map_info,pixel,exception);
14617  display_image->taint=MagickFalse;
14618  /*
14619    Initialize graphic context.
14620  */
14621  windows->context.id=(Window) NULL;
14622  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14623    resource_info,&windows->context);
14624  (void) CloneString(&class_hints->res_name,resource_info->client_name);
14625  (void) CloneString(&class_hints->res_class,resource_info->client_name);
14626  class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14627  manager_hints->flags=InputHint | StateHint;
14628  manager_hints->input=MagickFalse;
14629  manager_hints->initial_state=WithdrawnState;
14630  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14631    &windows->context);
14632  if (display_image->debug != MagickFalse )
14633    (void) LogMagickEvent(X11Event,GetMagickModule(),
14634      "Window id: 0x%lx (context)",windows->context.id);
14635  context_values.background=pixel->background_color.pixel;
14636  context_values.font=font_info->fid;
14637  context_values.foreground=pixel->foreground_color.pixel;
14638  context_values.graphics_exposures=MagickFalse;
14639  context_mask=(MagickStatusType)
14640    (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14641  if (pixel->annotate_context != (GC) NULL)
14642    (void) XFreeGC(display,pixel->annotate_context);
14643  pixel->annotate_context=XCreateGC(display,windows->context.id,
14644    context_mask,&context_values);
14645  if (pixel->annotate_context == (GC) NULL)
14646    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14647      display_image->filename);
14648  context_values.background=pixel->depth_color.pixel;
14649  if (pixel->widget_context != (GC) NULL)
14650    (void) XFreeGC(display,pixel->widget_context);
14651  pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14652    &context_values);
14653  if (pixel->widget_context == (GC) NULL)
14654    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14655      display_image->filename);
14656  context_values.background=pixel->foreground_color.pixel;
14657  context_values.foreground=pixel->background_color.pixel;
14658  context_values.plane_mask=context_values.background ^
14659    context_values.foreground;
14660  if (pixel->highlight_context != (GC) NULL)
14661    (void) XFreeGC(display,pixel->highlight_context);
14662  pixel->highlight_context=XCreateGC(display,windows->context.id,
14663    (size_t) (context_mask | GCPlaneMask),&context_values);
14664  if (pixel->highlight_context == (GC) NULL)
14665    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14666      display_image->filename);
14667  (void) XDestroyWindow(display,windows->context.id);
14668  /*
14669    Initialize icon window.
14670  */
14671  XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14672    icon_resources,&windows->icon);
14673  windows->icon.geometry=resource_info->icon_geometry;
14674  XBestIconSize(display,&windows->icon,display_image);
14675  windows->icon.attributes.colormap=XDefaultColormap(display,
14676    icon_visual->screen);
14677  windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14678  manager_hints->flags=InputHint | StateHint;
14679  manager_hints->input=MagickFalse;
14680  manager_hints->initial_state=IconicState;
14681  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14682    &windows->icon);
14683  if (display_image->debug != MagickFalse )
14684    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14685      windows->icon.id);
14686  /*
14687    Initialize graphic context for icon window.
14688  */
14689  if (icon_pixel->annotate_context != (GC) NULL)
14690    (void) XFreeGC(display,icon_pixel->annotate_context);
14691  context_values.background=icon_pixel->background_color.pixel;
14692  context_values.foreground=icon_pixel->foreground_color.pixel;
14693  icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14694    (size_t) (GCBackground | GCForeground),&context_values);
14695  if (icon_pixel->annotate_context == (GC) NULL)
14696    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14697      display_image->filename);
14698  windows->icon.annotate_context=icon_pixel->annotate_context;
14699  /*
14700    Initialize Image window.
14701  */
14702  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14703    &windows->image);
14704  windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14705  if (resource_info->use_shared_memory == MagickFalse)
14706    windows->image.shared_memory=MagickFalse;
14707  if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14708    {
14709      char
14710        *title;
14711
14712      title=InterpretImageProperties(resource_info->image_info,display_image,
14713        resource_info->title,exception);
14714      (void) CopyMagickString(windows->image.name,title,MagickPathExtent);
14715      (void) CopyMagickString(windows->image.icon_name,title,MagickPathExtent);
14716      title=DestroyString(title);
14717    }
14718  else
14719    {
14720      char
14721        filename[MagickPathExtent];
14722
14723      /*
14724        Window name is the base of the filename.
14725      */
14726      GetPathComponent(display_image->magick_filename,TailPath,filename);
14727      if (display_image->scene == 0)
14728        (void) FormatLocaleString(windows->image.name,MagickPathExtent,
14729          "%s: %s",MagickPackageName,filename);
14730      else
14731        (void) FormatLocaleString(windows->image.name,MagickPathExtent,
14732          "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14733          (double) display_image->scene,(double) GetImageListLength(
14734          display_image));
14735      (void) CopyMagickString(windows->image.icon_name,filename,MagickPathExtent);
14736    }
14737  if (resource_info->immutable)
14738    windows->image.immutable=MagickTrue;
14739  windows->image.use_pixmap=resource_info->use_pixmap;
14740  windows->image.geometry=resource_info->image_geometry;
14741  (void) FormatLocaleString(geometry,MagickPathExtent,"%ux%u+0+0>!",
14742    XDisplayWidth(display,visual_info->screen),
14743    XDisplayHeight(display,visual_info->screen));
14744  geometry_info.width=display_image->columns;
14745  geometry_info.height=display_image->rows;
14746  geometry_info.x=0;
14747  geometry_info.y=0;
14748  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14749    &geometry_info.width,&geometry_info.height);
14750  windows->image.width=(unsigned int) geometry_info.width;
14751  windows->image.height=(unsigned int) geometry_info.height;
14752  windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14753    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14754    KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14755    PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14756  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14757    resource_info,&windows->backdrop);
14758  if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14759    {
14760      /*
14761        Initialize backdrop window.
14762      */
14763      windows->backdrop.x=0;
14764      windows->backdrop.y=0;
14765      (void) CloneString(&windows->backdrop.name,"Backdrop");
14766      windows->backdrop.flags=(size_t) (USSize | USPosition);
14767      windows->backdrop.width=(unsigned int)
14768        XDisplayWidth(display,visual_info->screen);
14769      windows->backdrop.height=(unsigned int)
14770        XDisplayHeight(display,visual_info->screen);
14771      windows->backdrop.border_width=0;
14772      windows->backdrop.immutable=MagickTrue;
14773      windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14774        ButtonReleaseMask;
14775      windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14776        StructureNotifyMask;
14777      manager_hints->flags=IconWindowHint | InputHint | StateHint;
14778      manager_hints->icon_window=windows->icon.id;
14779      manager_hints->input=MagickTrue;
14780      manager_hints->initial_state=resource_info->iconic ? IconicState :
14781        NormalState;
14782      XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14783        &windows->backdrop);
14784      if (display_image->debug != MagickFalse )
14785        (void) LogMagickEvent(X11Event,GetMagickModule(),
14786          "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14787      (void) XMapWindow(display,windows->backdrop.id);
14788      (void) XClearWindow(display,windows->backdrop.id);
14789      if (windows->image.id != (Window) NULL)
14790        {
14791          (void) XDestroyWindow(display,windows->image.id);
14792          windows->image.id=(Window) NULL;
14793        }
14794      /*
14795        Position image in the center the backdrop.
14796      */
14797      windows->image.flags|=USPosition;
14798      windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14799        (windows->image.width/2);
14800      windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14801        (windows->image.height/2);
14802    }
14803  manager_hints->flags=IconWindowHint | InputHint | StateHint;
14804  manager_hints->icon_window=windows->icon.id;
14805  manager_hints->input=MagickTrue;
14806  manager_hints->initial_state=resource_info->iconic ? IconicState :
14807    NormalState;
14808  if (windows->group_leader.id != (Window) NULL)
14809    {
14810      /*
14811        Follow the leader.
14812      */
14813      manager_hints->flags|=WindowGroupHint;
14814      manager_hints->window_group=windows->group_leader.id;
14815      (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14816      if (display_image->debug != MagickFalse )
14817        (void) LogMagickEvent(X11Event,GetMagickModule(),
14818          "Window id: 0x%lx (group leader)",windows->group_leader.id);
14819    }
14820  XMakeWindow(display,
14821    (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14822    argv,argc,class_hints,manager_hints,&windows->image);
14823  (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14824    XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14825  if (windows->group_leader.id != (Window) NULL)
14826    (void) XSetTransientForHint(display,windows->image.id,
14827      windows->group_leader.id);
14828  if (display_image->debug != MagickFalse )
14829    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14830      windows->image.id);
14831  /*
14832    Initialize Info widget.
14833  */
14834  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14835    &windows->info);
14836  (void) CloneString(&windows->info.name,"Info");
14837  (void) CloneString(&windows->info.icon_name,"Info");
14838  windows->info.border_width=1;
14839  windows->info.x=2;
14840  windows->info.y=2;
14841  windows->info.flags|=PPosition;
14842  windows->info.attributes.win_gravity=UnmapGravity;
14843  windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14844    StructureNotifyMask;
14845  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14846  manager_hints->input=MagickFalse;
14847  manager_hints->initial_state=NormalState;
14848  manager_hints->window_group=windows->image.id;
14849  XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14850    &windows->info);
14851  windows->info.highlight_stipple=XCreateBitmapFromData(display,
14852    windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14853  windows->info.shadow_stipple=XCreateBitmapFromData(display,
14854    windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14855  (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14856  if (windows->image.mapped != MagickFalse )
14857    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14858  if (display_image->debug != MagickFalse )
14859    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14860      windows->info.id);
14861  /*
14862    Initialize Command widget.
14863  */
14864  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14865    resource_info,&windows->command);
14866  windows->command.data=MagickMenus;
14867  (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14868  (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.command",
14869    resource_info->client_name);
14870  windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14871    resource_name,"geometry",(char *) NULL);
14872  (void) CloneString(&windows->command.name,MagickTitle);
14873  windows->command.border_width=0;
14874  windows->command.flags|=PPosition;
14875  windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14876    ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14877    OwnerGrabButtonMask | StructureNotifyMask;
14878  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14879  manager_hints->input=MagickTrue;
14880  manager_hints->initial_state=NormalState;
14881  manager_hints->window_group=windows->image.id;
14882  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14883    &windows->command);
14884  windows->command.highlight_stipple=XCreateBitmapFromData(display,
14885    windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14886    HighlightHeight);
14887  windows->command.shadow_stipple=XCreateBitmapFromData(display,
14888    windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14889  (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14890  if (windows->command.mapped != MagickFalse )
14891    (void) XMapRaised(display,windows->command.id);
14892  if (display_image->debug != MagickFalse )
14893    (void) LogMagickEvent(X11Event,GetMagickModule(),
14894      "Window id: 0x%lx (command)",windows->command.id);
14895  /*
14896    Initialize Widget window.
14897  */
14898  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14899    resource_info,&windows->widget);
14900  (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.widget",
14901    resource_info->client_name);
14902  windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14903    resource_name,"geometry",(char *) NULL);
14904  windows->widget.border_width=0;
14905  windows->widget.flags|=PPosition;
14906  windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14907    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14908    KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14909    StructureNotifyMask;
14910  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14911  manager_hints->input=MagickTrue;
14912  manager_hints->initial_state=NormalState;
14913  manager_hints->window_group=windows->image.id;
14914  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14915    &windows->widget);
14916  windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14917    windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14918  windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14919    windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14920  (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14921  if (display_image->debug != MagickFalse )
14922    (void) LogMagickEvent(X11Event,GetMagickModule(),
14923      "Window id: 0x%lx (widget)",windows->widget.id);
14924  /*
14925    Initialize popup window.
14926  */
14927  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14928    resource_info,&windows->popup);
14929  windows->popup.border_width=0;
14930  windows->popup.flags|=PPosition;
14931  windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14932    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14933    KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14934  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14935  manager_hints->input=MagickTrue;
14936  manager_hints->initial_state=NormalState;
14937  manager_hints->window_group=windows->image.id;
14938  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14939    &windows->popup);
14940  windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14941    windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14942  windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14943    windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14944  (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14945  if (display_image->debug != MagickFalse )
14946    (void) LogMagickEvent(X11Event,GetMagickModule(),
14947      "Window id: 0x%lx (pop up)",windows->popup.id);
14948  /*
14949    Initialize Magnify window and cursor.
14950  */
14951  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14952    resource_info,&windows->magnify);
14953  if (resource_info->use_shared_memory == MagickFalse)
14954    windows->magnify.shared_memory=MagickFalse;
14955  (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.magnify",
14956    resource_info->client_name);
14957  windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14958    resource_name,"geometry",(char *) NULL);
14959  (void) FormatLocaleString(windows->magnify.name,MagickPathExtent,"Magnify %uX",
14960    resource_info->magnify);
14961  if (windows->magnify.cursor != (Cursor) NULL)
14962    (void) XFreeCursor(display,windows->magnify.cursor);
14963  windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14964    map_info->colormap,resource_info->background_color,
14965    resource_info->foreground_color);
14966  if (windows->magnify.cursor == (Cursor) NULL)
14967    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14968      display_image->filename);
14969  windows->magnify.width=MagnifySize;
14970  windows->magnify.height=MagnifySize;
14971  windows->magnify.flags|=PPosition;
14972  windows->magnify.min_width=MagnifySize;
14973  windows->magnify.min_height=MagnifySize;
14974  windows->magnify.width_inc=MagnifySize;
14975  windows->magnify.height_inc=MagnifySize;
14976  windows->magnify.data=resource_info->magnify;
14977  windows->magnify.attributes.cursor=windows->magnify.cursor;
14978  windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14979    ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14980    StructureNotifyMask;
14981  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14982  manager_hints->input=MagickTrue;
14983  manager_hints->initial_state=NormalState;
14984  manager_hints->window_group=windows->image.id;
14985  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14986    &windows->magnify);
14987  if (display_image->debug != MagickFalse )
14988    (void) LogMagickEvent(X11Event,GetMagickModule(),
14989      "Window id: 0x%lx (magnify)",windows->magnify.id);
14990  (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14991  /*
14992    Initialize panning window.
14993  */
14994  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14995    resource_info,&windows->pan);
14996  (void) CloneString(&windows->pan.name,"Pan Icon");
14997  windows->pan.width=windows->icon.width;
14998  windows->pan.height=windows->icon.height;
14999  (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.pan",
15000    resource_info->client_name);
15001  windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
15002    resource_name,"geometry",(char *) NULL);
15003  (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
15004    &windows->pan.width,&windows->pan.height);
15005  windows->pan.flags|=PPosition;
15006  windows->pan.immutable=MagickTrue;
15007  windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
15008    ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
15009    StructureNotifyMask;
15010  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
15011  manager_hints->input=MagickFalse;
15012  manager_hints->initial_state=NormalState;
15013  manager_hints->window_group=windows->image.id;
15014  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
15015    &windows->pan);
15016  if (display_image->debug != MagickFalse )
15017    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
15018      windows->pan.id);
15019  (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
15020  if (windows->info.mapped != MagickFalse )
15021    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15022  if ((windows->image.mapped == MagickFalse) ||
15023      (windows->backdrop.id != (Window) NULL))
15024    (void) XMapWindow(display,windows->image.id);
15025  /*
15026    Set our progress monitor and warning handlers.
15027  */
15028  if (warning_handler == (WarningHandler) NULL)
15029    {
15030      warning_handler=resource_info->display_warnings ?
15031        SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
15032      warning_handler=resource_info->display_warnings ?
15033        SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
15034    }
15035  /*
15036    Initialize Image and Magnify X images.
15037  */
15038  windows->image.x=0;
15039  windows->image.y=0;
15040  windows->magnify.shape=MagickFalse;
15041  width=(unsigned int) display_image->columns;
15042  height=(unsigned int) display_image->rows;
15043  if ((display_image->columns != width) || (display_image->rows != height))
15044    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15045      display_image->filename);
15046  status=XMakeImage(display,resource_info,&windows->image,display_image,
15047    width,height,exception);
15048  if (status == MagickFalse)
15049    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15050      display_image->filename);
15051  status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
15052    windows->magnify.width,windows->magnify.height,exception);
15053  if (status == MagickFalse)
15054    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15055      display_image->filename);
15056  if (windows->magnify.mapped != MagickFalse )
15057    (void) XMapRaised(display,windows->magnify.id);
15058  if (windows->pan.mapped != MagickFalse )
15059    (void) XMapRaised(display,windows->pan.id);
15060  windows->image.window_changes.width=(int) display_image->columns;
15061  windows->image.window_changes.height=(int) display_image->rows;
15062  (void) XConfigureImage(display,resource_info,windows,display_image,exception);
15063  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15064  (void) XSync(display,MagickFalse);
15065  /*
15066    Respond to events.
15067  */
15068  delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
15069  timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15070  update_time=0;
15071  if (resource_info->update != MagickFalse )
15072    {
15073      MagickBooleanType
15074        status;
15075
15076      /*
15077        Determine when file data was last modified.
15078      */
15079      status=GetPathAttributes(display_image->filename,&attributes);
15080      if (status != MagickFalse )
15081        update_time=attributes.st_mtime;
15082    }
15083  *state&=(~FormerImageState);
15084  *state&=(~MontageImageState);
15085  *state&=(~NextImageState);
15086  do
15087  {
15088    /*
15089      Handle a window event.
15090    */
15091    if (windows->image.mapped != MagickFalse )
15092      if ((display_image->delay != 0) || (resource_info->update != 0))
15093        {
15094          if (timer < time((time_t *) NULL))
15095            {
15096              if (resource_info->update == MagickFalse)
15097                *state|=NextImageState | ExitState;
15098              else
15099                {
15100                  MagickBooleanType
15101                    status;
15102
15103                  /*
15104                    Determine if image file was modified.
15105                  */
15106                  status=GetPathAttributes(display_image->filename,&attributes);
15107                  if (status != MagickFalse )
15108                    if (update_time != attributes.st_mtime)
15109                      {
15110                        /*
15111                          Redisplay image.
15112                        */
15113                        (void) FormatLocaleString(
15114                          resource_info->image_info->filename,MagickPathExtent,
15115                          "%s:%s",display_image->magick,
15116                          display_image->filename);
15117                        nexus=ReadImage(resource_info->image_info,exception);
15118                        if (nexus != (Image *) NULL)
15119                          *state|=NextImageState | ExitState;
15120                      }
15121                  delay=display_image->delay/MagickMax(
15122                    display_image->ticks_per_second,1L);
15123                  timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15124                }
15125            }
15126          if (XEventsQueued(display,QueuedAfterFlush) == 0)
15127            {
15128              /*
15129                Do not block if delay > 0.
15130              */
15131              XDelay(display,SuspendTime << 2);
15132              continue;
15133            }
15134        }
15135    timestamp=time((time_t *) NULL);
15136    (void) XNextEvent(display,&event);
15137    if ((windows->image.stasis == MagickFalse) ||
15138        (windows->magnify.stasis == MagickFalse))
15139      {
15140        if ((time((time_t *) NULL)-timestamp) > 0)
15141          {
15142            windows->image.stasis=MagickTrue;
15143            windows->magnify.stasis=MagickTrue;
15144          }
15145      }
15146    if (event.xany.window == windows->command.id)
15147      {
15148        /*
15149          Select a command from the Command widget.
15150        */
15151        id=XCommandWidget(display,windows,CommandMenu,&event);
15152        if (id < 0)
15153          continue;
15154        (void) CopyMagickString(command,CommandMenu[id],MagickPathExtent);
15155        command_type=CommandMenus[id];
15156        if (id < MagickMenus)
15157          {
15158            /*
15159              Select a command from a pop-up menu.
15160            */
15161            entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15162              command);
15163            if (entry < 0)
15164              continue;
15165            (void) CopyMagickString(command,Menus[id][entry],MagickPathExtent);
15166            command_type=Commands[id][entry];
15167          }
15168        if (command_type != NullCommand)
15169          nexus=XMagickCommand(display,resource_info,windows,command_type,
15170            &display_image,exception);
15171        continue;
15172      }
15173    switch (event.type)
15174    {
15175      case ButtonPress:
15176      {
15177        if (display_image->debug != MagickFalse )
15178          (void) LogMagickEvent(X11Event,GetMagickModule(),
15179            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15180            event.xbutton.button,event.xbutton.x,event.xbutton.y);
15181        if ((event.xbutton.button == Button3) &&
15182            (event.xbutton.state & Mod1Mask))
15183          {
15184            /*
15185              Convert Alt-Button3 to Button2.
15186            */
15187            event.xbutton.button=Button2;
15188            event.xbutton.state&=(~Mod1Mask);
15189          }
15190        if (event.xbutton.window == windows->backdrop.id)
15191          {
15192            (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15193              event.xbutton.time);
15194            break;
15195          }
15196        if (event.xbutton.window == windows->image.id)
15197          {
15198            switch (event.xbutton.button)
15199            {
15200              case Button1:
15201              {
15202                if (resource_info->immutable)
15203                  {
15204                    /*
15205                      Select a command from the Virtual menu.
15206                    */
15207                    entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15208                      command);
15209                    if (entry >= 0)
15210                      nexus=XMagickCommand(display,resource_info,windows,
15211                        VirtualCommands[entry],&display_image,exception);
15212                    break;
15213                  }
15214                /*
15215                  Map/unmap Command widget.
15216                */
15217                if (windows->command.mapped != MagickFalse )
15218                  (void) XWithdrawWindow(display,windows->command.id,
15219                    windows->command.screen);
15220                else
15221                  {
15222                    (void) XCommandWidget(display,windows,CommandMenu,
15223                      (XEvent *) NULL);
15224                    (void) XMapRaised(display,windows->command.id);
15225                  }
15226                break;
15227              }
15228              case Button2:
15229              {
15230                /*
15231                  User pressed the image magnify button.
15232                */
15233                (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15234                  &display_image,exception);
15235                XMagnifyImage(display,windows,&event,exception);
15236                break;
15237              }
15238              case Button3:
15239              {
15240                if (resource_info->immutable)
15241                  {
15242                    /*
15243                      Select a command from the Virtual menu.
15244                    */
15245                    entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15246                      command);
15247                    if (entry >= 0)
15248                      nexus=XMagickCommand(display,resource_info,windows,
15249                        VirtualCommands[entry],&display_image,exception);
15250                    break;
15251                  }
15252                if (display_image->montage != (char *) NULL)
15253                  {
15254                    /*
15255                      Open or delete a tile from a visual image directory.
15256                    */
15257                    nexus=XTileImage(display,resource_info,windows,
15258                      display_image,&event,exception);
15259                    if (nexus != (Image *) NULL)
15260                      *state|=MontageImageState | NextImageState | ExitState;
15261                    vid_info.x=(short int) windows->image.x;
15262                    vid_info.y=(short int) windows->image.y;
15263                    break;
15264                  }
15265                /*
15266                  Select a command from the Short Cuts menu.
15267                */
15268                entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15269                  command);
15270                if (entry >= 0)
15271                  nexus=XMagickCommand(display,resource_info,windows,
15272                    ShortCutsCommands[entry],&display_image,exception);
15273                break;
15274              }
15275              case Button4:
15276              {
15277                /*
15278                  Wheel up.
15279                */
15280                XTranslateImage(display,windows,*image,XK_Up);
15281                break;
15282              }
15283              case Button5:
15284              {
15285                /*
15286                  Wheel down.
15287                */
15288                XTranslateImage(display,windows,*image,XK_Down);
15289                break;
15290              }
15291              default:
15292                break;
15293            }
15294            break;
15295          }
15296        if (event.xbutton.window == windows->magnify.id)
15297          {
15298            int
15299              factor;
15300
15301            static const char
15302              *MagnifyMenu[] =
15303              {
15304                "2",
15305                "4",
15306                "5",
15307                "6",
15308                "7",
15309                "8",
15310                "9",
15311                "3",
15312                (char *) NULL,
15313              };
15314
15315            static KeySym
15316              MagnifyCommands[] =
15317              {
15318                XK_2,
15319                XK_4,
15320                XK_5,
15321                XK_6,
15322                XK_7,
15323                XK_8,
15324                XK_9,
15325                XK_3
15326              };
15327
15328            /*
15329              Select a magnify factor from the pop-up menu.
15330            */
15331            factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15332            if (factor >= 0)
15333              XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor],
15334                exception);
15335            break;
15336          }
15337        if (event.xbutton.window == windows->pan.id)
15338          {
15339            switch (event.xbutton.button)
15340            {
15341              case Button4:
15342              {
15343                /*
15344                  Wheel up.
15345                */
15346                XTranslateImage(display,windows,*image,XK_Up);
15347                break;
15348              }
15349              case Button5:
15350              {
15351                /*
15352                  Wheel down.
15353                */
15354                XTranslateImage(display,windows,*image,XK_Down);
15355                break;
15356              }
15357              default:
15358              {
15359                XPanImage(display,windows,&event,exception);
15360                break;
15361              }
15362            }
15363            break;
15364          }
15365        delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15366          1L);
15367        timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15368        break;
15369      }
15370      case ButtonRelease:
15371      {
15372        if (display_image->debug != MagickFalse )
15373          (void) LogMagickEvent(X11Event,GetMagickModule(),
15374            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15375            event.xbutton.button,event.xbutton.x,event.xbutton.y);
15376        break;
15377      }
15378      case ClientMessage:
15379      {
15380        if (display_image->debug != MagickFalse )
15381          (void) LogMagickEvent(X11Event,GetMagickModule(),
15382            "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15383            event.xclient.message_type,event.xclient.format,(unsigned long)
15384            event.xclient.data.l[0]);
15385        if (event.xclient.message_type == windows->im_protocols)
15386          {
15387            if (*event.xclient.data.l == (long) windows->im_update_widget)
15388              {
15389                (void) CloneString(&windows->command.name,MagickTitle);
15390                windows->command.data=MagickMenus;
15391                (void) XCommandWidget(display,windows,CommandMenu,
15392                  (XEvent *) NULL);
15393                break;
15394              }
15395            if (*event.xclient.data.l == (long) windows->im_update_colormap)
15396              {
15397                /*
15398                  Update graphic context and window colormap.
15399                */
15400                for (i=0; i < (int) number_windows; i++)
15401                {
15402                  if (magick_windows[i]->id == windows->icon.id)
15403                    continue;
15404                  context_values.background=pixel->background_color.pixel;
15405                  context_values.foreground=pixel->foreground_color.pixel;
15406                  (void) XChangeGC(display,magick_windows[i]->annotate_context,
15407                    context_mask,&context_values);
15408                  (void) XChangeGC(display,magick_windows[i]->widget_context,
15409                    context_mask,&context_values);
15410                  context_values.background=pixel->foreground_color.pixel;
15411                  context_values.foreground=pixel->background_color.pixel;
15412                  context_values.plane_mask=context_values.background ^
15413                    context_values.foreground;
15414                  (void) XChangeGC(display,magick_windows[i]->highlight_context,
15415                    (size_t) (context_mask | GCPlaneMask),
15416                    &context_values);
15417                  magick_windows[i]->attributes.background_pixel=
15418                    pixel->background_color.pixel;
15419                  magick_windows[i]->attributes.border_pixel=
15420                    pixel->border_color.pixel;
15421                  magick_windows[i]->attributes.colormap=map_info->colormap;
15422                  (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15423                    (unsigned long) magick_windows[i]->mask,
15424                    &magick_windows[i]->attributes);
15425                }
15426                if (windows->pan.mapped != MagickFalse )
15427                  {
15428                    (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15429                      windows->pan.pixmap);
15430                    (void) XClearWindow(display,windows->pan.id);
15431                    XDrawPanRectangle(display,windows);
15432                  }
15433                if (windows->backdrop.id != (Window) NULL)
15434                  (void) XInstallColormap(display,map_info->colormap);
15435                break;
15436              }
15437            if (*event.xclient.data.l == (long) windows->im_former_image)
15438              {
15439                *state|=FormerImageState | ExitState;
15440                break;
15441              }
15442            if (*event.xclient.data.l == (long) windows->im_next_image)
15443              {
15444                *state|=NextImageState | ExitState;
15445                break;
15446              }
15447            if (*event.xclient.data.l == (long) windows->im_retain_colors)
15448              {
15449                *state|=RetainColorsState;
15450                break;
15451              }
15452            if (*event.xclient.data.l == (long) windows->im_exit)
15453              {
15454                *state|=ExitState;
15455                break;
15456              }
15457            break;
15458          }
15459        if (event.xclient.message_type == windows->dnd_protocols)
15460          {
15461            Atom
15462              selection,
15463              type;
15464
15465            int
15466              format,
15467              status;
15468
15469            unsigned char
15470              *data;
15471
15472            unsigned long
15473              after,
15474              length;
15475
15476            /*
15477              Display image named by the Drag-and-Drop selection.
15478            */
15479            if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15480              break;
15481            selection=XInternAtom(display,"DndSelection",MagickFalse);
15482            status=XGetWindowProperty(display,root_window,selection,0L,(long)
15483              MagickPathExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15484              &length,&after,&data);
15485            if ((status != Success) || (length == 0))
15486              break;
15487            if (*event.xclient.data.l == 2)
15488              {
15489                /*
15490                  Offix DND.
15491                */
15492                (void) CopyMagickString(resource_info->image_info->filename,
15493                  (char *) data,MagickPathExtent);
15494              }
15495            else
15496              {
15497                /*
15498                  XDND.
15499                */
15500                if (strncmp((char *) data, "file:", 5) != 0)
15501                  {
15502                    (void) XFree((void *) data);
15503                    break;
15504                  }
15505                (void) CopyMagickString(resource_info->image_info->filename,
15506                  ((char *) data)+5,MagickPathExtent);
15507              }
15508            nexus=ReadImage(resource_info->image_info,exception);
15509            CatchException(exception);
15510            if (nexus != (Image *) NULL)
15511              *state|=NextImageState | ExitState;
15512            (void) XFree((void *) data);
15513            break;
15514          }
15515        /*
15516          If client window delete message, exit.
15517        */
15518        if (event.xclient.message_type != windows->wm_protocols)
15519          break;
15520        if (*event.xclient.data.l != (long) windows->wm_delete_window)
15521          break;
15522        (void) XWithdrawWindow(display,event.xclient.window,
15523          visual_info->screen);
15524        if (event.xclient.window == windows->image.id)
15525          {
15526            *state|=ExitState;
15527            break;
15528          }
15529        if (event.xclient.window == windows->pan.id)
15530          {
15531            /*
15532              Restore original image size when pan window is deleted.
15533            */
15534            windows->image.window_changes.width=windows->image.ximage->width;
15535            windows->image.window_changes.height=windows->image.ximage->height;
15536            (void) XConfigureImage(display,resource_info,windows,
15537              display_image,exception);
15538          }
15539        break;
15540      }
15541      case ConfigureNotify:
15542      {
15543        if (display_image->debug != MagickFalse )
15544          (void) LogMagickEvent(X11Event,GetMagickModule(),
15545            "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15546            event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15547            event.xconfigure.y,event.xconfigure.send_event);
15548        if (event.xconfigure.window == windows->image.id)
15549          {
15550            /*
15551              Image window has a new configuration.
15552            */
15553            if (event.xconfigure.send_event != 0)
15554              {
15555                XWindowChanges
15556                  window_changes;
15557
15558                /*
15559                  Position the transient windows relative of the Image window.
15560                */
15561                if (windows->command.geometry == (char *) NULL)
15562                  if (windows->command.mapped == MagickFalse)
15563                    {
15564                      windows->command.x=event.xconfigure.x-
15565                        windows->command.width-25;
15566                      windows->command.y=event.xconfigure.y;
15567                      XConstrainWindowPosition(display,&windows->command);
15568                      window_changes.x=windows->command.x;
15569                      window_changes.y=windows->command.y;
15570                      (void) XReconfigureWMWindow(display,windows->command.id,
15571                        windows->command.screen,(unsigned int) (CWX | CWY),
15572                        &window_changes);
15573                    }
15574                if (windows->widget.geometry == (char *) NULL)
15575                  if (windows->widget.mapped == MagickFalse)
15576                    {
15577                      windows->widget.x=event.xconfigure.x+
15578                        event.xconfigure.width/10;
15579                      windows->widget.y=event.xconfigure.y+
15580                        event.xconfigure.height/10;
15581                      XConstrainWindowPosition(display,&windows->widget);
15582                      window_changes.x=windows->widget.x;
15583                      window_changes.y=windows->widget.y;
15584                      (void) XReconfigureWMWindow(display,windows->widget.id,
15585                        windows->widget.screen,(unsigned int) (CWX | CWY),
15586                        &window_changes);
15587                    }
15588                if (windows->magnify.geometry == (char *) NULL)
15589                  if (windows->magnify.mapped == MagickFalse)
15590                    {
15591                      windows->magnify.x=event.xconfigure.x+
15592                        event.xconfigure.width+25;
15593                      windows->magnify.y=event.xconfigure.y;
15594                      XConstrainWindowPosition(display,&windows->magnify);
15595                      window_changes.x=windows->magnify.x;
15596                      window_changes.y=windows->magnify.y;
15597                      (void) XReconfigureWMWindow(display,windows->magnify.id,
15598                        windows->magnify.screen,(unsigned int) (CWX | CWY),
15599                        &window_changes);
15600                    }
15601                if (windows->pan.geometry == (char *) NULL)
15602                  if (windows->pan.mapped == MagickFalse)
15603                    {
15604                      windows->pan.x=event.xconfigure.x+
15605                        event.xconfigure.width+25;
15606                      windows->pan.y=event.xconfigure.y+
15607                        windows->magnify.height+50;
15608                      XConstrainWindowPosition(display,&windows->pan);
15609                      window_changes.x=windows->pan.x;
15610                      window_changes.y=windows->pan.y;
15611                      (void) XReconfigureWMWindow(display,windows->pan.id,
15612                        windows->pan.screen,(unsigned int) (CWX | CWY),
15613                        &window_changes);
15614                    }
15615              }
15616            if ((event.xconfigure.width == (int) windows->image.width) &&
15617                (event.xconfigure.height == (int) windows->image.height))
15618              break;
15619            windows->image.width=(unsigned int) event.xconfigure.width;
15620            windows->image.height=(unsigned int) event.xconfigure.height;
15621            windows->image.x=0;
15622            windows->image.y=0;
15623            if (display_image->montage != (char *) NULL)
15624              {
15625                windows->image.x=vid_info.x;
15626                windows->image.y=vid_info.y;
15627              }
15628            if (windows->image.mapped != MagickFalse &&
15629                windows->image.stasis != MagickFalse )
15630              {
15631                /*
15632                  Update image window configuration.
15633                */
15634                windows->image.window_changes.width=event.xconfigure.width;
15635                windows->image.window_changes.height=event.xconfigure.height;
15636                (void) XConfigureImage(display,resource_info,windows,
15637                  display_image,exception);
15638              }
15639            /*
15640              Update pan window configuration.
15641            */
15642            if ((event.xconfigure.width < windows->image.ximage->width) ||
15643                (event.xconfigure.height < windows->image.ximage->height))
15644              {
15645                (void) XMapRaised(display,windows->pan.id);
15646                XDrawPanRectangle(display,windows);
15647              }
15648            else
15649              if (windows->pan.mapped != MagickFalse )
15650                (void) XWithdrawWindow(display,windows->pan.id,
15651                  windows->pan.screen);
15652            break;
15653          }
15654        if (event.xconfigure.window == windows->magnify.id)
15655          {
15656            unsigned int
15657              magnify;
15658
15659            /*
15660              Magnify window has a new configuration.
15661            */
15662            windows->magnify.width=(unsigned int) event.xconfigure.width;
15663            windows->magnify.height=(unsigned int) event.xconfigure.height;
15664            if (windows->magnify.mapped == MagickFalse)
15665              break;
15666            magnify=1;
15667            while ((int) magnify <= event.xconfigure.width)
15668              magnify<<=1;
15669            while ((int) magnify <= event.xconfigure.height)
15670              magnify<<=1;
15671            magnify>>=1;
15672            if (((int) magnify != event.xconfigure.width) ||
15673                ((int) magnify != event.xconfigure.height))
15674              {
15675                window_changes.width=(int) magnify;
15676                window_changes.height=(int) magnify;
15677                (void) XReconfigureWMWindow(display,windows->magnify.id,
15678                  windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15679                  &window_changes);
15680                break;
15681              }
15682            if (windows->magnify.mapped != MagickFalse &&
15683                windows->magnify.stasis != MagickFalse )
15684              {
15685                status=XMakeImage(display,resource_info,&windows->magnify,
15686                  display_image,windows->magnify.width,windows->magnify.height,
15687                  exception);
15688                XMakeMagnifyImage(display,windows,exception);
15689              }
15690            break;
15691          }
15692        if (windows->magnify.mapped != MagickFalse &&
15693            (event.xconfigure.window == windows->pan.id))
15694          {
15695            /*
15696              Pan icon window has a new configuration.
15697            */
15698            if (event.xconfigure.send_event != 0)
15699              {
15700                windows->pan.x=event.xconfigure.x;
15701                windows->pan.y=event.xconfigure.y;
15702              }
15703            windows->pan.width=(unsigned int) event.xconfigure.width;
15704            windows->pan.height=(unsigned int) event.xconfigure.height;
15705            break;
15706          }
15707        if (event.xconfigure.window == windows->icon.id)
15708          {
15709            /*
15710              Icon window has a new configuration.
15711            */
15712            windows->icon.width=(unsigned int) event.xconfigure.width;
15713            windows->icon.height=(unsigned int) event.xconfigure.height;
15714            break;
15715          }
15716        break;
15717      }
15718      case DestroyNotify:
15719      {
15720        /*
15721          Group leader has exited.
15722        */
15723        if (display_image->debug != MagickFalse )
15724          (void) LogMagickEvent(X11Event,GetMagickModule(),
15725            "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15726        if (event.xdestroywindow.window == windows->group_leader.id)
15727          {
15728            *state|=ExitState;
15729            break;
15730          }
15731        break;
15732      }
15733      case EnterNotify:
15734      {
15735        /*
15736          Selectively install colormap.
15737        */
15738        if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15739          if (event.xcrossing.mode != NotifyUngrab)
15740            XInstallColormap(display,map_info->colormap);
15741        break;
15742      }
15743      case Expose:
15744      {
15745        if (display_image->debug != MagickFalse )
15746          (void) LogMagickEvent(X11Event,GetMagickModule(),
15747            "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15748            event.xexpose.width,event.xexpose.height,event.xexpose.x,
15749            event.xexpose.y);
15750        /*
15751          Refresh windows that are now exposed.
15752        */
15753        if ((event.xexpose.window == windows->image.id) &&
15754            windows->image.mapped != MagickFalse )
15755          {
15756            XRefreshWindow(display,&windows->image,&event);
15757            delay=display_image->delay/MagickMax(
15758              display_image->ticks_per_second,1L);
15759            timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15760            break;
15761          }
15762        if ((event.xexpose.window == windows->magnify.id) &&
15763            windows->magnify.mapped != MagickFalse)
15764          {
15765            XMakeMagnifyImage(display,windows,exception);
15766            break;
15767          }
15768        if (event.xexpose.window == windows->pan.id)
15769          {
15770            XDrawPanRectangle(display,windows);
15771            break;
15772          }
15773        if (event.xexpose.window == windows->icon.id)
15774          {
15775            XRefreshWindow(display,&windows->icon,&event);
15776            break;
15777          }
15778        break;
15779      }
15780      case KeyPress:
15781      {
15782        int
15783          length;
15784
15785        /*
15786          Respond to a user key press.
15787        */
15788        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15789          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15790        *(command+length)='\0';
15791        if (display_image->debug != MagickFalse )
15792          (void) LogMagickEvent(X11Event,GetMagickModule(),
15793            "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15794            key_symbol,command);
15795        if (event.xkey.window == windows->image.id)
15796          {
15797            command_type=XImageWindowCommand(display,resource_info,windows,
15798              event.xkey.state,key_symbol,&display_image,exception);
15799            if (command_type != NullCommand)
15800              nexus=XMagickCommand(display,resource_info,windows,command_type,
15801                &display_image,exception);
15802          }
15803        if (event.xkey.window == windows->magnify.id)
15804          XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol,
15805            exception);
15806        if (event.xkey.window == windows->pan.id)
15807          {
15808            if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15809              (void) XWithdrawWindow(display,windows->pan.id,
15810                windows->pan.screen);
15811            else
15812              if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15813                XTextViewWidget(display,resource_info,windows,MagickFalse,
15814                  "Help Viewer - Image Pan",ImagePanHelp);
15815              else
15816                XTranslateImage(display,windows,*image,key_symbol);
15817          }
15818        delay=display_image->delay/MagickMax(
15819          display_image->ticks_per_second,1L);
15820        timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15821        break;
15822      }
15823      case KeyRelease:
15824      {
15825        /*
15826          Respond to a user key release.
15827        */
15828        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15829          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15830        if (display_image->debug != MagickFalse )
15831          (void) LogMagickEvent(X11Event,GetMagickModule(),
15832            "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15833        break;
15834      }
15835      case LeaveNotify:
15836      {
15837        /*
15838          Selectively uninstall colormap.
15839        */
15840        if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15841          if (event.xcrossing.mode != NotifyUngrab)
15842            XUninstallColormap(display,map_info->colormap);
15843        break;
15844      }
15845      case MapNotify:
15846      {
15847        if (display_image->debug != MagickFalse )
15848          (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15849            event.xmap.window);
15850        if (event.xmap.window == windows->backdrop.id)
15851          {
15852            (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15853              CurrentTime);
15854            windows->backdrop.mapped=MagickTrue;
15855            break;
15856          }
15857        if (event.xmap.window == windows->image.id)
15858          {
15859            if (windows->backdrop.id != (Window) NULL)
15860              (void) XInstallColormap(display,map_info->colormap);
15861            if (LocaleCompare(display_image->magick,"LOGO") == 0)
15862              {
15863                if (LocaleCompare(display_image->filename,"LOGO") == 0)
15864                  nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15865              }
15866            if (((int) windows->image.width < windows->image.ximage->width) ||
15867                ((int) windows->image.height < windows->image.ximage->height))
15868              (void) XMapRaised(display,windows->pan.id);
15869            windows->image.mapped=MagickTrue;
15870            break;
15871          }
15872        if (event.xmap.window == windows->magnify.id)
15873          {
15874            XMakeMagnifyImage(display,windows,exception);
15875            windows->magnify.mapped=MagickTrue;
15876            (void) XWithdrawWindow(display,windows->info.id,
15877              windows->info.screen);
15878            break;
15879          }
15880        if (event.xmap.window == windows->pan.id)
15881          {
15882            XMakePanImage(display,resource_info,windows,display_image,
15883              exception);
15884            windows->pan.mapped=MagickTrue;
15885            break;
15886          }
15887        if (event.xmap.window == windows->info.id)
15888          {
15889            windows->info.mapped=MagickTrue;
15890            break;
15891          }
15892        if (event.xmap.window == windows->icon.id)
15893          {
15894            MagickBooleanType
15895              taint;
15896
15897            /*
15898              Create an icon image.
15899            */
15900            taint=display_image->taint;
15901            XMakeStandardColormap(display,icon_visual,icon_resources,
15902              display_image,icon_map,icon_pixel,exception);
15903            (void) XMakeImage(display,icon_resources,&windows->icon,
15904              display_image,windows->icon.width,windows->icon.height,
15905              exception);
15906            display_image->taint=taint;
15907            (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15908              windows->icon.pixmap);
15909            (void) XClearWindow(display,windows->icon.id);
15910            (void) XWithdrawWindow(display,windows->info.id,
15911              windows->info.screen);
15912            windows->icon.mapped=MagickTrue;
15913            break;
15914          }
15915        if (event.xmap.window == windows->command.id)
15916          {
15917            windows->command.mapped=MagickTrue;
15918            break;
15919          }
15920        if (event.xmap.window == windows->popup.id)
15921          {
15922            windows->popup.mapped=MagickTrue;
15923            break;
15924          }
15925        if (event.xmap.window == windows->widget.id)
15926          {
15927            windows->widget.mapped=MagickTrue;
15928            break;
15929          }
15930        break;
15931      }
15932      case MappingNotify:
15933      {
15934        (void) XRefreshKeyboardMapping(&event.xmapping);
15935        break;
15936      }
15937      case NoExpose:
15938        break;
15939      case PropertyNotify:
15940      {
15941        Atom
15942          type;
15943
15944        int
15945          format,
15946          status;
15947
15948        unsigned char
15949          *data;
15950
15951        unsigned long
15952          after,
15953          length;
15954
15955        if (display_image->debug != MagickFalse )
15956          (void) LogMagickEvent(X11Event,GetMagickModule(),
15957            "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15958            event.xproperty.atom,event.xproperty.state);
15959        if (event.xproperty.atom != windows->im_remote_command)
15960          break;
15961        /*
15962          Display image named by the remote command protocol.
15963        */
15964        status=XGetWindowProperty(display,event.xproperty.window,
15965          event.xproperty.atom,0L,(long) MagickPathExtent,MagickFalse,(Atom)
15966          AnyPropertyType,&type,&format,&length,&after,&data);
15967        if ((status != Success) || (length == 0))
15968          break;
15969        if (LocaleCompare((char *) data,"-quit") == 0)
15970          {
15971            XClientMessage(display,windows->image.id,windows->im_protocols,
15972              windows->im_exit,CurrentTime);
15973            (void) XFree((void *) data);
15974            break;
15975          }
15976        (void) CopyMagickString(resource_info->image_info->filename,
15977          (char *) data,MagickPathExtent);
15978        (void) XFree((void *) data);
15979        nexus=ReadImage(resource_info->image_info,exception);
15980        CatchException(exception);
15981        if (nexus != (Image *) NULL)
15982          *state|=NextImageState | ExitState;
15983        break;
15984      }
15985      case ReparentNotify:
15986      {
15987        if (display_image->debug != MagickFalse )
15988          (void) LogMagickEvent(X11Event,GetMagickModule(),
15989            "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15990            event.xreparent.window);
15991        break;
15992      }
15993      case UnmapNotify:
15994      {
15995        if (display_image->debug != MagickFalse )
15996          (void) LogMagickEvent(X11Event,GetMagickModule(),
15997            "Unmap Notify: 0x%lx",event.xunmap.window);
15998        if (event.xunmap.window == windows->backdrop.id)
15999          {
16000            windows->backdrop.mapped=MagickFalse;
16001            break;
16002          }
16003        if (event.xunmap.window == windows->image.id)
16004          {
16005            windows->image.mapped=MagickFalse;
16006            break;
16007          }
16008        if (event.xunmap.window == windows->magnify.id)
16009          {
16010            windows->magnify.mapped=MagickFalse;
16011            break;
16012          }
16013        if (event.xunmap.window == windows->pan.id)
16014          {
16015            windows->pan.mapped=MagickFalse;
16016            break;
16017          }
16018        if (event.xunmap.window == windows->info.id)
16019          {
16020            windows->info.mapped=MagickFalse;
16021            break;
16022          }
16023        if (event.xunmap.window == windows->icon.id)
16024          {
16025            if (map_info->colormap == icon_map->colormap)
16026              XConfigureImageColormap(display,resource_info,windows,
16027                display_image,exception);
16028            (void) XFreeStandardColormap(display,icon_visual,icon_map,
16029              icon_pixel);
16030            windows->icon.mapped=MagickFalse;
16031            break;
16032          }
16033        if (event.xunmap.window == windows->command.id)
16034          {
16035            windows->command.mapped=MagickFalse;
16036            break;
16037          }
16038        if (event.xunmap.window == windows->popup.id)
16039          {
16040            if (windows->backdrop.id != (Window) NULL)
16041              (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16042                CurrentTime);
16043            windows->popup.mapped=MagickFalse;
16044            break;
16045          }
16046        if (event.xunmap.window == windows->widget.id)
16047          {
16048            if (windows->backdrop.id != (Window) NULL)
16049              (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16050                CurrentTime);
16051            windows->widget.mapped=MagickFalse;
16052            break;
16053          }
16054        break;
16055      }
16056      default:
16057      {
16058        if (display_image->debug != MagickFalse )
16059          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
16060            event.type);
16061        break;
16062      }
16063    }
16064  } while (!(*state & ExitState));
16065  if ((*state & ExitState) == 0)
16066    (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
16067      &display_image,exception);
16068  else
16069    if (resource_info->confirm_edit != MagickFalse )
16070      {
16071        /*
16072          Query user if image has changed.
16073        */
16074        if ((resource_info->immutable == MagickFalse) &&
16075            display_image->taint != MagickFalse)
16076          {
16077            int
16078              status;
16079
16080            status=XConfirmWidget(display,windows,"Your image changed.",
16081              "Do you want to save it");
16082            if (status == 0)
16083              *state&=(~ExitState);
16084            else
16085              if (status > 0)
16086                (void) XMagickCommand(display,resource_info,windows,SaveCommand,
16087                  &display_image,exception);
16088          }
16089      }
16090  if ((windows->visual_info->klass == GrayScale) ||
16091      (windows->visual_info->klass == PseudoColor) ||
16092      (windows->visual_info->klass == DirectColor))
16093    {
16094      /*
16095        Withdraw pan and Magnify window.
16096      */
16097      if (windows->info.mapped != MagickFalse )
16098        (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16099      if (windows->magnify.mapped != MagickFalse )
16100        (void) XWithdrawWindow(display,windows->magnify.id,
16101          windows->magnify.screen);
16102      if (windows->command.mapped != MagickFalse )
16103        (void) XWithdrawWindow(display,windows->command.id,
16104          windows->command.screen);
16105    }
16106  if (windows->pan.mapped != MagickFalse )
16107    (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16108  if (resource_info->backdrop == MagickFalse)
16109    if (windows->backdrop.mapped)
16110      {
16111        (void) XWithdrawWindow(display,windows->backdrop.id,
16112          windows->backdrop.screen);
16113        (void) XDestroyWindow(display,windows->backdrop.id);
16114        windows->backdrop.id=(Window) NULL;
16115        (void) XWithdrawWindow(display,windows->image.id,
16116          windows->image.screen);
16117        (void) XDestroyWindow(display,windows->image.id);
16118        windows->image.id=(Window) NULL;
16119      }
16120  XSetCursorState(display,windows,MagickTrue);
16121  XCheckRefreshWindows(display,windows);
16122  if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16123    *state&=(~ExitState);
16124  if (*state & ExitState)
16125    {
16126      /*
16127        Free Standard Colormap.
16128      */
16129      (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16130      if (resource_info->map_type == (char *) NULL)
16131        (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16132      /*
16133        Free X resources.
16134      */
16135      if (resource_info->copy_image != (Image *) NULL)
16136        {
16137          resource_info->copy_image=DestroyImage(resource_info->copy_image);
16138          resource_info->copy_image=NewImageList();
16139        }
16140      DestroyXResources();
16141    }
16142  (void) XSync(display,MagickFalse);
16143  /*
16144    Restore our progress monitor and warning handlers.
16145  */
16146  (void) SetErrorHandler(warning_handler);
16147  (void) SetWarningHandler(warning_handler);
16148  /*
16149    Change to home directory.
16150  */
16151  directory=getcwd(working_directory,MagickPathExtent);
16152  (void) directory;
16153  {
16154    int
16155      status;
16156
16157    if (*resource_info->home_directory == '\0')
16158      (void) CopyMagickString(resource_info->home_directory,".",MagickPathExtent);
16159    status=chdir(resource_info->home_directory);
16160    if (status == -1)
16161      (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
16162        "UnableToOpenFile","%s",resource_info->home_directory);
16163  }
16164  *image=display_image;
16165  return(nexus);
16166}
16167#else
16168
16169/*
16170%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16171%                                                                             %
16172%                                                                             %
16173%                                                                             %
16174+   D i s p l a y I m a g e s                                                 %
16175%                                                                             %
16176%                                                                             %
16177%                                                                             %
16178%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16179%
16180%  DisplayImages() displays an image sequence to any X window screen.  It
16181%  returns a value other than 0 if successful.  Check the exception member
16182%  of image to determine the reason for any failure.
16183%
16184%  The format of the DisplayImages method is:
16185%
16186%      MagickBooleanType DisplayImages(const ImageInfo *image_info,
16187%        Image *images,ExceptionInfo *exception)
16188%
16189%  A description of each parameter follows:
16190%
16191%    o image_info: the image info.
16192%
16193%    o image: the image.
16194%
16195%    o exception: return any errors or warnings in this structure.
16196%
16197*/
16198MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16199  Image *image,ExceptionInfo *exception)
16200{
16201  assert(image_info != (const ImageInfo *) NULL);
16202  assert(image_info->signature == MagickCoreSignature);
16203  assert(image != (Image *) NULL);
16204  assert(image->signature == MagickCoreSignature);
16205  (void) image_info;
16206  if (image->debug != MagickFalse )
16207    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16208  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16209    "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image->filename);
16210  return(MagickFalse);
16211}
16212
16213/*
16214%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16215%                                                                             %
16216%                                                                             %
16217%                                                                             %
16218+   R e m o t e D i s p l a y C o m m a n d                                   %
16219%                                                                             %
16220%                                                                             %
16221%                                                                             %
16222%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16223%
16224%  RemoteDisplayCommand() encourages a remote display program to display the
16225%  specified image filename.
16226%
16227%  The format of the RemoteDisplayCommand method is:
16228%
16229%      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16230%        const char *window,const char *filename,ExceptionInfo *exception)
16231%
16232%  A description of each parameter follows:
16233%
16234%    o image_info: the image info.
16235%
16236%    o window: Specifies the name or id of an X window.
16237%
16238%    o filename: the name of the image filename to display.
16239%
16240%    o exception: return any errors or warnings in this structure.
16241%
16242*/
16243MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16244  const char *window,const char *filename,ExceptionInfo *exception)
16245{
16246  assert(image_info != (const ImageInfo *) NULL);
16247  assert(image_info->signature == MagickCoreSignature);
16248  assert(filename != (char *) NULL);
16249  (void) window;
16250  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16251  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16252    "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image_info->filename);
16253  return(MagickFalse);
16254}
16255#endif
16256