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