display.c revision 4d246fc57cf90dc0ac7addcd8192d5c013fce640
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-2014 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/option.h"
76#include "MagickCore/paint.h"
77#include "MagickCore/pixel.h"
78#include "MagickCore/pixel-accessor.h"
79#include "MagickCore/PreRvIcccm.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/threshold.h"
92#include "MagickCore/utility.h"
93#include "MagickCore/utility-private.h"
94#include "MagickCore/version.h"
95#include "MagickCore/widget.h"
96#include "MagickCore/widget-private.h"
97#include "MagickCore/xwindow.h"
98#include "MagickCore/xwindow-private.h"
99
100#if defined(MAGICKCORE_X11_DELEGATE)
101/*
102  Define declarations.
103*/
104#define MaxColors  MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
105
106/*
107  Constant declarations.
108*/
109static const unsigned char
110  HighlightBitmap[8] =
111  {
112    0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
113  },
114  OpaqueBitmap[8] =
115  {
116    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
117  },
118  ShadowBitmap[8] =
119  {
120    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
121  };
122
123static const char
124  *PageSizes[] =
125  {
126    "Letter",
127    "Tabloid",
128    "Ledger",
129    "Legal",
130    "Statement",
131    "Executive",
132    "A3",
133    "A4",
134    "A5",
135    "B4",
136    "B5",
137    "Folio",
138    "Quarto",
139    "10x14",
140    (char *) NULL
141  };
142
143/*
144  Help widget declarations.
145*/
146static const char
147  *ImageAnnotateHelp[] =
148  {
149    "In annotate mode, the Command widget has these options:",
150    "",
151    "    Font Name",
152    "      fixed",
153    "      variable",
154    "      5x8",
155    "      6x10",
156    "      7x13bold",
157    "      8x13bold",
158    "      9x15bold",
159    "      10x20",
160    "      12x24",
161    "      Browser...",
162    "    Font Color",
163    "      black",
164    "      blue",
165    "      cyan",
166    "      green",
167    "      gray",
168    "      red",
169    "      magenta",
170    "      yellow",
171    "      white",
172    "      transparent",
173    "      Browser...",
174    "    Font Color",
175    "      black",
176    "      blue",
177    "      cyan",
178    "      green",
179    "      gray",
180    "      red",
181    "      magenta",
182    "      yellow",
183    "      white",
184    "      transparent",
185    "      Browser...",
186    "    Rotate Text",
187    "      -90",
188    "      -45",
189    "      -30",
190    "      0",
191    "      30",
192    "      45",
193    "      90",
194    "      180",
195    "      Dialog...",
196    "    Help",
197    "    Dismiss",
198    "",
199    "Choose a font name from the Font Name sub-menu.  Additional",
200    "font names can be specified with the font browser.  You can",
201    "change the menu names by setting the X resources font1",
202    "through font9.",
203    "",
204    "Choose a font color from the Font Color sub-menu.",
205    "Additional font colors can be specified with the color",
206    "browser.  You can change the menu colors by setting the X",
207    "resources pen1 through pen9.",
208    "",
209    "If you select the color browser and press Grab, you can",
210    "choose the font color by moving the pointer to the desired",
211    "color on the screen and press any button.",
212    "",
213    "If you choose to rotate the text, choose Rotate Text from the",
214    "menu and select an angle.  Typically you will only want to",
215    "rotate one line of text at a time.  Depending on the angle you",
216    "choose, subsequent lines may end up overwriting each other.",
217    "",
218    "Choosing a font and its color is optional.  The default font",
219    "is fixed and the default color is black.  However, you must",
220    "choose a location to begin entering text and press button 1.",
221    "An underscore character will appear at the location of the",
222    "pointer.  The cursor changes to a pencil to indicate you are",
223    "in text mode.  To exit immediately, press Dismiss.",
224    "",
225    "In text mode, any key presses will display the character at",
226    "the location of the underscore and advance the underscore",
227    "cursor.  Enter your text and once completed press Apply to",
228    "finish your image annotation.  To correct errors press BACK",
229    "SPACE.  To delete an entire line of text, press DELETE.  Any",
230    "text that exceeds the boundaries of the image window is",
231    "automagically continued onto the next line.",
232    "",
233    "The actual color you request for the font is saved in the",
234    "image.  However, the color that appears in your image window",
235    "may be different.  For example, on a monochrome screen the",
236    "text will appear black or white even if you choose the color",
237    "red as the font color.  However, the image saved to a file",
238    "with -write is written with red lettering.  To assure the",
239    "correct color text in the final image, any PseudoClass image",
240    "is promoted to DirectClass (see miff(5)).  To force a",
241    "PseudoClass image to remain PseudoClass, use -colors.",
242    (char *) NULL,
243  },
244  *ImageChopHelp[] =
245  {
246    "In chop mode, the Command widget has these options:",
247    "",
248    "    Direction",
249    "      horizontal",
250    "      vertical",
251    "    Help",
252    "    Dismiss",
253    "",
254    "If the you choose the horizontal direction (this the",
255    "default), the area of the image between the two horizontal",
256    "endpoints of the chop line is removed.  Otherwise, the area",
257    "of the image between the two vertical endpoints of the chop",
258    "line is removed.",
259    "",
260    "Select a location within the image window to begin your chop,",
261    "press and hold any button.  Next, move the pointer to",
262    "another location in the image.  As you move a line will",
263    "connect the initial location and the pointer.  When you",
264    "release the button, the area within the image to chop is",
265    "determined by which direction you choose from the Command",
266    "widget.",
267    "",
268    "To cancel the image chopping, move the pointer back to the",
269    "starting point of the line and release the button.",
270    (char *) NULL,
271  },
272  *ImageColorEditHelp[] =
273  {
274    "In color edit mode, the Command widget has these options:",
275    "",
276    "    Method",
277    "      point",
278    "      replace",
279    "      floodfill",
280    "      filltoborder",
281    "      reset",
282    "    Pixel Color",
283    "      black",
284    "      blue",
285    "      cyan",
286    "      green",
287    "      gray",
288    "      red",
289    "      magenta",
290    "      yellow",
291    "      white",
292    "      Browser...",
293    "    Border Color",
294    "      black",
295    "      blue",
296    "      cyan",
297    "      green",
298    "      gray",
299    "      red",
300    "      magenta",
301    "      yellow",
302    "      white",
303    "      Browser...",
304    "    Fuzz",
305    "      0%",
306    "      2%",
307    "      5%",
308    "      10%",
309    "      15%",
310    "      Dialog...",
311    "    Undo",
312    "    Help",
313    "    Dismiss",
314    "",
315    "Choose a color editing method from the Method sub-menu",
316    "of the Command widget.  The point method recolors any pixel",
317    "selected with the pointer until the button is released.  The",
318    "replace method recolors any pixel that matches the color of",
319    "the pixel you select with a button press.  Floodfill recolors",
320    "any pixel that matches the color of the pixel you select with",
321    "a button press and is a neighbor.  Whereas filltoborder recolors",
322    "any neighbor pixel that is not the border color.  Finally reset",
323    "changes the entire image to the designated color.",
324    "",
325    "Next, choose a pixel color from the Pixel Color sub-menu.",
326    "Additional pixel colors can be specified with the color",
327    "browser.  You can change the menu colors by setting the X",
328    "resources pen1 through pen9.",
329    "",
330    "Now press button 1 to select a pixel within the image window",
331    "to change its color.  Additional pixels may be recolored as",
332    "prescribed by the method you choose.",
333    "",
334    "If the Magnify widget is mapped, it can be helpful in positioning",
335    "your pointer within the image (refer to button 2).",
336    "",
337    "The actual color you request for the pixels is saved in the",
338    "image.  However, the color that appears in your image window",
339    "may be different.  For example, on a monochrome screen the",
340    "pixel will appear black or white even if you choose the",
341    "color red as the pixel color.  However, the image saved to a",
342    "file with -write is written with red pixels.  To assure the",
343    "correct color text in the final image, any PseudoClass image",
344    "is promoted to DirectClass (see miff(5)).  To force a",
345    "PseudoClass image to remain PseudoClass, use -colors.",
346    (char *) NULL,
347  },
348  *ImageCompositeHelp[] =
349  {
350    "First a widget window is displayed requesting you to enter an",
351    "image name. Press Composite, Grab or type a file name.",
352    "Press Cancel if you choose not to create a composite image.",
353    "When you choose Grab, move the pointer to the desired window",
354    "and press any button.",
355    "",
356    "If the Composite image does not have any matte information,",
357    "you are informed and the file browser is displayed again.",
358    "Enter the name of a mask image.  The image is typically",
359    "grayscale and the same size as the composite image.  If the",
360    "image is not grayscale, it is converted to grayscale and the",
361    "resulting intensities are used as matte information.",
362    "",
363    "A small window appears showing the location of the cursor in",
364    "the image window. You are now in composite mode.  To exit",
365    "immediately, press Dismiss.  In composite mode, the Command",
366    "widget has these options:",
367    "",
368    "    Operators",
369    "      Over",
370    "      In",
371    "      Out",
372    "      Atop",
373    "      Xor",
374    "      Plus",
375    "      Minus",
376    "      Add",
377    "      Subtract",
378    "      Difference",
379    "      Multiply",
380    "      Bumpmap",
381    "      Copy",
382    "      CopyRed",
383    "      CopyGreen",
384    "      CopyBlue",
385    "      CopyOpacity",
386    "      Clear",
387    "    Dissolve",
388    "    Displace",
389    "    Help",
390    "    Dismiss",
391    "",
392    "Choose a composite operation from the Operators sub-menu of",
393    "the Command widget.  How each operator behaves is described",
394    "below.  Image window is the image currently displayed on",
395    "your X server and image is the image obtained with the File",
396    "Browser widget.",
397    "",
398    "Over     The result is the union of the two image shapes,",
399    "         with image obscuring image window in the region of",
400    "         overlap.",
401    "",
402    "In       The result is simply image cut by the shape of",
403    "         image window.  None of the image data of image",
404    "         window is in the result.",
405    "",
406    "Out      The resulting image is image with the shape of",
407    "         image window cut out.",
408    "",
409    "Atop     The result is the same shape as image image window,",
410    "         with image obscuring image window where the image",
411    "         shapes overlap.  Note this differs from over",
412    "         because the portion of image outside image window's",
413    "         shape does not appear in the result.",
414    "",
415    "Xor      The result is the image data from both image and",
416    "         image window that is outside the overlap region.",
417    "         The overlap region is blank.",
418    "",
419    "Plus     The result is just the sum of the image data.",
420    "         Output values are cropped to QuantumRange (no overflow).",
421    "",
422    "Minus    The result of image - image window, with underflow",
423    "         cropped to zero.",
424    "",
425    "Add      The result of image + image window, with overflow",
426    "         wrapping around (mod 256).",
427    "",
428    "Subtract The result of image - image window, with underflow",
429    "         wrapping around (mod 256).  The add and subtract",
430    "         operators can be used to perform reversible",
431    "         transformations.",
432    "",
433    "Difference",
434    "         The result of abs(image - image window).  This",
435    "         useful for comparing two very similar images.",
436    "",
437    "Multiply",
438    "         The result of image * image window.  This",
439    "         useful for the creation of drop-shadows.",
440    "",
441    "Bumpmap  The result of surface normals from image * image",
442    "         window.",
443    "",
444    "Copy     The resulting image is image window replaced with",
445    "         image.  Here the matte information is ignored.",
446    "",
447    "CopyRed  The red layer of the image window is replace with",
448    "         the red layer of the image.  The other layers are",
449    "         untouched.",
450    "",
451    "CopyGreen",
452    "         The green layer of the image window is replace with",
453    "         the green layer of the image.  The other layers are",
454    "         untouched.",
455    "",
456    "CopyBlue The blue layer of the image window is replace with",
457    "         the blue layer of the image.  The other layers are",
458    "         untouched.",
459    "",
460    "CopyOpacity",
461    "         The matte layer of the image window is replace with",
462    "         the matte layer of the image.  The other layers are",
463    "         untouched.",
464    "",
465    "The image compositor requires a matte, or alpha channel in",
466    "the image for some operations.  This extra channel usually",
467    "defines a mask which represents a sort of a cookie-cutter",
468    "for the image.  This the case when matte is opaque (full",
469    "coverage) for pixels inside the shape, zero outside, and",
470    "between 0 and QuantumRange on the boundary.  If image does not",
471    "have a matte channel, it is initialized with 0 for any pixel",
472    "matching in color to pixel location (0,0), otherwise QuantumRange.",
473    "",
474    "If you choose Dissolve, the composite operator becomes Over.  The",
475    "image matte channel percent transparency is initialized to factor.",
476    "The image window is initialized to (100-factor). Where factor is the",
477    "value you specify in the Dialog widget.",
478    "",
479    "Displace shifts the image pixels as defined by a displacement",
480    "map.  With this option, image is used as a displacement map.",
481    "Black, within the displacement map, is a maximum positive",
482    "displacement.  White is a maximum negative displacement and",
483    "middle gray is neutral.  The displacement is scaled to determine",
484    "the pixel shift.  By default, the displacement applies in both the",
485    "horizontal and vertical directions.  However, if you specify a mask,",
486    "image is the horizontal X displacement and mask the vertical Y",
487    "displacement.",
488    "",
489    "Note that matte information for image window is not retained",
490    "for colormapped X server visuals (e.g. StaticColor,",
491    "StaticColor, GrayScale, PseudoColor).  Correct compositing",
492    "behavior may require a TrueColor or DirectColor visual or a",
493    "Standard Colormap.",
494    "",
495    "Choosing a composite operator is optional.  The default",
496    "operator is replace.  However, you must choose a location to",
497    "composite your image and press button 1.  Press and hold the",
498    "button before releasing and an outline of the image will",
499    "appear to help you identify your location.",
500    "",
501    "The actual colors of the composite image is saved.  However,",
502    "the color that appears in image window may be different.",
503    "For example, on a monochrome screen image window will appear",
504    "black or white even though your composited image may have",
505    "many colors.  If the image is saved to a file it is written",
506    "with the correct colors.  To assure the correct colors are",
507    "saved in the final image, any PseudoClass image is promoted",
508    "to DirectClass (see miff(5)).  To force a PseudoClass image",
509    "to remain PseudoClass, use -colors.",
510    (char *) NULL,
511  },
512  *ImageCutHelp[] =
513  {
514    "In cut mode, the Command widget has these options:",
515    "",
516    "    Help",
517    "    Dismiss",
518    "",
519    "To define a cut region, press button 1 and drag.  The",
520    "cut region is defined by a highlighted rectangle that",
521    "expands or contracts as it follows the pointer.  Once you",
522    "are satisfied with the cut region, release the button.",
523    "You are now in rectify mode.  In rectify mode, the Command",
524    "widget has these options:",
525    "",
526    "    Cut",
527    "    Help",
528    "    Dismiss",
529    "",
530    "You can make adjustments by moving the pointer to one of the",
531    "cut rectangle corners, pressing a button, and dragging.",
532    "Finally, press Cut to commit your copy region.  To",
533    "exit without cutting the image, press Dismiss.",
534    (char *) NULL,
535  },
536  *ImageCopyHelp[] =
537  {
538    "In copy mode, the Command widget has these options:",
539    "",
540    "    Help",
541    "    Dismiss",
542    "",
543    "To define a copy region, press button 1 and drag.  The",
544    "copy region is defined by a highlighted rectangle that",
545    "expands or contracts as it follows the pointer.  Once you",
546    "are satisfied with the copy region, release the button.",
547    "You are now in rectify mode.  In rectify mode, the Command",
548    "widget has these options:",
549    "",
550    "    Copy",
551    "    Help",
552    "    Dismiss",
553    "",
554    "You can make adjustments by moving the pointer to one of the",
555    "copy rectangle corners, pressing a button, and dragging.",
556    "Finally, press Copy to commit your copy region.  To",
557    "exit without copying the image, press Dismiss.",
558    (char *) NULL,
559  },
560  *ImageCropHelp[] =
561  {
562    "In crop mode, the Command widget has these options:",
563    "",
564    "    Help",
565    "    Dismiss",
566    "",
567    "To define a cropping region, press button 1 and drag.  The",
568    "cropping region is defined by a highlighted rectangle that",
569    "expands or contracts as it follows the pointer.  Once you",
570    "are satisfied with the cropping region, release the button.",
571    "You are now in rectify mode.  In rectify mode, the Command",
572    "widget has these options:",
573    "",
574    "    Crop",
575    "    Help",
576    "    Dismiss",
577    "",
578    "You can make adjustments by moving the pointer to one of the",
579    "cropping rectangle corners, pressing a button, and dragging.",
580    "Finally, press Crop to commit your cropping region.  To",
581    "exit without cropping the image, press Dismiss.",
582    (char *) NULL,
583  },
584  *ImageDrawHelp[] =
585  {
586    "The cursor changes to a crosshair to indicate you are in",
587    "draw mode.  To exit immediately, press Dismiss.  In draw mode,",
588    "the Command widget has these options:",
589    "",
590    "    Element",
591    "      point",
592    "      line",
593    "      rectangle",
594    "      fill rectangle",
595    "      circle",
596    "      fill circle",
597    "      ellipse",
598    "      fill ellipse",
599    "      polygon",
600    "      fill polygon",
601    "    Color",
602    "      black",
603    "      blue",
604    "      cyan",
605    "      green",
606    "      gray",
607    "      red",
608    "      magenta",
609    "      yellow",
610    "      white",
611    "      transparent",
612    "      Browser...",
613    "    Stipple",
614    "      Brick",
615    "      Diagonal",
616    "      Scales",
617    "      Vertical",
618    "      Wavy",
619    "      Translucent",
620    "      Opaque",
621    "      Open...",
622    "    Width",
623    "      1",
624    "      2",
625    "      4",
626    "      8",
627    "      16",
628    "      Dialog...",
629    "    Undo",
630    "    Help",
631    "    Dismiss",
632    "",
633    "Choose a drawing primitive from the Element sub-menu.",
634    "",
635    "Choose a color from the Color sub-menu.  Additional",
636    "colors can be specified with the color browser.",
637    "",
638    "If you choose the color browser and press Grab, you can",
639    "select the color by moving the pointer to the desired",
640    "color on the screen and press any button.  The transparent",
641    "color updates the image matte channel and is useful for",
642    "image compositing.",
643    "",
644    "Choose a stipple, if appropriate, from the Stipple sub-menu.",
645    "Additional stipples can be specified with the file browser.",
646    "Stipples obtained from the file browser must be on disk in the",
647    "X11 bitmap format.",
648    "",
649    "Choose a width, if appropriate, from the Width sub-menu.  To",
650    "choose a specific width select the Dialog widget.",
651    "",
652    "Choose a point in the Image window and press button 1 and",
653    "hold.  Next, move the pointer to another location in the",
654    "image.  As you move, a line connects the initial location and",
655    "the pointer.  When you release the button, the image is",
656    "updated with the primitive you just drew.  For polygons, the",
657    "image is updated when you press and release the button without",
658    "moving the pointer.",
659    "",
660    "To cancel image drawing, move the pointer back to the",
661    "starting point of the line and release the button.",
662    (char *) NULL,
663  },
664  *DisplayHelp[] =
665  {
666    "BUTTONS",
667    "  The effects of each button press is described below.  Three",
668    "  buttons are required.  If you have a two button mouse,",
669    "  button 1 and 3 are returned.  Press ALT and button 3 to",
670    "  simulate button 2.",
671    "",
672    "  1    Press this button to map or unmap the Command widget.",
673    "",
674    "  2    Press and drag to define a region of the image to",
675    "       magnify.",
676    "",
677    "  3    Press and drag to choose from a select set of commands.",
678    "       This button behaves differently if the image being",
679    "       displayed is a visual image directory.  Here, choose a",
680    "       particular tile of the directory and press this button and",
681    "       drag to select a command from a pop-up menu.  Choose from",
682    "       these menu items:",
683    "",
684    "           Open",
685    "           Next",
686    "           Former",
687    "           Delete",
688    "           Update",
689    "",
690    "       If you choose Open, the image represented by the tile is",
691    "       displayed.  To return to the visual image directory, choose",
692    "       Next from the Command widget.  Next and Former moves to the",
693    "       next or former image respectively.  Choose Delete to delete",
694    "       a particular image tile.  Finally, choose Update to",
695    "       synchronize all the image tiles with their respective",
696    "       images.",
697    "",
698    "COMMAND WIDGET",
699    "  The Command widget lists a number of sub-menus and commands.",
700    "  They are",
701    "",
702    "      File",
703    "        Open...",
704    "        Next",
705    "        Former",
706    "        Select...",
707    "        Save...",
708    "        Print...",
709    "        Delete...",
710    "        New...",
711    "        Visual Directory...",
712    "        Quit",
713    "      Edit",
714    "        Undo",
715    "        Redo",
716    "        Cut",
717    "        Copy",
718    "        Paste",
719    "      View",
720    "        Half Size",
721    "        Original Size",
722    "        Double Size",
723    "        Resize...",
724    "        Apply",
725    "        Refresh",
726    "        Restore",
727    "      Transform",
728    "        Crop",
729    "        Chop",
730    "        Flop",
731    "        Flip",
732    "        Rotate Right",
733    "        Rotate Left",
734    "        Rotate...",
735    "        Shear...",
736    "        Roll...",
737    "        Trim Edges",
738    "      Enhance",
739    "        Brightness...",
740    "        Saturation...",
741    "        Hue...",
742    "        Gamma...",
743    "        Sharpen...",
744    "        Dull",
745    "        Contrast Stretch...",
746    "        Sigmoidal Contrast...",
747    "        Normalize",
748    "        Equalize",
749    "        Negate",
750    "        Grayscale",
751    "        Map...",
752    "        Quantize...",
753    "      Effects",
754    "        Despeckle",
755    "        Emboss",
756    "        Reduce Noise",
757    "        Add Noise",
758    "        Sharpen...",
759    "        Blur...",
760    "        Threshold...",
761    "        Edge Detect...",
762    "        Spread...",
763    "        Shade...",
764    "        Painting...",
765    "        Segment...",
766    "      F/X",
767    "        Solarize...",
768    "        Sepia Tone...",
769    "        Swirl...",
770    "        Implode...",
771    "        Vignette...",
772    "        Wave...",
773    "        Oil Painting...",
774    "        Charcoal Drawing...",
775    "      Image Edit",
776    "        Annotate...",
777    "        Draw...",
778    "        Color...",
779    "        Matte...",
780    "        Composite...",
781    "        Add Border...",
782    "        Add Frame...",
783    "        Comment...",
784    "        Launch...",
785    "        Region of Interest...",
786    "      Miscellany",
787    "        Image Info",
788    "        Zoom Image",
789    "        Show Preview...",
790    "        Show Histogram",
791    "        Show Matte",
792    "        Background...",
793    "        Slide Show",
794    "        Preferences...",
795    "      Help",
796    "        Overview",
797    "        Browse Documentation",
798    "        About Display",
799    "",
800    "  Menu items with a indented triangle have a sub-menu.  They",
801    "  are represented above as the indented items.  To access a",
802    "  sub-menu item, move the pointer to the appropriate menu and",
803    "  press a button and drag.  When you find the desired sub-menu",
804    "  item, release the button and the command is executed.  Move",
805    "  the pointer away from the sub-menu if you decide not to",
806    "  execute a particular command.",
807    "",
808    "KEYBOARD ACCELERATORS",
809    "  Accelerators are one or two key presses that effect a",
810    "  particular command.  The keyboard accelerators that",
811    "  display(1) understands is:",
812    "",
813    "  Ctl+O     Press to open an image from a file.",
814    "",
815    "  space     Press to display the next image.",
816    "",
817    "            If the image is a multi-paged document such as a Postscript",
818    "            document, you can skip ahead several pages by preceding",
819    "            this command with a number.  For example to display the",
820    "            third page beyond the current page, press 3<space>.",
821    "",
822    "  backspace Press to display the former image.",
823    "",
824    "            If the image is a multi-paged document such as a Postscript",
825    "            document, you can skip behind several pages by preceding",
826    "            this command with a number.  For example to display the",
827    "            third page preceding the current page, press 3<backspace>.",
828    "",
829    "  Ctl+S     Press to write the image to a file.",
830    "",
831    "  Ctl+P     Press to print the image to a Postscript printer.",
832    "",
833    "  Ctl+D     Press to delete an image file.",
834    "",
835    "  Ctl+N     Press to create a blank canvas.",
836    "",
837    "  Ctl+Q     Press to discard all images and exit program.",
838    "",
839    "  Ctl+Z     Press to undo last image transformation.",
840    "",
841    "  Ctl+R     Press to redo last image transformation.",
842    "",
843    "  Ctl+X     Press to cut a region of the image.",
844    "",
845    "  Ctl+C     Press to copy a region of the image.",
846    "",
847    "  Ctl+V     Press to paste a region to the image.",
848    "",
849    "  <         Press to half the image size.",
850    "",
851    "  -         Press to return to the original image size.",
852    "",
853    "  >         Press to double the image size.",
854    "",
855    "  %         Press to resize the image to a width and height you",
856    "            specify.",
857    "",
858    "Cmd-A       Press to make any image transformations permanent."
859    "",
860    "            By default, any image size transformations are applied",
861    "            to the original image to create the image displayed on",
862    "            the X server.  However, the transformations are not",
863    "            permanent (i.e. the original image does not change",
864    "            size only the X image does).  For example, if you",
865    "            press > the X image will appear to double in size,",
866    "            but the original image will in fact remain the same size.",
867    "            To force the original image to double in size, press >",
868    "            followed by Cmd-A.",
869    "",
870    "  @         Press to refresh the image window.",
871    "",
872    "  C         Press to cut out a rectangular region of the image.",
873    "",
874    "  [         Press to chop the image.",
875    "",
876    "  H         Press to flop image in the horizontal direction.",
877    "",
878    "  V         Press to flip image in the vertical direction.",
879    "",
880    "  /         Press to rotate the image 90 degrees clockwise.",
881    "",
882    " \\         Press to rotate the image 90 degrees counter-clockwise.",
883    "",
884    "  *         Press to rotate the image the number of degrees you",
885    "            specify.",
886    "",
887    "  S         Press to shear the image the number of degrees you",
888    "            specify.",
889    "",
890    "  R         Press to roll the image.",
891    "",
892    "  T         Press to trim the image edges.",
893    "",
894    "  Shft-H    Press to vary the image hue.",
895    "",
896    "  Shft-S    Press to vary the color saturation.",
897    "",
898    "  Shft-L    Press to vary the color brightness.",
899    "",
900    "  Shft-G    Press to gamma correct the image.",
901    "",
902    "  Shft-C    Press to sharpen the image contrast.",
903    "",
904    "  Shft-Z    Press to dull the image contrast.",
905    "",
906    "  =         Press to perform histogram equalization on the image.",
907    "",
908    "  Shft-N    Press to perform histogram normalization on the image.",
909    "",
910    "  Shft-~    Press to negate the colors of the image.",
911    "",
912    "  .         Press to convert the image colors to gray.",
913    "",
914    "  Shft-#    Press to set the maximum number of unique colors in the",
915    "            image.",
916    "",
917    "  F2        Press to reduce the speckles in an image.",
918    "",
919    "  F3        Press to eliminate peak noise from an image.",
920    "",
921    "  F4        Press to add noise to an image.",
922    "",
923    "  F5        Press to sharpen an image.",
924    "",
925    "  F6        Press to delete an image file.",
926    "",
927    "  F7        Press to threshold the image.",
928    "",
929    "  F8        Press to detect edges within an image.",
930    "",
931    "  F9        Press to emboss an image.",
932    "",
933    "  F10       Press to displace pixels by a random amount.",
934    "",
935    "  F11       Press to negate all pixels above the threshold level.",
936    "",
937    "  F12       Press to shade the image using a distant light source.",
938    "",
939    "  F13       Press to lighten or darken image edges to create a 3-D effect.",
940    "",
941    "  F14       Press to segment the image by color.",
942    "",
943    "  Meta-S    Press to swirl image pixels about the center.",
944    "",
945    "  Meta-I    Press to implode image pixels about the center.",
946    "",
947    "  Meta-W    Press to alter an image along a sine wave.",
948    "",
949    "  Meta-P    Press to simulate an oil painting.",
950    "",
951    "  Meta-C    Press to simulate a charcoal drawing.",
952    "",
953    "  Alt-A     Press to annotate the image with text.",
954    "",
955    "  Alt-D     Press to draw on an image.",
956    "",
957    "  Alt-P     Press to edit an image pixel color.",
958    "",
959    "  Alt-M     Press to edit the image matte information.",
960    "",
961    "  Alt-V     Press to composite the image with another.",
962    "",
963    "  Alt-B     Press to add a border to the image.",
964    "",
965    "  Alt-F     Press to add an ornamental border to the image.",
966    "",
967    "  Alt-Shft-!",
968    "            Press to add an image comment.",
969    "",
970    "  Ctl-A     Press to apply image processing techniques to a region",
971    "            of interest.",
972    "",
973    "  Shft-?    Press to display information about the image.",
974    "",
975    "  Shft-+    Press to map the zoom image window.",
976    "",
977    "  Shft-P    Press to preview an image enhancement, effect, or f/x.",
978    "",
979    "  F1        Press to display helpful information about display(1).",
980    "",
981    "  Find      Press to browse documentation about ImageMagick.",
982    "",
983    "  1-9       Press to change the level of magnification.",
984    "",
985    "  Use the arrow keys to move the image one pixel up, down,",
986    "  left, or right within the magnify window.  Be sure to first",
987    "  map the magnify window by pressing button 2.",
988    "",
989    "  Press ALT and one of the arrow keys to trim off one pixel",
990    "  from any side of the image.",
991    (char *) NULL,
992  },
993  *ImageMatteEditHelp[] =
994  {
995    "Matte information within an image is useful for some",
996    "operations such as image compositing (See IMAGE",
997    "COMPOSITING).  This extra channel usually defines a mask",
998    "which represents a sort of a cookie-cutter for the image.",
999    "This the case when matte is opaque (full coverage) for",
1000    "pixels inside the shape, zero outside, and between 0 and",
1001    "QuantumRange on the boundary.",
1002    "",
1003    "A small window appears showing the location of the cursor in",
1004    "the image window. You are now in matte edit mode.  To exit",
1005    "immediately, press Dismiss.  In matte edit mode, the Command",
1006    "widget has these options:",
1007    "",
1008    "    Method",
1009    "      point",
1010    "      replace",
1011    "      floodfill",
1012    "      filltoborder",
1013    "      reset",
1014    "    Border Color",
1015    "      black",
1016    "      blue",
1017    "      cyan",
1018    "      green",
1019    "      gray",
1020    "      red",
1021    "      magenta",
1022    "      yellow",
1023    "      white",
1024    "      Browser...",
1025    "    Fuzz",
1026    "      0%",
1027    "      2%",
1028    "      5%",
1029    "      10%",
1030    "      15%",
1031    "      Dialog...",
1032    "    Matte",
1033    "      Opaque",
1034    "      Transparent",
1035    "      Dialog...",
1036    "    Undo",
1037    "    Help",
1038    "    Dismiss",
1039    "",
1040    "Choose a matte editing method from the Method sub-menu of",
1041    "the Command widget.  The point method changes the matte value",
1042    "of any pixel selected with the pointer until the button is",
1043    "is released.  The replace method changes the matte value of",
1044    "any pixel that matches the color of the pixel you select with",
1045    "a button press.  Floodfill changes the matte value of any pixel",
1046    "that matches the color of the pixel you select with a button",
1047    "press and is a neighbor.  Whereas filltoborder changes the matte",
1048    "value any neighbor pixel that is not the border color.  Finally",
1049    "reset changes the entire image to the designated matte value.",
1050    "",
1051    "Choose Matte Value and pick Opaque or Transarent.  For other values",
1052    "select the Dialog entry.  Here a dialog appears requesting a matte",
1053    "value.  The value you select is assigned as the opacity value of the",
1054    "selected pixel or pixels.",
1055    "",
1056    "Now, press any button to select a pixel within the image",
1057    "window to change its matte value.",
1058    "",
1059    "If the Magnify widget is mapped, it can be helpful in positioning",
1060    "your pointer within the image (refer to button 2).",
1061    "",
1062    "Matte information is only valid in a DirectClass image.",
1063    "Therefore, any PseudoClass image is promoted to DirectClass",
1064    "(see miff(5)).  Note that matte information for PseudoClass",
1065    "is not retained for colormapped X server visuals (e.g.",
1066    "StaticColor, StaticColor, GrayScale, PseudoColor) unless you",
1067    "immediately save your image to a file (refer to Write).",
1068    "Correct matte editing behavior may require a TrueColor or",
1069    "DirectColor visual or a Standard Colormap.",
1070    (char *) NULL,
1071  },
1072  *ImagePanHelp[] =
1073  {
1074    "When an image exceeds the width or height of the X server",
1075    "screen, display maps a small panning icon.  The rectangle",
1076    "within the panning icon shows the area that is currently",
1077    "displayed in the image window.  To pan about the image,",
1078    "press any button and drag the pointer within the panning",
1079    "icon.  The pan rectangle moves with the pointer and the",
1080    "image window is updated to reflect the location of the",
1081    "rectangle within the panning icon.  When you have selected",
1082    "the area of the image you wish to view, release the button.",
1083    "",
1084    "Use the arrow keys to pan the image one pixel up, down,",
1085    "left, or right within the image window.",
1086    "",
1087    "The panning icon is withdrawn if the image becomes smaller",
1088    "than the dimensions of the X server screen.",
1089    (char *) NULL,
1090  },
1091  *ImagePasteHelp[] =
1092  {
1093    "A small window appears showing the location of the cursor in",
1094    "the image window. You are now in paste mode.  To exit",
1095    "immediately, press Dismiss.  In paste mode, the Command",
1096    "widget has these options:",
1097    "",
1098    "    Operators",
1099    "      over",
1100    "      in",
1101    "      out",
1102    "      atop",
1103    "      xor",
1104    "      plus",
1105    "      minus",
1106    "      add",
1107    "      subtract",
1108    "      difference",
1109    "      replace",
1110    "    Help",
1111    "    Dismiss",
1112    "",
1113    "Choose a composite operation from the Operators sub-menu of",
1114    "the Command widget.  How each operator behaves is described",
1115    "below.  Image window is the image currently displayed on",
1116    "your X server and image is the image obtained with the File",
1117    "Browser widget.",
1118    "",
1119    "Over     The result is the union of the two image shapes,",
1120    "         with image obscuring image window in the region of",
1121    "         overlap.",
1122    "",
1123    "In       The result is simply image cut by the shape of",
1124    "         image window.  None of the image data of image",
1125    "         window is in the result.",
1126    "",
1127    "Out      The resulting image is image with the shape of",
1128    "         image window cut out.",
1129    "",
1130    "Atop     The result is the same shape as image image window,",
1131    "         with image obscuring image window where the image",
1132    "         shapes overlap.  Note this differs from over",
1133    "         because the portion of image outside image window's",
1134    "         shape does not appear in the result.",
1135    "",
1136    "Xor      The result is the image data from both image and",
1137    "         image window that is outside the overlap region.",
1138    "         The overlap region is blank.",
1139    "",
1140    "Plus     The result is just the sum of the image data.",
1141    "         Output values are cropped to QuantumRange (no overflow).",
1142    "         This operation is independent of the matte",
1143    "         channels.",
1144    "",
1145    "Minus    The result of image - image window, with underflow",
1146    "         cropped to zero.",
1147    "",
1148    "Add      The result of image + image window, with overflow",
1149    "         wrapping around (mod 256).",
1150    "",
1151    "Subtract The result of image - image window, with underflow",
1152    "         wrapping around (mod 256).  The add and subtract",
1153    "         operators can be used to perform reversible",
1154    "         transformations.",
1155    "",
1156    "Difference",
1157    "         The result of abs(image - image window).  This",
1158    "         useful for comparing two very similar images.",
1159    "",
1160    "Copy     The resulting image is image window replaced with",
1161    "         image.  Here the matte information is ignored.",
1162    "",
1163    "CopyRed  The red layer of the image window is replace with",
1164    "         the red layer of the image.  The other layers are",
1165    "         untouched.",
1166    "",
1167    "CopyGreen",
1168    "         The green layer of the image window is replace with",
1169    "         the green layer of the image.  The other layers are",
1170    "         untouched.",
1171    "",
1172    "CopyBlue The blue layer of the image window is replace with",
1173    "         the blue layer of the image.  The other layers are",
1174    "         untouched.",
1175    "",
1176    "CopyOpacity",
1177    "         The matte layer of the image window is replace with",
1178    "         the matte layer of the image.  The other layers are",
1179    "         untouched.",
1180    "",
1181    "The image compositor requires a matte, or alpha channel in",
1182    "the image for some operations.  This extra channel usually",
1183    "defines a mask which represents a sort of a cookie-cutter",
1184    "for the image.  This the case when matte is opaque (full",
1185    "coverage) for pixels inside the shape, zero outside, and",
1186    "between 0 and QuantumRange on the boundary.  If image does not",
1187    "have a matte channel, it is initialized with 0 for any pixel",
1188    "matching in color to pixel location (0,0), otherwise QuantumRange.",
1189    "",
1190    "Note that matte information for image window is not retained",
1191    "for colormapped X server visuals (e.g. StaticColor,",
1192    "StaticColor, GrayScale, PseudoColor).  Correct compositing",
1193    "behavior may require a TrueColor or DirectColor visual or a",
1194    "Standard Colormap.",
1195    "",
1196    "Choosing a composite operator is optional.  The default",
1197    "operator is replace.  However, you must choose a location to",
1198    "paste your image and press button 1.  Press and hold the",
1199    "button before releasing and an outline of the image will",
1200    "appear to help you identify your location.",
1201    "",
1202    "The actual colors of the pasted image is saved.  However,",
1203    "the color that appears in image window may be different.",
1204    "For example, on a monochrome screen image window will appear",
1205    "black or white even though your pasted image may have",
1206    "many colors.  If the image is saved to a file it is written",
1207    "with the correct colors.  To assure the correct colors are",
1208    "saved in the final image, any PseudoClass image is promoted",
1209    "to DirectClass (see miff(5)).  To force a PseudoClass image",
1210    "to remain PseudoClass, use -colors.",
1211    (char *) NULL,
1212  },
1213  *ImageROIHelp[] =
1214  {
1215    "In region of interest mode, the Command widget has these",
1216    "options:",
1217    "",
1218    "    Help",
1219    "    Dismiss",
1220    "",
1221    "To define a region of interest, press button 1 and drag.",
1222    "The region of interest is defined by a highlighted rectangle",
1223    "that expands or contracts as it follows the pointer.  Once",
1224    "you are satisfied with the region of interest, release the",
1225    "button.  You are now in apply mode.  In apply mode the",
1226    "Command widget has these options:",
1227    "",
1228    "      File",
1229    "        Save...",
1230    "        Print...",
1231    "      Edit",
1232    "        Undo",
1233    "        Redo",
1234    "      Transform",
1235    "        Flop",
1236    "        Flip",
1237    "        Rotate Right",
1238    "        Rotate Left",
1239    "      Enhance",
1240    "        Hue...",
1241    "        Saturation...",
1242    "        Brightness...",
1243    "        Gamma...",
1244    "        Spiff",
1245    "        Dull",
1246    "        Contrast Stretch",
1247    "        Sigmoidal Contrast...",
1248    "        Normalize",
1249    "        Equalize",
1250    "        Negate",
1251    "        Grayscale",
1252    "        Map...",
1253    "        Quantize...",
1254    "      Effects",
1255    "        Despeckle",
1256    "        Emboss",
1257    "        Reduce Noise",
1258    "        Sharpen...",
1259    "        Blur...",
1260    "        Threshold...",
1261    "        Edge Detect...",
1262    "        Spread...",
1263    "        Shade...",
1264    "        Raise...",
1265    "        Segment...",
1266    "      F/X",
1267    "        Solarize...",
1268    "        Sepia Tone...",
1269    "        Swirl...",
1270    "        Implode...",
1271    "        Vignette...",
1272    "        Wave...",
1273    "        Oil Painting...",
1274    "        Charcoal Drawing...",
1275    "      Miscellany",
1276    "        Image Info",
1277    "        Zoom Image",
1278    "        Show Preview...",
1279    "        Show Histogram",
1280    "        Show Matte",
1281    "      Help",
1282    "      Dismiss",
1283    "",
1284    "You can make adjustments to the region of interest by moving",
1285    "the pointer to one of the rectangle corners, pressing a",
1286    "button, and dragging.  Finally, choose an image processing",
1287    "technique from the Command widget.  You can choose more than",
1288    "one image processing technique to apply to an area.",
1289    "Alternatively, you can move the region of interest before",
1290    "applying another image processing technique.  To exit, press",
1291    "Dismiss.",
1292    (char *) NULL,
1293  },
1294  *ImageRotateHelp[] =
1295  {
1296    "In rotate mode, the Command widget has these options:",
1297    "",
1298    "    Pixel Color",
1299    "      black",
1300    "      blue",
1301    "      cyan",
1302    "      green",
1303    "      gray",
1304    "      red",
1305    "      magenta",
1306    "      yellow",
1307    "      white",
1308    "      Browser...",
1309    "    Direction",
1310    "      horizontal",
1311    "      vertical",
1312    "    Help",
1313    "    Dismiss",
1314    "",
1315    "Choose a background color from the Pixel Color sub-menu.",
1316    "Additional background colors can be specified with the color",
1317    "browser.  You can change the menu colors by setting the X",
1318    "resources pen1 through pen9.",
1319    "",
1320    "If you choose the color browser and press Grab, you can",
1321    "select the background color by moving the pointer to the",
1322    "desired color on the screen and press any button.",
1323    "",
1324    "Choose a point in the image window and press this button and",
1325    "hold.  Next, move the pointer to another location in the",
1326    "image.  As you move a line connects the initial location and",
1327    "the pointer.  When you release the button, the degree of",
1328    "image rotation is determined by the slope of the line you",
1329    "just drew.  The slope is relative to the direction you",
1330    "choose from the Direction sub-menu of the Command widget.",
1331    "",
1332    "To cancel the image rotation, move the pointer back to the",
1333    "starting point of the line and release the button.",
1334    (char *) NULL,
1335  };
1336
1337/*
1338  Enumeration declarations.
1339*/
1340typedef enum
1341{
1342  CopyMode,
1343  CropMode,
1344  CutMode
1345} ClipboardMode;
1346
1347typedef enum
1348{
1349  OpenCommand,
1350  NextCommand,
1351  FormerCommand,
1352  SelectCommand,
1353  SaveCommand,
1354  PrintCommand,
1355  DeleteCommand,
1356  NewCommand,
1357  VisualDirectoryCommand,
1358  QuitCommand,
1359  UndoCommand,
1360  RedoCommand,
1361  CutCommand,
1362  CopyCommand,
1363  PasteCommand,
1364  HalfSizeCommand,
1365  OriginalSizeCommand,
1366  DoubleSizeCommand,
1367  ResizeCommand,
1368  ApplyCommand,
1369  RefreshCommand,
1370  RestoreCommand,
1371  CropCommand,
1372  ChopCommand,
1373  FlopCommand,
1374  FlipCommand,
1375  RotateRightCommand,
1376  RotateLeftCommand,
1377  RotateCommand,
1378  ShearCommand,
1379  RollCommand,
1380  TrimCommand,
1381  HueCommand,
1382  SaturationCommand,
1383  BrightnessCommand,
1384  GammaCommand,
1385  SpiffCommand,
1386  DullCommand,
1387  ContrastStretchCommand,
1388  SigmoidalContrastCommand,
1389  NormalizeCommand,
1390  EqualizeCommand,
1391  NegateCommand,
1392  GrayscaleCommand,
1393  MapCommand,
1394  QuantizeCommand,
1395  DespeckleCommand,
1396  EmbossCommand,
1397  ReduceNoiseCommand,
1398  AddNoiseCommand,
1399  SharpenCommand,
1400  BlurCommand,
1401  ThresholdCommand,
1402  EdgeDetectCommand,
1403  SpreadCommand,
1404  ShadeCommand,
1405  RaiseCommand,
1406  SegmentCommand,
1407  SolarizeCommand,
1408  SepiaToneCommand,
1409  SwirlCommand,
1410  ImplodeCommand,
1411  VignetteCommand,
1412  WaveCommand,
1413  OilPaintCommand,
1414  CharcoalDrawCommand,
1415  AnnotateCommand,
1416  DrawCommand,
1417  ColorCommand,
1418  MatteCommand,
1419  CompositeCommand,
1420  AddBorderCommand,
1421  AddFrameCommand,
1422  CommentCommand,
1423  LaunchCommand,
1424  RegionofInterestCommand,
1425  ROIHelpCommand,
1426  ROIDismissCommand,
1427  InfoCommand,
1428  ZoomCommand,
1429  ShowPreviewCommand,
1430  ShowHistogramCommand,
1431  ShowMatteCommand,
1432  BackgroundCommand,
1433  SlideShowCommand,
1434  PreferencesCommand,
1435  HelpCommand,
1436  BrowseDocumentationCommand,
1437  VersionCommand,
1438  SaveToUndoBufferCommand,
1439  FreeBuffersCommand,
1440  NullCommand
1441} CommandType;
1442
1443typedef enum
1444{
1445  AnnotateNameCommand,
1446  AnnotateFontColorCommand,
1447  AnnotateBackgroundColorCommand,
1448  AnnotateRotateCommand,
1449  AnnotateHelpCommand,
1450  AnnotateDismissCommand,
1451  TextHelpCommand,
1452  TextApplyCommand,
1453  ChopDirectionCommand,
1454  ChopHelpCommand,
1455  ChopDismissCommand,
1456  HorizontalChopCommand,
1457  VerticalChopCommand,
1458  ColorEditMethodCommand,
1459  ColorEditColorCommand,
1460  ColorEditBorderCommand,
1461  ColorEditFuzzCommand,
1462  ColorEditUndoCommand,
1463  ColorEditHelpCommand,
1464  ColorEditDismissCommand,
1465  CompositeOperatorsCommand,
1466  CompositeDissolveCommand,
1467  CompositeDisplaceCommand,
1468  CompositeHelpCommand,
1469  CompositeDismissCommand,
1470  CropHelpCommand,
1471  CropDismissCommand,
1472  RectifyCopyCommand,
1473  RectifyHelpCommand,
1474  RectifyDismissCommand,
1475  DrawElementCommand,
1476  DrawColorCommand,
1477  DrawStippleCommand,
1478  DrawWidthCommand,
1479  DrawUndoCommand,
1480  DrawHelpCommand,
1481  DrawDismissCommand,
1482  MatteEditMethod,
1483  MatteEditBorderCommand,
1484  MatteEditFuzzCommand,
1485  MatteEditValueCommand,
1486  MatteEditUndoCommand,
1487  MatteEditHelpCommand,
1488  MatteEditDismissCommand,
1489  PasteOperatorsCommand,
1490  PasteHelpCommand,
1491  PasteDismissCommand,
1492  RotateColorCommand,
1493  RotateDirectionCommand,
1494  RotateCropCommand,
1495  RotateSharpenCommand,
1496  RotateHelpCommand,
1497  RotateDismissCommand,
1498  HorizontalRotateCommand,
1499  VerticalRotateCommand,
1500  TileLoadCommand,
1501  TileNextCommand,
1502  TileFormerCommand,
1503  TileDeleteCommand,
1504  TileUpdateCommand
1505} ModeType;
1506
1507/*
1508  Stipples.
1509*/
1510#define BricksWidth  20
1511#define BricksHeight  20
1512#define DiagonalWidth  16
1513#define DiagonalHeight  16
1514#define HighlightWidth  8
1515#define HighlightHeight  8
1516#define OpaqueWidth  8
1517#define OpaqueHeight  8
1518#define ScalesWidth  16
1519#define ScalesHeight  16
1520#define ShadowWidth  8
1521#define ShadowHeight  8
1522#define VerticalWidth  16
1523#define VerticalHeight  16
1524#define WavyWidth  16
1525#define WavyHeight  16
1526
1527/*
1528  Constant declaration.
1529*/
1530static const int
1531  RoiDelta = 8;
1532
1533static const unsigned char
1534  BricksBitmap[] =
1535  {
1536    0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1537    0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1538    0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1539    0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1540    0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1541  },
1542  DiagonalBitmap[] =
1543  {
1544    0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1545    0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1546    0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1547  },
1548  ScalesBitmap[] =
1549  {
1550    0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1551    0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1552    0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1553  },
1554  VerticalBitmap[] =
1555  {
1556    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1557    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1558    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1559  },
1560  WavyBitmap[] =
1561  {
1562    0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1563    0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1564    0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1565  };
1566
1567/*
1568  Function prototypes.
1569*/
1570static CommandType
1571  XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1572    const MagickStatusType,KeySym,Image **,ExceptionInfo *);
1573
1574static Image
1575  *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
1576    Image **,ExceptionInfo *),
1577  *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1578  *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *,
1579    ExceptionInfo *),
1580  *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *,
1581    ExceptionInfo *);
1582
1583static MagickBooleanType
1584  XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *,
1585    ExceptionInfo *),
1586  XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **,
1587    ExceptionInfo *),
1588  XChopImage(Display *,XResourceInfo *,XWindows *,Image **,
1589    ExceptionInfo *),
1590  XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode,
1591    ExceptionInfo *),
1592  XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1593    ExceptionInfo *),
1594  XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *,
1595    ExceptionInfo *),
1596  XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1597  XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1598    ExceptionInfo *),
1599  XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1600    ExceptionInfo *),
1601  XPasteImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1602  XPrintImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1603  XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **,
1604    ExceptionInfo *),
1605  XROIImage(Display *,XResourceInfo *,XWindows *,Image **,ExceptionInfo *),
1606  XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1607  XTrimImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
1608
1609static void
1610  XDrawPanRectangle(Display *,XWindows *),
1611  XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **,
1612    ExceptionInfo *),
1613  XMagnifyImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1614  XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1615  XPanImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1616  XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1617    const KeySym,ExceptionInfo *),
1618  XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1619  XScreenEvent(Display *,XWindows *,XEvent *,ExceptionInfo *),
1620  XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1621
1622/*
1623%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1624%                                                                             %
1625%                                                                             %
1626%                                                                             %
1627%   D i s p l a y I m a g e s                                                 %
1628%                                                                             %
1629%                                                                             %
1630%                                                                             %
1631%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1632%
1633%  DisplayImages() displays an image sequence to any X window screen.  It
1634%  returns a value other than 0 if successful.  Check the exception member
1635%  of image to determine the reason for any failure.
1636%
1637%  The format of the DisplayImages method is:
1638%
1639%      MagickBooleanType DisplayImages(const ImageInfo *image_info,
1640%        Image *images,ExceptionInfo *exception)
1641%
1642%  A description of each parameter follows:
1643%
1644%    o image_info: the image info.
1645%
1646%    o image: the image.
1647%
1648%    o exception: return any errors or warnings in this structure.
1649%
1650*/
1651MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1652  Image *images,ExceptionInfo *exception)
1653{
1654  char
1655    *argv[1];
1656
1657  Display
1658    *display;
1659
1660  Image
1661    *image;
1662
1663  register ssize_t
1664    i;
1665
1666  size_t
1667    state;
1668
1669  XrmDatabase
1670    resource_database;
1671
1672  XResourceInfo
1673    resource_info;
1674
1675  assert(image_info != (const ImageInfo *) NULL);
1676  assert(image_info->signature == MagickSignature);
1677  assert(images != (Image *) NULL);
1678  assert(images->signature == MagickSignature);
1679  if (IfMagickTrue(images->debug) )
1680    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1681  display=XOpenDisplay(image_info->server_name);
1682  if (display == (Display *) NULL)
1683    {
1684      (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1685        "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1686      return(MagickFalse);
1687    }
1688  if (exception->severity != UndefinedException)
1689    CatchException(exception);
1690  (void) XSetErrorHandler(XError);
1691  resource_database=XGetResourceDatabase(display,GetClientName());
1692  (void) ResetMagickMemory(&resource_info,0,sizeof(resource_info));
1693  XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1694  if (image_info->page != (char *) NULL)
1695    resource_info.image_geometry=AcquireString(image_info->page);
1696  resource_info.immutable=MagickTrue;
1697  argv[0]=AcquireString(GetClientName());
1698  state=DefaultState;
1699  for (i=0; (state & ExitState) == 0; i++)
1700  {
1701    if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1702      break;
1703    image=GetImageFromList(images,i % GetImageListLength(images));
1704    (void) XDisplayImage(display,&resource_info,argv,1,&image,&state,exception);
1705  }
1706  (void) SetErrorHandler((ErrorHandler) NULL);
1707  (void) SetWarningHandler((WarningHandler) NULL);
1708  argv[0]=DestroyString(argv[0]);
1709  (void) XCloseDisplay(display);
1710  XDestroyResourceInfo(&resource_info);
1711  if (exception->severity != UndefinedException)
1712    return(MagickFalse);
1713  return(MagickTrue);
1714}
1715
1716/*
1717%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1718%                                                                             %
1719%                                                                             %
1720%                                                                             %
1721%   R e m o t e D i s p l a y C o m m a n d                                   %
1722%                                                                             %
1723%                                                                             %
1724%                                                                             %
1725%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1726%
1727%  RemoteDisplayCommand() encourages a remote display program to display the
1728%  specified image filename.
1729%
1730%  The format of the RemoteDisplayCommand method is:
1731%
1732%      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1733%        const char *window,const char *filename,ExceptionInfo *exception)
1734%
1735%  A description of each parameter follows:
1736%
1737%    o image_info: the image info.
1738%
1739%    o window: Specifies the name or id of an X window.
1740%
1741%    o filename: the name of the image filename to display.
1742%
1743%    o exception: return any errors or warnings in this structure.
1744%
1745*/
1746MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1747  const char *window,const char *filename,ExceptionInfo *exception)
1748{
1749  Display
1750    *display;
1751
1752  MagickStatusType
1753    status;
1754
1755  assert(image_info != (const ImageInfo *) NULL);
1756  assert(image_info->signature == MagickSignature);
1757  assert(filename != (char *) NULL);
1758  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1759  display=XOpenDisplay(image_info->server_name);
1760  if (display == (Display *) NULL)
1761    {
1762      (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1763        "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1764      return(MagickFalse);
1765    }
1766  (void) XSetErrorHandler(XError);
1767  status=XRemoteCommand(display,window,filename);
1768  (void) XCloseDisplay(display);
1769  return(IsMagickTrue(status));
1770}
1771
1772/*
1773%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1774%                                                                             %
1775%                                                                             %
1776%                                                                             %
1777+   X A n n o t a t e E d i t I m a g e                                       %
1778%                                                                             %
1779%                                                                             %
1780%                                                                             %
1781%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1782%
1783%  XAnnotateEditImage() annotates the image with text.
1784%
1785%  The format of the XAnnotateEditImage method is:
1786%
1787%      MagickBooleanType XAnnotateEditImage(Display *display,
1788%        XResourceInfo *resource_info,XWindows *windows,Image *image,
1789%        ExceptionInfo *exception)
1790%
1791%  A description of each parameter follows:
1792%
1793%    o display: Specifies a connection to an X server;  returned from
1794%      XOpenDisplay.
1795%
1796%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1797%
1798%    o windows: Specifies a pointer to a XWindows structure.
1799%
1800%    o image: the image; returned from ReadImage.
1801%
1802*/
1803
1804static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
1805{
1806  if (x > y)
1807    return(x);
1808  return(y);
1809}
1810
1811static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
1812{
1813  if (x < y)
1814    return(x);
1815  return(y);
1816}
1817
1818static MagickBooleanType XAnnotateEditImage(Display *display,
1819  XResourceInfo *resource_info,XWindows *windows,Image *image,
1820  ExceptionInfo *exception)
1821{
1822  static const char
1823    *AnnotateMenu[] =
1824    {
1825      "Font Name",
1826      "Font Color",
1827      "Box Color",
1828      "Rotate Text",
1829      "Help",
1830      "Dismiss",
1831      (char *) NULL
1832    },
1833    *TextMenu[] =
1834    {
1835      "Help",
1836      "Apply",
1837      (char *) NULL
1838    };
1839
1840  static const ModeType
1841    AnnotateCommands[] =
1842    {
1843      AnnotateNameCommand,
1844      AnnotateFontColorCommand,
1845      AnnotateBackgroundColorCommand,
1846      AnnotateRotateCommand,
1847      AnnotateHelpCommand,
1848      AnnotateDismissCommand
1849    },
1850    TextCommands[] =
1851    {
1852      TextHelpCommand,
1853      TextApplyCommand
1854    };
1855
1856  static MagickBooleanType
1857    transparent_box = MagickTrue,
1858    transparent_pen = MagickFalse;
1859
1860  static double
1861    degrees = 0.0;
1862
1863  static unsigned int
1864    box_id = MaxNumberPens-2,
1865    font_id = 0,
1866    pen_id = 0;
1867
1868  char
1869    command[MaxTextExtent],
1870    text[MaxTextExtent];
1871
1872  const char
1873    *ColorMenu[MaxNumberPens+1];
1874
1875  Cursor
1876    cursor;
1877
1878  GC
1879    annotate_context;
1880
1881  int
1882    id,
1883    pen_number,
1884    status,
1885    x,
1886    y;
1887
1888  KeySym
1889    key_symbol;
1890
1891  register char
1892    *p;
1893
1894  register ssize_t
1895    i;
1896
1897  unsigned int
1898    height,
1899    width;
1900
1901  size_t
1902    state;
1903
1904  XAnnotateInfo
1905    *annotate_info,
1906    *previous_info;
1907
1908  XColor
1909    color;
1910
1911  XFontStruct
1912    *font_info;
1913
1914  XEvent
1915    event,
1916    text_event;
1917
1918  /*
1919    Map Command widget.
1920  */
1921  (void) CloneString(&windows->command.name,"Annotate");
1922  windows->command.data=4;
1923  (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1924  (void) XMapRaised(display,windows->command.id);
1925  XClientMessage(display,windows->image.id,windows->im_protocols,
1926    windows->im_update_widget,CurrentTime);
1927  /*
1928    Track pointer until button 1 is pressed.
1929  */
1930  XQueryPosition(display,windows->image.id,&x,&y);
1931  (void) XSelectInput(display,windows->image.id,
1932    windows->image.attributes.event_mask | PointerMotionMask);
1933  cursor=XCreateFontCursor(display,XC_left_side);
1934  (void) XCheckDefineCursor(display,windows->image.id,cursor);
1935  state=DefaultState;
1936  do
1937  {
1938    if (IfMagickTrue(windows->info.mapped) )
1939      {
1940        /*
1941          Display pointer position.
1942        */
1943        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
1944          x+windows->image.x,y+windows->image.y);
1945        XInfoWidget(display,windows,text);
1946      }
1947    /*
1948      Wait for next event.
1949    */
1950    XScreenEvent(display,windows,&event,exception);
1951    if (event.xany.window == windows->command.id)
1952      {
1953        /*
1954          Select a command from the Command widget.
1955        */
1956        id=XCommandWidget(display,windows,AnnotateMenu,&event);
1957        (void) XCheckDefineCursor(display,windows->image.id,cursor);
1958        if (id < 0)
1959          continue;
1960        switch (AnnotateCommands[id])
1961        {
1962          case AnnotateNameCommand:
1963          {
1964            const char
1965              *FontMenu[MaxNumberFonts];
1966
1967            int
1968              font_number;
1969
1970            /*
1971              Initialize menu selections.
1972            */
1973            for (i=0; i < MaxNumberFonts; i++)
1974              FontMenu[i]=resource_info->font_name[i];
1975            FontMenu[MaxNumberFonts-2]="Browser...";
1976            FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1977            /*
1978              Select a font name from the pop-up menu.
1979            */
1980            font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1981              (const char **) FontMenu,command);
1982            if (font_number < 0)
1983              break;
1984            if (font_number == (MaxNumberFonts-2))
1985              {
1986                static char
1987                  font_name[MaxTextExtent] = "fixed";
1988
1989                /*
1990                  Select a font name from a browser.
1991                */
1992                resource_info->font_name[font_number]=font_name;
1993                XFontBrowserWidget(display,windows,"Select",font_name);
1994                if (*font_name == '\0')
1995                  break;
1996              }
1997            /*
1998              Initialize font info.
1999            */
2000            font_info=XLoadQueryFont(display,resource_info->font_name[
2001              font_number]);
2002            if (font_info == (XFontStruct *) NULL)
2003              {
2004                XNoticeWidget(display,windows,"Unable to load font:",
2005                  resource_info->font_name[font_number]);
2006                break;
2007              }
2008            font_id=(unsigned int) font_number;
2009            (void) XFreeFont(display,font_info);
2010            break;
2011          }
2012          case AnnotateFontColorCommand:
2013          {
2014            /*
2015              Initialize menu selections.
2016            */
2017            for (i=0; i < (int) (MaxNumberPens-2); i++)
2018              ColorMenu[i]=resource_info->pen_colors[i];
2019            ColorMenu[MaxNumberPens-2]="transparent";
2020            ColorMenu[MaxNumberPens-1]="Browser...";
2021            ColorMenu[MaxNumberPens]=(const char *) NULL;
2022            /*
2023              Select a pen color from the pop-up menu.
2024            */
2025            pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2026              (const char **) ColorMenu,command);
2027            if (pen_number < 0)
2028              break;
2029            transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
2030              MagickFalse;
2031            if (IfMagickTrue(transparent_pen) )
2032              break;
2033            if (pen_number == (MaxNumberPens-1))
2034              {
2035                static char
2036                  color_name[MaxTextExtent] = "gray";
2037
2038                /*
2039                  Select a pen color from a dialog.
2040                */
2041                resource_info->pen_colors[pen_number]=color_name;
2042                XColorBrowserWidget(display,windows,"Select",color_name);
2043                if (*color_name == '\0')
2044                  break;
2045              }
2046            /*
2047              Set pen color.
2048            */
2049            (void) XParseColor(display,windows->map_info->colormap,
2050              resource_info->pen_colors[pen_number],&color);
2051            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2052              (unsigned int) MaxColors,&color);
2053            windows->pixel_info->pen_colors[pen_number]=color;
2054            pen_id=(unsigned int) pen_number;
2055            break;
2056          }
2057          case AnnotateBackgroundColorCommand:
2058          {
2059            /*
2060              Initialize menu selections.
2061            */
2062            for (i=0; i < (int) (MaxNumberPens-2); i++)
2063              ColorMenu[i]=resource_info->pen_colors[i];
2064            ColorMenu[MaxNumberPens-2]="transparent";
2065            ColorMenu[MaxNumberPens-1]="Browser...";
2066            ColorMenu[MaxNumberPens]=(const char *) NULL;
2067            /*
2068              Select a pen color from the pop-up menu.
2069            */
2070            pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2071              (const char **) ColorMenu,command);
2072            if (pen_number < 0)
2073              break;
2074            transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2075              MagickFalse;
2076            if (IfMagickTrue(transparent_box) )
2077              break;
2078            if (pen_number == (MaxNumberPens-1))
2079              {
2080                static char
2081                  color_name[MaxTextExtent] = "gray";
2082
2083                /*
2084                  Select a pen color from a dialog.
2085                */
2086                resource_info->pen_colors[pen_number]=color_name;
2087                XColorBrowserWidget(display,windows,"Select",color_name);
2088                if (*color_name == '\0')
2089                  break;
2090              }
2091            /*
2092              Set pen color.
2093            */
2094            (void) XParseColor(display,windows->map_info->colormap,
2095              resource_info->pen_colors[pen_number],&color);
2096            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2097              (unsigned int) MaxColors,&color);
2098            windows->pixel_info->pen_colors[pen_number]=color;
2099            box_id=(unsigned int) pen_number;
2100            break;
2101          }
2102          case AnnotateRotateCommand:
2103          {
2104            int
2105              entry;
2106
2107            static char
2108              angle[MaxTextExtent] = "30.0";
2109
2110            static const char
2111              *RotateMenu[] =
2112              {
2113                "-90",
2114                "-45",
2115                "-30",
2116                "0",
2117                "30",
2118                "45",
2119                "90",
2120                "180",
2121                "Dialog...",
2122                (char *) NULL,
2123              };
2124
2125            /*
2126              Select a command from the pop-up menu.
2127            */
2128            entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2129              command);
2130            if (entry < 0)
2131              break;
2132            if (entry != 8)
2133              {
2134                degrees=StringToDouble(RotateMenu[entry],(char **) NULL);
2135                break;
2136              }
2137            (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2138              angle);
2139            if (*angle == '\0')
2140              break;
2141            degrees=StringToDouble(angle,(char **) NULL);
2142            break;
2143          }
2144          case AnnotateHelpCommand:
2145          {
2146            XTextViewWidget(display,resource_info,windows,MagickFalse,
2147              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2148            break;
2149          }
2150          case AnnotateDismissCommand:
2151          {
2152            /*
2153              Prematurely exit.
2154            */
2155            state|=EscapeState;
2156            state|=ExitState;
2157            break;
2158          }
2159          default:
2160            break;
2161        }
2162        continue;
2163      }
2164    switch (event.type)
2165    {
2166      case ButtonPress:
2167      {
2168        if (event.xbutton.button != Button1)
2169          break;
2170        if (event.xbutton.window != windows->image.id)
2171          break;
2172        /*
2173          Change to text entering mode.
2174        */
2175        x=event.xbutton.x;
2176        y=event.xbutton.y;
2177        state|=ExitState;
2178        break;
2179      }
2180      case ButtonRelease:
2181        break;
2182      case Expose:
2183        break;
2184      case KeyPress:
2185      {
2186        if (event.xkey.window != windows->image.id)
2187          break;
2188        /*
2189          Respond to a user key press.
2190        */
2191        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2192          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2193        switch ((int) key_symbol)
2194        {
2195          case XK_Escape:
2196          case XK_F20:
2197          {
2198            /*
2199              Prematurely exit.
2200            */
2201            state|=EscapeState;
2202            state|=ExitState;
2203            break;
2204          }
2205          case XK_F1:
2206          case XK_Help:
2207          {
2208            XTextViewWidget(display,resource_info,windows,MagickFalse,
2209              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2210            break;
2211          }
2212          default:
2213          {
2214            (void) XBell(display,0);
2215            break;
2216          }
2217        }
2218        break;
2219      }
2220      case MotionNotify:
2221      {
2222        /*
2223          Map and unmap Info widget as cursor crosses its boundaries.
2224        */
2225        x=event.xmotion.x;
2226        y=event.xmotion.y;
2227        if (IfMagickTrue(windows->info.mapped) )
2228          {
2229            if ((x < (int) (windows->info.x+windows->info.width)) &&
2230                (y < (int) (windows->info.y+windows->info.height)))
2231              (void) XWithdrawWindow(display,windows->info.id,
2232                windows->info.screen);
2233          }
2234        else
2235          if ((x > (int) (windows->info.x+windows->info.width)) ||
2236              (y > (int) (windows->info.y+windows->info.height)))
2237            (void) XMapWindow(display,windows->info.id);
2238        break;
2239      }
2240      default:
2241        break;
2242    }
2243  } while ((state & ExitState) == 0);
2244  (void) XSelectInput(display,windows->image.id,
2245    windows->image.attributes.event_mask);
2246  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2247  if ((state & EscapeState) != 0)
2248    return(MagickTrue);
2249  /*
2250    Set font info and check boundary conditions.
2251  */
2252  font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2253  if (font_info == (XFontStruct *) NULL)
2254    {
2255      XNoticeWidget(display,windows,"Unable to load font:",
2256        resource_info->font_name[font_id]);
2257      font_info=windows->font_info;
2258    }
2259  if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2260    x=(int) windows->image.width-font_info->max_bounds.width;
2261  if (y < (int) (font_info->ascent+font_info->descent))
2262    y=(int) font_info->ascent+font_info->descent;
2263  if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2264      ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2265    return(MagickFalse);
2266  /*
2267    Initialize annotate structure.
2268  */
2269  annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2270  if (annotate_info == (XAnnotateInfo *) NULL)
2271    return(MagickFalse);
2272  XGetAnnotateInfo(annotate_info);
2273  annotate_info->x=x;
2274  annotate_info->y=y;
2275  if (IfMagickFalse(transparent_box) && IfMagickFalse(transparent_pen))
2276    annotate_info->stencil=OpaqueStencil;
2277  else
2278    if (IfMagickFalse(transparent_box) )
2279      annotate_info->stencil=BackgroundStencil;
2280    else
2281      annotate_info->stencil=ForegroundStencil;
2282  annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2283  annotate_info->degrees=degrees;
2284  annotate_info->font_info=font_info;
2285  annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2286    windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
2287    sizeof(*annotate_info->text));
2288  if (annotate_info->text == (char *) NULL)
2289    return(MagickFalse);
2290  /*
2291    Create cursor and set graphic context.
2292  */
2293  cursor=XCreateFontCursor(display,XC_pencil);
2294  (void) XCheckDefineCursor(display,windows->image.id,cursor);
2295  annotate_context=windows->image.annotate_context;
2296  (void) XSetFont(display,annotate_context,font_info->fid);
2297  (void) XSetBackground(display,annotate_context,
2298    windows->pixel_info->pen_colors[box_id].pixel);
2299  (void) XSetForeground(display,annotate_context,
2300    windows->pixel_info->pen_colors[pen_id].pixel);
2301  /*
2302    Begin annotating the image with text.
2303  */
2304  (void) CloneString(&windows->command.name,"Text");
2305  windows->command.data=0;
2306  (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2307  state=DefaultState;
2308  (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2309  text_event.xexpose.width=(int) font_info->max_bounds.width;
2310  text_event.xexpose.height=font_info->max_bounds.ascent+
2311    font_info->max_bounds.descent;
2312  p=annotate_info->text;
2313  do
2314  {
2315    /*
2316      Display text cursor.
2317    */
2318    *p='\0';
2319    (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2320    /*
2321      Wait for next event.
2322    */
2323    XScreenEvent(display,windows,&event,exception);
2324    if (event.xany.window == windows->command.id)
2325      {
2326        /*
2327          Select a command from the Command widget.
2328        */
2329        (void) XSetBackground(display,annotate_context,
2330          windows->pixel_info->background_color.pixel);
2331        (void) XSetForeground(display,annotate_context,
2332          windows->pixel_info->foreground_color.pixel);
2333        id=XCommandWidget(display,windows,AnnotateMenu,&event);
2334        (void) XSetBackground(display,annotate_context,
2335          windows->pixel_info->pen_colors[box_id].pixel);
2336        (void) XSetForeground(display,annotate_context,
2337          windows->pixel_info->pen_colors[pen_id].pixel);
2338        if (id < 0)
2339          continue;
2340        switch (TextCommands[id])
2341        {
2342          case TextHelpCommand:
2343          {
2344            XTextViewWidget(display,resource_info,windows,MagickFalse,
2345              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2346            (void) XCheckDefineCursor(display,windows->image.id,cursor);
2347            break;
2348          }
2349          case TextApplyCommand:
2350          {
2351            /*
2352              Finished annotating.
2353            */
2354            annotate_info->width=(unsigned int) XTextWidth(font_info,
2355              annotate_info->text,(int) strlen(annotate_info->text));
2356            XRefreshWindow(display,&windows->image,&text_event);
2357            state|=ExitState;
2358            break;
2359          }
2360          default:
2361            break;
2362        }
2363        continue;
2364      }
2365    /*
2366      Erase text cursor.
2367    */
2368    text_event.xexpose.x=x;
2369    text_event.xexpose.y=y-font_info->max_bounds.ascent;
2370    (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2371      (unsigned int) text_event.xexpose.width,(unsigned int)
2372      text_event.xexpose.height,MagickFalse);
2373    XRefreshWindow(display,&windows->image,&text_event);
2374    switch (event.type)
2375    {
2376      case ButtonPress:
2377      {
2378        if (event.xbutton.window != windows->image.id)
2379          break;
2380        if (event.xbutton.button == Button2)
2381          {
2382            /*
2383              Request primary selection.
2384            */
2385            (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2386              windows->image.id,CurrentTime);
2387            break;
2388          }
2389        break;
2390      }
2391      case Expose:
2392      {
2393        if (event.xexpose.count == 0)
2394          {
2395            XAnnotateInfo
2396              *text_info;
2397
2398            /*
2399              Refresh Image window.
2400            */
2401            XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2402            text_info=annotate_info;
2403            while (text_info != (XAnnotateInfo *) NULL)
2404            {
2405              if (annotate_info->stencil == ForegroundStencil)
2406                (void) XDrawString(display,windows->image.id,annotate_context,
2407                  text_info->x,text_info->y,text_info->text,
2408                  (int) strlen(text_info->text));
2409              else
2410                (void) XDrawImageString(display,windows->image.id,
2411                  annotate_context,text_info->x,text_info->y,text_info->text,
2412                  (int) strlen(text_info->text));
2413              text_info=text_info->previous;
2414            }
2415            (void) XDrawString(display,windows->image.id,annotate_context,
2416              x,y,"_",1);
2417          }
2418        break;
2419      }
2420      case KeyPress:
2421      {
2422        int
2423          length;
2424
2425        if (event.xkey.window != windows->image.id)
2426          break;
2427        /*
2428          Respond to a user key press.
2429        */
2430        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2431          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2432        *(command+length)='\0';
2433        if (((event.xkey.state & ControlMask) != 0) ||
2434            ((event.xkey.state & Mod1Mask) != 0))
2435          state|=ModifierState;
2436        if ((state & ModifierState) != 0)
2437          switch ((int) key_symbol)
2438          {
2439            case XK_u:
2440            case XK_U:
2441            {
2442              key_symbol=DeleteCommand;
2443              break;
2444            }
2445            default:
2446              break;
2447          }
2448        switch ((int) key_symbol)
2449        {
2450          case XK_BackSpace:
2451          {
2452            /*
2453              Erase one character.
2454            */
2455            if (p == annotate_info->text)
2456              {
2457                if (annotate_info->previous == (XAnnotateInfo *) NULL)
2458                  break;
2459                else
2460                  {
2461                    /*
2462                      Go to end of the previous line of text.
2463                    */
2464                    annotate_info=annotate_info->previous;
2465                    p=annotate_info->text;
2466                    x=annotate_info->x+annotate_info->width;
2467                    y=annotate_info->y;
2468                    if (annotate_info->width != 0)
2469                      p+=strlen(annotate_info->text);
2470                    break;
2471                  }
2472              }
2473            p--;
2474            x-=XTextWidth(font_info,p,1);
2475            text_event.xexpose.x=x;
2476            text_event.xexpose.y=y-font_info->max_bounds.ascent;
2477            XRefreshWindow(display,&windows->image,&text_event);
2478            break;
2479          }
2480          case XK_bracketleft:
2481          {
2482            key_symbol=XK_Escape;
2483            break;
2484          }
2485          case DeleteCommand:
2486          {
2487            /*
2488              Erase the entire line of text.
2489            */
2490            while (p != annotate_info->text)
2491            {
2492              p--;
2493              x-=XTextWidth(font_info,p,1);
2494              text_event.xexpose.x=x;
2495              XRefreshWindow(display,&windows->image,&text_event);
2496            }
2497            break;
2498          }
2499          case XK_Escape:
2500          case XK_F20:
2501          {
2502            /*
2503              Finished annotating.
2504            */
2505            annotate_info->width=(unsigned int) XTextWidth(font_info,
2506              annotate_info->text,(int) strlen(annotate_info->text));
2507            XRefreshWindow(display,&windows->image,&text_event);
2508            state|=ExitState;
2509            break;
2510          }
2511          default:
2512          {
2513            /*
2514              Draw a single character on the Image window.
2515            */
2516            if ((state & ModifierState) != 0)
2517              break;
2518            if (*command == '\0')
2519              break;
2520            *p=(*command);
2521            if (annotate_info->stencil == ForegroundStencil)
2522              (void) XDrawString(display,windows->image.id,annotate_context,
2523                x,y,p,1);
2524            else
2525              (void) XDrawImageString(display,windows->image.id,
2526                annotate_context,x,y,p,1);
2527            x+=XTextWidth(font_info,p,1);
2528            p++;
2529            if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2530              break;
2531          }
2532          case XK_Return:
2533          case XK_KP_Enter:
2534          {
2535            /*
2536              Advance to the next line of text.
2537            */
2538            *p='\0';
2539            annotate_info->width=(unsigned int) XTextWidth(font_info,
2540              annotate_info->text,(int) strlen(annotate_info->text));
2541            if (annotate_info->next != (XAnnotateInfo *) NULL)
2542              {
2543                /*
2544                  Line of text already exists.
2545                */
2546                annotate_info=annotate_info->next;
2547                x=annotate_info->x;
2548                y=annotate_info->y;
2549                p=annotate_info->text;
2550                break;
2551              }
2552            annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2553              sizeof(*annotate_info->next));
2554            if (annotate_info->next == (XAnnotateInfo *) NULL)
2555              return(MagickFalse);
2556            *annotate_info->next=(*annotate_info);
2557            annotate_info->next->previous=annotate_info;
2558            annotate_info=annotate_info->next;
2559            annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2560              windows->image.width/MagickMax((ssize_t)
2561              font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2562            if (annotate_info->text == (char *) NULL)
2563              return(MagickFalse);
2564            annotate_info->y+=annotate_info->height;
2565            if (annotate_info->y > (int) windows->image.height)
2566              annotate_info->y=(int) annotate_info->height;
2567            annotate_info->next=(XAnnotateInfo *) NULL;
2568            x=annotate_info->x;
2569            y=annotate_info->y;
2570            p=annotate_info->text;
2571            break;
2572          }
2573        }
2574        break;
2575      }
2576      case KeyRelease:
2577      {
2578        /*
2579          Respond to a user key release.
2580        */
2581        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2582          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2583        state&=(~ModifierState);
2584        break;
2585      }
2586      case SelectionNotify:
2587      {
2588        Atom
2589          type;
2590
2591        int
2592          format;
2593
2594        unsigned char
2595          *data;
2596
2597        unsigned long
2598          after,
2599          length;
2600
2601        /*
2602          Obtain response from primary selection.
2603        */
2604        if (event.xselection.property == (Atom) None)
2605          break;
2606        status=XGetWindowProperty(display,event.xselection.requestor,
2607          event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
2608          &type,&format,&length,&after,&data);
2609        if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2610            (length == 0))
2611          break;
2612        /*
2613          Annotate Image window with primary selection.
2614        */
2615        for (i=0; i < (ssize_t) length; i++)
2616        {
2617          if ((char) data[i] != '\n')
2618            {
2619              /*
2620                Draw a single character on the Image window.
2621              */
2622              *p=(char) data[i];
2623              (void) XDrawString(display,windows->image.id,annotate_context,
2624                x,y,p,1);
2625              x+=XTextWidth(font_info,p,1);
2626              p++;
2627              if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2628                continue;
2629            }
2630          /*
2631            Advance to the next line of text.
2632          */
2633          *p='\0';
2634          annotate_info->width=(unsigned int) XTextWidth(font_info,
2635            annotate_info->text,(int) strlen(annotate_info->text));
2636          if (annotate_info->next != (XAnnotateInfo *) NULL)
2637            {
2638              /*
2639                Line of text already exists.
2640              */
2641              annotate_info=annotate_info->next;
2642              x=annotate_info->x;
2643              y=annotate_info->y;
2644              p=annotate_info->text;
2645              continue;
2646            }
2647          annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2648            sizeof(*annotate_info->next));
2649          if (annotate_info->next == (XAnnotateInfo *) NULL)
2650            return(MagickFalse);
2651          *annotate_info->next=(*annotate_info);
2652          annotate_info->next->previous=annotate_info;
2653          annotate_info=annotate_info->next;
2654          annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2655            windows->image.width/MagickMax((ssize_t)
2656            font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2657          if (annotate_info->text == (char *) NULL)
2658            return(MagickFalse);
2659          annotate_info->y+=annotate_info->height;
2660          if (annotate_info->y > (int) windows->image.height)
2661            annotate_info->y=(int) annotate_info->height;
2662          annotate_info->next=(XAnnotateInfo *) NULL;
2663          x=annotate_info->x;
2664          y=annotate_info->y;
2665          p=annotate_info->text;
2666        }
2667        (void) XFree((void *) data);
2668        break;
2669      }
2670      default:
2671        break;
2672    }
2673  } while ((state & ExitState) == 0);
2674  (void) XFreeCursor(display,cursor);
2675  /*
2676    Annotation is relative to image configuration.
2677  */
2678  width=(unsigned int) image->columns;
2679  height=(unsigned int) image->rows;
2680  x=0;
2681  y=0;
2682  if (windows->image.crop_geometry != (char *) NULL)
2683    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2684  /*
2685    Initialize annotated image.
2686  */
2687  XSetCursorState(display,windows,MagickTrue);
2688  XCheckRefreshWindows(display,windows);
2689  while (annotate_info != (XAnnotateInfo *) NULL)
2690  {
2691    if (annotate_info->width == 0)
2692      {
2693        /*
2694          No text on this line--  go to the next line of text.
2695        */
2696        previous_info=annotate_info->previous;
2697        annotate_info->text=(char *)
2698          RelinquishMagickMemory(annotate_info->text);
2699        annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2700        annotate_info=previous_info;
2701        continue;
2702      }
2703    /*
2704      Determine pixel index for box and pen color.
2705    */
2706    windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2707    if (windows->pixel_info->colors != 0)
2708      for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2709        if (windows->pixel_info->pixels[i] ==
2710            windows->pixel_info->pen_colors[box_id].pixel)
2711          {
2712            windows->pixel_info->box_index=(unsigned short) i;
2713            break;
2714          }
2715    windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2716    if (windows->pixel_info->colors != 0)
2717      for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2718        if (windows->pixel_info->pixels[i] ==
2719            windows->pixel_info->pen_colors[pen_id].pixel)
2720          {
2721            windows->pixel_info->pen_index=(unsigned short) i;
2722            break;
2723          }
2724    /*
2725      Define the annotate geometry string.
2726    */
2727    annotate_info->x=(int)
2728      width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2729    annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2730      windows->image.y)/windows->image.ximage->height;
2731    (void) FormatLocaleString(annotate_info->geometry,MaxTextExtent,
2732      "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2733      height*annotate_info->height/windows->image.ximage->height,
2734      annotate_info->x+x,annotate_info->y+y);
2735    /*
2736      Annotate image with text.
2737    */
2738    status=XAnnotateImage(display,windows->pixel_info,annotate_info,image,
2739      exception);
2740    if (status == 0)
2741      return(MagickFalse);
2742    /*
2743      Free up memory.
2744    */
2745    previous_info=annotate_info->previous;
2746    annotate_info->text=DestroyString(annotate_info->text);
2747    annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2748    annotate_info=previous_info;
2749  }
2750  (void) XSetForeground(display,annotate_context,
2751    windows->pixel_info->foreground_color.pixel);
2752  (void) XSetBackground(display,annotate_context,
2753    windows->pixel_info->background_color.pixel);
2754  (void) XSetFont(display,annotate_context,windows->font_info->fid);
2755  XSetCursorState(display,windows,MagickFalse);
2756  (void) XFreeFont(display,font_info);
2757  /*
2758    Update image configuration.
2759  */
2760  XConfigureImageColormap(display,resource_info,windows,image,exception);
2761  (void) XConfigureImage(display,resource_info,windows,image,exception);
2762  return(MagickTrue);
2763}
2764
2765/*
2766%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2767%                                                                             %
2768%                                                                             %
2769%                                                                             %
2770+   X B a c k g r o u n d I m a g e                                           %
2771%                                                                             %
2772%                                                                             %
2773%                                                                             %
2774%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2775%
2776%  XBackgroundImage() displays the image in the background of a window.
2777%
2778%  The format of the XBackgroundImage method is:
2779%
2780%      MagickBooleanType XBackgroundImage(Display *display,
2781%        XResourceInfo *resource_info,XWindows *windows,Image **image,
2782%        ExceptionInfo *exception)
2783%
2784%  A description of each parameter follows:
2785%
2786%    o display: Specifies a connection to an X server; returned from
2787%      XOpenDisplay.
2788%
2789%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2790%
2791%    o windows: Specifies a pointer to a XWindows structure.
2792%
2793%    o image: the image.
2794%
2795%    o exception: return any errors or warnings in this structure.
2796%
2797*/
2798static MagickBooleanType XBackgroundImage(Display *display,
2799  XResourceInfo *resource_info,XWindows *windows,Image **image,
2800  ExceptionInfo *exception)
2801{
2802#define BackgroundImageTag  "Background/Image"
2803
2804  int
2805    status;
2806
2807  static char
2808    window_id[MaxTextExtent] = "root";
2809
2810  XResourceInfo
2811    background_resources;
2812
2813  /*
2814    Put image in background.
2815  */
2816  status=XDialogWidget(display,windows,"Background",
2817    "Enter window id (id 0x00 selects window with pointer):",window_id);
2818  if (*window_id == '\0')
2819    return(MagickFalse);
2820  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
2821    exception);
2822  XInfoWidget(display,windows,BackgroundImageTag);
2823  XSetCursorState(display,windows,MagickTrue);
2824  XCheckRefreshWindows(display,windows);
2825  background_resources=(*resource_info);
2826  background_resources.window_id=window_id;
2827  background_resources.backdrop=IsMagickTrue(status);
2828  status=XDisplayBackgroundImage(display,&background_resources,*image,
2829    exception);
2830  if (IfMagickTrue(status))
2831    XClientMessage(display,windows->image.id,windows->im_protocols,
2832      windows->im_retain_colors,CurrentTime);
2833  XSetCursorState(display,windows,MagickFalse);
2834  (void) XMagickCommand(display,resource_info,windows,UndoCommand,image,
2835    exception);
2836  return(MagickTrue);
2837}
2838
2839/*
2840%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2841%                                                                             %
2842%                                                                             %
2843%                                                                             %
2844+   X C h o p I m a g e                                                       %
2845%                                                                             %
2846%                                                                             %
2847%                                                                             %
2848%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2849%
2850%  XChopImage() chops the X image.
2851%
2852%  The format of the XChopImage method is:
2853%
2854%    MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2855%      XWindows *windows,Image **image,ExceptionInfo *exception)
2856%
2857%  A description of each parameter follows:
2858%
2859%    o display: Specifies a connection to an X server; returned from
2860%      XOpenDisplay.
2861%
2862%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2863%
2864%    o windows: Specifies a pointer to a XWindows structure.
2865%
2866%    o image: the image.
2867%
2868%    o exception: return any errors or warnings in this structure.
2869%
2870*/
2871static MagickBooleanType XChopImage(Display *display,
2872  XResourceInfo *resource_info,XWindows *windows,Image **image,
2873  ExceptionInfo *exception)
2874{
2875  static const char
2876    *ChopMenu[] =
2877    {
2878      "Direction",
2879      "Help",
2880      "Dismiss",
2881      (char *) NULL
2882    };
2883
2884  static ModeType
2885    direction = HorizontalChopCommand;
2886
2887  static const ModeType
2888    ChopCommands[] =
2889    {
2890      ChopDirectionCommand,
2891      ChopHelpCommand,
2892      ChopDismissCommand
2893    },
2894    DirectionCommands[] =
2895    {
2896      HorizontalChopCommand,
2897      VerticalChopCommand
2898    };
2899
2900  char
2901    text[MaxTextExtent];
2902
2903  Image
2904    *chop_image;
2905
2906  int
2907    id,
2908    x,
2909    y;
2910
2911  double
2912    scale_factor;
2913
2914  RectangleInfo
2915    chop_info;
2916
2917  unsigned int
2918    distance,
2919    height,
2920    width;
2921
2922  size_t
2923    state;
2924
2925  XEvent
2926    event;
2927
2928  XSegment
2929    segment_info;
2930
2931  /*
2932    Map Command widget.
2933  */
2934  (void) CloneString(&windows->command.name,"Chop");
2935  windows->command.data=1;
2936  (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2937  (void) XMapRaised(display,windows->command.id);
2938  XClientMessage(display,windows->image.id,windows->im_protocols,
2939    windows->im_update_widget,CurrentTime);
2940  /*
2941    Track pointer until button 1 is pressed.
2942  */
2943  XQueryPosition(display,windows->image.id,&x,&y);
2944  (void) XSelectInput(display,windows->image.id,
2945    windows->image.attributes.event_mask | PointerMotionMask);
2946  state=DefaultState;
2947  do
2948  {
2949    if (IfMagickTrue(windows->info.mapped) )
2950      {
2951        /*
2952          Display pointer position.
2953        */
2954        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
2955          x+windows->image.x,y+windows->image.y);
2956        XInfoWidget(display,windows,text);
2957      }
2958    /*
2959      Wait for next event.
2960    */
2961    XScreenEvent(display,windows,&event,exception);
2962    if (event.xany.window == windows->command.id)
2963      {
2964        /*
2965          Select a command from the Command widget.
2966        */
2967        id=XCommandWidget(display,windows,ChopMenu,&event);
2968        if (id < 0)
2969          continue;
2970        switch (ChopCommands[id])
2971        {
2972          case ChopDirectionCommand:
2973          {
2974            char
2975              command[MaxTextExtent];
2976
2977            static const char
2978              *Directions[] =
2979              {
2980                "horizontal",
2981                "vertical",
2982                (char *) NULL,
2983              };
2984
2985            /*
2986              Select a command from the pop-up menu.
2987            */
2988            id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2989            if (id >= 0)
2990              direction=DirectionCommands[id];
2991            break;
2992          }
2993          case ChopHelpCommand:
2994          {
2995            XTextViewWidget(display,resource_info,windows,MagickFalse,
2996              "Help Viewer - Image Chop",ImageChopHelp);
2997            break;
2998          }
2999          case ChopDismissCommand:
3000          {
3001            /*
3002              Prematurely exit.
3003            */
3004            state|=EscapeState;
3005            state|=ExitState;
3006            break;
3007          }
3008          default:
3009            break;
3010        }
3011        continue;
3012      }
3013    switch (event.type)
3014    {
3015      case ButtonPress:
3016      {
3017        if (event.xbutton.button != Button1)
3018          break;
3019        if (event.xbutton.window != windows->image.id)
3020          break;
3021        /*
3022          User has committed to start point of chopping line.
3023        */
3024        segment_info.x1=(short int) event.xbutton.x;
3025        segment_info.x2=(short int) event.xbutton.x;
3026        segment_info.y1=(short int) event.xbutton.y;
3027        segment_info.y2=(short int) event.xbutton.y;
3028        state|=ExitState;
3029        break;
3030      }
3031      case ButtonRelease:
3032        break;
3033      case Expose:
3034        break;
3035      case KeyPress:
3036      {
3037        char
3038          command[MaxTextExtent];
3039
3040        KeySym
3041          key_symbol;
3042
3043        if (event.xkey.window != windows->image.id)
3044          break;
3045        /*
3046          Respond to a user key press.
3047        */
3048        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3049          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3050        switch ((int) key_symbol)
3051        {
3052          case XK_Escape:
3053          case XK_F20:
3054          {
3055            /*
3056              Prematurely exit.
3057            */
3058            state|=EscapeState;
3059            state|=ExitState;
3060            break;
3061          }
3062          case XK_F1:
3063          case XK_Help:
3064          {
3065            (void) XSetFunction(display,windows->image.highlight_context,
3066              GXcopy);
3067            XTextViewWidget(display,resource_info,windows,MagickFalse,
3068              "Help Viewer - Image Chop",ImageChopHelp);
3069            (void) XSetFunction(display,windows->image.highlight_context,
3070              GXinvert);
3071            break;
3072          }
3073          default:
3074          {
3075            (void) XBell(display,0);
3076            break;
3077          }
3078        }
3079        break;
3080      }
3081      case MotionNotify:
3082      {
3083        /*
3084          Map and unmap Info widget as text cursor crosses its boundaries.
3085        */
3086        x=event.xmotion.x;
3087        y=event.xmotion.y;
3088        if (IfMagickTrue(windows->info.mapped) )
3089          {
3090            if ((x < (int) (windows->info.x+windows->info.width)) &&
3091                (y < (int) (windows->info.y+windows->info.height)))
3092              (void) XWithdrawWindow(display,windows->info.id,
3093                windows->info.screen);
3094          }
3095        else
3096          if ((x > (int) (windows->info.x+windows->info.width)) ||
3097              (y > (int) (windows->info.y+windows->info.height)))
3098            (void) XMapWindow(display,windows->info.id);
3099      }
3100    }
3101  } while ((state & ExitState) == 0);
3102  (void) XSelectInput(display,windows->image.id,
3103    windows->image.attributes.event_mask);
3104  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3105  if ((state & EscapeState) != 0)
3106    return(MagickTrue);
3107  /*
3108    Draw line as pointer moves until the mouse button is released.
3109  */
3110  chop_info.width=0;
3111  chop_info.height=0;
3112  chop_info.x=0;
3113  chop_info.y=0;
3114  distance=0;
3115  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3116  state=DefaultState;
3117  do
3118  {
3119    if (distance > 9)
3120      {
3121        /*
3122          Display info and draw chopping line.
3123        */
3124        if (IfMagickFalse(windows->info.mapped) )
3125          (void) XMapWindow(display,windows->info.id);
3126        (void) FormatLocaleString(text,MaxTextExtent,
3127          " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3128          chop_info.height,(double) chop_info.x,(double) chop_info.y);
3129        XInfoWidget(display,windows,text);
3130        XHighlightLine(display,windows->image.id,
3131          windows->image.highlight_context,&segment_info);
3132      }
3133    else
3134      if (IfMagickTrue(windows->info.mapped) )
3135        (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3136    /*
3137      Wait for next event.
3138    */
3139    XScreenEvent(display,windows,&event,exception);
3140    if (distance > 9)
3141      XHighlightLine(display,windows->image.id,
3142        windows->image.highlight_context,&segment_info);
3143    switch (event.type)
3144    {
3145      case ButtonPress:
3146      {
3147        segment_info.x2=(short int) event.xmotion.x;
3148        segment_info.y2=(short int) event.xmotion.y;
3149        break;
3150      }
3151      case ButtonRelease:
3152      {
3153        /*
3154          User has committed to chopping line.
3155        */
3156        segment_info.x2=(short int) event.xbutton.x;
3157        segment_info.y2=(short int) event.xbutton.y;
3158        state|=ExitState;
3159        break;
3160      }
3161      case Expose:
3162        break;
3163      case MotionNotify:
3164      {
3165        segment_info.x2=(short int) event.xmotion.x;
3166        segment_info.y2=(short int) event.xmotion.y;
3167      }
3168      default:
3169        break;
3170    }
3171    /*
3172      Check boundary conditions.
3173    */
3174    if (segment_info.x2 < 0)
3175      segment_info.x2=0;
3176    else
3177      if (segment_info.x2 > windows->image.ximage->width)
3178        segment_info.x2=windows->image.ximage->width;
3179    if (segment_info.y2 < 0)
3180      segment_info.y2=0;
3181    else
3182      if (segment_info.y2 > windows->image.ximage->height)
3183        segment_info.y2=windows->image.ximage->height;
3184    distance=(unsigned int)
3185      (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3186       ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3187    /*
3188      Compute chopping geometry.
3189    */
3190    if (direction == HorizontalChopCommand)
3191      {
3192        chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3193        chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3194        chop_info.height=0;
3195        chop_info.y=0;
3196        if (segment_info.x1 > (int) segment_info.x2)
3197          {
3198            chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3199            chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3200          }
3201      }
3202    else
3203      {
3204        chop_info.width=0;
3205        chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3206        chop_info.x=0;
3207        chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3208        if (segment_info.y1 > segment_info.y2)
3209          {
3210            chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3211            chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3212          }
3213      }
3214  } while ((state & ExitState) == 0);
3215  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3216  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3217  if (distance <= 9)
3218    return(MagickTrue);
3219  /*
3220    Image chopping is relative to image configuration.
3221  */
3222  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
3223    exception);
3224  XSetCursorState(display,windows,MagickTrue);
3225  XCheckRefreshWindows(display,windows);
3226  windows->image.window_changes.width=windows->image.ximage->width-
3227    (unsigned int) chop_info.width;
3228  windows->image.window_changes.height=windows->image.ximage->height-
3229    (unsigned int) chop_info.height;
3230  width=(unsigned int) (*image)->columns;
3231  height=(unsigned int) (*image)->rows;
3232  x=0;
3233  y=0;
3234  if (windows->image.crop_geometry != (char *) NULL)
3235    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3236  scale_factor=(double) width/windows->image.ximage->width;
3237  chop_info.x+=x;
3238  chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3239  chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3240  scale_factor=(double) height/windows->image.ximage->height;
3241  chop_info.y+=y;
3242  chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3243  chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3244  /*
3245    Chop image.
3246  */
3247  chop_image=ChopImage(*image,&chop_info,exception);
3248  XSetCursorState(display,windows,MagickFalse);
3249  if (chop_image == (Image *) NULL)
3250    return(MagickFalse);
3251  *image=DestroyImage(*image);
3252  *image=chop_image;
3253  /*
3254    Update image configuration.
3255  */
3256  XConfigureImageColormap(display,resource_info,windows,*image,exception);
3257  (void) XConfigureImage(display,resource_info,windows,*image,exception);
3258  return(MagickTrue);
3259}
3260
3261/*
3262%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3263%                                                                             %
3264%                                                                             %
3265%                                                                             %
3266+   X C o l o r E d i t I m a g e                                             %
3267%                                                                             %
3268%                                                                             %
3269%                                                                             %
3270%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3271%
3272%  XColorEditImage() allows the user to interactively change the color of one
3273%  pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3274%
3275%  The format of the XColorEditImage method is:
3276%
3277%      MagickBooleanType XColorEditImage(Display *display,
3278%        XResourceInfo *resource_info,XWindows *windows,Image **image,
3279%          ExceptionInfo *exception)
3280%
3281%  A description of each parameter follows:
3282%
3283%    o display: Specifies a connection to an X server;  returned from
3284%      XOpenDisplay.
3285%
3286%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3287%
3288%    o windows: Specifies a pointer to a XWindows structure.
3289%
3290%    o image: the image; returned from ReadImage.
3291%
3292%    o exception: return any errors or warnings in this structure.
3293%
3294*/
3295static MagickBooleanType XColorEditImage(Display *display,
3296  XResourceInfo *resource_info,XWindows *windows,Image **image,
3297  ExceptionInfo *exception)
3298{
3299  static const char
3300    *ColorEditMenu[] =
3301    {
3302      "Method",
3303      "Pixel Color",
3304      "Border Color",
3305      "Fuzz",
3306      "Undo",
3307      "Help",
3308      "Dismiss",
3309      (char *) NULL
3310    };
3311
3312  static const ModeType
3313    ColorEditCommands[] =
3314    {
3315      ColorEditMethodCommand,
3316      ColorEditColorCommand,
3317      ColorEditBorderCommand,
3318      ColorEditFuzzCommand,
3319      ColorEditUndoCommand,
3320      ColorEditHelpCommand,
3321      ColorEditDismissCommand
3322    };
3323
3324  static PaintMethod
3325    method = PointMethod;
3326
3327  static unsigned int
3328    pen_id = 0;
3329
3330  static XColor
3331    border_color = { 0, 0, 0, 0, 0, 0 };
3332
3333  char
3334    command[MaxTextExtent],
3335    text[MaxTextExtent];
3336
3337  Cursor
3338    cursor;
3339
3340  int
3341    entry,
3342    id,
3343    x,
3344    x_offset,
3345    y,
3346    y_offset;
3347
3348  register Quantum
3349    *q;
3350
3351  register ssize_t
3352    i;
3353
3354  unsigned int
3355    height,
3356    width;
3357
3358  size_t
3359    state;
3360
3361  XColor
3362    color;
3363
3364  XEvent
3365    event;
3366
3367  /*
3368    Map Command widget.
3369  */
3370  (void) CloneString(&windows->command.name,"Color Edit");
3371  windows->command.data=4;
3372  (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3373  (void) XMapRaised(display,windows->command.id);
3374  XClientMessage(display,windows->image.id,windows->im_protocols,
3375    windows->im_update_widget,CurrentTime);
3376  /*
3377    Make cursor.
3378  */
3379  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3380    resource_info->background_color,resource_info->foreground_color);
3381  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3382  /*
3383    Track pointer until button 1 is pressed.
3384  */
3385  XQueryPosition(display,windows->image.id,&x,&y);
3386  (void) XSelectInput(display,windows->image.id,
3387    windows->image.attributes.event_mask | PointerMotionMask);
3388  state=DefaultState;
3389  do
3390  {
3391    if (IfMagickTrue(windows->info.mapped) )
3392      {
3393        /*
3394          Display pointer position.
3395        */
3396        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
3397          x+windows->image.x,y+windows->image.y);
3398        XInfoWidget(display,windows,text);
3399      }
3400    /*
3401      Wait for next event.
3402    */
3403    XScreenEvent(display,windows,&event,exception);
3404    if (event.xany.window == windows->command.id)
3405      {
3406        /*
3407          Select a command from the Command widget.
3408        */
3409        id=XCommandWidget(display,windows,ColorEditMenu,&event);
3410        if (id < 0)
3411          {
3412            (void) XCheckDefineCursor(display,windows->image.id,cursor);
3413            continue;
3414          }
3415        switch (ColorEditCommands[id])
3416        {
3417          case ColorEditMethodCommand:
3418          {
3419            char
3420              **methods;
3421
3422            /*
3423              Select a method from the pop-up menu.
3424            */
3425            methods=(char **) GetCommandOptions(MagickMethodOptions);
3426            if (methods == (char **) NULL)
3427              break;
3428            entry=XMenuWidget(display,windows,ColorEditMenu[id],
3429              (const char **) methods,command);
3430            if (entry >= 0)
3431              method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3432                MagickFalse,methods[entry]);
3433            methods=DestroyStringList(methods);
3434            break;
3435          }
3436          case ColorEditColorCommand:
3437          {
3438            const char
3439              *ColorMenu[MaxNumberPens];
3440
3441            int
3442              pen_number;
3443
3444            /*
3445              Initialize menu selections.
3446            */
3447            for (i=0; i < (int) (MaxNumberPens-2); i++)
3448              ColorMenu[i]=resource_info->pen_colors[i];
3449            ColorMenu[MaxNumberPens-2]="Browser...";
3450            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3451            /*
3452              Select a pen color from the pop-up menu.
3453            */
3454            pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3455              (const char **) ColorMenu,command);
3456            if (pen_number < 0)
3457              break;
3458            if (pen_number == (MaxNumberPens-2))
3459              {
3460                static char
3461                  color_name[MaxTextExtent] = "gray";
3462
3463                /*
3464                  Select a pen color from a dialog.
3465                */
3466                resource_info->pen_colors[pen_number]=color_name;
3467                XColorBrowserWidget(display,windows,"Select",color_name);
3468                if (*color_name == '\0')
3469                  break;
3470              }
3471            /*
3472              Set pen color.
3473            */
3474            (void) XParseColor(display,windows->map_info->colormap,
3475              resource_info->pen_colors[pen_number],&color);
3476            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3477              (unsigned int) MaxColors,&color);
3478            windows->pixel_info->pen_colors[pen_number]=color;
3479            pen_id=(unsigned int) pen_number;
3480            break;
3481          }
3482          case ColorEditBorderCommand:
3483          {
3484            const char
3485              *ColorMenu[MaxNumberPens];
3486
3487            int
3488              pen_number;
3489
3490            /*
3491              Initialize menu selections.
3492            */
3493            for (i=0; i < (int) (MaxNumberPens-2); i++)
3494              ColorMenu[i]=resource_info->pen_colors[i];
3495            ColorMenu[MaxNumberPens-2]="Browser...";
3496            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3497            /*
3498              Select a pen color from the pop-up menu.
3499            */
3500            pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3501              (const char **) ColorMenu,command);
3502            if (pen_number < 0)
3503              break;
3504            if (pen_number == (MaxNumberPens-2))
3505              {
3506                static char
3507                  color_name[MaxTextExtent] = "gray";
3508
3509                /*
3510                  Select a pen color from a dialog.
3511                */
3512                resource_info->pen_colors[pen_number]=color_name;
3513                XColorBrowserWidget(display,windows,"Select",color_name);
3514                if (*color_name == '\0')
3515                  break;
3516              }
3517            /*
3518              Set border color.
3519            */
3520            (void) XParseColor(display,windows->map_info->colormap,
3521              resource_info->pen_colors[pen_number],&border_color);
3522            break;
3523          }
3524          case ColorEditFuzzCommand:
3525          {
3526            static char
3527              fuzz[MaxTextExtent];
3528
3529            static const char
3530              *FuzzMenu[] =
3531              {
3532                "0%",
3533                "2%",
3534                "5%",
3535                "10%",
3536                "15%",
3537                "Dialog...",
3538                (char *) NULL,
3539              };
3540
3541            /*
3542              Select a command from the pop-up menu.
3543            */
3544            entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3545              command);
3546            if (entry < 0)
3547              break;
3548            if (entry != 5)
3549              {
3550                (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
3551                  QuantumRange+1.0);
3552                break;
3553              }
3554            (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
3555            (void) XDialogWidget(display,windows,"Ok",
3556              "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3557            if (*fuzz == '\0')
3558              break;
3559            (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
3560            (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
3561              1.0);
3562            break;
3563          }
3564          case ColorEditUndoCommand:
3565          {
3566            (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3567              image,exception);
3568            break;
3569          }
3570          case ColorEditHelpCommand:
3571          default:
3572          {
3573            XTextViewWidget(display,resource_info,windows,MagickFalse,
3574              "Help Viewer - Image Annotation",ImageColorEditHelp);
3575            break;
3576          }
3577          case ColorEditDismissCommand:
3578          {
3579            /*
3580              Prematurely exit.
3581            */
3582            state|=EscapeState;
3583            state|=ExitState;
3584            break;
3585          }
3586        }
3587        (void) XCheckDefineCursor(display,windows->image.id,cursor);
3588        continue;
3589      }
3590    switch (event.type)
3591    {
3592      case ButtonPress:
3593      {
3594        if (event.xbutton.button != Button1)
3595          break;
3596        if ((event.xbutton.window != windows->image.id) &&
3597            (event.xbutton.window != windows->magnify.id))
3598          break;
3599        /*
3600          exit loop.
3601        */
3602        x=event.xbutton.x;
3603        y=event.xbutton.y;
3604        (void) XMagickCommand(display,resource_info,windows,
3605          SaveToUndoBufferCommand,image,exception);
3606        state|=UpdateConfigurationState;
3607        break;
3608      }
3609      case ButtonRelease:
3610      {
3611        if (event.xbutton.button != Button1)
3612          break;
3613        if ((event.xbutton.window != windows->image.id) &&
3614            (event.xbutton.window != windows->magnify.id))
3615          break;
3616        /*
3617          Update colormap information.
3618        */
3619        x=event.xbutton.x;
3620        y=event.xbutton.y;
3621        XConfigureImageColormap(display,resource_info,windows,*image,exception);
3622        (void) XConfigureImage(display,resource_info,windows,*image,exception);
3623        XInfoWidget(display,windows,text);
3624        (void) XCheckDefineCursor(display,windows->image.id,cursor);
3625        state&=(~UpdateConfigurationState);
3626        break;
3627      }
3628      case Expose:
3629        break;
3630      case KeyPress:
3631      {
3632        KeySym
3633          key_symbol;
3634
3635        if (event.xkey.window == windows->magnify.id)
3636          {
3637            Window
3638              window;
3639
3640            window=windows->magnify.id;
3641            while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3642          }
3643        if (event.xkey.window != windows->image.id)
3644          break;
3645        /*
3646          Respond to a user key press.
3647        */
3648        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3649          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3650        switch ((int) key_symbol)
3651        {
3652          case XK_Escape:
3653          case XK_F20:
3654          {
3655            /*
3656              Prematurely exit.
3657            */
3658            state|=ExitState;
3659            break;
3660          }
3661          case XK_F1:
3662          case XK_Help:
3663          {
3664            XTextViewWidget(display,resource_info,windows,MagickFalse,
3665              "Help Viewer - Image Annotation",ImageColorEditHelp);
3666            break;
3667          }
3668          default:
3669          {
3670            (void) XBell(display,0);
3671            break;
3672          }
3673        }
3674        break;
3675      }
3676      case MotionNotify:
3677      {
3678        /*
3679          Map and unmap Info widget as cursor crosses its boundaries.
3680        */
3681        x=event.xmotion.x;
3682        y=event.xmotion.y;
3683        if (IfMagickTrue(windows->info.mapped) )
3684          {
3685            if ((x < (int) (windows->info.x+windows->info.width)) &&
3686                (y < (int) (windows->info.y+windows->info.height)))
3687              (void) XWithdrawWindow(display,windows->info.id,
3688                windows->info.screen);
3689          }
3690        else
3691          if ((x > (int) (windows->info.x+windows->info.width)) ||
3692              (y > (int) (windows->info.y+windows->info.height)))
3693            (void) XMapWindow(display,windows->info.id);
3694        break;
3695      }
3696      default:
3697        break;
3698    }
3699    if (event.xany.window == windows->magnify.id)
3700      {
3701        x=windows->magnify.x-windows->image.x;
3702        y=windows->magnify.y-windows->image.y;
3703      }
3704    x_offset=x;
3705    y_offset=y;
3706    if ((state & UpdateConfigurationState) != 0)
3707      {
3708        CacheView
3709          *image_view;
3710
3711        int
3712          x,
3713          y;
3714
3715        /*
3716          Pixel edit is relative to image configuration.
3717        */
3718        (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3719          MagickTrue);
3720        color=windows->pixel_info->pen_colors[pen_id];
3721        XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3722        width=(unsigned int) (*image)->columns;
3723        height=(unsigned int) (*image)->rows;
3724        x=0;
3725        y=0;
3726        if (windows->image.crop_geometry != (char *) NULL)
3727          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3728            &width,&height);
3729        x_offset=(int)
3730          (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3731        y_offset=(int)
3732          (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3733        if ((x_offset < 0) || (y_offset < 0))
3734          continue;
3735        if ((x_offset >= (int) (*image)->columns) ||
3736            (y_offset >= (int) (*image)->rows))
3737          continue;
3738        image_view=AcquireAuthenticCacheView(*image,exception);
3739        switch (method)
3740        {
3741          case PointMethod:
3742          default:
3743          {
3744            /*
3745              Update color information using point algorithm.
3746            */
3747            if (IfMagickFalse(SetImageStorageClass(*image,DirectClass,exception)) )
3748              return(MagickFalse);
3749            q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3750              (ssize_t) y_offset,1,1,exception);
3751            if (q == (Quantum *) NULL)
3752              break;
3753            SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3754            SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3755            SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3756            (void) SyncCacheViewAuthenticPixels(image_view,exception);
3757            break;
3758          }
3759          case ReplaceMethod:
3760          {
3761            PixelInfo
3762              pixel,
3763              target;
3764
3765            /*
3766              Update color information using replace algorithm.
3767            */
3768            (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
3769              x_offset,(ssize_t) y_offset,&target,exception);
3770            if ((*image)->storage_class == DirectClass)
3771              {
3772                for (y=0; y < (int) (*image)->rows; y++)
3773                {
3774                  q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3775                    (*image)->columns,1,exception);
3776                  if (q == (Quantum *) NULL)
3777                    break;
3778                  for (x=0; x < (int) (*image)->columns; x++)
3779                  {
3780                    GetPixelInfoPixel(*image,q,&pixel);
3781                    if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
3782                      {
3783                        SetPixelRed(*image,ScaleShortToQuantum(
3784                          color.red),q);
3785                        SetPixelGreen(*image,ScaleShortToQuantum(
3786                          color.green),q);
3787                        SetPixelBlue(*image,ScaleShortToQuantum(
3788                          color.blue),q);
3789                      }
3790                    q+=GetPixelChannels(*image);
3791                  }
3792                  if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
3793                    break;
3794                }
3795              }
3796            else
3797              {
3798                for (i=0; i < (ssize_t) (*image)->colors; i++)
3799                  if (IsFuzzyEquivalencePixelInfo((*image)->colormap+i,&target))
3800                    {
3801                      (*image)->colormap[i].red=(double) ScaleShortToQuantum(
3802                        color.red);
3803                      (*image)->colormap[i].green=(double) ScaleShortToQuantum(
3804                        color.green);
3805                      (*image)->colormap[i].blue=(double) ScaleShortToQuantum(
3806                        color.blue);
3807                    }
3808                (void) SyncImage(*image,exception);
3809              }
3810            break;
3811          }
3812          case FloodfillMethod:
3813          case FillToBorderMethod:
3814          {
3815            DrawInfo
3816              *draw_info;
3817
3818            PixelInfo
3819              target;
3820
3821            /*
3822              Update color information using floodfill algorithm.
3823            */
3824            (void) GetOneVirtualPixelInfo(*image,
3825              GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
3826              y_offset,&target,exception);
3827            if (method == FillToBorderMethod)
3828              {
3829                target.red=(double)
3830                  ScaleShortToQuantum(border_color.red);
3831                target.green=(double)
3832                  ScaleShortToQuantum(border_color.green);
3833                target.blue=(double)
3834                  ScaleShortToQuantum(border_color.blue);
3835              }
3836            draw_info=CloneDrawInfo(resource_info->image_info,
3837              (DrawInfo *) NULL);
3838            (void) QueryColorCompliance(resource_info->pen_colors[pen_id],
3839              AllCompliance,&draw_info->fill,exception);
3840            (void) FloodfillPaintImage(*image,draw_info,&target,
3841              (ssize_t)x_offset,(ssize_t)y_offset,
3842              IsMagickFalse(method == FloodfillMethod),exception);
3843            draw_info=DestroyDrawInfo(draw_info);
3844            break;
3845          }
3846          case ResetMethod:
3847          {
3848            /*
3849              Update color information using reset algorithm.
3850            */
3851            if (IfMagickFalse(SetImageStorageClass(*image,DirectClass,exception)) )
3852              return(MagickFalse);
3853            for (y=0; y < (int) (*image)->rows; y++)
3854            {
3855              q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3856                (*image)->columns,1,exception);
3857              if (q == (Quantum *) NULL)
3858                break;
3859              for (x=0; x < (int) (*image)->columns; x++)
3860              {
3861                SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3862                SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3863                SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3864                q+=GetPixelChannels(*image);
3865              }
3866              if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
3867                break;
3868            }
3869            break;
3870          }
3871        }
3872        image_view=DestroyCacheView(image_view);
3873        state&=(~UpdateConfigurationState);
3874      }
3875  } while ((state & ExitState) == 0);
3876  (void) XSelectInput(display,windows->image.id,
3877    windows->image.attributes.event_mask);
3878  XSetCursorState(display,windows,MagickFalse);
3879  (void) XFreeCursor(display,cursor);
3880  return(MagickTrue);
3881}
3882
3883/*
3884%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3885%                                                                             %
3886%                                                                             %
3887%                                                                             %
3888+   X C o m p o s i t e I m a g e                                             %
3889%                                                                             %
3890%                                                                             %
3891%                                                                             %
3892%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3893%
3894%  XCompositeImage() requests an image name from the user, reads the image and
3895%  composites it with the X window image at a location the user chooses with
3896%  the pointer.
3897%
3898%  The format of the XCompositeImage method is:
3899%
3900%      MagickBooleanType XCompositeImage(Display *display,
3901%        XResourceInfo *resource_info,XWindows *windows,Image *image,
3902%        ExceptionInfo *exception)
3903%
3904%  A description of each parameter follows:
3905%
3906%    o display: Specifies a connection to an X server;  returned from
3907%      XOpenDisplay.
3908%
3909%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3910%
3911%    o windows: Specifies a pointer to a XWindows structure.
3912%
3913%    o image: the image; returned from ReadImage.
3914%
3915%    o exception: return any errors or warnings in this structure.
3916%
3917*/
3918static MagickBooleanType XCompositeImage(Display *display,
3919  XResourceInfo *resource_info,XWindows *windows,Image *image,
3920  ExceptionInfo *exception)
3921{
3922  static char
3923    displacement_geometry[MaxTextExtent] = "30x30",
3924    filename[MaxTextExtent] = "\0";
3925
3926  static const char
3927    *CompositeMenu[] =
3928    {
3929      "Operators",
3930      "Dissolve",
3931      "Displace",
3932      "Help",
3933      "Dismiss",
3934      (char *) NULL
3935    };
3936
3937  static CompositeOperator
3938    compose = CopyCompositeOp;
3939
3940  static const ModeType
3941    CompositeCommands[] =
3942    {
3943      CompositeOperatorsCommand,
3944      CompositeDissolveCommand,
3945      CompositeDisplaceCommand,
3946      CompositeHelpCommand,
3947      CompositeDismissCommand
3948    };
3949
3950  char
3951    text[MaxTextExtent];
3952
3953  Cursor
3954    cursor;
3955
3956  Image
3957    *composite_image;
3958
3959  int
3960    entry,
3961    id,
3962    x,
3963    y;
3964
3965  double
3966    blend,
3967    scale_factor;
3968
3969  RectangleInfo
3970    highlight_info,
3971    composite_info;
3972
3973  unsigned int
3974    height,
3975    width;
3976
3977  size_t
3978    state;
3979
3980  XEvent
3981    event;
3982
3983  /*
3984    Request image file name from user.
3985  */
3986  XFileBrowserWidget(display,windows,"Composite",filename);
3987  if (*filename == '\0')
3988    return(MagickTrue);
3989  /*
3990    Read image.
3991  */
3992  XSetCursorState(display,windows,MagickTrue);
3993  XCheckRefreshWindows(display,windows);
3994  (void) CopyMagickString(resource_info->image_info->filename,filename,
3995    MaxTextExtent);
3996  composite_image=ReadImage(resource_info->image_info,exception);
3997  CatchException(exception);
3998  XSetCursorState(display,windows,MagickFalse);
3999  if (composite_image == (Image *) NULL)
4000    return(MagickFalse);
4001  /*
4002    Map Command widget.
4003  */
4004  (void) CloneString(&windows->command.name,"Composite");
4005  windows->command.data=1;
4006  (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
4007  (void) XMapRaised(display,windows->command.id);
4008  XClientMessage(display,windows->image.id,windows->im_protocols,
4009    windows->im_update_widget,CurrentTime);
4010  /*
4011    Track pointer until button 1 is pressed.
4012  */
4013  XQueryPosition(display,windows->image.id,&x,&y);
4014  (void) XSelectInput(display,windows->image.id,
4015    windows->image.attributes.event_mask | PointerMotionMask);
4016  composite_info.x=(ssize_t) windows->image.x+x;
4017  composite_info.y=(ssize_t) windows->image.y+y;
4018  composite_info.width=0;
4019  composite_info.height=0;
4020  cursor=XCreateFontCursor(display,XC_ul_angle);
4021  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4022  blend=0.0;
4023  state=DefaultState;
4024  do
4025  {
4026    if (IfMagickTrue(windows->info.mapped) )
4027      {
4028        /*
4029          Display pointer position.
4030        */
4031        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4032          (long) composite_info.x,(long) composite_info.y);
4033        XInfoWidget(display,windows,text);
4034      }
4035    highlight_info=composite_info;
4036    highlight_info.x=composite_info.x-windows->image.x;
4037    highlight_info.y=composite_info.y-windows->image.y;
4038    XHighlightRectangle(display,windows->image.id,
4039      windows->image.highlight_context,&highlight_info);
4040    /*
4041      Wait for next event.
4042    */
4043    XScreenEvent(display,windows,&event,exception);
4044    XHighlightRectangle(display,windows->image.id,
4045      windows->image.highlight_context,&highlight_info);
4046    if (event.xany.window == windows->command.id)
4047      {
4048        /*
4049          Select a command from the Command widget.
4050        */
4051        id=XCommandWidget(display,windows,CompositeMenu,&event);
4052        if (id < 0)
4053          continue;
4054        switch (CompositeCommands[id])
4055        {
4056          case CompositeOperatorsCommand:
4057          {
4058            char
4059              command[MaxTextExtent],
4060              **operators;
4061
4062            /*
4063              Select a command from the pop-up menu.
4064            */
4065            operators=GetCommandOptions(MagickComposeOptions);
4066            if (operators == (char **) NULL)
4067              break;
4068            entry=XMenuWidget(display,windows,CompositeMenu[id],
4069              (const char **) operators,command);
4070            if (entry >= 0)
4071              compose=(CompositeOperator) ParseCommandOption(
4072                MagickComposeOptions,MagickFalse,operators[entry]);
4073            operators=DestroyStringList(operators);
4074            break;
4075          }
4076          case CompositeDissolveCommand:
4077          {
4078            static char
4079              factor[MaxTextExtent] = "20.0";
4080
4081            /*
4082              Dissolve the two images a given percent.
4083            */
4084            (void) XSetFunction(display,windows->image.highlight_context,
4085              GXcopy);
4086            (void) XDialogWidget(display,windows,"Dissolve",
4087              "Enter the blend factor (0.0 - 99.9%):",factor);
4088            (void) XSetFunction(display,windows->image.highlight_context,
4089              GXinvert);
4090            if (*factor == '\0')
4091              break;
4092            blend=StringToDouble(factor,(char **) NULL);
4093            compose=DissolveCompositeOp;
4094            break;
4095          }
4096          case CompositeDisplaceCommand:
4097          {
4098            /*
4099              Get horizontal and vertical scale displacement geometry.
4100            */
4101            (void) XSetFunction(display,windows->image.highlight_context,
4102              GXcopy);
4103            (void) XDialogWidget(display,windows,"Displace",
4104              "Enter the horizontal and vertical scale:",displacement_geometry);
4105            (void) XSetFunction(display,windows->image.highlight_context,
4106              GXinvert);
4107            if (*displacement_geometry == '\0')
4108              break;
4109            compose=DisplaceCompositeOp;
4110            break;
4111          }
4112          case CompositeHelpCommand:
4113          {
4114            (void) XSetFunction(display,windows->image.highlight_context,
4115              GXcopy);
4116            XTextViewWidget(display,resource_info,windows,MagickFalse,
4117              "Help Viewer - Image Composite",ImageCompositeHelp);
4118            (void) XSetFunction(display,windows->image.highlight_context,
4119              GXinvert);
4120            break;
4121          }
4122          case CompositeDismissCommand:
4123          {
4124            /*
4125              Prematurely exit.
4126            */
4127            state|=EscapeState;
4128            state|=ExitState;
4129            break;
4130          }
4131          default:
4132            break;
4133        }
4134        continue;
4135      }
4136    switch (event.type)
4137    {
4138      case ButtonPress:
4139      {
4140        if (IfMagickTrue(image->debug) )
4141          (void) LogMagickEvent(X11Event,GetMagickModule(),
4142            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4143            event.xbutton.button,event.xbutton.x,event.xbutton.y);
4144        if (event.xbutton.button != Button1)
4145          break;
4146        if (event.xbutton.window != windows->image.id)
4147          break;
4148        /*
4149          Change cursor.
4150        */
4151        composite_info.width=composite_image->columns;
4152        composite_info.height=composite_image->rows;
4153        (void) XCheckDefineCursor(display,windows->image.id,cursor);
4154        composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4155        composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4156        break;
4157      }
4158      case ButtonRelease:
4159      {
4160        if (IfMagickTrue(image->debug) )
4161          (void) LogMagickEvent(X11Event,GetMagickModule(),
4162            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4163            event.xbutton.button,event.xbutton.x,event.xbutton.y);
4164        if (event.xbutton.button != Button1)
4165          break;
4166        if (event.xbutton.window != windows->image.id)
4167          break;
4168        if ((composite_info.width != 0) && (composite_info.height != 0))
4169          {
4170            /*
4171              User has selected the location of the composite image.
4172            */
4173            composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4174            composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4175            state|=ExitState;
4176          }
4177        break;
4178      }
4179      case Expose:
4180        break;
4181      case KeyPress:
4182      {
4183        char
4184          command[MaxTextExtent];
4185
4186        KeySym
4187          key_symbol;
4188
4189        int
4190          length;
4191
4192        if (event.xkey.window != windows->image.id)
4193          break;
4194        /*
4195          Respond to a user key press.
4196        */
4197        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4198          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4199        *(command+length)='\0';
4200        if (IfMagickTrue(image->debug) )
4201          (void) LogMagickEvent(X11Event,GetMagickModule(),
4202            "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4203        switch ((int) key_symbol)
4204        {
4205          case XK_Escape:
4206          case XK_F20:
4207          {
4208            /*
4209              Prematurely exit.
4210            */
4211            composite_image=DestroyImage(composite_image);
4212            state|=EscapeState;
4213            state|=ExitState;
4214            break;
4215          }
4216          case XK_F1:
4217          case XK_Help:
4218          {
4219            (void) XSetFunction(display,windows->image.highlight_context,
4220              GXcopy);
4221            XTextViewWidget(display,resource_info,windows,MagickFalse,
4222              "Help Viewer - Image Composite",ImageCompositeHelp);
4223            (void) XSetFunction(display,windows->image.highlight_context,
4224              GXinvert);
4225            break;
4226          }
4227          default:
4228          {
4229            (void) XBell(display,0);
4230            break;
4231          }
4232        }
4233        break;
4234      }
4235      case MotionNotify:
4236      {
4237        /*
4238          Map and unmap Info widget as text cursor crosses its boundaries.
4239        */
4240        x=event.xmotion.x;
4241        y=event.xmotion.y;
4242        if (IfMagickTrue(windows->info.mapped) )
4243          {
4244            if ((x < (int) (windows->info.x+windows->info.width)) &&
4245                (y < (int) (windows->info.y+windows->info.height)))
4246              (void) XWithdrawWindow(display,windows->info.id,
4247                windows->info.screen);
4248          }
4249        else
4250          if ((x > (int) (windows->info.x+windows->info.width)) ||
4251              (y > (int) (windows->info.y+windows->info.height)))
4252            (void) XMapWindow(display,windows->info.id);
4253        composite_info.x=(ssize_t) windows->image.x+x;
4254        composite_info.y=(ssize_t) windows->image.y+y;
4255        break;
4256      }
4257      default:
4258      {
4259        if (IfMagickTrue(image->debug) )
4260          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4261            event.type);
4262        break;
4263      }
4264    }
4265  } while ((state & ExitState) == 0);
4266  (void) XSelectInput(display,windows->image.id,
4267    windows->image.attributes.event_mask);
4268  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4269  XSetCursorState(display,windows,MagickFalse);
4270  (void) XFreeCursor(display,cursor);
4271  if ((state & EscapeState) != 0)
4272    return(MagickTrue);
4273  /*
4274    Image compositing is relative to image configuration.
4275  */
4276  XSetCursorState(display,windows,MagickTrue);
4277  XCheckRefreshWindows(display,windows);
4278  width=(unsigned int) image->columns;
4279  height=(unsigned int) image->rows;
4280  x=0;
4281  y=0;
4282  if (windows->image.crop_geometry != (char *) NULL)
4283    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4284  scale_factor=(double) width/windows->image.ximage->width;
4285  composite_info.x+=x;
4286  composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4287  composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4288  scale_factor=(double) height/windows->image.ximage->height;
4289  composite_info.y+=y;
4290  composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4291  composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4292  if ((composite_info.width != composite_image->columns) ||
4293      (composite_info.height != composite_image->rows))
4294    {
4295      Image
4296        *resize_image;
4297
4298      /*
4299        Scale composite image.
4300      */
4301      resize_image=ResizeImage(composite_image,composite_info.width,
4302        composite_info.height,composite_image->filter,exception);
4303      composite_image=DestroyImage(composite_image);
4304      if (resize_image == (Image *) NULL)
4305        {
4306          XSetCursorState(display,windows,MagickFalse);
4307          return(MagickFalse);
4308        }
4309      composite_image=resize_image;
4310    }
4311  if (compose == DisplaceCompositeOp)
4312    (void) SetImageArtifact(composite_image,"compose:args",
4313      displacement_geometry);
4314  if (blend != 0.0)
4315    {
4316      CacheView
4317        *image_view;
4318
4319      int
4320        y;
4321
4322      Quantum
4323        opacity;
4324
4325      register int
4326        x;
4327
4328      register Quantum
4329        *q;
4330
4331      /*
4332        Create mattes for blending.
4333      */
4334      (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,exception);
4335      opacity=(Quantum) (ScaleQuantumToChar(QuantumRange)-
4336        ((ssize_t) ScaleQuantumToChar(QuantumRange)*blend)/100);
4337      if (IfMagickFalse(SetImageStorageClass(image,DirectClass,exception)) )
4338        return(MagickFalse);
4339      image->alpha_trait=BlendPixelTrait;
4340      image_view=AcquireAuthenticCacheView(image,exception);
4341      for (y=0; y < (int) image->rows; y++)
4342      {
4343        q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4344          exception);
4345        if (q == (Quantum *) NULL)
4346          break;
4347        for (x=0; x < (int) image->columns; x++)
4348        {
4349          SetPixelAlpha(image,opacity,q);
4350          q+=GetPixelChannels(image);
4351        }
4352        if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
4353          break;
4354      }
4355      image_view=DestroyCacheView(image_view);
4356    }
4357  /*
4358    Composite image with X Image window.
4359  */
4360  (void) CompositeImage(image,composite_image,compose,MagickTrue,
4361    composite_info.x,composite_info.y,exception);
4362  composite_image=DestroyImage(composite_image);
4363  XSetCursorState(display,windows,MagickFalse);
4364  /*
4365    Update image configuration.
4366  */
4367  XConfigureImageColormap(display,resource_info,windows,image,exception);
4368  (void) XConfigureImage(display,resource_info,windows,image,exception);
4369  return(MagickTrue);
4370}
4371
4372/*
4373%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4374%                                                                             %
4375%                                                                             %
4376%                                                                             %
4377+   X C o n f i g u r e I m a g e                                             %
4378%                                                                             %
4379%                                                                             %
4380%                                                                             %
4381%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4382%
4383%  XConfigureImage() creates a new X image.  It also notifies the window
4384%  manager of the new image size and configures the transient widows.
4385%
4386%  The format of the XConfigureImage method is:
4387%
4388%      MagickBooleanType XConfigureImage(Display *display,
4389%        XResourceInfo *resource_info,XWindows *windows,Image *image,
4390%        ExceptionInfo *exception)
4391%
4392%  A description of each parameter follows:
4393%
4394%    o display: Specifies a connection to an X server; returned from
4395%      XOpenDisplay.
4396%
4397%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4398%
4399%    o windows: Specifies a pointer to a XWindows structure.
4400%
4401%    o image: the image.
4402%
4403%    o exception: return any errors or warnings in this structure.
4404%
4405%    o exception: return any errors or warnings in this structure.
4406%
4407*/
4408static MagickBooleanType XConfigureImage(Display *display,
4409  XResourceInfo *resource_info,XWindows *windows,Image *image,
4410  ExceptionInfo *exception)
4411{
4412  char
4413    geometry[MaxTextExtent];
4414
4415  MagickStatusType
4416    status;
4417
4418  size_t
4419    mask,
4420    height,
4421    width;
4422
4423  ssize_t
4424    x,
4425    y;
4426
4427  XSizeHints
4428    *size_hints;
4429
4430  XWindowChanges
4431    window_changes;
4432
4433  /*
4434    Dismiss if window dimensions are zero.
4435  */
4436  width=(unsigned int) windows->image.window_changes.width;
4437  height=(unsigned int) windows->image.window_changes.height;
4438  if (IfMagickTrue(image->debug) )
4439    (void) LogMagickEvent(X11Event,GetMagickModule(),
4440      "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4441      windows->image.ximage->height,(double) width,(double) height);
4442  if ((width*height) == 0)
4443    return(MagickTrue);
4444  x=0;
4445  y=0;
4446  /*
4447    Resize image to fit Image window dimensions.
4448  */
4449  XSetCursorState(display,windows,MagickTrue);
4450  (void) XFlush(display);
4451  if (((int) width != windows->image.ximage->width) ||
4452      ((int) height != windows->image.ximage->height))
4453    image->taint=MagickTrue;
4454  windows->magnify.x=(int)
4455    width*windows->magnify.x/windows->image.ximage->width;
4456  windows->magnify.y=(int)
4457    height*windows->magnify.y/windows->image.ximage->height;
4458  windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4459  windows->image.y=(int)
4460    (height*windows->image.y/windows->image.ximage->height);
4461  status=XMakeImage(display,resource_info,&windows->image,image,
4462    (unsigned int) width,(unsigned int) height,exception);
4463  if (IfMagickFalse(status) )
4464    XNoticeWidget(display,windows,"Unable to configure X image:",
4465      windows->image.name);
4466  /*
4467    Notify window manager of the new configuration.
4468  */
4469  if (resource_info->image_geometry != (char *) NULL)
4470    (void) FormatLocaleString(geometry,MaxTextExtent,"%s>!",
4471      resource_info->image_geometry);
4472  else
4473    (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
4474      XDisplayWidth(display,windows->image.screen),
4475      XDisplayHeight(display,windows->image.screen));
4476  (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4477  window_changes.width=(int) width;
4478  if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4479    window_changes.width=XDisplayWidth(display,windows->image.screen);
4480  window_changes.height=(int) height;
4481  if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4482    window_changes.height=XDisplayHeight(display,windows->image.screen);
4483  mask=(size_t) (CWWidth | CWHeight);
4484  if (resource_info->backdrop)
4485    {
4486      mask|=CWX | CWY;
4487      window_changes.x=(int)
4488        ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4489      window_changes.y=(int)
4490        ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4491    }
4492  (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4493    (unsigned int) mask,&window_changes);
4494  (void) XClearWindow(display,windows->image.id);
4495  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4496  /*
4497    Update Magnify window configuration.
4498  */
4499  if (IfMagickTrue(windows->magnify.mapped) )
4500    XMakeMagnifyImage(display,windows,exception);
4501  windows->pan.crop_geometry=windows->image.crop_geometry;
4502  XBestIconSize(display,&windows->pan,image);
4503  while (((windows->pan.width << 1) < MaxIconSize) &&
4504         ((windows->pan.height << 1) < MaxIconSize))
4505  {
4506    windows->pan.width<<=1;
4507    windows->pan.height<<=1;
4508  }
4509  if (windows->pan.geometry != (char *) NULL)
4510    (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4511      &windows->pan.width,&windows->pan.height);
4512  window_changes.width=(int) windows->pan.width;
4513  window_changes.height=(int) windows->pan.height;
4514  size_hints=XAllocSizeHints();
4515  if (size_hints != (XSizeHints *) NULL)
4516    {
4517      /*
4518        Set new size hints.
4519      */
4520      size_hints->flags=PSize | PMinSize | PMaxSize;
4521      size_hints->width=window_changes.width;
4522      size_hints->height=window_changes.height;
4523      size_hints->min_width=size_hints->width;
4524      size_hints->min_height=size_hints->height;
4525      size_hints->max_width=size_hints->width;
4526      size_hints->max_height=size_hints->height;
4527      (void) XSetNormalHints(display,windows->pan.id,size_hints);
4528      (void) XFree((void *) size_hints);
4529    }
4530  (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4531    (unsigned int) (CWWidth | CWHeight),&window_changes);
4532  /*
4533    Update icon window configuration.
4534  */
4535  windows->icon.crop_geometry=windows->image.crop_geometry;
4536  XBestIconSize(display,&windows->icon,image);
4537  window_changes.width=(int) windows->icon.width;
4538  window_changes.height=(int) windows->icon.height;
4539  (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4540    (unsigned int) (CWWidth | CWHeight),&window_changes);
4541  XSetCursorState(display,windows,MagickFalse);
4542  return(IsMagickTrue(status));
4543}
4544
4545/*
4546%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4547%                                                                             %
4548%                                                                             %
4549%                                                                             %
4550+   X C r o p I m a g e                                                       %
4551%                                                                             %
4552%                                                                             %
4553%                                                                             %
4554%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4555%
4556%  XCropImage() allows the user to select a region of the image and crop, copy,
4557%  or cut it.  For copy or cut, the image can subsequently be composited onto
4558%  the image with XPasteImage.
4559%
4560%  The format of the XCropImage method is:
4561%
4562%      MagickBooleanType XCropImage(Display *display,
4563%        XResourceInfo *resource_info,XWindows *windows,Image *image,
4564%        const ClipboardMode mode,ExceptionInfo *exception)
4565%
4566%  A description of each parameter follows:
4567%
4568%    o display: Specifies a connection to an X server; returned from
4569%      XOpenDisplay.
4570%
4571%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4572%
4573%    o windows: Specifies a pointer to a XWindows structure.
4574%
4575%    o image: the image; returned from ReadImage.
4576%
4577%    o mode: This unsigned value specified whether the image should be
4578%      cropped, copied, or cut.
4579%
4580%    o exception: return any errors or warnings in this structure.
4581%
4582*/
4583static MagickBooleanType XCropImage(Display *display,
4584  XResourceInfo *resource_info,XWindows *windows,Image *image,
4585  const ClipboardMode mode,ExceptionInfo *exception)
4586{
4587  static const char
4588    *CropModeMenu[] =
4589    {
4590      "Help",
4591      "Dismiss",
4592      (char *) NULL
4593    },
4594    *RectifyModeMenu[] =
4595    {
4596      "Crop",
4597      "Help",
4598      "Dismiss",
4599      (char *) NULL
4600    };
4601
4602  static const ModeType
4603    CropCommands[] =
4604    {
4605      CropHelpCommand,
4606      CropDismissCommand
4607    },
4608    RectifyCommands[] =
4609    {
4610      RectifyCopyCommand,
4611      RectifyHelpCommand,
4612      RectifyDismissCommand
4613    };
4614
4615  CacheView
4616    *image_view;
4617
4618  char
4619    command[MaxTextExtent],
4620    text[MaxTextExtent];
4621
4622  Cursor
4623    cursor;
4624
4625  int
4626    id,
4627    x,
4628    y;
4629
4630  KeySym
4631    key_symbol;
4632
4633  Image
4634    *crop_image;
4635
4636  double
4637    scale_factor;
4638
4639  RectangleInfo
4640    crop_info,
4641    highlight_info;
4642
4643  register Quantum
4644    *q;
4645
4646  unsigned int
4647    height,
4648    width;
4649
4650  size_t
4651    state;
4652
4653  XEvent
4654    event;
4655
4656  /*
4657    Map Command widget.
4658  */
4659  switch (mode)
4660  {
4661    case CopyMode:
4662    {
4663      (void) CloneString(&windows->command.name,"Copy");
4664      break;
4665    }
4666    case CropMode:
4667    {
4668      (void) CloneString(&windows->command.name,"Crop");
4669      break;
4670    }
4671    case CutMode:
4672    {
4673      (void) CloneString(&windows->command.name,"Cut");
4674      break;
4675    }
4676  }
4677  RectifyModeMenu[0]=windows->command.name;
4678  windows->command.data=0;
4679  (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4680  (void) XMapRaised(display,windows->command.id);
4681  XClientMessage(display,windows->image.id,windows->im_protocols,
4682    windows->im_update_widget,CurrentTime);
4683  /*
4684    Track pointer until button 1 is pressed.
4685  */
4686  XQueryPosition(display,windows->image.id,&x,&y);
4687  (void) XSelectInput(display,windows->image.id,
4688    windows->image.attributes.event_mask | PointerMotionMask);
4689  crop_info.x=(ssize_t) windows->image.x+x;
4690  crop_info.y=(ssize_t) windows->image.y+y;
4691  crop_info.width=0;
4692  crop_info.height=0;
4693  cursor=XCreateFontCursor(display,XC_fleur);
4694  state=DefaultState;
4695  do
4696  {
4697    if (IfMagickTrue(windows->info.mapped) )
4698      {
4699        /*
4700          Display pointer position.
4701        */
4702        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4703          (long) crop_info.x,(long) crop_info.y);
4704        XInfoWidget(display,windows,text);
4705      }
4706    /*
4707      Wait for next event.
4708    */
4709    XScreenEvent(display,windows,&event,exception);
4710    if (event.xany.window == windows->command.id)
4711      {
4712        /*
4713          Select a command from the Command widget.
4714        */
4715        id=XCommandWidget(display,windows,CropModeMenu,&event);
4716        if (id < 0)
4717          continue;
4718        switch (CropCommands[id])
4719        {
4720          case CropHelpCommand:
4721          {
4722            switch (mode)
4723            {
4724              case CopyMode:
4725              {
4726                XTextViewWidget(display,resource_info,windows,MagickFalse,
4727                  "Help Viewer - Image Copy",ImageCopyHelp);
4728                break;
4729              }
4730              case CropMode:
4731              {
4732                XTextViewWidget(display,resource_info,windows,MagickFalse,
4733                  "Help Viewer - Image Crop",ImageCropHelp);
4734                break;
4735              }
4736              case CutMode:
4737              {
4738                XTextViewWidget(display,resource_info,windows,MagickFalse,
4739                  "Help Viewer - Image Cut",ImageCutHelp);
4740                break;
4741              }
4742            }
4743            break;
4744          }
4745          case CropDismissCommand:
4746          {
4747            /*
4748              Prematurely exit.
4749            */
4750            state|=EscapeState;
4751            state|=ExitState;
4752            break;
4753          }
4754          default:
4755            break;
4756        }
4757        continue;
4758      }
4759    switch (event.type)
4760    {
4761      case ButtonPress:
4762      {
4763        if (event.xbutton.button != Button1)
4764          break;
4765        if (event.xbutton.window != windows->image.id)
4766          break;
4767        /*
4768          Note first corner of cropping rectangle-- exit loop.
4769        */
4770        (void) XCheckDefineCursor(display,windows->image.id,cursor);
4771        crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4772        crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4773        state|=ExitState;
4774        break;
4775      }
4776      case ButtonRelease:
4777        break;
4778      case Expose:
4779        break;
4780      case KeyPress:
4781      {
4782        if (event.xkey.window != windows->image.id)
4783          break;
4784        /*
4785          Respond to a user key press.
4786        */
4787        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4788          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4789        switch ((int) key_symbol)
4790        {
4791          case XK_Escape:
4792          case XK_F20:
4793          {
4794            /*
4795              Prematurely exit.
4796            */
4797            state|=EscapeState;
4798            state|=ExitState;
4799            break;
4800          }
4801          case XK_F1:
4802          case XK_Help:
4803          {
4804            switch (mode)
4805            {
4806              case CopyMode:
4807              {
4808                XTextViewWidget(display,resource_info,windows,MagickFalse,
4809                  "Help Viewer - Image Copy",ImageCopyHelp);
4810                break;
4811              }
4812              case CropMode:
4813              {
4814                XTextViewWidget(display,resource_info,windows,MagickFalse,
4815                  "Help Viewer - Image Crop",ImageCropHelp);
4816                break;
4817              }
4818              case CutMode:
4819              {
4820                XTextViewWidget(display,resource_info,windows,MagickFalse,
4821                  "Help Viewer - Image Cut",ImageCutHelp);
4822                break;
4823              }
4824            }
4825            break;
4826          }
4827          default:
4828          {
4829            (void) XBell(display,0);
4830            break;
4831          }
4832        }
4833        break;
4834      }
4835      case MotionNotify:
4836      {
4837        if (event.xmotion.window != windows->image.id)
4838          break;
4839        /*
4840          Map and unmap Info widget as text cursor crosses its boundaries.
4841        */
4842        x=event.xmotion.x;
4843        y=event.xmotion.y;
4844        if (IfMagickTrue(windows->info.mapped) )
4845          {
4846            if ((x < (int) (windows->info.x+windows->info.width)) &&
4847                (y < (int) (windows->info.y+windows->info.height)))
4848              (void) XWithdrawWindow(display,windows->info.id,
4849                windows->info.screen);
4850          }
4851        else
4852          if ((x > (int) (windows->info.x+windows->info.width)) ||
4853              (y > (int) (windows->info.y+windows->info.height)))
4854            (void) XMapWindow(display,windows->info.id);
4855        crop_info.x=(ssize_t) windows->image.x+x;
4856        crop_info.y=(ssize_t) windows->image.y+y;
4857        break;
4858      }
4859      default:
4860        break;
4861    }
4862  } while ((state & ExitState) == 0);
4863  (void) XSelectInput(display,windows->image.id,
4864    windows->image.attributes.event_mask);
4865  if ((state & EscapeState) != 0)
4866    {
4867      /*
4868        User want to exit without cropping.
4869      */
4870      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4871      (void) XFreeCursor(display,cursor);
4872      return(MagickTrue);
4873    }
4874  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4875  do
4876  {
4877    /*
4878      Size rectangle as pointer moves until the mouse button is released.
4879    */
4880    x=(int) crop_info.x;
4881    y=(int) crop_info.y;
4882    crop_info.width=0;
4883    crop_info.height=0;
4884    state=DefaultState;
4885    do
4886    {
4887      highlight_info=crop_info;
4888      highlight_info.x=crop_info.x-windows->image.x;
4889      highlight_info.y=crop_info.y-windows->image.y;
4890      if ((highlight_info.width > 3) && (highlight_info.height > 3))
4891        {
4892          /*
4893            Display info and draw cropping rectangle.
4894          */
4895          if (IfMagickFalse(windows->info.mapped) )
4896            (void) XMapWindow(display,windows->info.id);
4897          (void) FormatLocaleString(text,MaxTextExtent,
4898            " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4899            crop_info.height,(double) crop_info.x,(double) crop_info.y);
4900          XInfoWidget(display,windows,text);
4901          XHighlightRectangle(display,windows->image.id,
4902            windows->image.highlight_context,&highlight_info);
4903        }
4904      else
4905        if (IfMagickTrue(windows->info.mapped) )
4906          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4907      /*
4908        Wait for next event.
4909      */
4910      XScreenEvent(display,windows,&event,exception);
4911      if ((highlight_info.width > 3) && (highlight_info.height > 3))
4912        XHighlightRectangle(display,windows->image.id,
4913          windows->image.highlight_context,&highlight_info);
4914      switch (event.type)
4915      {
4916        case ButtonPress:
4917        {
4918          crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4919          crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4920          break;
4921        }
4922        case ButtonRelease:
4923        {
4924          /*
4925            User has committed to cropping rectangle.
4926          */
4927          crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4928          crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4929          XSetCursorState(display,windows,MagickFalse);
4930          state|=ExitState;
4931          windows->command.data=0;
4932          (void) XCommandWidget(display,windows,RectifyModeMenu,
4933            (XEvent *) NULL);
4934          break;
4935        }
4936        case Expose:
4937          break;
4938        case MotionNotify:
4939        {
4940          crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4941          crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4942        }
4943        default:
4944          break;
4945      }
4946      if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4947          ((state & ExitState) != 0))
4948        {
4949          /*
4950            Check boundary conditions.
4951          */
4952          if (crop_info.x < 0)
4953            crop_info.x=0;
4954          else
4955            if (crop_info.x > (ssize_t) windows->image.ximage->width)
4956              crop_info.x=(ssize_t) windows->image.ximage->width;
4957          if ((int) crop_info.x < x)
4958            crop_info.width=(unsigned int) (x-crop_info.x);
4959          else
4960            {
4961              crop_info.width=(unsigned int) (crop_info.x-x);
4962              crop_info.x=(ssize_t) x;
4963            }
4964          if (crop_info.y < 0)
4965            crop_info.y=0;
4966          else
4967            if (crop_info.y > (ssize_t) windows->image.ximage->height)
4968              crop_info.y=(ssize_t) windows->image.ximage->height;
4969          if ((int) crop_info.y < y)
4970            crop_info.height=(unsigned int) (y-crop_info.y);
4971          else
4972            {
4973              crop_info.height=(unsigned int) (crop_info.y-y);
4974              crop_info.y=(ssize_t) y;
4975            }
4976        }
4977    } while ((state & ExitState) == 0);
4978    /*
4979      Wait for user to grab a corner of the rectangle or press return.
4980    */
4981    state=DefaultState;
4982    (void) XMapWindow(display,windows->info.id);
4983    do
4984    {
4985      if (IfMagickTrue(windows->info.mapped) )
4986        {
4987          /*
4988            Display pointer position.
4989          */
4990          (void) FormatLocaleString(text,MaxTextExtent,
4991            " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4992            crop_info.height,(double) crop_info.x,(double) crop_info.y);
4993          XInfoWidget(display,windows,text);
4994        }
4995      highlight_info=crop_info;
4996      highlight_info.x=crop_info.x-windows->image.x;
4997      highlight_info.y=crop_info.y-windows->image.y;
4998      if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4999        {
5000          state|=EscapeState;
5001          state|=ExitState;
5002          break;
5003        }
5004      XHighlightRectangle(display,windows->image.id,
5005        windows->image.highlight_context,&highlight_info);
5006      XScreenEvent(display,windows,&event,exception);
5007      if (event.xany.window == windows->command.id)
5008        {
5009          /*
5010            Select a command from the Command widget.
5011          */
5012          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5013          id=XCommandWidget(display,windows,RectifyModeMenu,&event);
5014          (void) XSetFunction(display,windows->image.highlight_context,
5015            GXinvert);
5016          XHighlightRectangle(display,windows->image.id,
5017            windows->image.highlight_context,&highlight_info);
5018          if (id >= 0)
5019            switch (RectifyCommands[id])
5020            {
5021              case RectifyCopyCommand:
5022              {
5023                state|=ExitState;
5024                break;
5025              }
5026              case RectifyHelpCommand:
5027              {
5028                (void) XSetFunction(display,windows->image.highlight_context,
5029                  GXcopy);
5030                switch (mode)
5031                {
5032                  case CopyMode:
5033                  {
5034                    XTextViewWidget(display,resource_info,windows,MagickFalse,
5035                      "Help Viewer - Image Copy",ImageCopyHelp);
5036                    break;
5037                  }
5038                  case CropMode:
5039                  {
5040                    XTextViewWidget(display,resource_info,windows,MagickFalse,
5041                      "Help Viewer - Image Crop",ImageCropHelp);
5042                    break;
5043                  }
5044                  case CutMode:
5045                  {
5046                    XTextViewWidget(display,resource_info,windows,MagickFalse,
5047                      "Help Viewer - Image Cut",ImageCutHelp);
5048                    break;
5049                  }
5050                }
5051                (void) XSetFunction(display,windows->image.highlight_context,
5052                  GXinvert);
5053                break;
5054              }
5055              case RectifyDismissCommand:
5056              {
5057                /*
5058                  Prematurely exit.
5059                */
5060                state|=EscapeState;
5061                state|=ExitState;
5062                break;
5063              }
5064              default:
5065                break;
5066            }
5067          continue;
5068        }
5069      XHighlightRectangle(display,windows->image.id,
5070        windows->image.highlight_context,&highlight_info);
5071      switch (event.type)
5072      {
5073        case ButtonPress:
5074        {
5075          if (event.xbutton.button != Button1)
5076            break;
5077          if (event.xbutton.window != windows->image.id)
5078            break;
5079          x=windows->image.x+event.xbutton.x;
5080          y=windows->image.y+event.xbutton.y;
5081          if ((x < (int) (crop_info.x+RoiDelta)) &&
5082              (x > (int) (crop_info.x-RoiDelta)) &&
5083              (y < (int) (crop_info.y+RoiDelta)) &&
5084              (y > (int) (crop_info.y-RoiDelta)))
5085            {
5086              crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5087              crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5088              state|=UpdateConfigurationState;
5089              break;
5090            }
5091          if ((x < (int) (crop_info.x+RoiDelta)) &&
5092              (x > (int) (crop_info.x-RoiDelta)) &&
5093              (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5094              (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5095            {
5096              crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5097              state|=UpdateConfigurationState;
5098              break;
5099            }
5100          if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5101              (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5102              (y < (int) (crop_info.y+RoiDelta)) &&
5103              (y > (int) (crop_info.y-RoiDelta)))
5104            {
5105              crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5106              state|=UpdateConfigurationState;
5107              break;
5108            }
5109          if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5110              (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5111              (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5112              (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5113            {
5114              state|=UpdateConfigurationState;
5115              break;
5116            }
5117        }
5118        case ButtonRelease:
5119        {
5120          if (event.xbutton.window == windows->pan.id)
5121            if ((highlight_info.x != crop_info.x-windows->image.x) ||
5122                (highlight_info.y != crop_info.y-windows->image.y))
5123              XHighlightRectangle(display,windows->image.id,
5124                windows->image.highlight_context,&highlight_info);
5125          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5126            event.xbutton.time);
5127          break;
5128        }
5129        case Expose:
5130        {
5131          if (event.xexpose.window == windows->image.id)
5132            if (event.xexpose.count == 0)
5133              {
5134                event.xexpose.x=(int) highlight_info.x;
5135                event.xexpose.y=(int) highlight_info.y;
5136                event.xexpose.width=(int) highlight_info.width;
5137                event.xexpose.height=(int) highlight_info.height;
5138                XRefreshWindow(display,&windows->image,&event);
5139              }
5140          if (event.xexpose.window == windows->info.id)
5141            if (event.xexpose.count == 0)
5142              XInfoWidget(display,windows,text);
5143          break;
5144        }
5145        case KeyPress:
5146        {
5147          if (event.xkey.window != windows->image.id)
5148            break;
5149          /*
5150            Respond to a user key press.
5151          */
5152          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5153            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5154          switch ((int) key_symbol)
5155          {
5156            case XK_Escape:
5157            case XK_F20:
5158              state|=EscapeState;
5159            case XK_Return:
5160            {
5161              state|=ExitState;
5162              break;
5163            }
5164            case XK_Home:
5165            case XK_KP_Home:
5166            {
5167              crop_info.x=(ssize_t) (windows->image.width/2L-
5168                crop_info.width/2L);
5169              crop_info.y=(ssize_t) (windows->image.height/2L-
5170                crop_info.height/2L);
5171              break;
5172            }
5173            case XK_Left:
5174            case XK_KP_Left:
5175            {
5176              crop_info.x--;
5177              break;
5178            }
5179            case XK_Up:
5180            case XK_KP_Up:
5181            case XK_Next:
5182            {
5183              crop_info.y--;
5184              break;
5185            }
5186            case XK_Right:
5187            case XK_KP_Right:
5188            {
5189              crop_info.x++;
5190              break;
5191            }
5192            case XK_Prior:
5193            case XK_Down:
5194            case XK_KP_Down:
5195            {
5196              crop_info.y++;
5197              break;
5198            }
5199            case XK_F1:
5200            case XK_Help:
5201            {
5202              (void) XSetFunction(display,windows->image.highlight_context,
5203                GXcopy);
5204              switch (mode)
5205              {
5206                case CopyMode:
5207                {
5208                  XTextViewWidget(display,resource_info,windows,MagickFalse,
5209                    "Help Viewer - Image Copy",ImageCopyHelp);
5210                  break;
5211                }
5212                case CropMode:
5213                {
5214                  XTextViewWidget(display,resource_info,windows,MagickFalse,
5215                    "Help Viewer - Image Cropg",ImageCropHelp);
5216                  break;
5217                }
5218                case CutMode:
5219                {
5220                  XTextViewWidget(display,resource_info,windows,MagickFalse,
5221                    "Help Viewer - Image Cutg",ImageCutHelp);
5222                  break;
5223                }
5224              }
5225              (void) XSetFunction(display,windows->image.highlight_context,
5226                GXinvert);
5227              break;
5228            }
5229            default:
5230            {
5231              (void) XBell(display,0);
5232              break;
5233            }
5234          }
5235          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5236            event.xkey.time);
5237          break;
5238        }
5239        case KeyRelease:
5240          break;
5241        case MotionNotify:
5242        {
5243          if (event.xmotion.window != windows->image.id)
5244            break;
5245          /*
5246            Map and unmap Info widget as text cursor crosses its boundaries.
5247          */
5248          x=event.xmotion.x;
5249          y=event.xmotion.y;
5250          if (IfMagickTrue(windows->info.mapped) )
5251            {
5252              if ((x < (int) (windows->info.x+windows->info.width)) &&
5253                  (y < (int) (windows->info.y+windows->info.height)))
5254                (void) XWithdrawWindow(display,windows->info.id,
5255                  windows->info.screen);
5256            }
5257          else
5258            if ((x > (int) (windows->info.x+windows->info.width)) ||
5259                (y > (int) (windows->info.y+windows->info.height)))
5260              (void) XMapWindow(display,windows->info.id);
5261          crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5262          crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5263          break;
5264        }
5265        case SelectionRequest:
5266        {
5267          XSelectionEvent
5268            notify;
5269
5270          XSelectionRequestEvent
5271            *request;
5272
5273          /*
5274            Set primary selection.
5275          */
5276          (void) FormatLocaleString(text,MaxTextExtent,
5277            "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5278            crop_info.height,(double) crop_info.x,(double) crop_info.y);
5279          request=(&(event.xselectionrequest));
5280          (void) XChangeProperty(request->display,request->requestor,
5281            request->property,request->target,8,PropModeReplace,
5282            (unsigned char *) text,(int) strlen(text));
5283          notify.type=SelectionNotify;
5284          notify.display=request->display;
5285          notify.requestor=request->requestor;
5286          notify.selection=request->selection;
5287          notify.target=request->target;
5288          notify.time=request->time;
5289          if (request->property == None)
5290            notify.property=request->target;
5291          else
5292            notify.property=request->property;
5293          (void) XSendEvent(request->display,request->requestor,False,0,
5294            (XEvent *) &notify);
5295        }
5296        default:
5297          break;
5298      }
5299      if ((state & UpdateConfigurationState) != 0)
5300        {
5301          (void) XPutBackEvent(display,&event);
5302          (void) XCheckDefineCursor(display,windows->image.id,cursor);
5303          break;
5304        }
5305    } while ((state & ExitState) == 0);
5306  } while ((state & ExitState) == 0);
5307  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5308  XSetCursorState(display,windows,MagickFalse);
5309  if ((state & EscapeState) != 0)
5310    return(MagickTrue);
5311  if (mode == CropMode)
5312    if (((int) crop_info.width != windows->image.ximage->width) ||
5313        ((int) crop_info.height != windows->image.ximage->height))
5314      {
5315        /*
5316          Reconfigure Image window as defined by cropping rectangle.
5317        */
5318        XSetCropGeometry(display,windows,&crop_info,image);
5319        windows->image.window_changes.width=(int) crop_info.width;
5320        windows->image.window_changes.height=(int) crop_info.height;
5321        (void) XConfigureImage(display,resource_info,windows,image,exception);
5322        return(MagickTrue);
5323      }
5324  /*
5325    Copy image before applying image transforms.
5326  */
5327  XSetCursorState(display,windows,MagickTrue);
5328  XCheckRefreshWindows(display,windows);
5329  width=(unsigned int) image->columns;
5330  height=(unsigned int) image->rows;
5331  x=0;
5332  y=0;
5333  if (windows->image.crop_geometry != (char *) NULL)
5334    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5335  scale_factor=(double) width/windows->image.ximage->width;
5336  crop_info.x+=x;
5337  crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5338  crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5339  scale_factor=(double) height/windows->image.ximage->height;
5340  crop_info.y+=y;
5341  crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5342  crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5343  crop_image=CropImage(image,&crop_info,exception);
5344  XSetCursorState(display,windows,MagickFalse);
5345  if (crop_image == (Image *) NULL)
5346    return(MagickFalse);
5347  if (resource_info->copy_image != (Image *) NULL)
5348    resource_info->copy_image=DestroyImage(resource_info->copy_image);
5349  resource_info->copy_image=crop_image;
5350  if (mode == CopyMode)
5351    {
5352      (void) XConfigureImage(display,resource_info,windows,image,exception);
5353      return(MagickTrue);
5354    }
5355  /*
5356    Cut image.
5357  */
5358  if (IfMagickFalse(SetImageStorageClass(image,DirectClass,exception)) )
5359    return(MagickFalse);
5360  image->alpha_trait=BlendPixelTrait;
5361  image_view=AcquireAuthenticCacheView(image,exception);
5362  for (y=0; y < (int) crop_info.height; y++)
5363  {
5364    q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5365      crop_info.width,1,exception);
5366    if (q == (Quantum *) NULL)
5367      break;
5368    for (x=0; x < (int) crop_info.width; x++)
5369    {
5370      SetPixelAlpha(image,TransparentAlpha,q);
5371      q+=GetPixelChannels(image);
5372    }
5373    if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
5374      break;
5375  }
5376  image_view=DestroyCacheView(image_view);
5377  /*
5378    Update image configuration.
5379  */
5380  XConfigureImageColormap(display,resource_info,windows,image,exception);
5381  (void) XConfigureImage(display,resource_info,windows,image,exception);
5382  return(MagickTrue);
5383}
5384
5385/*
5386%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5387%                                                                             %
5388%                                                                             %
5389%                                                                             %
5390+   X D r a w I m a g e                                                       %
5391%                                                                             %
5392%                                                                             %
5393%                                                                             %
5394%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5395%
5396%  XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5397%  the image.
5398%
5399%  The format of the XDrawEditImage method is:
5400%
5401%      MagickBooleanType XDrawEditImage(Display *display,
5402%        XResourceInfo *resource_info,XWindows *windows,Image **image,
5403%        ExceptionInfo *exception)
5404%
5405%  A description of each parameter follows:
5406%
5407%    o display: Specifies a connection to an X server; returned from
5408%      XOpenDisplay.
5409%
5410%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5411%
5412%    o windows: Specifies a pointer to a XWindows structure.
5413%
5414%    o image: the image.
5415%
5416%    o exception: return any errors or warnings in this structure.
5417%
5418*/
5419static MagickBooleanType XDrawEditImage(Display *display,
5420  XResourceInfo *resource_info,XWindows *windows,Image **image,
5421  ExceptionInfo *exception)
5422{
5423  static const char
5424    *DrawMenu[] =
5425    {
5426      "Element",
5427      "Color",
5428      "Stipple",
5429      "Width",
5430      "Undo",
5431      "Help",
5432      "Dismiss",
5433      (char *) NULL
5434    };
5435
5436  static ElementType
5437    element = PointElement;
5438
5439  static const ModeType
5440    DrawCommands[] =
5441    {
5442      DrawElementCommand,
5443      DrawColorCommand,
5444      DrawStippleCommand,
5445      DrawWidthCommand,
5446      DrawUndoCommand,
5447      DrawHelpCommand,
5448      DrawDismissCommand
5449    };
5450
5451  static Pixmap
5452    stipple = (Pixmap) NULL;
5453
5454  static unsigned int
5455    pen_id = 0,
5456    line_width = 1;
5457
5458  char
5459    command[MaxTextExtent],
5460    text[MaxTextExtent];
5461
5462  Cursor
5463    cursor;
5464
5465  int
5466    entry,
5467    id,
5468    number_coordinates,
5469    x,
5470    y;
5471
5472  double
5473    degrees;
5474
5475  MagickStatusType
5476    status;
5477
5478  RectangleInfo
5479    rectangle_info;
5480
5481  register int
5482    i;
5483
5484  unsigned int
5485    distance,
5486    height,
5487    max_coordinates,
5488    width;
5489
5490  size_t
5491    state;
5492
5493  Window
5494    root_window;
5495
5496  XDrawInfo
5497    draw_info;
5498
5499  XEvent
5500    event;
5501
5502  XPoint
5503    *coordinate_info;
5504
5505  XSegment
5506    line_info;
5507
5508  /*
5509    Allocate polygon info.
5510  */
5511  max_coordinates=2048;
5512  coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5513    sizeof(*coordinate_info));
5514  if (coordinate_info == (XPoint *) NULL)
5515    {
5516      (void) ThrowMagickException(exception,GetMagickModule(),
5517        ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5518      return(MagickFalse);
5519    }
5520  /*
5521    Map Command widget.
5522  */
5523  (void) CloneString(&windows->command.name,"Draw");
5524  windows->command.data=4;
5525  (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5526  (void) XMapRaised(display,windows->command.id);
5527  XClientMessage(display,windows->image.id,windows->im_protocols,
5528    windows->im_update_widget,CurrentTime);
5529  /*
5530    Wait for first button press.
5531  */
5532  root_window=XRootWindow(display,XDefaultScreen(display));
5533  draw_info.stencil=OpaqueStencil;
5534  status=MagickTrue;
5535  cursor=XCreateFontCursor(display,XC_tcross);
5536  for ( ; ; )
5537  {
5538    XQueryPosition(display,windows->image.id,&x,&y);
5539    (void) XSelectInput(display,windows->image.id,
5540      windows->image.attributes.event_mask | PointerMotionMask);
5541    (void) XCheckDefineCursor(display,windows->image.id,cursor);
5542    state=DefaultState;
5543    do
5544    {
5545      if (IfMagickTrue(windows->info.mapped) )
5546        {
5547          /*
5548            Display pointer position.
5549          */
5550          (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
5551            x+windows->image.x,y+windows->image.y);
5552          XInfoWidget(display,windows,text);
5553        }
5554      /*
5555        Wait for next event.
5556      */
5557      XScreenEvent(display,windows,&event,exception);
5558      if (event.xany.window == windows->command.id)
5559        {
5560          /*
5561            Select a command from the Command widget.
5562          */
5563          id=XCommandWidget(display,windows,DrawMenu,&event);
5564          if (id < 0)
5565            continue;
5566          switch (DrawCommands[id])
5567          {
5568            case DrawElementCommand:
5569            {
5570              static const char
5571                *Elements[] =
5572                {
5573                  "point",
5574                  "line",
5575                  "rectangle",
5576                  "fill rectangle",
5577                  "circle",
5578                  "fill circle",
5579                  "ellipse",
5580                  "fill ellipse",
5581                  "polygon",
5582                  "fill polygon",
5583                  (char *) NULL,
5584                };
5585
5586              /*
5587                Select a command from the pop-up menu.
5588              */
5589              element=(ElementType) (XMenuWidget(display,windows,
5590                DrawMenu[id],Elements,command)+1);
5591              break;
5592            }
5593            case DrawColorCommand:
5594            {
5595              const char
5596                *ColorMenu[MaxNumberPens+1];
5597
5598              int
5599                pen_number;
5600
5601              MagickBooleanType
5602                transparent;
5603
5604              XColor
5605                color;
5606
5607              /*
5608                Initialize menu selections.
5609              */
5610              for (i=0; i < (int) (MaxNumberPens-2); i++)
5611                ColorMenu[i]=resource_info->pen_colors[i];
5612              ColorMenu[MaxNumberPens-2]="transparent";
5613              ColorMenu[MaxNumberPens-1]="Browser...";
5614              ColorMenu[MaxNumberPens]=(char *) NULL;
5615              /*
5616                Select a pen color from the pop-up menu.
5617              */
5618              pen_number=XMenuWidget(display,windows,DrawMenu[id],
5619                (const char **) ColorMenu,command);
5620              if (pen_number < 0)
5621                break;
5622              transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5623                MagickFalse;
5624              if (IfMagickTrue(transparent) )
5625                {
5626                  draw_info.stencil=TransparentStencil;
5627                  break;
5628                }
5629              if (pen_number == (MaxNumberPens-1))
5630                {
5631                  static char
5632                    color_name[MaxTextExtent] = "gray";
5633
5634                  /*
5635                    Select a pen color from a dialog.
5636                  */
5637                  resource_info->pen_colors[pen_number]=color_name;
5638                  XColorBrowserWidget(display,windows,"Select",color_name);
5639                  if (*color_name == '\0')
5640                    break;
5641                }
5642              /*
5643                Set pen color.
5644              */
5645              (void) XParseColor(display,windows->map_info->colormap,
5646                resource_info->pen_colors[pen_number],&color);
5647              XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5648                (unsigned int) MaxColors,&color);
5649              windows->pixel_info->pen_colors[pen_number]=color;
5650              pen_id=(unsigned int) pen_number;
5651              draw_info.stencil=OpaqueStencil;
5652              break;
5653            }
5654            case DrawStippleCommand:
5655            {
5656              Image
5657                *stipple_image;
5658
5659              ImageInfo
5660                *image_info;
5661
5662              int
5663                status;
5664
5665              static char
5666                filename[MaxTextExtent] = "\0";
5667
5668              static const char
5669                *StipplesMenu[] =
5670                {
5671                  "Brick",
5672                  "Diagonal",
5673                  "Scales",
5674                  "Vertical",
5675                  "Wavy",
5676                  "Translucent",
5677                  "Opaque",
5678                  (char *) NULL,
5679                  (char *) NULL,
5680                };
5681
5682              /*
5683                Select a command from the pop-up menu.
5684              */
5685              StipplesMenu[7]="Open...";
5686              entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5687                command);
5688              if (entry < 0)
5689                break;
5690              if (stipple != (Pixmap) NULL)
5691                (void) XFreePixmap(display,stipple);
5692              stipple=(Pixmap) NULL;
5693              if (entry != 7)
5694                {
5695                  switch (entry)
5696                  {
5697                    case 0:
5698                    {
5699                      stipple=XCreateBitmapFromData(display,root_window,
5700                        (char *) BricksBitmap,BricksWidth,BricksHeight);
5701                      break;
5702                    }
5703                    case 1:
5704                    {
5705                      stipple=XCreateBitmapFromData(display,root_window,
5706                        (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5707                      break;
5708                    }
5709                    case 2:
5710                    {
5711                      stipple=XCreateBitmapFromData(display,root_window,
5712                        (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5713                      break;
5714                    }
5715                    case 3:
5716                    {
5717                      stipple=XCreateBitmapFromData(display,root_window,
5718                        (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5719                      break;
5720                    }
5721                    case 4:
5722                    {
5723                      stipple=XCreateBitmapFromData(display,root_window,
5724                        (char *) WavyBitmap,WavyWidth,WavyHeight);
5725                      break;
5726                    }
5727                    case 5:
5728                    {
5729                      stipple=XCreateBitmapFromData(display,root_window,
5730                        (char *) HighlightBitmap,HighlightWidth,
5731                        HighlightHeight);
5732                      break;
5733                    }
5734                    case 6:
5735                    default:
5736                    {
5737                      stipple=XCreateBitmapFromData(display,root_window,
5738                        (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5739                      break;
5740                    }
5741                  }
5742                  break;
5743                }
5744              XFileBrowserWidget(display,windows,"Stipple",filename);
5745              if (*filename == '\0')
5746                break;
5747              /*
5748                Read image.
5749              */
5750              XSetCursorState(display,windows,MagickTrue);
5751              XCheckRefreshWindows(display,windows);
5752              image_info=AcquireImageInfo();
5753              (void) CopyMagickString(image_info->filename,filename,
5754                MaxTextExtent);
5755              stipple_image=ReadImage(image_info,exception);
5756              CatchException(exception);
5757              XSetCursorState(display,windows,MagickFalse);
5758              if (stipple_image == (Image *) NULL)
5759                break;
5760              (void) AcquireUniqueFileResource(filename);
5761              (void) FormatLocaleString(stipple_image->filename,MaxTextExtent,
5762                "xbm:%s",filename);
5763              (void) WriteImage(image_info,stipple_image,exception);
5764              stipple_image=DestroyImage(stipple_image);
5765              image_info=DestroyImageInfo(image_info);
5766              status=XReadBitmapFile(display,root_window,filename,&width,
5767                &height,&stipple,&x,&y);
5768              (void) RelinquishUniqueFileResource(filename);
5769              if ((status != BitmapSuccess) != 0)
5770                XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5771                  filename);
5772              break;
5773            }
5774            case DrawWidthCommand:
5775            {
5776              static char
5777                width[MaxTextExtent] = "0";
5778
5779              static const char
5780                *WidthsMenu[] =
5781                {
5782                  "1",
5783                  "2",
5784                  "4",
5785                  "8",
5786                  "16",
5787                  "Dialog...",
5788                  (char *) NULL,
5789                };
5790
5791              /*
5792                Select a command from the pop-up menu.
5793              */
5794              entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5795                command);
5796              if (entry < 0)
5797                break;
5798              if (entry != 5)
5799                {
5800                  line_width=(unsigned int) StringToUnsignedLong(
5801                    WidthsMenu[entry]);
5802                  break;
5803                }
5804              (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5805                width);
5806              if (*width == '\0')
5807                break;
5808              line_width=(unsigned int) StringToUnsignedLong(width);
5809              break;
5810            }
5811            case DrawUndoCommand:
5812            {
5813              (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5814                image,exception);
5815              break;
5816            }
5817            case DrawHelpCommand:
5818            {
5819              XTextViewWidget(display,resource_info,windows,MagickFalse,
5820                "Help Viewer - Image Rotation",ImageDrawHelp);
5821              (void) XCheckDefineCursor(display,windows->image.id,cursor);
5822              break;
5823            }
5824            case DrawDismissCommand:
5825            {
5826              /*
5827                Prematurely exit.
5828              */
5829              state|=EscapeState;
5830              state|=ExitState;
5831              break;
5832            }
5833            default:
5834              break;
5835          }
5836          (void) XCheckDefineCursor(display,windows->image.id,cursor);
5837          continue;
5838        }
5839      switch (event.type)
5840      {
5841        case ButtonPress:
5842        {
5843          if (event.xbutton.button != Button1)
5844            break;
5845          if (event.xbutton.window != windows->image.id)
5846            break;
5847          /*
5848            exit loop.
5849          */
5850          x=event.xbutton.x;
5851          y=event.xbutton.y;
5852          state|=ExitState;
5853          break;
5854        }
5855        case ButtonRelease:
5856          break;
5857        case Expose:
5858          break;
5859        case KeyPress:
5860        {
5861          KeySym
5862            key_symbol;
5863
5864          if (event.xkey.window != windows->image.id)
5865            break;
5866          /*
5867            Respond to a user key press.
5868          */
5869          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5870            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5871          switch ((int) key_symbol)
5872          {
5873            case XK_Escape:
5874            case XK_F20:
5875            {
5876              /*
5877                Prematurely exit.
5878              */
5879              state|=EscapeState;
5880              state|=ExitState;
5881              break;
5882            }
5883            case XK_F1:
5884            case XK_Help:
5885            {
5886              XTextViewWidget(display,resource_info,windows,MagickFalse,
5887                "Help Viewer - Image Rotation",ImageDrawHelp);
5888              break;
5889            }
5890            default:
5891            {
5892              (void) XBell(display,0);
5893              break;
5894            }
5895          }
5896          break;
5897        }
5898        case MotionNotify:
5899        {
5900          /*
5901            Map and unmap Info widget as text cursor crosses its boundaries.
5902          */
5903          x=event.xmotion.x;
5904          y=event.xmotion.y;
5905          if (IfMagickTrue(windows->info.mapped) )
5906            {
5907              if ((x < (int) (windows->info.x+windows->info.width)) &&
5908                  (y < (int) (windows->info.y+windows->info.height)))
5909                (void) XWithdrawWindow(display,windows->info.id,
5910                  windows->info.screen);
5911            }
5912          else
5913            if ((x > (int) (windows->info.x+windows->info.width)) ||
5914                (y > (int) (windows->info.y+windows->info.height)))
5915              (void) XMapWindow(display,windows->info.id);
5916          break;
5917        }
5918      }
5919    } while ((state & ExitState) == 0);
5920    (void) XSelectInput(display,windows->image.id,
5921      windows->image.attributes.event_mask);
5922    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5923    if ((state & EscapeState) != 0)
5924      break;
5925    /*
5926      Draw element as pointer moves until the button is released.
5927    */
5928    distance=0;
5929    degrees=0.0;
5930    line_info.x1=x;
5931    line_info.y1=y;
5932    line_info.x2=x;
5933    line_info.y2=y;
5934    rectangle_info.x=(ssize_t) x;
5935    rectangle_info.y=(ssize_t) y;
5936    rectangle_info.width=0;
5937    rectangle_info.height=0;
5938    number_coordinates=1;
5939    coordinate_info->x=x;
5940    coordinate_info->y=y;
5941    (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5942    state=DefaultState;
5943    do
5944    {
5945      switch (element)
5946      {
5947        case PointElement:
5948        default:
5949        {
5950          if (number_coordinates > 1)
5951            {
5952              (void) XDrawLines(display,windows->image.id,
5953                windows->image.highlight_context,coordinate_info,
5954                number_coordinates,CoordModeOrigin);
5955              (void) FormatLocaleString(text,MaxTextExtent," %+d%+d",
5956                coordinate_info[number_coordinates-1].x,
5957                coordinate_info[number_coordinates-1].y);
5958              XInfoWidget(display,windows,text);
5959            }
5960          break;
5961        }
5962        case LineElement:
5963        {
5964          if (distance > 9)
5965            {
5966              /*
5967                Display angle of the line.
5968              */
5969              degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5970                line_info.y1),(double) (line_info.x2-line_info.x1)));
5971              (void) FormatLocaleString(text,MaxTextExtent," %g",
5972                (double) degrees);
5973              XInfoWidget(display,windows,text);
5974              XHighlightLine(display,windows->image.id,
5975                windows->image.highlight_context,&line_info);
5976            }
5977          else
5978            if (IfMagickTrue(windows->info.mapped) )
5979              (void) XWithdrawWindow(display,windows->info.id,
5980                windows->info.screen);
5981          break;
5982        }
5983        case RectangleElement:
5984        case FillRectangleElement:
5985        {
5986          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5987            {
5988              /*
5989                Display info and draw drawing rectangle.
5990              */
5991              (void) FormatLocaleString(text,MaxTextExtent,
5992                " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5993                (double) rectangle_info.height,(double) rectangle_info.x,
5994                (double) rectangle_info.y);
5995              XInfoWidget(display,windows,text);
5996              XHighlightRectangle(display,windows->image.id,
5997                windows->image.highlight_context,&rectangle_info);
5998            }
5999          else
6000            if (IfMagickTrue(windows->info.mapped) )
6001              (void) XWithdrawWindow(display,windows->info.id,
6002                windows->info.screen);
6003          break;
6004        }
6005        case CircleElement:
6006        case FillCircleElement:
6007        case EllipseElement:
6008        case FillEllipseElement:
6009        {
6010          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6011            {
6012              /*
6013                Display info and draw drawing rectangle.
6014              */
6015              (void) FormatLocaleString(text,MaxTextExtent,
6016                " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
6017                (double) rectangle_info.height,(double) rectangle_info.x,
6018                (double) rectangle_info.y);
6019              XInfoWidget(display,windows,text);
6020              XHighlightEllipse(display,windows->image.id,
6021                windows->image.highlight_context,&rectangle_info);
6022            }
6023          else
6024            if (IfMagickTrue(windows->info.mapped) )
6025              (void) XWithdrawWindow(display,windows->info.id,
6026                windows->info.screen);
6027          break;
6028        }
6029        case PolygonElement:
6030        case FillPolygonElement:
6031        {
6032          if (number_coordinates > 1)
6033            (void) XDrawLines(display,windows->image.id,
6034              windows->image.highlight_context,coordinate_info,
6035              number_coordinates,CoordModeOrigin);
6036          if (distance > 9)
6037            {
6038              /*
6039                Display angle of the line.
6040              */
6041              degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
6042                line_info.y1),(double) (line_info.x2-line_info.x1)));
6043              (void) FormatLocaleString(text,MaxTextExtent," %g",
6044                (double) degrees);
6045              XInfoWidget(display,windows,text);
6046              XHighlightLine(display,windows->image.id,
6047                windows->image.highlight_context,&line_info);
6048            }
6049          else
6050            if (IfMagickTrue(windows->info.mapped) )
6051              (void) XWithdrawWindow(display,windows->info.id,
6052                windows->info.screen);
6053          break;
6054        }
6055      }
6056      /*
6057        Wait for next event.
6058      */
6059      XScreenEvent(display,windows,&event,exception);
6060      switch (element)
6061      {
6062        case PointElement:
6063        default:
6064        {
6065          if (number_coordinates > 1)
6066            (void) XDrawLines(display,windows->image.id,
6067              windows->image.highlight_context,coordinate_info,
6068              number_coordinates,CoordModeOrigin);
6069          break;
6070        }
6071        case LineElement:
6072        {
6073          if (distance > 9)
6074            XHighlightLine(display,windows->image.id,
6075              windows->image.highlight_context,&line_info);
6076          break;
6077        }
6078        case RectangleElement:
6079        case FillRectangleElement:
6080        {
6081          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6082            XHighlightRectangle(display,windows->image.id,
6083              windows->image.highlight_context,&rectangle_info);
6084          break;
6085        }
6086        case CircleElement:
6087        case FillCircleElement:
6088        case EllipseElement:
6089        case FillEllipseElement:
6090        {
6091          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6092            XHighlightEllipse(display,windows->image.id,
6093              windows->image.highlight_context,&rectangle_info);
6094          break;
6095        }
6096        case PolygonElement:
6097        case FillPolygonElement:
6098        {
6099          if (number_coordinates > 1)
6100            (void) XDrawLines(display,windows->image.id,
6101              windows->image.highlight_context,coordinate_info,
6102              number_coordinates,CoordModeOrigin);
6103          if (distance > 9)
6104            XHighlightLine(display,windows->image.id,
6105              windows->image.highlight_context,&line_info);
6106          break;
6107        }
6108      }
6109      switch (event.type)
6110      {
6111        case ButtonPress:
6112          break;
6113        case ButtonRelease:
6114        {
6115          /*
6116            User has committed to element.
6117          */
6118          line_info.x2=event.xbutton.x;
6119          line_info.y2=event.xbutton.y;
6120          rectangle_info.x=(ssize_t) event.xbutton.x;
6121          rectangle_info.y=(ssize_t) event.xbutton.y;
6122          coordinate_info[number_coordinates].x=event.xbutton.x;
6123          coordinate_info[number_coordinates].y=event.xbutton.y;
6124          if (((element != PolygonElement) &&
6125               (element != FillPolygonElement)) || (distance <= 9))
6126            {
6127              state|=ExitState;
6128              break;
6129            }
6130          number_coordinates++;
6131          if (number_coordinates < (int) max_coordinates)
6132            {
6133              line_info.x1=event.xbutton.x;
6134              line_info.y1=event.xbutton.y;
6135              break;
6136            }
6137          max_coordinates<<=1;
6138          coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6139            max_coordinates,sizeof(*coordinate_info));
6140          if (coordinate_info == (XPoint *) NULL)
6141            (void) ThrowMagickException(exception,GetMagickModule(),
6142              ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6143          break;
6144        }
6145        case Expose:
6146          break;
6147        case MotionNotify:
6148        {
6149          if (event.xmotion.window != windows->image.id)
6150            break;
6151          if (element != PointElement)
6152            {
6153              line_info.x2=event.xmotion.x;
6154              line_info.y2=event.xmotion.y;
6155              rectangle_info.x=(ssize_t) event.xmotion.x;
6156              rectangle_info.y=(ssize_t) event.xmotion.y;
6157              break;
6158            }
6159          coordinate_info[number_coordinates].x=event.xbutton.x;
6160          coordinate_info[number_coordinates].y=event.xbutton.y;
6161          number_coordinates++;
6162          if (number_coordinates < (int) max_coordinates)
6163            break;
6164          max_coordinates<<=1;
6165          coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6166            max_coordinates,sizeof(*coordinate_info));
6167          if (coordinate_info == (XPoint *) NULL)
6168            (void) ThrowMagickException(exception,GetMagickModule(),
6169              ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6170          break;
6171        }
6172        default:
6173          break;
6174      }
6175      /*
6176        Check boundary conditions.
6177      */
6178      if (line_info.x2 < 0)
6179        line_info.x2=0;
6180      else
6181        if (line_info.x2 > (int) windows->image.width)
6182          line_info.x2=(short) windows->image.width;
6183      if (line_info.y2 < 0)
6184        line_info.y2=0;
6185      else
6186        if (line_info.y2 > (int) windows->image.height)
6187          line_info.y2=(short) windows->image.height;
6188      distance=(unsigned int)
6189        (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6190         ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6191      if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6192          ((state & ExitState) != 0))
6193        {
6194          if (rectangle_info.x < 0)
6195            rectangle_info.x=0;
6196          else
6197            if (rectangle_info.x > (ssize_t) windows->image.width)
6198              rectangle_info.x=(ssize_t) windows->image.width;
6199          if ((int) rectangle_info.x < x)
6200            rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6201          else
6202            {
6203              rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6204              rectangle_info.x=(ssize_t) x;
6205            }
6206          if (rectangle_info.y < 0)
6207            rectangle_info.y=0;
6208          else
6209            if (rectangle_info.y > (ssize_t) windows->image.height)
6210              rectangle_info.y=(ssize_t) windows->image.height;
6211          if ((int) rectangle_info.y < y)
6212            rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6213          else
6214            {
6215              rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6216              rectangle_info.y=(ssize_t) y;
6217            }
6218        }
6219    } while ((state & ExitState) == 0);
6220    (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6221    if ((element == PointElement) || (element == PolygonElement) ||
6222        (element == FillPolygonElement))
6223      {
6224        /*
6225          Determine polygon bounding box.
6226        */
6227        rectangle_info.x=(ssize_t) coordinate_info->x;
6228        rectangle_info.y=(ssize_t) coordinate_info->y;
6229        x=coordinate_info->x;
6230        y=coordinate_info->y;
6231        for (i=1; i < number_coordinates; i++)
6232        {
6233          if (coordinate_info[i].x > x)
6234            x=coordinate_info[i].x;
6235          if (coordinate_info[i].y > y)
6236            y=coordinate_info[i].y;
6237          if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6238            rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6239          if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6240            rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6241        }
6242        rectangle_info.width=(size_t) (x-rectangle_info.x);
6243        rectangle_info.height=(size_t) (y-rectangle_info.y);
6244        for (i=0; i < number_coordinates; i++)
6245        {
6246          coordinate_info[i].x-=rectangle_info.x;
6247          coordinate_info[i].y-=rectangle_info.y;
6248        }
6249      }
6250    else
6251      if (distance <= 9)
6252        continue;
6253      else
6254        if ((element == RectangleElement) ||
6255            (element == CircleElement) || (element == EllipseElement))
6256          {
6257            rectangle_info.width--;
6258            rectangle_info.height--;
6259          }
6260    /*
6261      Drawing is relative to image configuration.
6262    */
6263    draw_info.x=(int) rectangle_info.x;
6264    draw_info.y=(int) rectangle_info.y;
6265    (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6266      image,exception);
6267    width=(unsigned int) (*image)->columns;
6268    height=(unsigned int) (*image)->rows;
6269    x=0;
6270    y=0;
6271    if (windows->image.crop_geometry != (char *) NULL)
6272      (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6273    draw_info.x+=windows->image.x-(line_width/2);
6274    if (draw_info.x < 0)
6275      draw_info.x=0;
6276    draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6277    draw_info.y+=windows->image.y-(line_width/2);
6278    if (draw_info.y < 0)
6279      draw_info.y=0;
6280    draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6281    draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6282    if (draw_info.width > (unsigned int) (*image)->columns)
6283      draw_info.width=(unsigned int) (*image)->columns;
6284    draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6285    if (draw_info.height > (unsigned int) (*image)->rows)
6286      draw_info.height=(unsigned int) (*image)->rows;
6287    (void) FormatLocaleString(draw_info.geometry,MaxTextExtent,"%ux%u%+d%+d",
6288      width*draw_info.width/windows->image.ximage->width,
6289      height*draw_info.height/windows->image.ximage->height,
6290      draw_info.x+x,draw_info.y+y);
6291    /*
6292      Initialize drawing attributes.
6293    */
6294    draw_info.degrees=0.0;
6295    draw_info.element=element;
6296    draw_info.stipple=stipple;
6297    draw_info.line_width=line_width;
6298    draw_info.line_info=line_info;
6299    if (line_info.x1 > (int) (line_width/2))
6300      draw_info.line_info.x1=(short) line_width/2;
6301    if (line_info.y1 > (int) (line_width/2))
6302      draw_info.line_info.y1=(short) line_width/2;
6303    draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6304    draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6305    if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6306      {
6307        draw_info.line_info.x2=(-draw_info.line_info.x2);
6308        draw_info.line_info.y2=(-draw_info.line_info.y2);
6309      }
6310    if (draw_info.line_info.x2 < 0)
6311      {
6312        draw_info.line_info.x2=(-draw_info.line_info.x2);
6313        Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6314      }
6315    if (draw_info.line_info.y2 < 0)
6316      {
6317        draw_info.line_info.y2=(-draw_info.line_info.y2);
6318        Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6319      }
6320    draw_info.rectangle_info=rectangle_info;
6321    if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6322      draw_info.rectangle_info.x=(ssize_t) line_width/2;
6323    if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6324      draw_info.rectangle_info.y=(ssize_t) line_width/2;
6325    draw_info.number_coordinates=(unsigned int) number_coordinates;
6326    draw_info.coordinate_info=coordinate_info;
6327    windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6328    /*
6329      Draw element on image.
6330    */
6331    XSetCursorState(display,windows,MagickTrue);
6332    XCheckRefreshWindows(display,windows);
6333    status=XDrawImage(display,windows->pixel_info,&draw_info,*image,exception);
6334    XSetCursorState(display,windows,MagickFalse);
6335    /*
6336      Update image colormap and return to image drawing.
6337    */
6338    XConfigureImageColormap(display,resource_info,windows,*image,exception);
6339    (void) XConfigureImage(display,resource_info,windows,*image,exception);
6340  }
6341  XSetCursorState(display,windows,MagickFalse);
6342  coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6343  return(IsMagickTrue(status));
6344}
6345
6346/*
6347%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6348%                                                                             %
6349%                                                                             %
6350%                                                                             %
6351+   X D r a w P a n R e c t a n g l e                                         %
6352%                                                                             %
6353%                                                                             %
6354%                                                                             %
6355%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6356%
6357%  XDrawPanRectangle() draws a rectangle in the pan window.  The pan window
6358%  displays a zoom image and the rectangle shows which portion of the image is
6359%  displayed in the Image window.
6360%
6361%  The format of the XDrawPanRectangle method is:
6362%
6363%      XDrawPanRectangle(Display *display,XWindows *windows)
6364%
6365%  A description of each parameter follows:
6366%
6367%    o display: Specifies a connection to an X server;  returned from
6368%      XOpenDisplay.
6369%
6370%    o windows: Specifies a pointer to a XWindows structure.
6371%
6372*/
6373static void XDrawPanRectangle(Display *display,XWindows *windows)
6374{
6375  double
6376    scale_factor;
6377
6378  RectangleInfo
6379    highlight_info;
6380
6381  /*
6382    Determine dimensions of the panning rectangle.
6383  */
6384  scale_factor=(double) windows->pan.width/windows->image.ximage->width;
6385  highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6386  highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6387  scale_factor=(double)
6388    windows->pan.height/windows->image.ximage->height;
6389  highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6390  highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6391  /*
6392    Display the panning rectangle.
6393  */
6394  (void) XClearWindow(display,windows->pan.id);
6395  XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6396    &highlight_info);
6397}
6398
6399/*
6400%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6401%                                                                             %
6402%                                                                             %
6403%                                                                             %
6404+   X I m a g e C a c h e                                                     %
6405%                                                                             %
6406%                                                                             %
6407%                                                                             %
6408%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6409%
6410%  XImageCache() handles the creation, manipulation, and destruction of the
6411%  image cache (undo and redo buffers).
6412%
6413%  The format of the XImageCache method is:
6414%
6415%      void XImageCache(Display *display,XResourceInfo *resource_info,
6416%        XWindows *windows,const CommandType command,Image **image,
6417%        ExceptionInfo *exception)
6418%
6419%  A description of each parameter follows:
6420%
6421%    o display: Specifies a connection to an X server; returned from
6422%      XOpenDisplay.
6423%
6424%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6425%
6426%    o windows: Specifies a pointer to a XWindows structure.
6427%
6428%    o command: Specifies a command to perform.
6429%
6430%    o image: the image;  XImageCache may transform the image and return a new
6431%      image pointer.
6432%
6433%    o exception: return any errors or warnings in this structure.
6434%
6435*/
6436static void XImageCache(Display *display,XResourceInfo *resource_info,
6437  XWindows *windows,const CommandType command,Image **image,
6438  ExceptionInfo *exception)
6439{
6440  Image
6441    *cache_image;
6442
6443  static Image
6444    *redo_image = (Image *) NULL,
6445    *undo_image = (Image *) NULL;
6446
6447  switch (command)
6448  {
6449    case FreeBuffersCommand:
6450    {
6451      /*
6452        Free memory from the undo and redo cache.
6453      */
6454      while (undo_image != (Image *) NULL)
6455      {
6456        cache_image=undo_image;
6457        undo_image=GetPreviousImageInList(undo_image);
6458        cache_image->list=DestroyImage(cache_image->list);
6459        cache_image=DestroyImage(cache_image);
6460      }
6461      undo_image=NewImageList();
6462      if (redo_image != (Image *) NULL)
6463        redo_image=DestroyImage(redo_image);
6464      redo_image=NewImageList();
6465      return;
6466    }
6467    case UndoCommand:
6468    {
6469      char
6470        image_geometry[MaxTextExtent];
6471
6472      /*
6473        Undo the last image transformation.
6474      */
6475      if (undo_image == (Image *) NULL)
6476        {
6477          (void) XBell(display,0);
6478          return;
6479        }
6480      cache_image=undo_image;
6481      undo_image=GetPreviousImageInList(undo_image);
6482      windows->image.window_changes.width=(int) cache_image->columns;
6483      windows->image.window_changes.height=(int) cache_image->rows;
6484      (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
6485        windows->image.ximage->width,windows->image.ximage->height);
6486      (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
6487        exception);
6488      if (windows->image.crop_geometry != (char *) NULL)
6489        windows->image.crop_geometry=(char *) RelinquishMagickMemory(
6490          windows->image.crop_geometry);
6491      windows->image.crop_geometry=cache_image->geometry;
6492      if (redo_image != (Image *) NULL)
6493        redo_image=DestroyImage(redo_image);
6494      redo_image=(*image);
6495      *image=cache_image->list;
6496      cache_image=DestroyImage(cache_image);
6497      if (IfMagickTrue(windows->image.orphan) )
6498        return;
6499      XConfigureImageColormap(display,resource_info,windows,*image,exception);
6500      (void) XConfigureImage(display,resource_info,windows,*image,exception);
6501      return;
6502    }
6503    case CutCommand:
6504    case PasteCommand:
6505    case ApplyCommand:
6506    case HalfSizeCommand:
6507    case OriginalSizeCommand:
6508    case DoubleSizeCommand:
6509    case ResizeCommand:
6510    case TrimCommand:
6511    case CropCommand:
6512    case ChopCommand:
6513    case FlipCommand:
6514    case FlopCommand:
6515    case RotateRightCommand:
6516    case RotateLeftCommand:
6517    case RotateCommand:
6518    case ShearCommand:
6519    case RollCommand:
6520    case NegateCommand:
6521    case ContrastStretchCommand:
6522    case SigmoidalContrastCommand:
6523    case NormalizeCommand:
6524    case EqualizeCommand:
6525    case HueCommand:
6526    case SaturationCommand:
6527    case BrightnessCommand:
6528    case GammaCommand:
6529    case SpiffCommand:
6530    case DullCommand:
6531    case GrayscaleCommand:
6532    case MapCommand:
6533    case QuantizeCommand:
6534    case DespeckleCommand:
6535    case EmbossCommand:
6536    case ReduceNoiseCommand:
6537    case AddNoiseCommand:
6538    case SharpenCommand:
6539    case BlurCommand:
6540    case ThresholdCommand:
6541    case EdgeDetectCommand:
6542    case SpreadCommand:
6543    case ShadeCommand:
6544    case RaiseCommand:
6545    case SegmentCommand:
6546    case SolarizeCommand:
6547    case SepiaToneCommand:
6548    case SwirlCommand:
6549    case ImplodeCommand:
6550    case VignetteCommand:
6551    case WaveCommand:
6552    case OilPaintCommand:
6553    case CharcoalDrawCommand:
6554    case AnnotateCommand:
6555    case AddBorderCommand:
6556    case AddFrameCommand:
6557    case CompositeCommand:
6558    case CommentCommand:
6559    case LaunchCommand:
6560    case RegionofInterestCommand:
6561    case SaveToUndoBufferCommand:
6562    case RedoCommand:
6563    {
6564      Image
6565        *previous_image;
6566
6567      ssize_t
6568        bytes;
6569
6570      bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelInfo));
6571      if (undo_image != (Image *) NULL)
6572        {
6573          /*
6574            Ensure the undo cache has enough memory available.
6575          */
6576          previous_image=undo_image;
6577          while (previous_image != (Image *) NULL)
6578          {
6579            bytes+=previous_image->list->columns*previous_image->list->rows*
6580              sizeof(PixelInfo);
6581            if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6582              {
6583                previous_image=GetPreviousImageInList(previous_image);
6584                continue;
6585              }
6586            bytes-=previous_image->list->columns*previous_image->list->rows*
6587              sizeof(PixelInfo);
6588            if (previous_image == undo_image)
6589              undo_image=NewImageList();
6590            else
6591              previous_image->next->previous=NewImageList();
6592            break;
6593          }
6594          while (previous_image != (Image *) NULL)
6595          {
6596            /*
6597              Delete any excess memory from undo cache.
6598            */
6599            cache_image=previous_image;
6600            previous_image=GetPreviousImageInList(previous_image);
6601            cache_image->list=DestroyImage(cache_image->list);
6602            cache_image=DestroyImage(cache_image);
6603          }
6604        }
6605      if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6606        break;
6607      /*
6608        Save image before transformations are applied.
6609      */
6610      cache_image=AcquireImage((ImageInfo *) NULL,exception);
6611      if (cache_image == (Image *) NULL)
6612        break;
6613      XSetCursorState(display,windows,MagickTrue);
6614      XCheckRefreshWindows(display,windows);
6615      cache_image->list=CloneImage(*image,0,0,MagickTrue,exception);
6616      XSetCursorState(display,windows,MagickFalse);
6617      if (cache_image->list == (Image *) NULL)
6618        {
6619          cache_image=DestroyImage(cache_image);
6620          break;
6621        }
6622      cache_image->columns=(size_t) windows->image.ximage->width;
6623      cache_image->rows=(size_t) windows->image.ximage->height;
6624      cache_image->geometry=windows->image.crop_geometry;
6625      if (windows->image.crop_geometry != (char *) NULL)
6626        {
6627          cache_image->geometry=AcquireString((char *) NULL);
6628          (void) CopyMagickString(cache_image->geometry,
6629            windows->image.crop_geometry,MaxTextExtent);
6630        }
6631      if (undo_image == (Image *) NULL)
6632        {
6633          undo_image=cache_image;
6634          break;
6635        }
6636      undo_image->next=cache_image;
6637      undo_image->next->previous=undo_image;
6638      undo_image=undo_image->next;
6639      break;
6640    }
6641    default:
6642      break;
6643  }
6644  if (command == RedoCommand)
6645    {
6646      /*
6647        Redo the last image transformation.
6648      */
6649      if (redo_image == (Image *) NULL)
6650        {
6651          (void) XBell(display,0);
6652          return;
6653        }
6654      windows->image.window_changes.width=(int) redo_image->columns;
6655      windows->image.window_changes.height=(int) redo_image->rows;
6656      if (windows->image.crop_geometry != (char *) NULL)
6657        windows->image.crop_geometry=(char *)
6658          RelinquishMagickMemory(windows->image.crop_geometry);
6659      windows->image.crop_geometry=redo_image->geometry;
6660      *image=DestroyImage(*image);
6661      *image=redo_image;
6662      redo_image=NewImageList();
6663      if (IfMagickTrue(windows->image.orphan) )
6664        return;
6665      XConfigureImageColormap(display,resource_info,windows,*image,exception);
6666      (void) XConfigureImage(display,resource_info,windows,*image,exception);
6667      return;
6668    }
6669  if (command != InfoCommand)
6670    return;
6671  /*
6672    Display image info.
6673  */
6674  XSetCursorState(display,windows,MagickTrue);
6675  XCheckRefreshWindows(display,windows);
6676  XDisplayImageInfo(display,resource_info,windows,undo_image,*image,exception);
6677  XSetCursorState(display,windows,MagickFalse);
6678}
6679
6680/*
6681%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6682%                                                                             %
6683%                                                                             %
6684%                                                                             %
6685+   X I m a g e W i n d o w C o m m a n d                                     %
6686%                                                                             %
6687%                                                                             %
6688%                                                                             %
6689%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6690%
6691%  XImageWindowCommand() makes a transform to the image or Image window as
6692%  specified by a user menu button or keyboard command.
6693%
6694%  The format of the XImageWindowCommand method is:
6695%
6696%      CommandType XImageWindowCommand(Display *display,
6697%        XResourceInfo *resource_info,XWindows *windows,
6698%        const MagickStatusType state,KeySym key_symbol,Image **image,
6699%        ExceptionInfo *exception)
6700%
6701%  A description of each parameter follows:
6702%
6703%    o nexus:  Method XImageWindowCommand returns an image when the
6704%      user chooses 'Open Image' from the command menu.  Otherwise a null
6705%      image is returned.
6706%
6707%    o display: Specifies a connection to an X server; returned from
6708%      XOpenDisplay.
6709%
6710%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6711%
6712%    o windows: Specifies a pointer to a XWindows structure.
6713%
6714%    o state: key mask.
6715%
6716%    o key_symbol: Specifies a command to perform.
6717%
6718%    o image: the image;  XImageWIndowCommand may transform the image and
6719%      return a new image pointer.
6720%
6721%    o exception: return any errors or warnings in this structure.
6722%
6723*/
6724static CommandType XImageWindowCommand(Display *display,
6725  XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6726  KeySym key_symbol,Image **image,ExceptionInfo *exception)
6727{
6728  static char
6729    delta[MaxTextExtent] = "";
6730
6731  static const char
6732    Digits[] = "01234567890";
6733
6734  static KeySym
6735    last_symbol = XK_0;
6736
6737  if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6738    {
6739      if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6740        {
6741          *delta='\0';
6742          resource_info->quantum=1;
6743        }
6744      last_symbol=key_symbol;
6745      delta[strlen(delta)+1]='\0';
6746      delta[strlen(delta)]=Digits[key_symbol-XK_0];
6747      resource_info->quantum=StringToLong(delta);
6748      return(NullCommand);
6749    }
6750  last_symbol=key_symbol;
6751  if (resource_info->immutable)
6752    {
6753      /*
6754        Virtual image window has a restricted command set.
6755      */
6756      switch (key_symbol)
6757      {
6758        case XK_question:
6759          return(InfoCommand);
6760        case XK_p:
6761        case XK_Print:
6762          return(PrintCommand);
6763        case XK_space:
6764          return(NextCommand);
6765        case XK_q:
6766        case XK_Escape:
6767          return(QuitCommand);
6768        default:
6769          break;
6770      }
6771      return(NullCommand);
6772    }
6773  switch ((int) key_symbol)
6774  {
6775    case XK_o:
6776    {
6777      if ((state & ControlMask) == 0)
6778        break;
6779      return(OpenCommand);
6780    }
6781    case XK_space:
6782      return(NextCommand);
6783    case XK_BackSpace:
6784      return(FormerCommand);
6785    case XK_s:
6786    {
6787      if ((state & Mod1Mask) != 0)
6788        return(SwirlCommand);
6789      if ((state & ControlMask) == 0)
6790        return(ShearCommand);
6791      return(SaveCommand);
6792    }
6793    case XK_p:
6794    case XK_Print:
6795    {
6796      if ((state & Mod1Mask) != 0)
6797        return(OilPaintCommand);
6798      if ((state & Mod4Mask) != 0)
6799        return(ColorCommand);
6800      if ((state & ControlMask) == 0)
6801        return(NullCommand);
6802      return(PrintCommand);
6803    }
6804    case XK_d:
6805    {
6806      if ((state & Mod4Mask) != 0)
6807        return(DrawCommand);
6808      if ((state & ControlMask) == 0)
6809        return(NullCommand);
6810      return(DeleteCommand);
6811    }
6812    case XK_Select:
6813    {
6814      if ((state & ControlMask) == 0)
6815        return(NullCommand);
6816      return(SelectCommand);
6817    }
6818    case XK_n:
6819    {
6820      if ((state & ControlMask) == 0)
6821        return(NullCommand);
6822      return(NewCommand);
6823    }
6824    case XK_q:
6825    case XK_Escape:
6826      return(QuitCommand);
6827    case XK_z:
6828    case XK_Undo:
6829    {
6830      if ((state & ControlMask) == 0)
6831        return(NullCommand);
6832      return(UndoCommand);
6833    }
6834    case XK_r:
6835    case XK_Redo:
6836    {
6837      if ((state & ControlMask) == 0)
6838        return(RollCommand);
6839      return(RedoCommand);
6840    }
6841    case XK_x:
6842    {
6843      if ((state & ControlMask) == 0)
6844        return(NullCommand);
6845      return(CutCommand);
6846    }
6847    case XK_c:
6848    {
6849      if ((state & Mod1Mask) != 0)
6850        return(CharcoalDrawCommand);
6851      if ((state & ControlMask) == 0)
6852        return(CropCommand);
6853      return(CopyCommand);
6854    }
6855    case XK_v:
6856    case XK_Insert:
6857    {
6858      if ((state & Mod4Mask) != 0)
6859        return(CompositeCommand);
6860      if ((state & ControlMask) == 0)
6861        return(FlipCommand);
6862      return(PasteCommand);
6863    }
6864    case XK_less:
6865      return(HalfSizeCommand);
6866    case XK_minus:
6867      return(OriginalSizeCommand);
6868    case XK_greater:
6869      return(DoubleSizeCommand);
6870    case XK_percent:
6871      return(ResizeCommand);
6872    case XK_at:
6873      return(RefreshCommand);
6874    case XK_bracketleft:
6875      return(ChopCommand);
6876    case XK_h:
6877      return(FlopCommand);
6878    case XK_slash:
6879      return(RotateRightCommand);
6880    case XK_backslash:
6881      return(RotateLeftCommand);
6882    case XK_asterisk:
6883      return(RotateCommand);
6884    case XK_t:
6885      return(TrimCommand);
6886    case XK_H:
6887      return(HueCommand);
6888    case XK_S:
6889      return(SaturationCommand);
6890    case XK_L:
6891      return(BrightnessCommand);
6892    case XK_G:
6893      return(GammaCommand);
6894    case XK_C:
6895      return(SpiffCommand);
6896    case XK_Z:
6897      return(DullCommand);
6898    case XK_N:
6899      return(NormalizeCommand);
6900    case XK_equal:
6901      return(EqualizeCommand);
6902    case XK_asciitilde:
6903      return(NegateCommand);
6904    case XK_period:
6905      return(GrayscaleCommand);
6906    case XK_numbersign:
6907      return(QuantizeCommand);
6908    case XK_F2:
6909      return(DespeckleCommand);
6910    case XK_F3:
6911      return(EmbossCommand);
6912    case XK_F4:
6913      return(ReduceNoiseCommand);
6914    case XK_F5:
6915      return(AddNoiseCommand);
6916    case XK_F6:
6917      return(SharpenCommand);
6918    case XK_F7:
6919      return(BlurCommand);
6920    case XK_F8:
6921      return(ThresholdCommand);
6922    case XK_F9:
6923      return(EdgeDetectCommand);
6924    case XK_F10:
6925      return(SpreadCommand);
6926    case XK_F11:
6927      return(ShadeCommand);
6928    case XK_F12:
6929      return(RaiseCommand);
6930    case XK_F13:
6931      return(SegmentCommand);
6932    case XK_i:
6933    {
6934      if ((state & Mod1Mask) == 0)
6935        return(NullCommand);
6936      return(ImplodeCommand);
6937    }
6938    case XK_w:
6939    {
6940      if ((state & Mod1Mask) == 0)
6941        return(NullCommand);
6942      return(WaveCommand);
6943    }
6944    case XK_m:
6945    {
6946      if ((state & Mod4Mask) == 0)
6947        return(NullCommand);
6948      return(MatteCommand);
6949    }
6950    case XK_b:
6951    {
6952      if ((state & Mod4Mask) == 0)
6953        return(NullCommand);
6954      return(AddBorderCommand);
6955    }
6956    case XK_f:
6957    {
6958      if ((state & Mod4Mask) == 0)
6959        return(NullCommand);
6960      return(AddFrameCommand);
6961    }
6962    case XK_exclam:
6963    {
6964      if ((state & Mod4Mask) == 0)
6965        return(NullCommand);
6966      return(CommentCommand);
6967    }
6968    case XK_a:
6969    {
6970      if ((state & Mod1Mask) != 0)
6971        return(ApplyCommand);
6972      if ((state & Mod4Mask) != 0)
6973        return(AnnotateCommand);
6974      if ((state & ControlMask) == 0)
6975        return(NullCommand);
6976      return(RegionofInterestCommand);
6977    }
6978    case XK_question:
6979      return(InfoCommand);
6980    case XK_plus:
6981      return(ZoomCommand);
6982    case XK_P:
6983    {
6984      if ((state & ShiftMask) == 0)
6985        return(NullCommand);
6986      return(ShowPreviewCommand);
6987    }
6988    case XK_Execute:
6989      return(LaunchCommand);
6990    case XK_F1:
6991      return(HelpCommand);
6992    case XK_Find:
6993      return(BrowseDocumentationCommand);
6994    case XK_Menu:
6995    {
6996      (void) XMapRaised(display,windows->command.id);
6997      return(NullCommand);
6998    }
6999    case XK_Next:
7000    case XK_Prior:
7001    case XK_Home:
7002    case XK_KP_Home:
7003    {
7004      XTranslateImage(display,windows,*image,key_symbol);
7005      return(NullCommand);
7006    }
7007    case XK_Up:
7008    case XK_KP_Up:
7009    case XK_Down:
7010    case XK_KP_Down:
7011    case XK_Left:
7012    case XK_KP_Left:
7013    case XK_Right:
7014    case XK_KP_Right:
7015    {
7016      if ((state & Mod1Mask) != 0)
7017        {
7018          RectangleInfo
7019            crop_info;
7020
7021          /*
7022            Trim one pixel from edge of image.
7023          */
7024          crop_info.x=0;
7025          crop_info.y=0;
7026          crop_info.width=(size_t) windows->image.ximage->width;
7027          crop_info.height=(size_t) windows->image.ximage->height;
7028          if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
7029            {
7030              if (resource_info->quantum >= (int) crop_info.height)
7031                resource_info->quantum=(int) crop_info.height-1;
7032              crop_info.height-=resource_info->quantum;
7033            }
7034          if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
7035            {
7036              if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
7037                resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
7038              crop_info.y+=resource_info->quantum;
7039              crop_info.height-=resource_info->quantum;
7040            }
7041          if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
7042            {
7043              if (resource_info->quantum >= (int) crop_info.width)
7044                resource_info->quantum=(int) crop_info.width-1;
7045              crop_info.width-=resource_info->quantum;
7046            }
7047          if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
7048            {
7049              if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
7050                resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
7051              crop_info.x+=resource_info->quantum;
7052              crop_info.width-=resource_info->quantum;
7053            }
7054          if ((int) (windows->image.x+windows->image.width) >
7055              (int) crop_info.width)
7056            windows->image.x=(int) (crop_info.width-windows->image.width);
7057          if ((int) (windows->image.y+windows->image.height) >
7058              (int) crop_info.height)
7059            windows->image.y=(int) (crop_info.height-windows->image.height);
7060          XSetCropGeometry(display,windows,&crop_info,*image);
7061          windows->image.window_changes.width=(int) crop_info.width;
7062          windows->image.window_changes.height=(int) crop_info.height;
7063          (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
7064          (void) XConfigureImage(display,resource_info,windows,*image,
7065            exception);
7066          return(NullCommand);
7067        }
7068      XTranslateImage(display,windows,*image,key_symbol);
7069      return(NullCommand);
7070    }
7071    default:
7072      return(NullCommand);
7073  }
7074  return(NullCommand);
7075}
7076
7077/*
7078%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7079%                                                                             %
7080%                                                                             %
7081%                                                                             %
7082+   X M a g i c k C o m m a n d                                               %
7083%                                                                             %
7084%                                                                             %
7085%                                                                             %
7086%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7087%
7088%  XMagickCommand() makes a transform to the image or Image window as
7089%  specified by a user menu button or keyboard command.
7090%
7091%  The format of the XMagickCommand method is:
7092%
7093%      Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7094%        XWindows *windows,const CommandType command,Image **image,
7095%        ExceptionInfo *exception)
7096%
7097%  A description of each parameter follows:
7098%
7099%    o display: Specifies a connection to an X server; returned from
7100%      XOpenDisplay.
7101%
7102%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7103%
7104%    o windows: Specifies a pointer to a XWindows structure.
7105%
7106%    o command: Specifies a command to perform.
7107%
7108%    o image: the image;  XMagickCommand may transform the image and return a
7109%      new image pointer.
7110%
7111%    o exception: return any errors or warnings in this structure.
7112%
7113*/
7114static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7115  XWindows *windows,const CommandType command,Image **image,
7116  ExceptionInfo *exception)
7117{
7118  char
7119    filename[MaxTextExtent],
7120    geometry[MaxTextExtent],
7121    modulate_factors[MaxTextExtent];
7122
7123  GeometryInfo
7124    geometry_info;
7125
7126  Image
7127    *nexus;
7128
7129  ImageInfo
7130    *image_info;
7131
7132  int
7133    x,
7134    y;
7135
7136  MagickStatusType
7137    flags,
7138    status;
7139
7140  QuantizeInfo
7141    quantize_info;
7142
7143  RectangleInfo
7144    page_geometry;
7145
7146  register int
7147    i;
7148
7149  static char
7150    color[MaxTextExtent] = "gray";
7151
7152  unsigned int
7153    height,
7154    width;
7155
7156  /*
7157    Process user command.
7158  */
7159  XCheckRefreshWindows(display,windows);
7160  XImageCache(display,resource_info,windows,command,image,exception);
7161  nexus=NewImageList();
7162  windows->image.window_changes.width=windows->image.ximage->width;
7163  windows->image.window_changes.height=windows->image.ximage->height;
7164  image_info=CloneImageInfo(resource_info->image_info);
7165  SetGeometryInfo(&geometry_info);
7166  GetQuantizeInfo(&quantize_info);
7167  switch (command)
7168  {
7169    case OpenCommand:
7170    {
7171      /*
7172        Load image.
7173      */
7174      nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7175      break;
7176    }
7177    case NextCommand:
7178    {
7179      /*
7180        Display next image.
7181      */
7182      for (i=0; i < resource_info->quantum; i++)
7183        XClientMessage(display,windows->image.id,windows->im_protocols,
7184          windows->im_next_image,CurrentTime);
7185      break;
7186    }
7187    case FormerCommand:
7188    {
7189      /*
7190        Display former image.
7191      */
7192      for (i=0; i < resource_info->quantum; i++)
7193        XClientMessage(display,windows->image.id,windows->im_protocols,
7194          windows->im_former_image,CurrentTime);
7195      break;
7196    }
7197    case SelectCommand:
7198    {
7199      int
7200        status;
7201
7202      /*
7203        Select image.
7204      */
7205      if (*resource_info->home_directory == '\0')
7206        (void) CopyMagickString(resource_info->home_directory,".",
7207          MaxTextExtent);
7208      status=chdir(resource_info->home_directory);
7209      if (status == -1)
7210        (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
7211          "UnableToOpenFile","%s",resource_info->home_directory);
7212      nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7213      break;
7214    }
7215    case SaveCommand:
7216    {
7217      /*
7218        Save image.
7219      */
7220      status=XSaveImage(display,resource_info,windows,*image,exception);
7221      if (IfMagickFalse(status) )
7222        {
7223          char
7224            message[MaxTextExtent];
7225
7226          (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7227            exception->reason != (char *) NULL ? exception->reason : "",
7228            exception->description != (char *) NULL ? exception->description :
7229            "");
7230          XNoticeWidget(display,windows,"Unable to save file:",message);
7231          break;
7232        }
7233      break;
7234    }
7235    case PrintCommand:
7236    {
7237      /*
7238        Print image.
7239      */
7240      status=XPrintImage(display,resource_info,windows,*image,exception);
7241      if (IfMagickFalse(status) )
7242        {
7243          char
7244            message[MaxTextExtent];
7245
7246          (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7247            exception->reason != (char *) NULL ? exception->reason : "",
7248            exception->description != (char *) NULL ? exception->description :
7249            "");
7250          XNoticeWidget(display,windows,"Unable to print file:",message);
7251          break;
7252        }
7253      break;
7254    }
7255    case DeleteCommand:
7256    {
7257      static char
7258        filename[MaxTextExtent] = "\0";
7259
7260      /*
7261        Delete image file.
7262      */
7263      XFileBrowserWidget(display,windows,"Delete",filename);
7264      if (*filename == '\0')
7265        break;
7266      status=ShredFile(filename);
7267      if (IfMagickTrue(status) )
7268        XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7269      break;
7270    }
7271    case NewCommand:
7272    {
7273      int
7274        status;
7275
7276      static char
7277        color[MaxTextExtent] = "gray",
7278        geometry[MaxTextExtent] = "640x480";
7279
7280      static const char
7281        *format = "gradient";
7282
7283      /*
7284        Query user for canvas geometry.
7285      */
7286      status=XDialogWidget(display,windows,"New","Enter image geometry:",
7287        geometry);
7288      if (*geometry == '\0')
7289        break;
7290      if (status == 0)
7291        format="xc";
7292      XColorBrowserWidget(display,windows,"Select",color);
7293      if (*color == '\0')
7294        break;
7295      /*
7296        Create canvas.
7297      */
7298      (void) FormatLocaleString(image_info->filename,MaxTextExtent,
7299        "%s:%s",format,color);
7300      (void) CloneString(&image_info->size,geometry);
7301      nexus=ReadImage(image_info,exception);
7302      CatchException(exception);
7303      XClientMessage(display,windows->image.id,windows->im_protocols,
7304        windows->im_next_image,CurrentTime);
7305      break;
7306    }
7307    case VisualDirectoryCommand:
7308    {
7309      /*
7310        Visual Image directory.
7311      */
7312      nexus=XVisualDirectoryImage(display,resource_info,windows,exception);
7313      break;
7314    }
7315    case QuitCommand:
7316    {
7317      /*
7318        exit program.
7319      */
7320      if (IfMagickFalse(resource_info->confirm_exit) )
7321        XClientMessage(display,windows->image.id,windows->im_protocols,
7322          windows->im_exit,CurrentTime);
7323      else
7324        {
7325          int
7326            status;
7327
7328          /*
7329            Confirm program exit.
7330          */
7331          status=XConfirmWidget(display,windows,"Do you really want to exit",
7332            resource_info->client_name);
7333          if (status > 0)
7334            XClientMessage(display,windows->image.id,windows->im_protocols,
7335              windows->im_exit,CurrentTime);
7336        }
7337      break;
7338    }
7339    case CutCommand:
7340    {
7341      /*
7342        Cut image.
7343      */
7344      (void) XCropImage(display,resource_info,windows,*image,CutMode,exception);
7345      break;
7346    }
7347    case CopyCommand:
7348    {
7349      /*
7350        Copy image.
7351      */
7352      (void) XCropImage(display,resource_info,windows,*image,CopyMode,
7353        exception);
7354      break;
7355    }
7356    case PasteCommand:
7357    {
7358      /*
7359        Paste image.
7360      */
7361      status=XPasteImage(display,resource_info,windows,*image,exception);
7362      if (IfMagickFalse(status) )
7363        {
7364          XNoticeWidget(display,windows,"Unable to paste X image",
7365            (*image)->filename);
7366          break;
7367        }
7368      break;
7369    }
7370    case HalfSizeCommand:
7371    {
7372      /*
7373        Half image size.
7374      */
7375      windows->image.window_changes.width=windows->image.ximage->width/2;
7376      windows->image.window_changes.height=windows->image.ximage->height/2;
7377      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7378      break;
7379    }
7380    case OriginalSizeCommand:
7381    {
7382      /*
7383        Original image size.
7384      */
7385      windows->image.window_changes.width=(int) (*image)->columns;
7386      windows->image.window_changes.height=(int) (*image)->rows;
7387      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7388      break;
7389    }
7390    case DoubleSizeCommand:
7391    {
7392      /*
7393        Double the image size.
7394      */
7395      windows->image.window_changes.width=windows->image.ximage->width << 1;
7396      windows->image.window_changes.height=windows->image.ximage->height << 1;
7397      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7398      break;
7399    }
7400    case ResizeCommand:
7401    {
7402      int
7403        status;
7404
7405      size_t
7406        height,
7407        width;
7408
7409      ssize_t
7410        x,
7411        y;
7412
7413      /*
7414        Resize image.
7415      */
7416      width=(size_t) windows->image.ximage->width;
7417      height=(size_t) windows->image.ximage->height;
7418      x=0;
7419      y=0;
7420      (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g+0+0",
7421        (double) width,(double) height);
7422      status=XDialogWidget(display,windows,"Resize",
7423        "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7424      if (*geometry == '\0')
7425        break;
7426      if (status == 0)
7427        (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7428      (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7429      windows->image.window_changes.width=(int) width;
7430      windows->image.window_changes.height=(int) height;
7431      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7432      break;
7433    }
7434    case ApplyCommand:
7435    {
7436      char
7437        image_geometry[MaxTextExtent];
7438
7439      if ((windows->image.crop_geometry == (char *) NULL) &&
7440          ((int) (*image)->columns == windows->image.ximage->width) &&
7441          ((int) (*image)->rows == windows->image.ximage->height))
7442        break;
7443      /*
7444        Apply size transforms to image.
7445      */
7446      XSetCursorState(display,windows,MagickTrue);
7447      XCheckRefreshWindows(display,windows);
7448      /*
7449        Crop and/or scale displayed image.
7450      */
7451      (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
7452        windows->image.ximage->width,windows->image.ximage->height);
7453      (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
7454        exception);
7455      if (windows->image.crop_geometry != (char *) NULL)
7456        windows->image.crop_geometry=(char *) RelinquishMagickMemory(
7457          windows->image.crop_geometry);
7458      windows->image.x=0;
7459      windows->image.y=0;
7460      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7461      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7462      break;
7463    }
7464    case RefreshCommand:
7465    {
7466      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7467      break;
7468    }
7469    case RestoreCommand:
7470    {
7471      /*
7472        Restore Image window to its original size.
7473      */
7474      if ((windows->image.width == (unsigned int) (*image)->columns) &&
7475          (windows->image.height == (unsigned int) (*image)->rows) &&
7476          (windows->image.crop_geometry == (char *) NULL))
7477        {
7478          (void) XBell(display,0);
7479          break;
7480        }
7481      windows->image.window_changes.width=(int) (*image)->columns;
7482      windows->image.window_changes.height=(int) (*image)->rows;
7483      if (windows->image.crop_geometry != (char *) NULL)
7484        {
7485          windows->image.crop_geometry=(char *)
7486            RelinquishMagickMemory(windows->image.crop_geometry);
7487          windows->image.crop_geometry=(char *) NULL;
7488          windows->image.x=0;
7489          windows->image.y=0;
7490        }
7491      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7492      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7493      break;
7494    }
7495    case CropCommand:
7496    {
7497      /*
7498        Crop image.
7499      */
7500      (void) XCropImage(display,resource_info,windows,*image,CropMode,
7501        exception);
7502      break;
7503    }
7504    case ChopCommand:
7505    {
7506      /*
7507        Chop image.
7508      */
7509      status=XChopImage(display,resource_info,windows,image,exception);
7510      if (IfMagickFalse(status) )
7511        {
7512          XNoticeWidget(display,windows,"Unable to cut X image",
7513            (*image)->filename);
7514          break;
7515        }
7516      break;
7517    }
7518    case FlopCommand:
7519    {
7520      Image
7521        *flop_image;
7522
7523      /*
7524        Flop image scanlines.
7525      */
7526      XSetCursorState(display,windows,MagickTrue);
7527      XCheckRefreshWindows(display,windows);
7528      flop_image=FlopImage(*image,exception);
7529      if (flop_image != (Image *) NULL)
7530        {
7531          *image=DestroyImage(*image);
7532          *image=flop_image;
7533        }
7534      CatchException(exception);
7535      XSetCursorState(display,windows,MagickFalse);
7536      if (windows->image.crop_geometry != (char *) NULL)
7537        {
7538          /*
7539            Flop crop geometry.
7540          */
7541          width=(unsigned int) (*image)->columns;
7542          height=(unsigned int) (*image)->rows;
7543          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7544            &width,&height);
7545          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7546            "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7547        }
7548      if (IfMagickTrue(windows->image.orphan) )
7549        break;
7550      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7551      break;
7552    }
7553    case FlipCommand:
7554    {
7555      Image
7556        *flip_image;
7557
7558      /*
7559        Flip image scanlines.
7560      */
7561      XSetCursorState(display,windows,MagickTrue);
7562      XCheckRefreshWindows(display,windows);
7563      flip_image=FlipImage(*image,exception);
7564      if (flip_image != (Image *) NULL)
7565        {
7566          *image=DestroyImage(*image);
7567          *image=flip_image;
7568        }
7569      CatchException(exception);
7570      XSetCursorState(display,windows,MagickFalse);
7571      if (windows->image.crop_geometry != (char *) NULL)
7572        {
7573          /*
7574            Flip crop geometry.
7575          */
7576          width=(unsigned int) (*image)->columns;
7577          height=(unsigned int) (*image)->rows;
7578          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7579            &width,&height);
7580          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7581            "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7582        }
7583      if (IfMagickTrue(windows->image.orphan) )
7584        break;
7585      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7586      break;
7587    }
7588    case RotateRightCommand:
7589    {
7590      /*
7591        Rotate image 90 degrees clockwise.
7592      */
7593      status=XRotateImage(display,resource_info,windows,90.0,image,exception);
7594      if (IfMagickFalse(status) )
7595        {
7596          XNoticeWidget(display,windows,"Unable to rotate X image",
7597            (*image)->filename);
7598          break;
7599        }
7600      break;
7601    }
7602    case RotateLeftCommand:
7603    {
7604      /*
7605        Rotate image 90 degrees counter-clockwise.
7606      */
7607      status=XRotateImage(display,resource_info,windows,-90.0,image,exception);
7608      if (IfMagickFalse(status) )
7609        {
7610          XNoticeWidget(display,windows,"Unable to rotate X image",
7611            (*image)->filename);
7612          break;
7613        }
7614      break;
7615    }
7616    case RotateCommand:
7617    {
7618      /*
7619        Rotate image.
7620      */
7621      status=XRotateImage(display,resource_info,windows,0.0,image,exception);
7622      if (IfMagickFalse(status) )
7623        {
7624          XNoticeWidget(display,windows,"Unable to rotate X image",
7625            (*image)->filename);
7626          break;
7627        }
7628      break;
7629    }
7630    case ShearCommand:
7631    {
7632      Image
7633        *shear_image;
7634
7635      static char
7636        geometry[MaxTextExtent] = "45.0x45.0";
7637
7638      /*
7639        Query user for shear color and geometry.
7640      */
7641      XColorBrowserWidget(display,windows,"Select",color);
7642      if (*color == '\0')
7643        break;
7644      (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7645        geometry);
7646      if (*geometry == '\0')
7647        break;
7648      /*
7649        Shear image.
7650      */
7651      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7652        exception);
7653      XSetCursorState(display,windows,MagickTrue);
7654      XCheckRefreshWindows(display,windows);
7655      (void) QueryColorCompliance(color,AllCompliance,
7656        &(*image)->background_color,exception);
7657      flags=ParseGeometry(geometry,&geometry_info);
7658      if ((flags & SigmaValue) == 0)
7659        geometry_info.sigma=geometry_info.rho;
7660      shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7661        exception);
7662      if (shear_image != (Image *) NULL)
7663        {
7664          *image=DestroyImage(*image);
7665          *image=shear_image;
7666        }
7667      CatchException(exception);
7668      XSetCursorState(display,windows,MagickFalse);
7669      if (IfMagickTrue(windows->image.orphan) )
7670        break;
7671      windows->image.window_changes.width=(int) (*image)->columns;
7672      windows->image.window_changes.height=(int) (*image)->rows;
7673      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7674      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7675      break;
7676    }
7677    case RollCommand:
7678    {
7679      Image
7680        *roll_image;
7681
7682      static char
7683        geometry[MaxTextExtent] = "+2+2";
7684
7685      /*
7686        Query user for the roll geometry.
7687      */
7688      (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7689        geometry);
7690      if (*geometry == '\0')
7691        break;
7692      /*
7693        Roll image.
7694      */
7695      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7696        exception);
7697      XSetCursorState(display,windows,MagickTrue);
7698      XCheckRefreshWindows(display,windows);
7699      (void) ParsePageGeometry(*image,geometry,&page_geometry,
7700        exception);
7701      roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7702        exception);
7703      if (roll_image != (Image *) NULL)
7704        {
7705          *image=DestroyImage(*image);
7706          *image=roll_image;
7707        }
7708      CatchException(exception);
7709      XSetCursorState(display,windows,MagickFalse);
7710      if (IfMagickTrue(windows->image.orphan) )
7711        break;
7712      windows->image.window_changes.width=(int) (*image)->columns;
7713      windows->image.window_changes.height=(int) (*image)->rows;
7714      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7715      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7716      break;
7717    }
7718    case TrimCommand:
7719    {
7720      static char
7721        fuzz[MaxTextExtent];
7722
7723      /*
7724        Query user for the fuzz factor.
7725      */
7726      (void) FormatLocaleString(fuzz,MaxTextExtent,"%g%%",100.0*
7727        (*image)->fuzz/(QuantumRange+1.0));
7728      (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7729      if (*fuzz == '\0')
7730        break;
7731      (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+1.0);
7732      /*
7733        Trim image.
7734      */
7735      status=XTrimImage(display,resource_info,windows,*image,exception);
7736      if (IfMagickFalse(status) )
7737        {
7738          XNoticeWidget(display,windows,"Unable to trim X image",
7739            (*image)->filename);
7740          break;
7741        }
7742      break;
7743    }
7744    case HueCommand:
7745    {
7746      static char
7747        hue_percent[MaxTextExtent] = "110";
7748
7749      /*
7750        Query user for percent hue change.
7751      */
7752      (void) XDialogWidget(display,windows,"Apply",
7753        "Enter percent change in image hue (0-200):",hue_percent);
7754      if (*hue_percent == '\0')
7755        break;
7756      /*
7757        Vary the image hue.
7758      */
7759      XSetCursorState(display,windows,MagickTrue);
7760      XCheckRefreshWindows(display,windows);
7761      (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7762      (void) ConcatenateMagickString(modulate_factors,hue_percent,
7763        MaxTextExtent);
7764      (void) ModulateImage(*image,modulate_factors,exception);
7765      XSetCursorState(display,windows,MagickFalse);
7766      if (IfMagickTrue(windows->image.orphan) )
7767        break;
7768      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7769      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7770      break;
7771    }
7772    case SaturationCommand:
7773    {
7774      static char
7775        saturation_percent[MaxTextExtent] = "110";
7776
7777      /*
7778        Query user for percent saturation change.
7779      */
7780      (void) XDialogWidget(display,windows,"Apply",
7781        "Enter percent change in color saturation (0-200):",saturation_percent);
7782      if (*saturation_percent == '\0')
7783        break;
7784      /*
7785        Vary color saturation.
7786      */
7787      XSetCursorState(display,windows,MagickTrue);
7788      XCheckRefreshWindows(display,windows);
7789      (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7790      (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7791        MaxTextExtent);
7792      (void) ModulateImage(*image,modulate_factors,exception);
7793      XSetCursorState(display,windows,MagickFalse);
7794      if (IfMagickTrue(windows->image.orphan) )
7795        break;
7796      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7797      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7798      break;
7799    }
7800    case BrightnessCommand:
7801    {
7802      static char
7803        brightness_percent[MaxTextExtent] = "110";
7804
7805      /*
7806        Query user for percent brightness change.
7807      */
7808      (void) XDialogWidget(display,windows,"Apply",
7809        "Enter percent change in color brightness (0-200):",brightness_percent);
7810      if (*brightness_percent == '\0')
7811        break;
7812      /*
7813        Vary the color brightness.
7814      */
7815      XSetCursorState(display,windows,MagickTrue);
7816      XCheckRefreshWindows(display,windows);
7817      (void) CopyMagickString(modulate_factors,brightness_percent,
7818        MaxTextExtent);
7819      (void) ModulateImage(*image,modulate_factors,exception);
7820      XSetCursorState(display,windows,MagickFalse);
7821      if (IfMagickTrue(windows->image.orphan) )
7822        break;
7823      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7824      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7825      break;
7826    }
7827    case GammaCommand:
7828    {
7829      static char
7830        factor[MaxTextExtent] = "1.6";
7831
7832      /*
7833        Query user for gamma value.
7834      */
7835      (void) XDialogWidget(display,windows,"Gamma",
7836        "Enter gamma value (e.g. 1.2):",factor);
7837      if (*factor == '\0')
7838        break;
7839      /*
7840        Gamma correct image.
7841      */
7842      XSetCursorState(display,windows,MagickTrue);
7843      XCheckRefreshWindows(display,windows);
7844      (void) GammaImage(*image,atof(factor),exception);
7845      XSetCursorState(display,windows,MagickFalse);
7846      if (IfMagickTrue(windows->image.orphan) )
7847        break;
7848      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7849      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7850      break;
7851    }
7852    case SpiffCommand:
7853    {
7854      /*
7855        Sharpen the image contrast.
7856      */
7857      XSetCursorState(display,windows,MagickTrue);
7858      XCheckRefreshWindows(display,windows);
7859      (void) ContrastImage(*image,MagickTrue,exception);
7860      XSetCursorState(display,windows,MagickFalse);
7861      if (IfMagickTrue(windows->image.orphan) )
7862        break;
7863      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7864      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7865      break;
7866    }
7867    case DullCommand:
7868    {
7869      /*
7870        Dull the image contrast.
7871      */
7872      XSetCursorState(display,windows,MagickTrue);
7873      XCheckRefreshWindows(display,windows);
7874      (void) ContrastImage(*image,MagickFalse,exception);
7875      XSetCursorState(display,windows,MagickFalse);
7876      if (IfMagickTrue(windows->image.orphan) )
7877        break;
7878      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7879      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7880      break;
7881    }
7882    case ContrastStretchCommand:
7883    {
7884      double
7885        black_point,
7886        white_point;
7887
7888      static char
7889        levels[MaxTextExtent] = "1%";
7890
7891      /*
7892        Query user for gamma value.
7893      */
7894      (void) XDialogWidget(display,windows,"Contrast Stretch",
7895        "Enter black and white points:",levels);
7896      if (*levels == '\0')
7897        break;
7898      /*
7899        Contrast stretch image.
7900      */
7901      XSetCursorState(display,windows,MagickTrue);
7902      XCheckRefreshWindows(display,windows);
7903      flags=ParseGeometry(levels,&geometry_info);
7904      black_point=geometry_info.rho;
7905      white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7906      if ((flags & PercentValue) != 0)
7907        {
7908          black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7909          white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7910        }
7911      white_point=(double) (*image)->columns*(*image)->rows-white_point;
7912      (void) ContrastStretchImage(*image,black_point,white_point,
7913        exception);
7914      XSetCursorState(display,windows,MagickFalse);
7915      if (IfMagickTrue(windows->image.orphan) )
7916        break;
7917      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7918      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7919      break;
7920    }
7921    case SigmoidalContrastCommand:
7922    {
7923      GeometryInfo
7924        geometry_info;
7925
7926      MagickStatusType
7927        flags;
7928
7929      static char
7930        levels[MaxTextExtent] = "3x50%";
7931
7932      /*
7933        Query user for gamma value.
7934      */
7935      (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7936        "Enter contrast and midpoint:",levels);
7937      if (*levels == '\0')
7938        break;
7939      /*
7940        Contrast stretch image.
7941      */
7942      XSetCursorState(display,windows,MagickTrue);
7943      XCheckRefreshWindows(display,windows);
7944      flags=ParseGeometry(levels,&geometry_info);
7945      if ((flags & SigmaValue) == 0)
7946        geometry_info.sigma=1.0*QuantumRange/2.0;
7947      if ((flags & PercentValue) != 0)
7948        geometry_info.sigma=1.0*QuantumRange*geometry_info.sigma/100.0;
7949      (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
7950        geometry_info.sigma,exception);
7951      XSetCursorState(display,windows,MagickFalse);
7952      if (IfMagickTrue(windows->image.orphan) )
7953        break;
7954      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7955      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7956      break;
7957    }
7958    case NormalizeCommand:
7959    {
7960      /*
7961        Perform histogram normalization on the image.
7962      */
7963      XSetCursorState(display,windows,MagickTrue);
7964      XCheckRefreshWindows(display,windows);
7965      (void) NormalizeImage(*image,exception);
7966      XSetCursorState(display,windows,MagickFalse);
7967      if (IfMagickTrue(windows->image.orphan) )
7968        break;
7969      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7970      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7971      break;
7972    }
7973    case EqualizeCommand:
7974    {
7975      /*
7976        Perform histogram equalization on the image.
7977      */
7978      XSetCursorState(display,windows,MagickTrue);
7979      XCheckRefreshWindows(display,windows);
7980      (void) EqualizeImage(*image,exception);
7981      XSetCursorState(display,windows,MagickFalse);
7982      if (IfMagickTrue(windows->image.orphan) )
7983        break;
7984      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7985      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7986      break;
7987    }
7988    case NegateCommand:
7989    {
7990      /*
7991        Negate colors in image.
7992      */
7993      XSetCursorState(display,windows,MagickTrue);
7994      XCheckRefreshWindows(display,windows);
7995      (void) NegateImage(*image,MagickFalse,exception);
7996      XSetCursorState(display,windows,MagickFalse);
7997      if (IfMagickTrue(windows->image.orphan) )
7998        break;
7999      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8000      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8001      break;
8002    }
8003    case GrayscaleCommand:
8004    {
8005      /*
8006        Convert image to grayscale.
8007      */
8008      XSetCursorState(display,windows,MagickTrue);
8009      XCheckRefreshWindows(display,windows);
8010      (void) SetImageType(*image,(*image)->alpha_trait != BlendPixelTrait ?
8011        GrayscaleType : GrayscaleMatteType,exception);
8012      XSetCursorState(display,windows,MagickFalse);
8013      if (IfMagickTrue(windows->image.orphan) )
8014        break;
8015      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8016      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8017      break;
8018    }
8019    case MapCommand:
8020    {
8021      Image
8022        *affinity_image;
8023
8024      static char
8025        filename[MaxTextExtent] = "\0";
8026
8027      /*
8028        Request image file name from user.
8029      */
8030      XFileBrowserWidget(display,windows,"Map",filename);
8031      if (*filename == '\0')
8032        break;
8033      /*
8034        Map image.
8035      */
8036      XSetCursorState(display,windows,MagickTrue);
8037      XCheckRefreshWindows(display,windows);
8038      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
8039      affinity_image=ReadImage(image_info,exception);
8040      if (affinity_image != (Image *) NULL)
8041        {
8042          (void) RemapImage(&quantize_info,*image,affinity_image,exception);
8043          affinity_image=DestroyImage(affinity_image);
8044        }
8045      CatchException(exception);
8046      XSetCursorState(display,windows,MagickFalse);
8047      if (IfMagickTrue(windows->image.orphan) )
8048        break;
8049      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8050      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8051      break;
8052    }
8053    case QuantizeCommand:
8054    {
8055      int
8056        status;
8057
8058      static char
8059        colors[MaxTextExtent] = "256";
8060
8061      /*
8062        Query user for maximum number of colors.
8063      */
8064      status=XDialogWidget(display,windows,"Quantize",
8065        "Maximum number of colors:",colors);
8066      if (*colors == '\0')
8067        break;
8068      /*
8069        Color reduce the image.
8070      */
8071      XSetCursorState(display,windows,MagickTrue);
8072      XCheckRefreshWindows(display,windows);
8073      quantize_info.number_colors=StringToUnsignedLong(colors);
8074      quantize_info.dither_method=status != 0 ? RiemersmaDitherMethod :
8075        NoDitherMethod;
8076      (void) QuantizeImage(&quantize_info,*image,exception);
8077      XSetCursorState(display,windows,MagickFalse);
8078      if (IfMagickTrue(windows->image.orphan) )
8079        break;
8080      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8081      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8082      break;
8083    }
8084    case DespeckleCommand:
8085    {
8086      Image
8087        *despeckle_image;
8088
8089      /*
8090        Despeckle image.
8091      */
8092      XSetCursorState(display,windows,MagickTrue);
8093      XCheckRefreshWindows(display,windows);
8094      despeckle_image=DespeckleImage(*image,exception);
8095      if (despeckle_image != (Image *) NULL)
8096        {
8097          *image=DestroyImage(*image);
8098          *image=despeckle_image;
8099        }
8100      CatchException(exception);
8101      XSetCursorState(display,windows,MagickFalse);
8102      if (IfMagickTrue(windows->image.orphan) )
8103        break;
8104      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8105      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8106      break;
8107    }
8108    case EmbossCommand:
8109    {
8110      Image
8111        *emboss_image;
8112
8113      static char
8114        radius[MaxTextExtent] = "0.0x1.0";
8115
8116      /*
8117        Query user for emboss radius.
8118      */
8119      (void) XDialogWidget(display,windows,"Emboss",
8120        "Enter the emboss radius and standard deviation:",radius);
8121      if (*radius == '\0')
8122        break;
8123      /*
8124        Reduce noise in the image.
8125      */
8126      XSetCursorState(display,windows,MagickTrue);
8127      XCheckRefreshWindows(display,windows);
8128      flags=ParseGeometry(radius,&geometry_info);
8129      if ((flags & SigmaValue) == 0)
8130        geometry_info.sigma=1.0;
8131      emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8132        exception);
8133      if (emboss_image != (Image *) NULL)
8134        {
8135          *image=DestroyImage(*image);
8136          *image=emboss_image;
8137        }
8138      CatchException(exception);
8139      XSetCursorState(display,windows,MagickFalse);
8140      if (IfMagickTrue(windows->image.orphan) )
8141        break;
8142      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8143      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8144      break;
8145    }
8146    case ReduceNoiseCommand:
8147    {
8148      Image
8149        *noise_image;
8150
8151      static char
8152        radius[MaxTextExtent] = "0";
8153
8154      /*
8155        Query user for noise radius.
8156      */
8157      (void) XDialogWidget(display,windows,"Reduce Noise",
8158        "Enter the noise radius:",radius);
8159      if (*radius == '\0')
8160        break;
8161      /*
8162        Reduce noise in the image.
8163      */
8164      XSetCursorState(display,windows,MagickTrue);
8165      XCheckRefreshWindows(display,windows);
8166      flags=ParseGeometry(radius,&geometry_info);
8167      noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8168        geometry_info.rho,(size_t) geometry_info.rho,exception);
8169      if (noise_image != (Image *) NULL)
8170        {
8171          *image=DestroyImage(*image);
8172          *image=noise_image;
8173        }
8174      CatchException(exception);
8175      XSetCursorState(display,windows,MagickFalse);
8176      if (IfMagickTrue(windows->image.orphan) )
8177        break;
8178      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8179      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8180      break;
8181    }
8182    case AddNoiseCommand:
8183    {
8184      char
8185        **noises;
8186
8187      Image
8188        *noise_image;
8189
8190      static char
8191        noise_type[MaxTextExtent] = "Gaussian";
8192
8193      /*
8194        Add noise to the image.
8195      */
8196      noises=GetCommandOptions(MagickNoiseOptions);
8197      if (noises == (char **) NULL)
8198        break;
8199      XListBrowserWidget(display,windows,&windows->widget,
8200        (const char **) noises,"Add Noise",
8201        "Select a type of noise to add to your image:",noise_type);
8202      noises=DestroyStringList(noises);
8203      if (*noise_type == '\0')
8204        break;
8205      XSetCursorState(display,windows,MagickTrue);
8206      XCheckRefreshWindows(display,windows);
8207      noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8208        MagickNoiseOptions,MagickFalse,noise_type),1.0,exception);
8209      if (noise_image != (Image *) NULL)
8210        {
8211          *image=DestroyImage(*image);
8212          *image=noise_image;
8213        }
8214      CatchException(exception);
8215      XSetCursorState(display,windows,MagickFalse);
8216      if (IfMagickTrue(windows->image.orphan) )
8217        break;
8218      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8219      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8220      break;
8221    }
8222    case SharpenCommand:
8223    {
8224      Image
8225        *sharp_image;
8226
8227      static char
8228        radius[MaxTextExtent] = "0.0x1.0";
8229
8230      /*
8231        Query user for sharpen radius.
8232      */
8233      (void) XDialogWidget(display,windows,"Sharpen",
8234        "Enter the sharpen radius and standard deviation:",radius);
8235      if (*radius == '\0')
8236        break;
8237      /*
8238        Sharpen image scanlines.
8239      */
8240      XSetCursorState(display,windows,MagickTrue);
8241      XCheckRefreshWindows(display,windows);
8242      flags=ParseGeometry(radius,&geometry_info);
8243      sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8244        exception);
8245      if (sharp_image != (Image *) NULL)
8246        {
8247          *image=DestroyImage(*image);
8248          *image=sharp_image;
8249        }
8250      CatchException(exception);
8251      XSetCursorState(display,windows,MagickFalse);
8252      if (IfMagickTrue(windows->image.orphan) )
8253        break;
8254      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8255      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8256      break;
8257    }
8258    case BlurCommand:
8259    {
8260      Image
8261        *blur_image;
8262
8263      static char
8264        radius[MaxTextExtent] = "0.0x1.0";
8265
8266      /*
8267        Query user for blur radius.
8268      */
8269      (void) XDialogWidget(display,windows,"Blur",
8270        "Enter the blur radius and standard deviation:",radius);
8271      if (*radius == '\0')
8272        break;
8273      /*
8274        Blur an image.
8275      */
8276      XSetCursorState(display,windows,MagickTrue);
8277      XCheckRefreshWindows(display,windows);
8278      flags=ParseGeometry(radius,&geometry_info);
8279      blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8280        exception);
8281      if (blur_image != (Image *) NULL)
8282        {
8283          *image=DestroyImage(*image);
8284          *image=blur_image;
8285        }
8286      CatchException(exception);
8287      XSetCursorState(display,windows,MagickFalse);
8288      if (IfMagickTrue(windows->image.orphan) )
8289        break;
8290      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8291      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8292      break;
8293    }
8294    case ThresholdCommand:
8295    {
8296      double
8297        threshold;
8298
8299      static char
8300        factor[MaxTextExtent] = "128";
8301
8302      /*
8303        Query user for threshold value.
8304      */
8305      (void) XDialogWidget(display,windows,"Threshold",
8306        "Enter threshold value:",factor);
8307      if (*factor == '\0')
8308        break;
8309      /*
8310        Gamma correct image.
8311      */
8312      XSetCursorState(display,windows,MagickTrue);
8313      XCheckRefreshWindows(display,windows);
8314      threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8315      (void) BilevelImage(*image,threshold,exception);
8316      XSetCursorState(display,windows,MagickFalse);
8317      if (IfMagickTrue(windows->image.orphan) )
8318        break;
8319      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8320      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8321      break;
8322    }
8323    case EdgeDetectCommand:
8324    {
8325      Image
8326        *edge_image;
8327
8328      static char
8329        radius[MaxTextExtent] = "0";
8330
8331      /*
8332        Query user for edge factor.
8333      */
8334      (void) XDialogWidget(display,windows,"Detect Edges",
8335        "Enter the edge detect radius:",radius);
8336      if (*radius == '\0')
8337        break;
8338      /*
8339        Detect edge in image.
8340      */
8341      XSetCursorState(display,windows,MagickTrue);
8342      XCheckRefreshWindows(display,windows);
8343      flags=ParseGeometry(radius,&geometry_info);
8344      edge_image=EdgeImage(*image,geometry_info.rho,exception);
8345      if (edge_image != (Image *) NULL)
8346        {
8347          *image=DestroyImage(*image);
8348          *image=edge_image;
8349        }
8350      CatchException(exception);
8351      XSetCursorState(display,windows,MagickFalse);
8352      if (IfMagickTrue(windows->image.orphan) )
8353        break;
8354      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8355      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8356      break;
8357    }
8358    case SpreadCommand:
8359    {
8360      Image
8361        *spread_image;
8362
8363      static char
8364        amount[MaxTextExtent] = "2";
8365
8366      /*
8367        Query user for spread amount.
8368      */
8369      (void) XDialogWidget(display,windows,"Spread",
8370        "Enter the displacement amount:",amount);
8371      if (*amount == '\0')
8372        break;
8373      /*
8374        Displace image pixels by a random amount.
8375      */
8376      XSetCursorState(display,windows,MagickTrue);
8377      XCheckRefreshWindows(display,windows);
8378      flags=ParseGeometry(amount,&geometry_info);
8379      spread_image=EdgeImage(*image,geometry_info.rho,exception);
8380      if (spread_image != (Image *) NULL)
8381        {
8382          *image=DestroyImage(*image);
8383          *image=spread_image;
8384        }
8385      CatchException(exception);
8386      XSetCursorState(display,windows,MagickFalse);
8387      if (IfMagickTrue(windows->image.orphan) )
8388        break;
8389      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8390      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8391      break;
8392    }
8393    case ShadeCommand:
8394    {
8395      Image
8396        *shade_image;
8397
8398      int
8399        status;
8400
8401      static char
8402        geometry[MaxTextExtent] = "30x30";
8403
8404      /*
8405        Query user for the shade geometry.
8406      */
8407      status=XDialogWidget(display,windows,"Shade",
8408        "Enter the azimuth and elevation of the light source:",geometry);
8409      if (*geometry == '\0')
8410        break;
8411      /*
8412        Shade image pixels.
8413      */
8414      XSetCursorState(display,windows,MagickTrue);
8415      XCheckRefreshWindows(display,windows);
8416      flags=ParseGeometry(geometry,&geometry_info);
8417      if ((flags & SigmaValue) == 0)
8418        geometry_info.sigma=1.0;
8419      shade_image=ShadeImage(*image,IsMagickTrue(status),
8420        geometry_info.rho,geometry_info.sigma,exception);
8421      if (shade_image != (Image *) NULL)
8422        {
8423          *image=DestroyImage(*image);
8424          *image=shade_image;
8425        }
8426      CatchException(exception);
8427      XSetCursorState(display,windows,MagickFalse);
8428      if (IfMagickTrue(windows->image.orphan) )
8429        break;
8430      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8431      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8432      break;
8433    }
8434    case RaiseCommand:
8435    {
8436      static char
8437        bevel_width[MaxTextExtent] = "10";
8438
8439      /*
8440        Query user for bevel width.
8441      */
8442      (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8443      if (*bevel_width == '\0')
8444        break;
8445      /*
8446        Raise an image.
8447      */
8448      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8449        exception);
8450      XSetCursorState(display,windows,MagickTrue);
8451      XCheckRefreshWindows(display,windows);
8452      (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8453        exception);
8454      (void) RaiseImage(*image,&page_geometry,MagickTrue,exception);
8455      XSetCursorState(display,windows,MagickFalse);
8456      if (IfMagickTrue(windows->image.orphan) )
8457        break;
8458      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8459      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8460      break;
8461    }
8462    case SegmentCommand:
8463    {
8464      static char
8465        threshold[MaxTextExtent] = "1.0x1.5";
8466
8467      /*
8468        Query user for smoothing threshold.
8469      */
8470      (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8471        threshold);
8472      if (*threshold == '\0')
8473        break;
8474      /*
8475        Segment an image.
8476      */
8477      XSetCursorState(display,windows,MagickTrue);
8478      XCheckRefreshWindows(display,windows);
8479      flags=ParseGeometry(threshold,&geometry_info);
8480      if ((flags & SigmaValue) == 0)
8481        geometry_info.sigma=1.0;
8482      (void) SegmentImage(*image,sRGBColorspace,MagickFalse,geometry_info.rho,
8483        geometry_info.sigma,exception);
8484      XSetCursorState(display,windows,MagickFalse);
8485      if (IfMagickTrue(windows->image.orphan) )
8486        break;
8487      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8488      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8489      break;
8490    }
8491    case SepiaToneCommand:
8492    {
8493      double
8494        threshold;
8495
8496      Image
8497        *sepia_image;
8498
8499      static char
8500        factor[MaxTextExtent] = "80%";
8501
8502      /*
8503        Query user for sepia-tone factor.
8504      */
8505      (void) XDialogWidget(display,windows,"Sepia Tone",
8506        "Enter the sepia tone factor (0 - 99.9%):",factor);
8507      if (*factor == '\0')
8508        break;
8509      /*
8510        Sepia tone image pixels.
8511      */
8512      XSetCursorState(display,windows,MagickTrue);
8513      XCheckRefreshWindows(display,windows);
8514      threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8515      sepia_image=SepiaToneImage(*image,threshold,exception);
8516      if (sepia_image != (Image *) NULL)
8517        {
8518          *image=DestroyImage(*image);
8519          *image=sepia_image;
8520        }
8521      CatchException(exception);
8522      XSetCursorState(display,windows,MagickFalse);
8523      if (IfMagickTrue(windows->image.orphan) )
8524        break;
8525      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8526      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8527      break;
8528    }
8529    case SolarizeCommand:
8530    {
8531      double
8532        threshold;
8533
8534      static char
8535        factor[MaxTextExtent] = "60%";
8536
8537      /*
8538        Query user for solarize factor.
8539      */
8540      (void) XDialogWidget(display,windows,"Solarize",
8541        "Enter the solarize factor (0 - 99.9%):",factor);
8542      if (*factor == '\0')
8543        break;
8544      /*
8545        Solarize image pixels.
8546      */
8547      XSetCursorState(display,windows,MagickTrue);
8548      XCheckRefreshWindows(display,windows);
8549      threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8550      (void) SolarizeImage(*image,threshold,exception);
8551      XSetCursorState(display,windows,MagickFalse);
8552      if (IfMagickTrue(windows->image.orphan) )
8553        break;
8554      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8555      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8556      break;
8557    }
8558    case SwirlCommand:
8559    {
8560      Image
8561        *swirl_image;
8562
8563      static char
8564        degrees[MaxTextExtent] = "60";
8565
8566      /*
8567        Query user for swirl angle.
8568      */
8569      (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8570        degrees);
8571      if (*degrees == '\0')
8572        break;
8573      /*
8574        Swirl image pixels about the center.
8575      */
8576      XSetCursorState(display,windows,MagickTrue);
8577      XCheckRefreshWindows(display,windows);
8578      flags=ParseGeometry(degrees,&geometry_info);
8579      swirl_image=SwirlImage(*image,geometry_info.rho,(*image)->interpolate,
8580        exception);
8581      if (swirl_image != (Image *) NULL)
8582        {
8583          *image=DestroyImage(*image);
8584          *image=swirl_image;
8585        }
8586      CatchException(exception);
8587      XSetCursorState(display,windows,MagickFalse);
8588      if (IfMagickTrue(windows->image.orphan) )
8589        break;
8590      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8591      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8592      break;
8593    }
8594    case ImplodeCommand:
8595    {
8596      Image
8597        *implode_image;
8598
8599      static char
8600        factor[MaxTextExtent] = "0.3";
8601
8602      /*
8603        Query user for implode factor.
8604      */
8605      (void) XDialogWidget(display,windows,"Implode",
8606        "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8607      if (*factor == '\0')
8608        break;
8609      /*
8610        Implode image pixels about the center.
8611      */
8612      XSetCursorState(display,windows,MagickTrue);
8613      XCheckRefreshWindows(display,windows);
8614      flags=ParseGeometry(factor,&geometry_info);
8615      implode_image=ImplodeImage(*image,geometry_info.rho,(*image)->interpolate,
8616        exception);
8617      if (implode_image != (Image *) NULL)
8618        {
8619          *image=DestroyImage(*image);
8620          *image=implode_image;
8621        }
8622      CatchException(exception);
8623      XSetCursorState(display,windows,MagickFalse);
8624      if (IfMagickTrue(windows->image.orphan) )
8625        break;
8626      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8627      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8628      break;
8629    }
8630    case VignetteCommand:
8631    {
8632      Image
8633        *vignette_image;
8634
8635      static char
8636        geometry[MaxTextExtent] = "0x20";
8637
8638      /*
8639        Query user for the vignette geometry.
8640      */
8641      (void) XDialogWidget(display,windows,"Vignette",
8642        "Enter the radius, sigma, and x and y offsets:",geometry);
8643      if (*geometry == '\0')
8644        break;
8645      /*
8646        Soften the edges of the image in vignette style
8647      */
8648      XSetCursorState(display,windows,MagickTrue);
8649      XCheckRefreshWindows(display,windows);
8650      flags=ParseGeometry(geometry,&geometry_info);
8651      if ((flags & SigmaValue) == 0)
8652        geometry_info.sigma=1.0;
8653      if ((flags & XiValue) == 0)
8654        geometry_info.xi=0.1*(*image)->columns;
8655      if ((flags & PsiValue) == 0)
8656        geometry_info.psi=0.1*(*image)->rows;
8657      vignette_image=VignetteImage(*image,geometry_info.rho,0.0,(ssize_t)
8658        ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-0.5),
8659        exception);
8660      if (vignette_image != (Image *) NULL)
8661        {
8662          *image=DestroyImage(*image);
8663          *image=vignette_image;
8664        }
8665      CatchException(exception);
8666      XSetCursorState(display,windows,MagickFalse);
8667      if (IfMagickTrue(windows->image.orphan) )
8668        break;
8669      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8670      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8671      break;
8672    }
8673    case WaveCommand:
8674    {
8675      Image
8676        *wave_image;
8677
8678      static char
8679        geometry[MaxTextExtent] = "25x150";
8680
8681      /*
8682        Query user for the wave geometry.
8683      */
8684      (void) XDialogWidget(display,windows,"Wave",
8685        "Enter the amplitude and length of the wave:",geometry);
8686      if (*geometry == '\0')
8687        break;
8688      /*
8689        Alter an image along a sine wave.
8690      */
8691      XSetCursorState(display,windows,MagickTrue);
8692      XCheckRefreshWindows(display,windows);
8693      flags=ParseGeometry(geometry,&geometry_info);
8694      if ((flags & SigmaValue) == 0)
8695        geometry_info.sigma=1.0;
8696      wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8697        (*image)->interpolate,exception);
8698      if (wave_image != (Image *) NULL)
8699        {
8700          *image=DestroyImage(*image);
8701          *image=wave_image;
8702        }
8703      CatchException(exception);
8704      XSetCursorState(display,windows,MagickFalse);
8705      if (IfMagickTrue(windows->image.orphan) )
8706        break;
8707      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8708      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8709      break;
8710    }
8711    case OilPaintCommand:
8712    {
8713      Image
8714        *paint_image;
8715
8716      static char
8717        radius[MaxTextExtent] = "0";
8718
8719      /*
8720        Query user for circular neighborhood radius.
8721      */
8722      (void) XDialogWidget(display,windows,"Oil Paint",
8723        "Enter the mask radius:",radius);
8724      if (*radius == '\0')
8725        break;
8726      /*
8727        OilPaint image scanlines.
8728      */
8729      XSetCursorState(display,windows,MagickTrue);
8730      XCheckRefreshWindows(display,windows);
8731      flags=ParseGeometry(radius,&geometry_info);
8732      paint_image=OilPaintImage(*image,geometry_info.rho,geometry_info.sigma,
8733        exception);
8734      if (paint_image != (Image *) NULL)
8735        {
8736          *image=DestroyImage(*image);
8737          *image=paint_image;
8738        }
8739      CatchException(exception);
8740      XSetCursorState(display,windows,MagickFalse);
8741      if (IfMagickTrue(windows->image.orphan) )
8742        break;
8743      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8744      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8745      break;
8746    }
8747    case CharcoalDrawCommand:
8748    {
8749      Image
8750        *charcoal_image;
8751
8752      static char
8753        radius[MaxTextExtent] = "0x1";
8754
8755      /*
8756        Query user for charcoal radius.
8757      */
8758      (void) XDialogWidget(display,windows,"Charcoal Draw",
8759        "Enter the charcoal radius and sigma:",radius);
8760      if (*radius == '\0')
8761        break;
8762      /*
8763        Charcoal the image.
8764      */
8765      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8766        exception);
8767      XSetCursorState(display,windows,MagickTrue);
8768      XCheckRefreshWindows(display,windows);
8769      flags=ParseGeometry(radius,&geometry_info);
8770      if ((flags & SigmaValue) == 0)
8771        geometry_info.sigma=geometry_info.rho;
8772      charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8773        exception);
8774      if (charcoal_image != (Image *) NULL)
8775        {
8776          *image=DestroyImage(*image);
8777          *image=charcoal_image;
8778        }
8779      CatchException(exception);
8780      XSetCursorState(display,windows,MagickFalse);
8781      if (IfMagickTrue(windows->image.orphan) )
8782        break;
8783      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8784      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8785      break;
8786    }
8787    case AnnotateCommand:
8788    {
8789      /*
8790        Annotate the image with text.
8791      */
8792      status=XAnnotateEditImage(display,resource_info,windows,*image,exception);
8793      if (IfMagickFalse(status) )
8794        {
8795          XNoticeWidget(display,windows,"Unable to annotate X image",
8796            (*image)->filename);
8797          break;
8798        }
8799      break;
8800    }
8801    case DrawCommand:
8802    {
8803      /*
8804        Draw image.
8805      */
8806      status=XDrawEditImage(display,resource_info,windows,image,exception);
8807      if (IfMagickFalse(status) )
8808        {
8809          XNoticeWidget(display,windows,"Unable to draw on the X image",
8810            (*image)->filename);
8811          break;
8812        }
8813      break;
8814    }
8815    case ColorCommand:
8816    {
8817      /*
8818        Color edit.
8819      */
8820      status=XColorEditImage(display,resource_info,windows,image,exception);
8821      if (IfMagickFalse(status) )
8822        {
8823          XNoticeWidget(display,windows,"Unable to pixel edit X image",
8824            (*image)->filename);
8825          break;
8826        }
8827      break;
8828    }
8829    case MatteCommand:
8830    {
8831      /*
8832        Matte edit.
8833      */
8834      status=XMatteEditImage(display,resource_info,windows,image,exception);
8835      if (IfMagickFalse(status) )
8836        {
8837          XNoticeWidget(display,windows,"Unable to matte edit X image",
8838            (*image)->filename);
8839          break;
8840        }
8841      break;
8842    }
8843    case CompositeCommand:
8844    {
8845      /*
8846        Composite image.
8847      */
8848      status=XCompositeImage(display,resource_info,windows,*image,
8849        exception);
8850      if (IfMagickFalse(status) )
8851        {
8852          XNoticeWidget(display,windows,"Unable to composite X image",
8853            (*image)->filename);
8854          break;
8855        }
8856      break;
8857    }
8858    case AddBorderCommand:
8859    {
8860      Image
8861        *border_image;
8862
8863      static char
8864        geometry[MaxTextExtent] = "6x6";
8865
8866      /*
8867        Query user for border color and geometry.
8868      */
8869      XColorBrowserWidget(display,windows,"Select",color);
8870      if (*color == '\0')
8871        break;
8872      (void) XDialogWidget(display,windows,"Add Border",
8873        "Enter border geometry:",geometry);
8874      if (*geometry == '\0')
8875        break;
8876      /*
8877        Add a border to the image.
8878      */
8879      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8880        exception);
8881      XSetCursorState(display,windows,MagickTrue);
8882      XCheckRefreshWindows(display,windows);
8883      (void) QueryColorCompliance(color,AllCompliance,&(*image)->border_color,
8884        exception);
8885      (void) ParsePageGeometry(*image,geometry,&page_geometry,
8886        exception);
8887      border_image=BorderImage(*image,&page_geometry,(*image)->compose,
8888        exception);
8889      if (border_image != (Image *) NULL)
8890        {
8891          *image=DestroyImage(*image);
8892          *image=border_image;
8893        }
8894      CatchException(exception);
8895      XSetCursorState(display,windows,MagickFalse);
8896      if (IfMagickTrue(windows->image.orphan) )
8897        break;
8898      windows->image.window_changes.width=(int) (*image)->columns;
8899      windows->image.window_changes.height=(int) (*image)->rows;
8900      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8901      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8902      break;
8903    }
8904    case AddFrameCommand:
8905    {
8906      FrameInfo
8907        frame_info;
8908
8909      Image
8910        *frame_image;
8911
8912      static char
8913        geometry[MaxTextExtent] = "6x6";
8914
8915      /*
8916        Query user for frame color and geometry.
8917      */
8918      XColorBrowserWidget(display,windows,"Select",color);
8919      if (*color == '\0')
8920        break;
8921      (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8922        geometry);
8923      if (*geometry == '\0')
8924        break;
8925      /*
8926        Surround image with an ornamental border.
8927      */
8928      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8929        exception);
8930      XSetCursorState(display,windows,MagickTrue);
8931      XCheckRefreshWindows(display,windows);
8932      (void) QueryColorCompliance(color,AllCompliance,&(*image)->matte_color,
8933        exception);
8934      (void) ParsePageGeometry(*image,geometry,&page_geometry,
8935        exception);
8936      frame_info.width=page_geometry.width;
8937      frame_info.height=page_geometry.height;
8938      frame_info.outer_bevel=page_geometry.x;
8939      frame_info.inner_bevel=page_geometry.y;
8940      frame_info.x=(ssize_t) frame_info.width;
8941      frame_info.y=(ssize_t) frame_info.height;
8942      frame_info.width=(*image)->columns+2*frame_info.width;
8943      frame_info.height=(*image)->rows+2*frame_info.height;
8944      frame_image=FrameImage(*image,&frame_info,(*image)->compose,exception);
8945      if (frame_image != (Image *) NULL)
8946        {
8947          *image=DestroyImage(*image);
8948          *image=frame_image;
8949        }
8950      CatchException(exception);
8951      XSetCursorState(display,windows,MagickFalse);
8952      if (IfMagickTrue(windows->image.orphan) )
8953        break;
8954      windows->image.window_changes.width=(int) (*image)->columns;
8955      windows->image.window_changes.height=(int) (*image)->rows;
8956      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8957      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8958      break;
8959    }
8960    case CommentCommand:
8961    {
8962      const char
8963        *value;
8964
8965      FILE
8966        *file;
8967
8968      int
8969        unique_file;
8970
8971      /*
8972        Edit image comment.
8973      */
8974      unique_file=AcquireUniqueFileResource(image_info->filename);
8975      if (unique_file == -1)
8976        XNoticeWidget(display,windows,"Unable to edit image comment",
8977          image_info->filename);
8978      value=GetImageProperty(*image,"comment",exception);
8979      if (value == (char *) NULL)
8980        unique_file=close(unique_file)-1;
8981      else
8982        {
8983          register const char
8984            *p;
8985
8986          file=fdopen(unique_file,"w");
8987          if (file == (FILE *) NULL)
8988            {
8989              XNoticeWidget(display,windows,"Unable to edit image comment",
8990                image_info->filename);
8991              break;
8992            }
8993          for (p=value; *p != '\0'; p++)
8994            (void) fputc((int) *p,file);
8995          (void) fputc('\n',file);
8996          (void) fclose(file);
8997        }
8998      XSetCursorState(display,windows,MagickTrue);
8999      XCheckRefreshWindows(display,windows);
9000      status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
9001        exception);
9002      if (IfMagickFalse(status) )
9003        XNoticeWidget(display,windows,"Unable to edit image comment",
9004          (char *) NULL);
9005      else
9006        {
9007          char
9008            *comment;
9009
9010          comment=FileToString(image_info->filename,~0UL,exception);
9011          if (comment != (char *) NULL)
9012            {
9013              (void) SetImageProperty(*image,"comment",comment,exception);
9014              (*image)->taint=MagickTrue;
9015            }
9016        }
9017      (void) RelinquishUniqueFileResource(image_info->filename);
9018      XSetCursorState(display,windows,MagickFalse);
9019      break;
9020    }
9021    case LaunchCommand:
9022    {
9023      /*
9024        Launch program.
9025      */
9026      XSetCursorState(display,windows,MagickTrue);
9027      XCheckRefreshWindows(display,windows);
9028      (void) AcquireUniqueFilename(filename);
9029      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"launch:%s",
9030        filename);
9031      status=WriteImage(image_info,*image,exception);
9032      if (IfMagickFalse(status) )
9033        XNoticeWidget(display,windows,"Unable to launch image editor",
9034          (char *) NULL);
9035      else
9036        {
9037          nexus=ReadImage(resource_info->image_info,exception);
9038          CatchException(exception);
9039          XClientMessage(display,windows->image.id,windows->im_protocols,
9040            windows->im_next_image,CurrentTime);
9041        }
9042      (void) RelinquishUniqueFileResource(filename);
9043      XSetCursorState(display,windows,MagickFalse);
9044      break;
9045    }
9046    case RegionofInterestCommand:
9047    {
9048      /*
9049        Apply an image processing technique to a region of interest.
9050      */
9051      (void) XROIImage(display,resource_info,windows,image,exception);
9052      break;
9053    }
9054    case InfoCommand:
9055      break;
9056    case ZoomCommand:
9057    {
9058      /*
9059        Zoom image.
9060      */
9061      if (IfMagickTrue(windows->magnify.mapped) )
9062        (void) XRaiseWindow(display,windows->magnify.id);
9063      else
9064        {
9065          /*
9066            Make magnify image.
9067          */
9068          XSetCursorState(display,windows,MagickTrue);
9069          (void) XMapRaised(display,windows->magnify.id);
9070          XSetCursorState(display,windows,MagickFalse);
9071        }
9072      break;
9073    }
9074    case ShowPreviewCommand:
9075    {
9076      char
9077        **previews;
9078
9079      Image
9080        *preview_image;
9081
9082      static char
9083        preview_type[MaxTextExtent] = "Gamma";
9084
9085      /*
9086        Select preview type from menu.
9087      */
9088      previews=GetCommandOptions(MagickPreviewOptions);
9089      if (previews == (char **) NULL)
9090        break;
9091      XListBrowserWidget(display,windows,&windows->widget,
9092        (const char **) previews,"Preview",
9093        "Select an enhancement, effect, or F/X:",preview_type);
9094      previews=DestroyStringList(previews);
9095      if (*preview_type == '\0')
9096        break;
9097      /*
9098        Show image preview.
9099      */
9100      XSetCursorState(display,windows,MagickTrue);
9101      XCheckRefreshWindows(display,windows);
9102      image_info->preview_type=(PreviewType)
9103        ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
9104      image_info->group=(ssize_t) windows->image.id;
9105      (void) DeleteImageProperty(*image,"label");
9106      (void) SetImageProperty(*image,"label","Preview",exception);
9107      (void) AcquireUniqueFilename(filename);
9108      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"preview:%s",
9109        filename);
9110      status=WriteImage(image_info,*image,exception);
9111      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9112      preview_image=ReadImage(image_info,exception);
9113      (void) RelinquishUniqueFileResource(filename);
9114      if (preview_image == (Image *) NULL)
9115        break;
9116      (void) FormatLocaleString(preview_image->filename,MaxTextExtent,"show:%s",
9117        filename);
9118      status=WriteImage(image_info,preview_image,exception);
9119      preview_image=DestroyImage(preview_image);
9120      if (IfMagickFalse(status) )
9121        XNoticeWidget(display,windows,"Unable to show image preview",
9122          (*image)->filename);
9123      XDelay(display,1500);
9124      XSetCursorState(display,windows,MagickFalse);
9125      break;
9126    }
9127    case ShowHistogramCommand:
9128    {
9129      Image
9130        *histogram_image;
9131
9132      /*
9133        Show image histogram.
9134      */
9135      XSetCursorState(display,windows,MagickTrue);
9136      XCheckRefreshWindows(display,windows);
9137      image_info->group=(ssize_t) windows->image.id;
9138      (void) DeleteImageProperty(*image,"label");
9139      (void) SetImageProperty(*image,"label","Histogram",exception);
9140      (void) AcquireUniqueFilename(filename);
9141      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"histogram:%s",
9142        filename);
9143      status=WriteImage(image_info,*image,exception);
9144      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9145      histogram_image=ReadImage(image_info,exception);
9146      (void) RelinquishUniqueFileResource(filename);
9147      if (histogram_image == (Image *) NULL)
9148        break;
9149      (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
9150        "show:%s",filename);
9151      status=WriteImage(image_info,histogram_image,exception);
9152      histogram_image=DestroyImage(histogram_image);
9153      if (IfMagickFalse(status) )
9154        XNoticeWidget(display,windows,"Unable to show histogram",
9155          (*image)->filename);
9156      XDelay(display,1500);
9157      XSetCursorState(display,windows,MagickFalse);
9158      break;
9159    }
9160    case ShowMatteCommand:
9161    {
9162      Image
9163        *matte_image;
9164
9165      if ((*image)->alpha_trait != BlendPixelTrait)
9166        {
9167          XNoticeWidget(display,windows,
9168            "Image does not have any matte information",(*image)->filename);
9169          break;
9170        }
9171      /*
9172        Show image matte.
9173      */
9174      XSetCursorState(display,windows,MagickTrue);
9175      XCheckRefreshWindows(display,windows);
9176      image_info->group=(ssize_t) windows->image.id;
9177      (void) DeleteImageProperty(*image,"label");
9178      (void) SetImageProperty(*image,"label","Matte",exception);
9179      (void) AcquireUniqueFilename(filename);
9180      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"matte:%s",
9181        filename);
9182      status=WriteImage(image_info,*image,exception);
9183      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
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,MaxTextExtent,"show:%s",
9189        filename);
9190      status=WriteImage(image_info,matte_image,exception);
9191      matte_image=DestroyImage(matte_image);
9192      if (IfMagickFalse(status) )
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 (IfMagickFalse(status) )
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[MaxTextExtent] = "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 (IfMagickFalse(status) )
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[MaxTextExtent],
9272            *url;
9273
9274          /*
9275            Display documentation using Netscape remote control.
9276          */
9277          url=GetMagickHomeURL();
9278          (void) FormatLocaleString(command,MaxTextExtent,
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 (IfMagickFalse(status) )
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[MaxTextExtent];
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 (IfMagickTrue(windows->info.mapped) )
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 (IfMagickTrue(windows->info.mapped) )
9386      {
9387        /*
9388          Display pointer position.
9389        */
9390        (void) FormatLocaleString(text,MaxTextExtent," %+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 (IfMagickFalse(status) )
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[MaxTextExtent] = "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[MaxTextExtent],
9700    text[MaxTextExtent];
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 (IfMagickTrue(windows->info.mapped) )
9754      {
9755        /*
9756          Display pointer position.
9757        */
9758        (void) FormatLocaleString(text,MaxTextExtent," %+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[MaxTextExtent] = "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[MaxTextExtent];
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%",MaxTextExtent);
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,"%",MaxTextExtent);
9876            (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
9877              1.0);
9878            break;
9879          }
9880          case MatteEditValueCommand:
9881          {
9882            static char
9883              message[MaxTextExtent];
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,MaxTextExtent,QuantumFormat,
9904                  OpaqueAlpha);
9905                if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9906                  (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9907                    (Quantum) TransparentAlpha);
9908                break;
9909              }
9910            (void) FormatLocaleString(message,MaxTextExtent,
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[MaxTextExtent];
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 (IfMagickTrue(windows->info.mapped) )
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 (IfMagickFalse(SetImageStorageClass(*image,DirectClass,exception)) )
10097          return(MagickFalse);
10098        if ((*image)->alpha_trait != BlendPixelTrait)
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 (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
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              IsMagickFalse(method == FloodfillMethod),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 (IfMagickFalse(SetImageStorageClass(*image,DirectClass,exception)) )
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 (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
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[MaxTextExtent] = "\0";
10270
10271  /*
10272    Request file name from user.
10273  */
10274  if (IfMagickFalse(command) )
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,MaxTextExtent);
10323  exception=AcquireExceptionInfo();
10324  (void) SetImageInfo(image_info,0,exception);
10325  if (LocaleCompare(image_info->magick,"X") == 0)
10326    {
10327      char
10328        seconds[MaxTextExtent];
10329
10330      /*
10331        User may want to delay the X server screen grab.
10332      */
10333      (void) CopyMagickString(seconds,"0",MaxTextExtent);
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      IfMagickTrue(magick_info->raw))
10343    {
10344      char
10345        geometry[MaxTextExtent];
10346
10347      /*
10348        Request image size from the user.
10349      */
10350      (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10351      if (image_info->size != (char *) NULL)
10352        (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
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,MaxTextExtent);
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[MaxTextExtent];
10386
10387          register int
10388            i;
10389
10390          (void) FormatLocaleString(title,MaxTextExtent,
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[MaxTextExtent];
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,MaxTextExtent," %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[MaxTextExtent];
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 (IfMagickTrue(windows->info.mapped) )
10698      {
10699        /*
10700          Display pointer position.
10701        */
10702        (void) FormatLocaleString(text,MaxTextExtent," %+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[MaxTextExtent],
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 (IfMagickTrue(image->debug) )
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 (IfMagickTrue(image->debug) )
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[MaxTextExtent];
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 (IfMagickTrue(image->debug) )
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 (IfMagickTrue(windows->info.mapped) )
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 (IfMagickTrue(image->debug) )
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[MaxTextExtent],
10986    geometry[MaxTextExtent];
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,MaxTextExtent,"Letter");
11002  if (image_info->page != (char *) NULL)
11003    (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
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,MaxTextExtent,"%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,MaxTextExtent,"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(IsMagickTrue(status));
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[MaxTextExtent],
11282    text[MaxTextExtent];
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 (IfMagickTrue(windows->info.mapped) )
11344      {
11345        /*
11346          Display pointer position.
11347        */
11348        (void) FormatLocaleString(text,MaxTextExtent," %+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 (IfMagickTrue(windows->info.mapped) )
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 (IfMagickFalse(windows->info.mapped) )
11505            (void) XMapWindow(display,windows->info.id);
11506          (void) FormatLocaleString(text,MaxTextExtent,
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 (IfMagickTrue(windows->info.mapped) )
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 (IfMagickTrue(windows->info.mapped) )
11600        {
11601          /*
11602            Display pointer position.
11603          */
11604          (void) FormatLocaleString(text,MaxTextExtent,
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],MaxTextExtent);
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                        MaxTextExtent);
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 (IfMagickTrue(windows->info.mapped) )
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,MaxTextExtent,
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[MaxTextExtent],
12057    text[MaxTextExtent];
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[MaxTextExtent] = "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[MaxTextExtent];
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 (IfMagickFalse(windows->info.mapped) )
12322              (void) XMapWindow(display,windows->info.id);
12323            (void) FormatLocaleString(text,MaxTextExtent," %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 (IfMagickTrue(windows->info.mapped) )
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,MaxTextExtent,
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,MaxTextExtent,
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,MaxTextExtent,
12460            "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12461          break;
12462        }
12463      }
12464    }
12465  if (IfMagickTrue(windows->image.orphan) )
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[MaxTextExtent],
12538    geometry[MaxTextExtent];
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      MaxTextExtent);
12555  else
12556    {
12557      char
12558        path[MaxTextExtent];
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 (IfMagickTrue(IsPathAccessible(filename)) )
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,MaxTextExtent);
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[MaxTextExtent];
12596
12597      int
12598        status;
12599
12600      /*
12601        Request JPEG quality from user.
12602      */
12603      (void) FormatLocaleString(quality,MaxTextExtent,"%.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[MaxTextExtent];
12619
12620      /*
12621        Request page geometry from user.
12622      */
12623      (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12624      if (LocaleCompare(image_info->magick,"PDF") == 0)
12625        (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12626      if (image_info->page != (char *) NULL)
12627        (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
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,MaxTextExtent,"%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,MaxTextExtent);
12649  status=WriteImage(image_info,save_image,exception);
12650  if (IfMagickTrue(status) )
12651    image->taint=MagickFalse;
12652  save_image=DestroyImage(save_image);
12653  image_info=DestroyImageInfo(image_info);
12654  XSetCursorState(display,windows,MagickFalse);
12655  return(IsMagickTrue(status));
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 (IfMagickFalse(windows->magnify.mapped) )
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 (IfMagickFalse(windows->magnify.mapped) )
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[MaxTextExtent];
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 (IfMagickTrue(windows->magnify.mapped) )
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[MaxTextExtent];
12968
12969  int
12970    x,
12971    y;
12972
12973  double
12974    scale_factor;
12975
12976  unsigned int
12977    height,
12978    width;
12979
12980  if (IfMagickTrue(windows->info.mapped) )
12981    {
12982      /*
12983        Display info on cropping rectangle.
12984      */
12985      (void) FormatLocaleString(text,MaxTextExtent," %.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,MaxTextExtent,
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[MaxTextExtent],
13085    filename[MaxTextExtent];
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        MaxTextExtent);
13188      (void) CopyMagickString(resource_info->image_info->filename,filename,
13189        MaxTextExtent);
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 (IfMagickFalse(IsPathAccessible(filename)) )
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 (IfMagickTrue(status) )
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 (IfMagickTrue(IsPathAccessible(filename)) )
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            SetPixelInfoPixel(image,&pixel,s);
13285            s+=GetPixelChannels(image);
13286          }
13287          if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
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[MaxTextExtent];
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,MaxTextExtent," %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[MaxTextExtent] = "\0",
13634    filenames[MaxTextExtent] = "*";
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 (IfMagickFalse(status) || (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,MaxTextExtent,
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=IsMagickTrue( (windows->visual_info->klass == TrueColor) ||
13679    (windows->visual_info->klass == DirectColor) );
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],MaxTextExtent);
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 (IfMagickFalse(proceed) )
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,MaxTextExtent);
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[MaxTextExtent],
13792    visual_type[MaxTextExtent];
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 == MagickSignature);
13834  if (IfMagickTrue(image->debug) )
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",MaxTextExtent);
13861  status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13862  if (status != 0)
13863    (void) FormatLocaleString(visual_type,MaxTextExtent,"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,MaxTextExtent,"%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 (IfMagickFalse(status) )
13945    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13946      image->filename);
13947  window_info.x=0;
13948  window_info.y=0;
13949  if (IfMagickTrue(image->debug) )
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 (IfMagickTrue(resources.backdrop) )
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[MaxTextExtent];
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,MaxTextExtent,"%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(IsMagickTrue(window_info.id == root_window));
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[MaxTextExtent],
14430    *directory,
14431    geometry[MaxTextExtent],
14432    resource_name[MaxTextExtent];
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[MaxTextExtent];
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 == MagickSignature);
14529  if (IfMagickTrue((*image)->debug) )
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,".",MaxTextExtent);
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 (IfMagickTrue(display_image->debug) )
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 (IfMagickTrue(display_image->debug) )
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 (IfMagickTrue(display_image->debug) )
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 (IfMagickFalse(resource_info->use_shared_memory) )
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,MaxTextExtent);
14714      (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
14715      title=DestroyString(title);
14716    }
14717  else
14718    {
14719      char
14720        filename[MaxTextExtent];
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,MaxTextExtent,
14728          "%s: %s",MagickPackageName,filename);
14729      else
14730        (void) FormatLocaleString(windows->image.name,MaxTextExtent,
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,MaxTextExtent);
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,MaxTextExtent,"%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 (IfMagickTrue(display_image->debug) )
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 (IfMagickTrue(display_image->debug) )
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 (IfMagickTrue(display_image->debug) )
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 (IfMagickTrue(windows->image.mapped) )
14856    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14857  if (IfMagickTrue(display_image->debug) )
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,MaxTextExtent,"%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 (IfMagickTrue(windows->command.mapped) )
14890    (void) XMapRaised(display,windows->command.id);
14891  if (IfMagickTrue(display_image->debug) )
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,MaxTextExtent,"%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 (IfMagickTrue(display_image->debug) )
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 (IfMagickTrue(display_image->debug) )
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 (IfMagickFalse(resource_info->use_shared_memory) )
14953    windows->magnify.shared_memory=MagickFalse;
14954  (void) FormatLocaleString(resource_name,MaxTextExtent,"%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,MaxTextExtent,"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 (IfMagickTrue(display_image->debug) )
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,MaxTextExtent,"%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 (IfMagickTrue(display_image->debug) )
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 (IfMagickTrue(windows->info.mapped) )
15020    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15021  if (IfMagickFalse(windows->image.mapped) ||
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 (IfMagickFalse(status) )
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 (IfMagickFalse(status))
15053    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15054      display_image->filename);
15055  if (IfMagickTrue(windows->magnify.mapped) )
15056    (void) XMapRaised(display,windows->magnify.id);
15057  if (IfMagickTrue(windows->pan.mapped) )
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 (IfMagickTrue(resource_info->update) )
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 (IfMagickTrue(status) )
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 (IfMagickTrue(windows->image.mapped) )
15091      if ((display_image->delay != 0) || (resource_info->update != 0))
15092        {
15093          if (timer < time((time_t *) NULL))
15094            {
15095              if (IfMagickFalse(resource_info->update) )
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 (IfMagickTrue(status) )
15107                    if (update_time != attributes.st_mtime)
15108                      {
15109                        /*
15110                          Redisplay image.
15111                        */
15112                        (void) FormatLocaleString(
15113                          resource_info->image_info->filename,MaxTextExtent,
15114                          "%s:%s",display_image->magick,
15115                          display_image->filename);
15116                        nexus=ReadImage(resource_info->image_info,exception);
15117                        if (nexus != (Image *) NULL)
15118                          {
15119                            nexus=DestroyImage(nexus);
15120                            *state|=NextImageState | ExitState;
15121                          }
15122                      }
15123                  delay=display_image->delay/MagickMax(
15124                    display_image->ticks_per_second,1L);
15125                  timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15126                }
15127            }
15128          if (XEventsQueued(display,QueuedAfterFlush) == 0)
15129            {
15130              /*
15131                Do not block if delay > 0.
15132              */
15133              XDelay(display,SuspendTime << 2);
15134              continue;
15135            }
15136        }
15137    timestamp=time((time_t *) NULL);
15138    (void) XNextEvent(display,&event);
15139    if (IfMagickFalse(windows->image.stasis) )
15140      windows->image.stasis=IsMagickTrue((time((time_t *) NULL)-timestamp) > 0);
15141    if (IfMagickFalse(windows->magnify.stasis) )
15142      windows->magnify.stasis=IsMagickTrue((time((time_t *) NULL)-timestamp) > 0);
15143    if (event.xany.window == windows->command.id)
15144      {
15145        /*
15146          Select a command from the Command widget.
15147        */
15148        id=XCommandWidget(display,windows,CommandMenu,&event);
15149        if (id < 0)
15150          continue;
15151        (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15152        command_type=CommandMenus[id];
15153        if (id < MagickMenus)
15154          {
15155            /*
15156              Select a command from a pop-up menu.
15157            */
15158            entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15159              command);
15160            if (entry < 0)
15161              continue;
15162            (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15163            command_type=Commands[id][entry];
15164          }
15165        if (command_type != NullCommand)
15166          nexus=XMagickCommand(display,resource_info,windows,command_type,
15167            &display_image,exception);
15168        continue;
15169      }
15170    switch (event.type)
15171    {
15172      case ButtonPress:
15173      {
15174        if (IfMagickTrue(display_image->debug) )
15175          (void) LogMagickEvent(X11Event,GetMagickModule(),
15176            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15177            event.xbutton.button,event.xbutton.x,event.xbutton.y);
15178        if ((event.xbutton.button == Button3) &&
15179            (event.xbutton.state & Mod1Mask))
15180          {
15181            /*
15182              Convert Alt-Button3 to Button2.
15183            */
15184            event.xbutton.button=Button2;
15185            event.xbutton.state&=(~Mod1Mask);
15186          }
15187        if (event.xbutton.window == windows->backdrop.id)
15188          {
15189            (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15190              event.xbutton.time);
15191            break;
15192          }
15193        if (event.xbutton.window == windows->image.id)
15194          {
15195            switch (event.xbutton.button)
15196            {
15197              case Button1:
15198              {
15199                if (resource_info->immutable)
15200                  {
15201                    /*
15202                      Select a command from the Virtual menu.
15203                    */
15204                    entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15205                      command);
15206                    if (entry >= 0)
15207                      nexus=XMagickCommand(display,resource_info,windows,
15208                        VirtualCommands[entry],&display_image,exception);
15209                    break;
15210                  }
15211                /*
15212                  Map/unmap Command widget.
15213                */
15214                if (IfMagickTrue(windows->command.mapped) )
15215                  (void) XWithdrawWindow(display,windows->command.id,
15216                    windows->command.screen);
15217                else
15218                  {
15219                    (void) XCommandWidget(display,windows,CommandMenu,
15220                      (XEvent *) NULL);
15221                    (void) XMapRaised(display,windows->command.id);
15222                  }
15223                break;
15224              }
15225              case Button2:
15226              {
15227                /*
15228                  User pressed the image magnify button.
15229                */
15230                (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15231                  &display_image,exception);
15232                XMagnifyImage(display,windows,&event,exception);
15233                break;
15234              }
15235              case Button3:
15236              {
15237                if (resource_info->immutable)
15238                  {
15239                    /*
15240                      Select a command from the Virtual menu.
15241                    */
15242                    entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15243                      command);
15244                    if (entry >= 0)
15245                      nexus=XMagickCommand(display,resource_info,windows,
15246                        VirtualCommands[entry],&display_image,exception);
15247                    break;
15248                  }
15249                if (display_image->montage != (char *) NULL)
15250                  {
15251                    /*
15252                      Open or delete a tile from a visual image directory.
15253                    */
15254                    nexus=XTileImage(display,resource_info,windows,
15255                      display_image,&event,exception);
15256                    if (nexus != (Image *) NULL)
15257                      *state|=MontageImageState | NextImageState | ExitState;
15258                    vid_info.x=(short int) windows->image.x;
15259                    vid_info.y=(short int) windows->image.y;
15260                    break;
15261                  }
15262                /*
15263                  Select a command from the Short Cuts menu.
15264                */
15265                entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15266                  command);
15267                if (entry >= 0)
15268                  nexus=XMagickCommand(display,resource_info,windows,
15269                    ShortCutsCommands[entry],&display_image,exception);
15270                break;
15271              }
15272              case Button4:
15273              {
15274                /*
15275                  Wheel up.
15276                */
15277                XTranslateImage(display,windows,*image,XK_Up);
15278                break;
15279              }
15280              case Button5:
15281              {
15282                /*
15283                  Wheel down.
15284                */
15285                XTranslateImage(display,windows,*image,XK_Down);
15286                break;
15287              }
15288              default:
15289                break;
15290            }
15291            break;
15292          }
15293        if (event.xbutton.window == windows->magnify.id)
15294          {
15295            int
15296              factor;
15297
15298            static const char
15299              *MagnifyMenu[] =
15300              {
15301                "2",
15302                "4",
15303                "5",
15304                "6",
15305                "7",
15306                "8",
15307                "9",
15308                "3",
15309                (char *) NULL,
15310              };
15311
15312            static KeySym
15313              MagnifyCommands[] =
15314              {
15315                XK_2,
15316                XK_4,
15317                XK_5,
15318                XK_6,
15319                XK_7,
15320                XK_8,
15321                XK_9,
15322                XK_3
15323              };
15324
15325            /*
15326              Select a magnify factor from the pop-up menu.
15327            */
15328            factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15329            if (factor >= 0)
15330              XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor],
15331                exception);
15332            break;
15333          }
15334        if (event.xbutton.window == windows->pan.id)
15335          {
15336            switch (event.xbutton.button)
15337            {
15338              case Button4:
15339              {
15340                /*
15341                  Wheel up.
15342                */
15343                XTranslateImage(display,windows,*image,XK_Up);
15344                break;
15345              }
15346              case Button5:
15347              {
15348                /*
15349                  Wheel down.
15350                */
15351                XTranslateImage(display,windows,*image,XK_Down);
15352                break;
15353              }
15354              default:
15355              {
15356                XPanImage(display,windows,&event,exception);
15357                break;
15358              }
15359            }
15360            break;
15361          }
15362        delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15363          1L);
15364        timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15365        break;
15366      }
15367      case ButtonRelease:
15368      {
15369        if (IfMagickTrue(display_image->debug) )
15370          (void) LogMagickEvent(X11Event,GetMagickModule(),
15371            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15372            event.xbutton.button,event.xbutton.x,event.xbutton.y);
15373        break;
15374      }
15375      case ClientMessage:
15376      {
15377        if (IfMagickTrue(display_image->debug) )
15378          (void) LogMagickEvent(X11Event,GetMagickModule(),
15379            "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15380            event.xclient.message_type,event.xclient.format,(unsigned long)
15381            event.xclient.data.l[0]);
15382        if (event.xclient.message_type == windows->im_protocols)
15383          {
15384            if (*event.xclient.data.l == (long) windows->im_update_widget)
15385              {
15386                (void) CloneString(&windows->command.name,MagickTitle);
15387                windows->command.data=MagickMenus;
15388                (void) XCommandWidget(display,windows,CommandMenu,
15389                  (XEvent *) NULL);
15390                break;
15391              }
15392            if (*event.xclient.data.l == (long) windows->im_update_colormap)
15393              {
15394                /*
15395                  Update graphic context and window colormap.
15396                */
15397                for (i=0; i < (int) number_windows; i++)
15398                {
15399                  if (magick_windows[i]->id == windows->icon.id)
15400                    continue;
15401                  context_values.background=pixel->background_color.pixel;
15402                  context_values.foreground=pixel->foreground_color.pixel;
15403                  (void) XChangeGC(display,magick_windows[i]->annotate_context,
15404                    context_mask,&context_values);
15405                  (void) XChangeGC(display,magick_windows[i]->widget_context,
15406                    context_mask,&context_values);
15407                  context_values.background=pixel->foreground_color.pixel;
15408                  context_values.foreground=pixel->background_color.pixel;
15409                  context_values.plane_mask=context_values.background ^
15410                    context_values.foreground;
15411                  (void) XChangeGC(display,magick_windows[i]->highlight_context,
15412                    (size_t) (context_mask | GCPlaneMask),
15413                    &context_values);
15414                  magick_windows[i]->attributes.background_pixel=
15415                    pixel->background_color.pixel;
15416                  magick_windows[i]->attributes.border_pixel=
15417                    pixel->border_color.pixel;
15418                  magick_windows[i]->attributes.colormap=map_info->colormap;
15419                  (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15420                    (unsigned long) magick_windows[i]->mask,
15421                    &magick_windows[i]->attributes);
15422                }
15423                if (IfMagickTrue(windows->pan.mapped) )
15424                  {
15425                    (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15426                      windows->pan.pixmap);
15427                    (void) XClearWindow(display,windows->pan.id);
15428                    XDrawPanRectangle(display,windows);
15429                  }
15430                if (windows->backdrop.id != (Window) NULL)
15431                  (void) XInstallColormap(display,map_info->colormap);
15432                break;
15433              }
15434            if (*event.xclient.data.l == (long) windows->im_former_image)
15435              {
15436                *state|=FormerImageState | ExitState;
15437                break;
15438              }
15439            if (*event.xclient.data.l == (long) windows->im_next_image)
15440              {
15441                *state|=NextImageState | ExitState;
15442                break;
15443              }
15444            if (*event.xclient.data.l == (long) windows->im_retain_colors)
15445              {
15446                *state|=RetainColorsState;
15447                break;
15448              }
15449            if (*event.xclient.data.l == (long) windows->im_exit)
15450              {
15451                *state|=ExitState;
15452                break;
15453              }
15454            break;
15455          }
15456        if (event.xclient.message_type == windows->dnd_protocols)
15457          {
15458            Atom
15459              selection,
15460              type;
15461
15462            int
15463              format,
15464              status;
15465
15466            unsigned char
15467              *data;
15468
15469            unsigned long
15470              after,
15471              length;
15472
15473            /*
15474              Display image named by the Drag-and-Drop selection.
15475            */
15476            if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15477              break;
15478            selection=XInternAtom(display,"DndSelection",MagickFalse);
15479            status=XGetWindowProperty(display,root_window,selection,0L,(long)
15480              MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15481              &length,&after,&data);
15482            if ((status != Success) || (length == 0))
15483              break;
15484            if (*event.xclient.data.l == 2)
15485              {
15486                /*
15487                  Offix DND.
15488                */
15489                (void) CopyMagickString(resource_info->image_info->filename,
15490                  (char *) data,MaxTextExtent);
15491              }
15492            else
15493              {
15494                /*
15495                  XDND.
15496                */
15497                if (strncmp((char *) data, "file:", 5) != 0)
15498                  {
15499                    (void) XFree((void *) data);
15500                    break;
15501                  }
15502                (void) CopyMagickString(resource_info->image_info->filename,
15503                  ((char *) data)+5,MaxTextExtent);
15504              }
15505            nexus=ReadImage(resource_info->image_info,exception);
15506            CatchException(exception);
15507            if (nexus != (Image *) NULL)
15508              *state|=NextImageState | ExitState;
15509            (void) XFree((void *) data);
15510            break;
15511          }
15512        /*
15513          If client window delete message, exit.
15514        */
15515        if (event.xclient.message_type != windows->wm_protocols)
15516          break;
15517        if (*event.xclient.data.l != (long) windows->wm_delete_window)
15518          break;
15519        (void) XWithdrawWindow(display,event.xclient.window,
15520          visual_info->screen);
15521        if (event.xclient.window == windows->image.id)
15522          {
15523            *state|=ExitState;
15524            break;
15525          }
15526        if (event.xclient.window == windows->pan.id)
15527          {
15528            /*
15529              Restore original image size when pan window is deleted.
15530            */
15531            windows->image.window_changes.width=windows->image.ximage->width;
15532            windows->image.window_changes.height=windows->image.ximage->height;
15533            (void) XConfigureImage(display,resource_info,windows,
15534              display_image,exception);
15535          }
15536        break;
15537      }
15538      case ConfigureNotify:
15539      {
15540        if (IfMagickTrue(display_image->debug) )
15541          (void) LogMagickEvent(X11Event,GetMagickModule(),
15542            "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15543            event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15544            event.xconfigure.y,event.xconfigure.send_event);
15545        if (event.xconfigure.window == windows->image.id)
15546          {
15547            /*
15548              Image window has a new configuration.
15549            */
15550            if (event.xconfigure.send_event != 0)
15551              {
15552                XWindowChanges
15553                  window_changes;
15554
15555                /*
15556                  Position the transient windows relative of the Image window.
15557                */
15558                if (windows->command.geometry == (char *) NULL)
15559                  if (IfMagickFalse(windows->command.mapped) )
15560                    {
15561                      windows->command.x=event.xconfigure.x-
15562                        windows->command.width-25;
15563                      windows->command.y=event.xconfigure.y;
15564                      XConstrainWindowPosition(display,&windows->command);
15565                      window_changes.x=windows->command.x;
15566                      window_changes.y=windows->command.y;
15567                      (void) XReconfigureWMWindow(display,windows->command.id,
15568                        windows->command.screen,(unsigned int) (CWX | CWY),
15569                        &window_changes);
15570                    }
15571                if (windows->widget.geometry == (char *) NULL)
15572                  if (IfMagickFalse(windows->widget.mapped) )
15573                    {
15574                      windows->widget.x=event.xconfigure.x+
15575                        event.xconfigure.width/10;
15576                      windows->widget.y=event.xconfigure.y+
15577                        event.xconfigure.height/10;
15578                      XConstrainWindowPosition(display,&windows->widget);
15579                      window_changes.x=windows->widget.x;
15580                      window_changes.y=windows->widget.y;
15581                      (void) XReconfigureWMWindow(display,windows->widget.id,
15582                        windows->widget.screen,(unsigned int) (CWX | CWY),
15583                        &window_changes);
15584                    }
15585                if (windows->magnify.geometry == (char *) NULL)
15586                  if (IfMagickFalse(windows->magnify.mapped) )
15587                    {
15588                      windows->magnify.x=event.xconfigure.x+
15589                        event.xconfigure.width+25;
15590                      windows->magnify.y=event.xconfigure.y;
15591                      XConstrainWindowPosition(display,&windows->magnify);
15592                      window_changes.x=windows->magnify.x;
15593                      window_changes.y=windows->magnify.y;
15594                      (void) XReconfigureWMWindow(display,windows->magnify.id,
15595                        windows->magnify.screen,(unsigned int) (CWX | CWY),
15596                        &window_changes);
15597                    }
15598                if (windows->pan.geometry == (char *) NULL)
15599                  if (IfMagickFalse(windows->pan.mapped) )
15600                    {
15601                      windows->pan.x=event.xconfigure.x+
15602                        event.xconfigure.width+25;
15603                      windows->pan.y=event.xconfigure.y+
15604                        windows->magnify.height+50;
15605                      XConstrainWindowPosition(display,&windows->pan);
15606                      window_changes.x=windows->pan.x;
15607                      window_changes.y=windows->pan.y;
15608                      (void) XReconfigureWMWindow(display,windows->pan.id,
15609                        windows->pan.screen,(unsigned int) (CWX | CWY),
15610                        &window_changes);
15611                    }
15612              }
15613            if ((event.xconfigure.width == (int) windows->image.width) &&
15614                (event.xconfigure.height == (int) windows->image.height))
15615              break;
15616            windows->image.width=(unsigned int) event.xconfigure.width;
15617            windows->image.height=(unsigned int) event.xconfigure.height;
15618            windows->image.x=0;
15619            windows->image.y=0;
15620            if (display_image->montage != (char *) NULL)
15621              {
15622                windows->image.x=vid_info.x;
15623                windows->image.y=vid_info.y;
15624              }
15625            if (IfMagickTrue(windows->image.mapped) &&
15626                IfMagickTrue(windows->image.stasis) )
15627              {
15628                /*
15629                  Update image window configuration.
15630                */
15631                windows->image.window_changes.width=event.xconfigure.width;
15632                windows->image.window_changes.height=event.xconfigure.height;
15633                (void) XConfigureImage(display,resource_info,windows,
15634                  display_image,exception);
15635              }
15636            /*
15637              Update pan window configuration.
15638            */
15639            if ((event.xconfigure.width < windows->image.ximage->width) ||
15640                (event.xconfigure.height < windows->image.ximage->height))
15641              {
15642                (void) XMapRaised(display,windows->pan.id);
15643                XDrawPanRectangle(display,windows);
15644              }
15645            else
15646              if (IfMagickTrue(windows->pan.mapped) )
15647                (void) XWithdrawWindow(display,windows->pan.id,
15648                  windows->pan.screen);
15649            break;
15650          }
15651        if (event.xconfigure.window == windows->magnify.id)
15652          {
15653            unsigned int
15654              magnify;
15655
15656            /*
15657              Magnify window has a new configuration.
15658            */
15659            windows->magnify.width=(unsigned int) event.xconfigure.width;
15660            windows->magnify.height=(unsigned int) event.xconfigure.height;
15661            if (IfMagickFalse(windows->magnify.mapped) )
15662              break;
15663            magnify=1;
15664            while ((int) magnify <= event.xconfigure.width)
15665              magnify<<=1;
15666            while ((int) magnify <= event.xconfigure.height)
15667              magnify<<=1;
15668            magnify>>=1;
15669            if (((int) magnify != event.xconfigure.width) ||
15670                ((int) magnify != event.xconfigure.height))
15671              {
15672                window_changes.width=(int) magnify;
15673                window_changes.height=(int) magnify;
15674                (void) XReconfigureWMWindow(display,windows->magnify.id,
15675                  windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15676                  &window_changes);
15677                break;
15678              }
15679            if (IfMagickTrue(windows->magnify.mapped) &&
15680                IfMagickTrue(windows->magnify.stasis) )
15681              {
15682                status=XMakeImage(display,resource_info,&windows->magnify,
15683                  display_image,windows->magnify.width,windows->magnify.height,
15684                  exception);
15685                XMakeMagnifyImage(display,windows,exception);
15686              }
15687            break;
15688          }
15689        if (IfMagickTrue(windows->magnify.mapped) &&
15690            (event.xconfigure.window == windows->pan.id))
15691          {
15692            /*
15693              Pan icon window has a new configuration.
15694            */
15695            if (event.xconfigure.send_event != 0)
15696              {
15697                windows->pan.x=event.xconfigure.x;
15698                windows->pan.y=event.xconfigure.y;
15699              }
15700            windows->pan.width=(unsigned int) event.xconfigure.width;
15701            windows->pan.height=(unsigned int) event.xconfigure.height;
15702            break;
15703          }
15704        if (event.xconfigure.window == windows->icon.id)
15705          {
15706            /*
15707              Icon window has a new configuration.
15708            */
15709            windows->icon.width=(unsigned int) event.xconfigure.width;
15710            windows->icon.height=(unsigned int) event.xconfigure.height;
15711            break;
15712          }
15713        break;
15714      }
15715      case DestroyNotify:
15716      {
15717        /*
15718          Group leader has exited.
15719        */
15720        if (IfMagickTrue(display_image->debug) )
15721          (void) LogMagickEvent(X11Event,GetMagickModule(),
15722            "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15723        if (event.xdestroywindow.window == windows->group_leader.id)
15724          {
15725            *state|=ExitState;
15726            break;
15727          }
15728        break;
15729      }
15730      case EnterNotify:
15731      {
15732        /*
15733          Selectively install colormap.
15734        */
15735        if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15736          if (event.xcrossing.mode != NotifyUngrab)
15737            XInstallColormap(display,map_info->colormap);
15738        break;
15739      }
15740      case Expose:
15741      {
15742        if (IfMagickTrue(display_image->debug) )
15743          (void) LogMagickEvent(X11Event,GetMagickModule(),
15744            "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15745            event.xexpose.width,event.xexpose.height,event.xexpose.x,
15746            event.xexpose.y);
15747        /*
15748          Refresh windows that are now exposed.
15749        */
15750        if ((event.xexpose.window == windows->image.id) &&
15751            IfMagickTrue(windows->image.mapped) )
15752          {
15753            XRefreshWindow(display,&windows->image,&event);
15754            delay=display_image->delay/MagickMax(
15755              display_image->ticks_per_second,1L);
15756            timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15757            break;
15758          }
15759        if ((event.xexpose.window == windows->magnify.id) &&
15760            IfMagickTrue(windows->magnify.mapped))
15761          {
15762            XMakeMagnifyImage(display,windows,exception);
15763            break;
15764          }
15765        if (event.xexpose.window == windows->pan.id)
15766          {
15767            XDrawPanRectangle(display,windows);
15768            break;
15769          }
15770        if (event.xexpose.window == windows->icon.id)
15771          {
15772            XRefreshWindow(display,&windows->icon,&event);
15773            break;
15774          }
15775        break;
15776      }
15777      case KeyPress:
15778      {
15779        int
15780          length;
15781
15782        /*
15783          Respond to a user key press.
15784        */
15785        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15786          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15787        *(command+length)='\0';
15788        if (IfMagickTrue(display_image->debug) )
15789          (void) LogMagickEvent(X11Event,GetMagickModule(),
15790            "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15791            key_symbol,command);
15792        if (event.xkey.window == windows->image.id)
15793          {
15794            command_type=XImageWindowCommand(display,resource_info,windows,
15795              event.xkey.state,key_symbol,&display_image,exception);
15796            if (command_type != NullCommand)
15797              nexus=XMagickCommand(display,resource_info,windows,command_type,
15798                &display_image,exception);
15799          }
15800        if (event.xkey.window == windows->magnify.id)
15801          XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol,
15802            exception);
15803        if (event.xkey.window == windows->pan.id)
15804          {
15805            if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15806              (void) XWithdrawWindow(display,windows->pan.id,
15807                windows->pan.screen);
15808            else
15809              if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15810                XTextViewWidget(display,resource_info,windows,MagickFalse,
15811                  "Help Viewer - Image Pan",ImagePanHelp);
15812              else
15813                XTranslateImage(display,windows,*image,key_symbol);
15814          }
15815        delay=display_image->delay/MagickMax(
15816          display_image->ticks_per_second,1L);
15817        timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15818        break;
15819      }
15820      case KeyRelease:
15821      {
15822        /*
15823          Respond to a user key release.
15824        */
15825        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15826          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15827        if (IfMagickTrue(display_image->debug) )
15828          (void) LogMagickEvent(X11Event,GetMagickModule(),
15829            "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15830        break;
15831      }
15832      case LeaveNotify:
15833      {
15834        /*
15835          Selectively uninstall colormap.
15836        */
15837        if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15838          if (event.xcrossing.mode != NotifyUngrab)
15839            XUninstallColormap(display,map_info->colormap);
15840        break;
15841      }
15842      case MapNotify:
15843      {
15844        if (IfMagickTrue(display_image->debug) )
15845          (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15846            event.xmap.window);
15847        if (event.xmap.window == windows->backdrop.id)
15848          {
15849            (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15850              CurrentTime);
15851            windows->backdrop.mapped=MagickTrue;
15852            break;
15853          }
15854        if (event.xmap.window == windows->image.id)
15855          {
15856            if (windows->backdrop.id != (Window) NULL)
15857              (void) XInstallColormap(display,map_info->colormap);
15858            if (LocaleCompare(display_image->magick,"LOGO") == 0)
15859              {
15860                if (LocaleCompare(display_image->filename,"LOGO") == 0)
15861                  nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15862              }
15863            if (((int) windows->image.width < windows->image.ximage->width) ||
15864                ((int) windows->image.height < windows->image.ximage->height))
15865              (void) XMapRaised(display,windows->pan.id);
15866            windows->image.mapped=MagickTrue;
15867            break;
15868          }
15869        if (event.xmap.window == windows->magnify.id)
15870          {
15871            XMakeMagnifyImage(display,windows,exception);
15872            windows->magnify.mapped=MagickTrue;
15873            (void) XWithdrawWindow(display,windows->info.id,
15874              windows->info.screen);
15875            break;
15876          }
15877        if (event.xmap.window == windows->pan.id)
15878          {
15879            XMakePanImage(display,resource_info,windows,display_image,
15880              exception);
15881            windows->pan.mapped=MagickTrue;
15882            break;
15883          }
15884        if (event.xmap.window == windows->info.id)
15885          {
15886            windows->info.mapped=MagickTrue;
15887            break;
15888          }
15889        if (event.xmap.window == windows->icon.id)
15890          {
15891            MagickBooleanType
15892              taint;
15893
15894            /*
15895              Create an icon image.
15896            */
15897            taint=display_image->taint;
15898            XMakeStandardColormap(display,icon_visual,icon_resources,
15899              display_image,icon_map,icon_pixel,exception);
15900            (void) XMakeImage(display,icon_resources,&windows->icon,
15901              display_image,windows->icon.width,windows->icon.height,
15902              exception);
15903            display_image->taint=taint;
15904            (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15905              windows->icon.pixmap);
15906            (void) XClearWindow(display,windows->icon.id);
15907            (void) XWithdrawWindow(display,windows->info.id,
15908              windows->info.screen);
15909            windows->icon.mapped=MagickTrue;
15910            break;
15911          }
15912        if (event.xmap.window == windows->command.id)
15913          {
15914            windows->command.mapped=MagickTrue;
15915            break;
15916          }
15917        if (event.xmap.window == windows->popup.id)
15918          {
15919            windows->popup.mapped=MagickTrue;
15920            break;
15921          }
15922        if (event.xmap.window == windows->widget.id)
15923          {
15924            windows->widget.mapped=MagickTrue;
15925            break;
15926          }
15927        break;
15928      }
15929      case MappingNotify:
15930      {
15931        (void) XRefreshKeyboardMapping(&event.xmapping);
15932        break;
15933      }
15934      case NoExpose:
15935        break;
15936      case PropertyNotify:
15937      {
15938        Atom
15939          type;
15940
15941        int
15942          format,
15943          status;
15944
15945        unsigned char
15946          *data;
15947
15948        unsigned long
15949          after,
15950          length;
15951
15952        if (IfMagickTrue(display_image->debug) )
15953          (void) LogMagickEvent(X11Event,GetMagickModule(),
15954            "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15955            event.xproperty.atom,event.xproperty.state);
15956        if (event.xproperty.atom != windows->im_remote_command)
15957          break;
15958        /*
15959          Display image named by the remote command protocol.
15960        */
15961        status=XGetWindowProperty(display,event.xproperty.window,
15962          event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15963          AnyPropertyType,&type,&format,&length,&after,&data);
15964        if ((status != Success) || (length == 0))
15965          break;
15966        if (LocaleCompare((char *) data,"-quit") == 0)
15967          {
15968            XClientMessage(display,windows->image.id,windows->im_protocols,
15969              windows->im_exit,CurrentTime);
15970            (void) XFree((void *) data);
15971            break;
15972          }
15973        (void) CopyMagickString(resource_info->image_info->filename,
15974          (char *) data,MaxTextExtent);
15975        (void) XFree((void *) data);
15976        nexus=ReadImage(resource_info->image_info,exception);
15977        CatchException(exception);
15978        if (nexus != (Image *) NULL)
15979          *state|=NextImageState | ExitState;
15980        break;
15981      }
15982      case ReparentNotify:
15983      {
15984        if (IfMagickTrue(display_image->debug) )
15985          (void) LogMagickEvent(X11Event,GetMagickModule(),
15986            "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15987            event.xreparent.window);
15988        break;
15989      }
15990      case UnmapNotify:
15991      {
15992        if (IfMagickTrue(display_image->debug) )
15993          (void) LogMagickEvent(X11Event,GetMagickModule(),
15994            "Unmap Notify: 0x%lx",event.xunmap.window);
15995        if (event.xunmap.window == windows->backdrop.id)
15996          {
15997            windows->backdrop.mapped=MagickFalse;
15998            break;
15999          }
16000        if (event.xunmap.window == windows->image.id)
16001          {
16002            windows->image.mapped=MagickFalse;
16003            break;
16004          }
16005        if (event.xunmap.window == windows->magnify.id)
16006          {
16007            windows->magnify.mapped=MagickFalse;
16008            break;
16009          }
16010        if (event.xunmap.window == windows->pan.id)
16011          {
16012            windows->pan.mapped=MagickFalse;
16013            break;
16014          }
16015        if (event.xunmap.window == windows->info.id)
16016          {
16017            windows->info.mapped=MagickFalse;
16018            break;
16019          }
16020        if (event.xunmap.window == windows->icon.id)
16021          {
16022            if (map_info->colormap == icon_map->colormap)
16023              XConfigureImageColormap(display,resource_info,windows,
16024                display_image,exception);
16025            (void) XFreeStandardColormap(display,icon_visual,icon_map,
16026              icon_pixel);
16027            windows->icon.mapped=MagickFalse;
16028            break;
16029          }
16030        if (event.xunmap.window == windows->command.id)
16031          {
16032            windows->command.mapped=MagickFalse;
16033            break;
16034          }
16035        if (event.xunmap.window == windows->popup.id)
16036          {
16037            if (windows->backdrop.id != (Window) NULL)
16038              (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16039                CurrentTime);
16040            windows->popup.mapped=MagickFalse;
16041            break;
16042          }
16043        if (event.xunmap.window == windows->widget.id)
16044          {
16045            if (windows->backdrop.id != (Window) NULL)
16046              (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16047                CurrentTime);
16048            windows->widget.mapped=MagickFalse;
16049            break;
16050          }
16051        break;
16052      }
16053      default:
16054      {
16055        if (IfMagickTrue(display_image->debug) )
16056          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
16057            event.type);
16058        break;
16059      }
16060    }
16061  } while (!(*state & ExitState));
16062  if ((*state & ExitState) == 0)
16063    (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
16064      &display_image,exception);
16065  else
16066    if (IfMagickTrue(resource_info->confirm_edit) )
16067      {
16068        /*
16069          Query user if image has changed.
16070        */
16071        if (IfMagickFalse(resource_info->immutable) &&
16072            IfMagickTrue(display_image->taint))
16073          {
16074            int
16075              status;
16076
16077            status=XConfirmWidget(display,windows,"Your image changed.",
16078              "Do you want to save it");
16079            if (status == 0)
16080              *state&=(~ExitState);
16081            else
16082              if (status > 0)
16083                (void) XMagickCommand(display,resource_info,windows,SaveCommand,
16084                  &display_image,exception);
16085          }
16086      }
16087  if ((windows->visual_info->klass == GrayScale) ||
16088      (windows->visual_info->klass == PseudoColor) ||
16089      (windows->visual_info->klass == DirectColor))
16090    {
16091      /*
16092        Withdraw pan and Magnify window.
16093      */
16094      if (IfMagickTrue(windows->info.mapped) )
16095        (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16096      if (IfMagickTrue(windows->magnify.mapped) )
16097        (void) XWithdrawWindow(display,windows->magnify.id,
16098          windows->magnify.screen);
16099      if (IfMagickTrue(windows->command.mapped) )
16100        (void) XWithdrawWindow(display,windows->command.id,
16101          windows->command.screen);
16102    }
16103  if (IfMagickTrue(windows->pan.mapped) )
16104    (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16105  if (IfMagickFalse(resource_info->backdrop) )
16106    if (windows->backdrop.mapped)
16107      {
16108        (void) XWithdrawWindow(display,windows->backdrop.id,
16109          windows->backdrop.screen);
16110        (void) XDestroyWindow(display,windows->backdrop.id);
16111        windows->backdrop.id=(Window) NULL;
16112        (void) XWithdrawWindow(display,windows->image.id,
16113          windows->image.screen);
16114        (void) XDestroyWindow(display,windows->image.id);
16115        windows->image.id=(Window) NULL;
16116      }
16117  XSetCursorState(display,windows,MagickTrue);
16118  XCheckRefreshWindows(display,windows);
16119  if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16120    *state&=(~ExitState);
16121  if (*state & ExitState)
16122    {
16123      /*
16124        Free Standard Colormap.
16125      */
16126      (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16127      if (resource_info->map_type == (char *) NULL)
16128        (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16129      /*
16130        Free X resources.
16131      */
16132      if (resource_info->copy_image != (Image *) NULL)
16133        {
16134          resource_info->copy_image=DestroyImage(resource_info->copy_image);
16135          resource_info->copy_image=NewImageList();
16136        }
16137      DestroyXResources();
16138    }
16139  (void) XSync(display,MagickFalse);
16140  /*
16141    Restore our progress monitor and warning handlers.
16142  */
16143  (void) SetErrorHandler(warning_handler);
16144  (void) SetWarningHandler(warning_handler);
16145  /*
16146    Change to home directory.
16147  */
16148  directory=getcwd(working_directory,MaxTextExtent);
16149  (void) directory;
16150  {
16151    int
16152      status;
16153
16154    if (*resource_info->home_directory == '\0')
16155      (void) CopyMagickString(resource_info->home_directory,".",MaxTextExtent);
16156    status=chdir(resource_info->home_directory);
16157    if (status == -1)
16158      (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
16159        "UnableToOpenFile","%s",resource_info->home_directory);
16160  }
16161  *image=display_image;
16162  return(nexus);
16163}
16164#else
16165
16166/*
16167%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16168%                                                                             %
16169%                                                                             %
16170%                                                                             %
16171+   D i s p l a y I m a g e s                                                 %
16172%                                                                             %
16173%                                                                             %
16174%                                                                             %
16175%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16176%
16177%  DisplayImages() displays an image sequence to any X window screen.  It
16178%  returns a value other than 0 if successful.  Check the exception member
16179%  of image to determine the reason for any failure.
16180%
16181%  The format of the DisplayImages method is:
16182%
16183%      MagickBooleanType DisplayImages(const ImageInfo *image_info,
16184%        Image *images,ExceptionInfo *exception)
16185%
16186%  A description of each parameter follows:
16187%
16188%    o image_info: the image info.
16189%
16190%    o image: the image.
16191%
16192%    o exception: return any errors or warnings in this structure.
16193%
16194*/
16195MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16196  Image *image,ExceptionInfo *exception)
16197{
16198  assert(image_info != (const ImageInfo *) NULL);
16199  assert(image_info->signature == MagickSignature);
16200  assert(image != (Image *) NULL);
16201  assert(image->signature == MagickSignature);
16202  (void) image_info;
16203  if (IfMagickTrue(image->debug) )
16204    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16205  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16206    "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image->filename);
16207  return(MagickFalse);
16208}
16209
16210/*
16211%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16212%                                                                             %
16213%                                                                             %
16214%                                                                             %
16215+   R e m o t e D i s p l a y C o m m a n d                                   %
16216%                                                                             %
16217%                                                                             %
16218%                                                                             %
16219%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16220%
16221%  RemoteDisplayCommand() encourages a remote display program to display the
16222%  specified image filename.
16223%
16224%  The format of the RemoteDisplayCommand method is:
16225%
16226%      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16227%        const char *window,const char *filename,ExceptionInfo *exception)
16228%
16229%  A description of each parameter follows:
16230%
16231%    o image_info: the image info.
16232%
16233%    o window: Specifies the name or id of an X window.
16234%
16235%    o filename: the name of the image filename to display.
16236%
16237%    o exception: return any errors or warnings in this structure.
16238%
16239*/
16240MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16241  const char *window,const char *filename,ExceptionInfo *exception)
16242{
16243  assert(image_info != (const ImageInfo *) NULL);
16244  assert(image_info->signature == MagickSignature);
16245  assert(filename != (char *) NULL);
16246  (void) window;
16247  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16248  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16249    "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image_info->filename);
16250  return(MagickFalse);
16251}
16252#endif
16253