display.c revision d1dd6e4fefa0810b9893e6ac9418f79c97c1b39a
1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3%                                                                             %
4%                                                                             %
5%                                                                             %
6%               DDDD   IIIII  SSSSS  PPPP   L       AAA   Y   Y               %
7%               D   D    I    SS     P   P  L      A   A   Y Y                %
8%               D   D    I     SSS   PPPP   L      AAAAA    Y                 %
9%               D   D    I       SS  P      L      A   A    Y                 %
10%               DDDD   IIIII  SSSSS  P      LLLLL  A   A    Y                 %
11%                                                                             %
12%                                                                             %
13%        MagickCore Methods to Interactively Display and Edit an Image        %
14%                                                                             %
15%                             Software Design                                 %
16%                               John Cristy                                   %
17%                                July 1992                                    %
18%                                                                             %
19%                                                                             %
20%  Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization      %
21%  dedicated to making software imaging solutions freely available.           %
22%                                                                             %
23%  You may not use this file except in compliance with the License.  You may  %
24%  obtain a copy of the License at                                            %
25%                                                                             %
26%    http://www.imagemagick.org/script/license.php                            %
27%                                                                             %
28%  Unless required by applicable law or agreed to in writing, software        %
29%  distributed under the License is distributed on an "AS IS" BASIS,          %
30%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31%  See the License for the specific language governing permissions and        %
32%  limitations under the License.                                             %
33%                                                                             %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37*/
38
39/*
40  Include declarations.
41*/
42#include "MagickCore/studio.h"
43#include "MagickCore/artifact.h"
44#include "MagickCore/blob.h"
45#include "MagickCore/cache.h"
46#include "MagickCore/client.h"
47#include "MagickCore/color.h"
48#include "MagickCore/colorspace.h"
49#include "MagickCore/composite.h"
50#include "MagickCore/constitute.h"
51#include "MagickCore/decorate.h"
52#include "MagickCore/delegate.h"
53#include "MagickCore/display.h"
54#include "MagickCore/display-private.h"
55#include "MagickCore/draw.h"
56#include "MagickCore/effect.h"
57#include "MagickCore/enhance.h"
58#include "MagickCore/exception.h"
59#include "MagickCore/exception-private.h"
60#include "MagickCore/fx.h"
61#include "MagickCore/geometry.h"
62#include "MagickCore/image.h"
63#include "MagickCore/image-private.h"
64#include "MagickCore/list.h"
65#include "MagickCore/log.h"
66#include "MagickCore/magick.h"
67#include "MagickCore/memory_.h"
68#include "MagickCore/monitor.h"
69#include "MagickCore/monitor-private.h"
70#include "MagickCore/montage.h"
71#include "MagickCore/option.h"
72#include "MagickCore/paint.h"
73#include "MagickCore/pixel.h"
74#include "MagickCore/pixel-accessor.h"
75#include "MagickCore/PreRvIcccm.h"
76#include "MagickCore/property.h"
77#include "MagickCore/quantum.h"
78#include "MagickCore/quantum-private.h"
79#include "MagickCore/resize.h"
80#include "MagickCore/resource_.h"
81#include "MagickCore/shear.h"
82#include "MagickCore/segment.h"
83#include "MagickCore/string_.h"
84#include "MagickCore/string-private.h"
85#include "MagickCore/transform.h"
86#include "MagickCore/threshold.h"
87#include "MagickCore/utility.h"
88#include "MagickCore/utility-private.h"
89#include "MagickCore/version.h"
90#include "MagickCore/widget.h"
91#include "MagickCore/widget-private.h"
92#include "MagickCore/xwindow.h"
93#include "MagickCore/xwindow-private.h"
94
95#if defined(MAGICKCORE_X11_DELEGATE)
96/*
97  Define declarations.
98*/
99#define MaxColors  MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
100
101/*
102  Constant declarations.
103*/
104static const unsigned char
105  HighlightBitmap[8] =
106  {
107    0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
108  },
109  OpaqueBitmap[8] =
110  {
111    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
112  },
113  ShadowBitmap[8] =
114  {
115    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
116  };
117
118static const char
119  *PageSizes[] =
120  {
121    "Letter",
122    "Tabloid",
123    "Ledger",
124    "Legal",
125    "Statement",
126    "Executive",
127    "A3",
128    "A4",
129    "A5",
130    "B4",
131    "B5",
132    "Folio",
133    "Quarto",
134    "10x14",
135    (char *) NULL
136  };
137
138/*
139  Help widget declarations.
140*/
141static const char
142  *ImageAnnotateHelp[] =
143  {
144    "In annotate mode, the Command widget has these options:",
145    "",
146    "    Font Name",
147    "      fixed",
148    "      variable",
149    "      5x8",
150    "      6x10",
151    "      7x13bold",
152    "      8x13bold",
153    "      9x15bold",
154    "      10x20",
155    "      12x24",
156    "      Browser...",
157    "    Font Color",
158    "      black",
159    "      blue",
160    "      cyan",
161    "      green",
162    "      gray",
163    "      red",
164    "      magenta",
165    "      yellow",
166    "      white",
167    "      transparent",
168    "      Browser...",
169    "    Font Color",
170    "      black",
171    "      blue",
172    "      cyan",
173    "      green",
174    "      gray",
175    "      red",
176    "      magenta",
177    "      yellow",
178    "      white",
179    "      transparent",
180    "      Browser...",
181    "    Rotate Text",
182    "      -90",
183    "      -45",
184    "      -30",
185    "      0",
186    "      30",
187    "      45",
188    "      90",
189    "      180",
190    "      Dialog...",
191    "    Help",
192    "    Dismiss",
193    "",
194    "Choose a font name from the Font Name sub-menu.  Additional",
195    "font names can be specified with the font browser.  You can",
196    "change the menu names by setting the X resources font1",
197    "through font9.",
198    "",
199    "Choose a font color from the Font Color sub-menu.",
200    "Additional font colors can be specified with the color",
201    "browser.  You can change the menu colors by setting the X",
202    "resources pen1 through pen9.",
203    "",
204    "If you select the color browser and press Grab, you can",
205    "choose the font color by moving the pointer to the desired",
206    "color on the screen and press any button.",
207    "",
208    "If you choose to rotate the text, choose Rotate Text from the",
209    "menu and select an angle.  Typically you will only want to",
210    "rotate one line of text at a time.  Depending on the angle you",
211    "choose, subsequent lines may end up overwriting each other.",
212    "",
213    "Choosing a font and its color is optional.  The default font",
214    "is fixed and the default color is black.  However, you must",
215    "choose a location to begin entering text and press button 1.",
216    "An underscore character will appear at the location of the",
217    "pointer.  The cursor changes to a pencil to indicate you are",
218    "in text mode.  To exit immediately, press Dismiss.",
219    "",
220    "In text mode, any key presses will display the character at",
221    "the location of the underscore and advance the underscore",
222    "cursor.  Enter your text and once completed press Apply to",
223    "finish your image annotation.  To correct errors press BACK",
224    "SPACE.  To delete an entire line of text, press DELETE.  Any",
225    "text that exceeds the boundaries of the image window is",
226    "automagically continued onto the next line.",
227    "",
228    "The actual color you request for the font is saved in the",
229    "image.  However, the color that appears in your image window",
230    "may be different.  For example, on a monochrome screen the",
231    "text will appear black or white even if you choose the color",
232    "red as the font color.  However, the image saved to a file",
233    "with -write is written with red lettering.  To assure the",
234    "correct color text in the final image, any PseudoClass image",
235    "is promoted to DirectClass (see miff(5)).  To force a",
236    "PseudoClass image to remain PseudoClass, use -colors.",
237    (char *) NULL,
238  },
239  *ImageChopHelp[] =
240  {
241    "In chop mode, the Command widget has these options:",
242    "",
243    "    Direction",
244    "      horizontal",
245    "      vertical",
246    "    Help",
247    "    Dismiss",
248    "",
249    "If the you choose the horizontal direction (this the",
250    "default), the area of the image between the two horizontal",
251    "endpoints of the chop line is removed.  Otherwise, the area",
252    "of the image between the two vertical endpoints of the chop",
253    "line is removed.",
254    "",
255    "Select a location within the image window to begin your chop,",
256    "press and hold any button.  Next, move the pointer to",
257    "another location in the image.  As you move a line will",
258    "connect the initial location and the pointer.  When you",
259    "release the button, the area within the image to chop is",
260    "determined by which direction you choose from the Command",
261    "widget.",
262    "",
263    "To cancel the image chopping, move the pointer back to the",
264    "starting point of the line and release the button.",
265    (char *) NULL,
266  },
267  *ImageColorEditHelp[] =
268  {
269    "In color edit mode, the Command widget has these options:",
270    "",
271    "    Method",
272    "      point",
273    "      replace",
274    "      floodfill",
275    "      filltoborder",
276    "      reset",
277    "    Pixel Color",
278    "      black",
279    "      blue",
280    "      cyan",
281    "      green",
282    "      gray",
283    "      red",
284    "      magenta",
285    "      yellow",
286    "      white",
287    "      Browser...",
288    "    Border Color",
289    "      black",
290    "      blue",
291    "      cyan",
292    "      green",
293    "      gray",
294    "      red",
295    "      magenta",
296    "      yellow",
297    "      white",
298    "      Browser...",
299    "    Fuzz",
300    "      0%",
301    "      2%",
302    "      5%",
303    "      10%",
304    "      15%",
305    "      Dialog...",
306    "    Undo",
307    "    Help",
308    "    Dismiss",
309    "",
310    "Choose a color editing method from the Method sub-menu",
311    "of the Command widget.  The point method recolors any pixel",
312    "selected with the pointer until the button is released.  The",
313    "replace method recolors any pixel that matches the color of",
314    "the pixel you select with a button press.  Floodfill recolors",
315    "any pixel that matches the color of the pixel you select with",
316    "a button press and is a neighbor.  Whereas filltoborder recolors",
317    "any neighbor pixel that is not the border color.  Finally reset",
318    "changes the entire image to the designated color.",
319    "",
320    "Next, choose a pixel color from the Pixel Color sub-menu.",
321    "Additional pixel colors can be specified with the color",
322    "browser.  You can change the menu colors by setting the X",
323    "resources pen1 through pen9.",
324    "",
325    "Now press button 1 to select a pixel within the image window",
326    "to change its color.  Additional pixels may be recolored as",
327    "prescribed by the method you choose.",
328    "",
329    "If the Magnify widget is mapped, it can be helpful in positioning",
330    "your pointer within the image (refer to button 2).",
331    "",
332    "The actual color you request for the pixels is saved in the",
333    "image.  However, the color that appears in your image window",
334    "may be different.  For example, on a monochrome screen the",
335    "pixel will appear black or white even if you choose the",
336    "color red as the pixel color.  However, the image saved to a",
337    "file with -write is written with red pixels.  To assure the",
338    "correct color text in the final image, any PseudoClass image",
339    "is promoted to DirectClass (see miff(5)).  To force a",
340    "PseudoClass image to remain PseudoClass, use -colors.",
341    (char *) NULL,
342  },
343  *ImageCompositeHelp[] =
344  {
345    "First a widget window is displayed requesting you to enter an",
346    "image name. Press Composite, Grab or type a file name.",
347    "Press Cancel if you choose not to create a composite image.",
348    "When you choose Grab, move the pointer to the desired window",
349    "and press any button.",
350    "",
351    "If the Composite image does not have any matte information,",
352    "you are informed and the file browser is displayed again.",
353    "Enter the name of a mask image.  The image is typically",
354    "grayscale and the same size as the composite image.  If the",
355    "image is not grayscale, it is converted to grayscale and the",
356    "resulting intensities are used as matte information.",
357    "",
358    "A small window appears showing the location of the cursor in",
359    "the image window. You are now in composite mode.  To exit",
360    "immediately, press Dismiss.  In composite mode, the Command",
361    "widget has these options:",
362    "",
363    "    Operators",
364    "      Over",
365    "      In",
366    "      Out",
367    "      Atop",
368    "      Xor",
369    "      Plus",
370    "      Minus",
371    "      Add",
372    "      Subtract",
373    "      Difference",
374    "      Multiply",
375    "      Bumpmap",
376    "      Copy",
377    "      CopyRed",
378    "      CopyGreen",
379    "      CopyBlue",
380    "      CopyOpacity",
381    "      Clear",
382    "    Dissolve",
383    "    Displace",
384    "    Help",
385    "    Dismiss",
386    "",
387    "Choose a composite operation from the Operators sub-menu of",
388    "the Command widget.  How each operator behaves is described",
389    "below.  Image window is the image currently displayed on",
390    "your X server and image is the image obtained with the File",
391    "Browser widget.",
392    "",
393    "Over     The result is the union of the two image shapes,",
394    "         with image obscuring image window in the region of",
395    "         overlap.",
396    "",
397    "In       The result is simply image cut by the shape of",
398    "         image window.  None of the image data of image",
399    "         window is in the result.",
400    "",
401    "Out      The resulting image is image with the shape of",
402    "         image window cut out.",
403    "",
404    "Atop     The result is the same shape as image image window,",
405    "         with image obscuring image window where the image",
406    "         shapes overlap.  Note this differs from over",
407    "         because the portion of image outside image window's",
408    "         shape does not appear in the result.",
409    "",
410    "Xor      The result is the image data from both image and",
411    "         image window that is outside the overlap region.",
412    "         The overlap region is blank.",
413    "",
414    "Plus     The result is just the sum of the image data.",
415    "         Output values are cropped to QuantumRange (no overflow).",
416    "",
417    "Minus    The result of image - image window, with underflow",
418    "         cropped to zero.",
419    "",
420    "Add      The result of image + image window, with overflow",
421    "         wrapping around (mod 256).",
422    "",
423    "Subtract The result of image - image window, with underflow",
424    "         wrapping around (mod 256).  The add and subtract",
425    "         operators can be used to perform reversible",
426    "         transformations.",
427    "",
428    "Difference",
429    "         The result of abs(image - image window).  This",
430    "         useful for comparing two very similar images.",
431    "",
432    "Multiply",
433    "         The result of image * image window.  This",
434    "         useful for the creation of drop-shadows.",
435    "",
436    "Bumpmap  The result of surface normals from image * image",
437    "         window.",
438    "",
439    "Copy     The resulting image is image window replaced with",
440    "         image.  Here the matte information is ignored.",
441    "",
442    "CopyRed  The red layer of the image window is replace with",
443    "         the red layer of the image.  The other layers are",
444    "         untouched.",
445    "",
446    "CopyGreen",
447    "         The green layer of the image window is replace with",
448    "         the green layer of the image.  The other layers are",
449    "         untouched.",
450    "",
451    "CopyBlue The blue layer of the image window is replace with",
452    "         the blue layer of the image.  The other layers are",
453    "         untouched.",
454    "",
455    "CopyOpacity",
456    "         The matte layer of the image window is replace with",
457    "         the matte layer of the image.  The other layers are",
458    "         untouched.",
459    "",
460    "The image compositor requires a matte, or alpha channel in",
461    "the image for some operations.  This extra channel usually",
462    "defines a mask which represents a sort of a cookie-cutter",
463    "for the image.  This the case when matte is opaque (full",
464    "coverage) for pixels inside the shape, zero outside, and",
465    "between 0 and QuantumRange on the boundary.  If image does not",
466    "have a matte channel, it is initialized with 0 for any pixel",
467    "matching in color to pixel location (0,0), otherwise QuantumRange.",
468    "",
469    "If you choose Dissolve, the composite operator becomes Over.  The",
470    "image matte channel percent transparency is initialized to factor.",
471    "The image window is initialized to (100-factor). Where factor is the",
472    "value you specify in the Dialog widget.",
473    "",
474    "Displace shifts the image pixels as defined by a displacement",
475    "map.  With this option, image is used as a displacement map.",
476    "Black, within the displacement map, is a maximum positive",
477    "displacement.  White is a maximum negative displacement and",
478    "middle gray is neutral.  The displacement is scaled to determine",
479    "the pixel shift.  By default, the displacement applies in both the",
480    "horizontal and vertical directions.  However, if you specify a mask,",
481    "image is the horizontal X displacement and mask the vertical Y",
482    "displacement.",
483    "",
484    "Note that matte information for image window is not retained",
485    "for colormapped X server visuals (e.g. StaticColor,",
486    "StaticColor, GrayScale, PseudoColor).  Correct compositing",
487    "behavior may require a TrueColor or DirectColor visual or a",
488    "Standard Colormap.",
489    "",
490    "Choosing a composite operator is optional.  The default",
491    "operator is replace.  However, you must choose a location to",
492    "composite your image and press button 1.  Press and hold the",
493    "button before releasing and an outline of the image will",
494    "appear to help you identify your location.",
495    "",
496    "The actual colors of the composite image is saved.  However,",
497    "the color that appears in image window may be different.",
498    "For example, on a monochrome screen image window will appear",
499    "black or white even though your composited image may have",
500    "many colors.  If the image is saved to a file it is written",
501    "with the correct colors.  To assure the correct colors are",
502    "saved in the final image, any PseudoClass image is promoted",
503    "to DirectClass (see miff(5)).  To force a PseudoClass image",
504    "to remain PseudoClass, use -colors.",
505    (char *) NULL,
506  },
507  *ImageCutHelp[] =
508  {
509    "In cut mode, the Command widget has these options:",
510    "",
511    "    Help",
512    "    Dismiss",
513    "",
514    "To define a cut region, press button 1 and drag.  The",
515    "cut region is defined by a highlighted rectangle that",
516    "expands or contracts as it follows the pointer.  Once you",
517    "are satisfied with the cut region, release the button.",
518    "You are now in rectify mode.  In rectify mode, the Command",
519    "widget has these options:",
520    "",
521    "    Cut",
522    "    Help",
523    "    Dismiss",
524    "",
525    "You can make adjustments by moving the pointer to one of the",
526    "cut rectangle corners, pressing a button, and dragging.",
527    "Finally, press Cut to commit your copy region.  To",
528    "exit without cutting the image, press Dismiss.",
529    (char *) NULL,
530  },
531  *ImageCopyHelp[] =
532  {
533    "In copy mode, the Command widget has these options:",
534    "",
535    "    Help",
536    "    Dismiss",
537    "",
538    "To define a copy region, press button 1 and drag.  The",
539    "copy region is defined by a highlighted rectangle that",
540    "expands or contracts as it follows the pointer.  Once you",
541    "are satisfied with the copy region, release the button.",
542    "You are now in rectify mode.  In rectify mode, the Command",
543    "widget has these options:",
544    "",
545    "    Copy",
546    "    Help",
547    "    Dismiss",
548    "",
549    "You can make adjustments by moving the pointer to one of the",
550    "copy rectangle corners, pressing a button, and dragging.",
551    "Finally, press Copy to commit your copy region.  To",
552    "exit without copying the image, press Dismiss.",
553    (char *) NULL,
554  },
555  *ImageCropHelp[] =
556  {
557    "In crop mode, the Command widget has these options:",
558    "",
559    "    Help",
560    "    Dismiss",
561    "",
562    "To define a cropping region, press button 1 and drag.  The",
563    "cropping region is defined by a highlighted rectangle that",
564    "expands or contracts as it follows the pointer.  Once you",
565    "are satisfied with the cropping region, release the button.",
566    "You are now in rectify mode.  In rectify mode, the Command",
567    "widget has these options:",
568    "",
569    "    Crop",
570    "    Help",
571    "    Dismiss",
572    "",
573    "You can make adjustments by moving the pointer to one of the",
574    "cropping rectangle corners, pressing a button, and dragging.",
575    "Finally, press Crop to commit your cropping region.  To",
576    "exit without cropping the image, press Dismiss.",
577    (char *) NULL,
578  },
579  *ImageDrawHelp[] =
580  {
581    "The cursor changes to a crosshair to indicate you are in",
582    "draw mode.  To exit immediately, press Dismiss.  In draw mode,",
583    "the Command widget has these options:",
584    "",
585    "    Element",
586    "      point",
587    "      line",
588    "      rectangle",
589    "      fill rectangle",
590    "      circle",
591    "      fill circle",
592    "      ellipse",
593    "      fill ellipse",
594    "      polygon",
595    "      fill polygon",
596    "    Color",
597    "      black",
598    "      blue",
599    "      cyan",
600    "      green",
601    "      gray",
602    "      red",
603    "      magenta",
604    "      yellow",
605    "      white",
606    "      transparent",
607    "      Browser...",
608    "    Stipple",
609    "      Brick",
610    "      Diagonal",
611    "      Scales",
612    "      Vertical",
613    "      Wavy",
614    "      Translucent",
615    "      Opaque",
616    "      Open...",
617    "    Width",
618    "      1",
619    "      2",
620    "      4",
621    "      8",
622    "      16",
623    "      Dialog...",
624    "    Undo",
625    "    Help",
626    "    Dismiss",
627    "",
628    "Choose a drawing primitive from the Element sub-menu.",
629    "",
630    "Choose a color from the Color sub-menu.  Additional",
631    "colors can be specified with the color browser.",
632    "",
633    "If you choose the color browser and press Grab, you can",
634    "select the color by moving the pointer to the desired",
635    "color on the screen and press any button.  The transparent",
636    "color updates the image matte channel and is useful for",
637    "image compositing.",
638    "",
639    "Choose a stipple, if appropriate, from the Stipple sub-menu.",
640    "Additional stipples can be specified with the file browser.",
641    "Stipples obtained from the file browser must be on disk in the",
642    "X11 bitmap format.",
643    "",
644    "Choose a width, if appropriate, from the Width sub-menu.  To",
645    "choose a specific width select the Dialog widget.",
646    "",
647    "Choose a point in the Image window and press button 1 and",
648    "hold.  Next, move the pointer to another location in the",
649    "image.  As you move, a line connects the initial location and",
650    "the pointer.  When you release the button, the image is",
651    "updated with the primitive you just drew.  For polygons, the",
652    "image is updated when you press and release the button without",
653    "moving the pointer.",
654    "",
655    "To cancel image drawing, move the pointer back to the",
656    "starting point of the line and release the button.",
657    (char *) NULL,
658  },
659  *DisplayHelp[] =
660  {
661    "BUTTONS",
662    "  The effects of each button press is described below.  Three",
663    "  buttons are required.  If you have a two button mouse,",
664    "  button 1 and 3 are returned.  Press ALT and button 3 to",
665    "  simulate button 2.",
666    "",
667    "  1    Press this button to map or unmap the Command widget.",
668    "",
669    "  2    Press and drag to define a region of the image to",
670    "       magnify.",
671    "",
672    "  3    Press and drag to choose from a select set of commands.",
673    "       This button behaves differently if the image being",
674    "       displayed is a visual image directory.  Here, choose a",
675    "       particular tile of the directory and press this button and",
676    "       drag to select a command from a pop-up menu.  Choose from",
677    "       these menu items:",
678    "",
679    "           Open",
680    "           Next",
681    "           Former",
682    "           Delete",
683    "           Update",
684    "",
685    "       If you choose Open, the image represented by the tile is",
686    "       displayed.  To return to the visual image directory, choose",
687    "       Next from the Command widget.  Next and Former moves to the",
688    "       next or former image respectively.  Choose Delete to delete",
689    "       a particular image tile.  Finally, choose Update to",
690    "       synchronize all the image tiles with their respective",
691    "       images.",
692    "",
693    "COMMAND WIDGET",
694    "  The Command widget lists a number of sub-menus and commands.",
695    "  They are",
696    "",
697    "      File",
698    "        Open...",
699    "        Next",
700    "        Former",
701    "        Select...",
702    "        Save...",
703    "        Print...",
704    "        Delete...",
705    "        New...",
706    "        Visual Directory...",
707    "        Quit",
708    "      Edit",
709    "        Undo",
710    "        Redo",
711    "        Cut",
712    "        Copy",
713    "        Paste",
714    "      View",
715    "        Half Size",
716    "        Original Size",
717    "        Double Size",
718    "        Resize...",
719    "        Apply",
720    "        Refresh",
721    "        Restore",
722    "      Transform",
723    "        Crop",
724    "        Chop",
725    "        Flop",
726    "        Flip",
727    "        Rotate Right",
728    "        Rotate Left",
729    "        Rotate...",
730    "        Shear...",
731    "        Roll...",
732    "        Trim Edges",
733    "      Enhance",
734    "        Brightness...",
735    "        Saturation...",
736    "        Hue...",
737    "        Gamma...",
738    "        Sharpen...",
739    "        Dull",
740    "        Contrast Stretch...",
741    "        Sigmoidal Contrast...",
742    "        Normalize",
743    "        Equalize",
744    "        Negate",
745    "        Grayscale",
746    "        Map...",
747    "        Quantize...",
748    "      Effects",
749    "        Despeckle",
750    "        Emboss",
751    "        Reduce Noise",
752    "        Add Noise",
753    "        Sharpen...",
754    "        Blur...",
755    "        Threshold...",
756    "        Edge Detect...",
757    "        Spread...",
758    "        Shade...",
759    "        Painting...",
760    "        Segment...",
761    "      F/X",
762    "        Solarize...",
763    "        Sepia Tone...",
764    "        Swirl...",
765    "        Implode...",
766    "        Vignette...",
767    "        Wave...",
768    "        Oil Painting...",
769    "        Charcoal Drawing...",
770    "      Image Edit",
771    "        Annotate...",
772    "        Draw...",
773    "        Color...",
774    "        Matte...",
775    "        Composite...",
776    "        Add Border...",
777    "        Add Frame...",
778    "        Comment...",
779    "        Launch...",
780    "        Region of Interest...",
781    "      Miscellany",
782    "        Image Info",
783    "        Zoom Image",
784    "        Show Preview...",
785    "        Show Histogram",
786    "        Show Matte",
787    "        Background...",
788    "        Slide Show",
789    "        Preferences...",
790    "      Help",
791    "        Overview",
792    "        Browse Documentation",
793    "        About Display",
794    "",
795    "  Menu items with a indented triangle have a sub-menu.  They",
796    "  are represented above as the indented items.  To access a",
797    "  sub-menu item, move the pointer to the appropriate menu and",
798    "  press a button and drag.  When you find the desired sub-menu",
799    "  item, release the button and the command is executed.  Move",
800    "  the pointer away from the sub-menu if you decide not to",
801    "  execute a particular command.",
802    "",
803    "KEYBOARD ACCELERATORS",
804    "  Accelerators are one or two key presses that effect a",
805    "  particular command.  The keyboard accelerators that",
806    "  display(1) understands is:",
807    "",
808    "  Ctl+O     Press to open an image from a file.",
809    "",
810    "  space     Press to display the next image.",
811    "",
812    "            If the image is a multi-paged document such as a Postscript",
813    "            document, you can skip ahead several pages by preceding",
814    "            this command with a number.  For example to display the",
815    "            third page beyond the current page, press 3<space>.",
816    "",
817    "  backspace Press to display the former image.",
818    "",
819    "            If the image is a multi-paged document such as a Postscript",
820    "            document, you can skip behind several pages by preceding",
821    "            this command with a number.  For example to display the",
822    "            third page preceding the current page, press 3<backspace>.",
823    "",
824    "  Ctl+S     Press to write the image to a file.",
825    "",
826    "  Ctl+P     Press to print the image to a Postscript printer.",
827    "",
828    "  Ctl+D     Press to delete an image file.",
829    "",
830    "  Ctl+N     Press to create a blank canvas.",
831    "",
832    "  Ctl+Q     Press to discard all images and exit program.",
833    "",
834    "  Ctl+Z     Press to undo last image transformation.",
835    "",
836    "  Ctl+R     Press to redo last image transformation.",
837    "",
838    "  Ctl+X     Press to cut a region of the image.",
839    "",
840    "  Ctl+C     Press to copy a region of the image.",
841    "",
842    "  Ctl+V     Press to paste a region to the image.",
843    "",
844    "  <         Press to half the image size.",
845    "",
846    "  -         Press to return to the original image size.",
847    "",
848    "  >         Press to double the image size.",
849    "",
850    "  %         Press to resize the image to a width and height you",
851    "            specify.",
852    "",
853    "Cmd-A       Press to make any image transformations permanent."
854    "",
855    "            By default, any image size transformations are applied",
856    "            to the original image to create the image displayed on",
857    "            the X server.  However, the transformations are not",
858    "            permanent (i.e. the original image does not change",
859    "            size only the X image does).  For example, if you",
860    "            press > the X image will appear to double in size,",
861    "            but the original image will in fact remain the same size.",
862    "            To force the original image to double in size, press >",
863    "            followed by Cmd-A.",
864    "",
865    "  @         Press to refresh the image window.",
866    "",
867    "  C         Press to cut out a rectangular region of the image.",
868    "",
869    "  [         Press to chop the image.",
870    "",
871    "  H         Press to flop image in the horizontal direction.",
872    "",
873    "  V         Press to flip image in the vertical direction.",
874    "",
875    "  /         Press to rotate the image 90 degrees clockwise.",
876    "",
877    " \\         Press to rotate the image 90 degrees counter-clockwise.",
878    "",
879    "  *         Press to rotate the image the number of degrees you",
880    "            specify.",
881    "",
882    "  S         Press to shear the image the number of degrees you",
883    "            specify.",
884    "",
885    "  R         Press to roll the image.",
886    "",
887    "  T         Press to trim the image edges.",
888    "",
889    "  Shft-H    Press to vary the image hue.",
890    "",
891    "  Shft-S    Press to vary the color saturation.",
892    "",
893    "  Shft-L    Press to vary the color brightness.",
894    "",
895    "  Shft-G    Press to gamma correct the image.",
896    "",
897    "  Shft-C    Press to sharpen the image contrast.",
898    "",
899    "  Shft-Z    Press to dull the image contrast.",
900    "",
901    "  =         Press to perform histogram equalization on the image.",
902    "",
903    "  Shft-N    Press to perform histogram normalization on the image.",
904    "",
905    "  Shft-~    Press to negate the colors of the image.",
906    "",
907    "  .         Press to convert the image colors to gray.",
908    "",
909    "  Shft-#    Press to set the maximum number of unique colors in the",
910    "            image.",
911    "",
912    "  F2        Press to reduce the speckles in an image.",
913    "",
914    "  F3        Press to eliminate peak noise from an image.",
915    "",
916    "  F4        Press to add noise to an image.",
917    "",
918    "  F5        Press to sharpen an image.",
919    "",
920    "  F6        Press to delete an image file.",
921    "",
922    "  F7        Press to threshold the image.",
923    "",
924    "  F8        Press to detect edges within an image.",
925    "",
926    "  F9        Press to emboss an image.",
927    "",
928    "  F10       Press to displace pixels by a random amount.",
929    "",
930    "  F11       Press to negate all pixels above the threshold level.",
931    "",
932    "  F12       Press to shade the image using a distant light source.",
933    "",
934    "  F13       Press to lighten or darken image edges to create a 3-D effect.",
935    "",
936    "  F14       Press to segment the image by color.",
937    "",
938    "  Meta-S    Press to swirl image pixels about the center.",
939    "",
940    "  Meta-I    Press to implode image pixels about the center.",
941    "",
942    "  Meta-W    Press to alter an image along a sine wave.",
943    "",
944    "  Meta-P    Press to simulate an oil painting.",
945    "",
946    "  Meta-C    Press to simulate a charcoal drawing.",
947    "",
948    "  Alt-A     Press to annotate the image with text.",
949    "",
950    "  Alt-D     Press to draw on an image.",
951    "",
952    "  Alt-P     Press to edit an image pixel color.",
953    "",
954    "  Alt-M     Press to edit the image matte information.",
955    "",
956    "  Alt-V     Press to composite the image with another.",
957    "",
958    "  Alt-B     Press to add a border to the image.",
959    "",
960    "  Alt-F     Press to add an ornamental border to the image.",
961    "",
962    "  Alt-Shft-!",
963    "            Press to add an image comment.",
964    "",
965    "  Ctl-A     Press to apply image processing techniques to a region",
966    "            of interest.",
967    "",
968    "  Shft-?    Press to display information about the image.",
969    "",
970    "  Shft-+    Press to map the zoom image window.",
971    "",
972    "  Shft-P    Press to preview an image enhancement, effect, or f/x.",
973    "",
974    "  F1        Press to display helpful information about display(1).",
975    "",
976    "  Find      Press to browse documentation about ImageMagick.",
977    "",
978    "  1-9       Press to change the level of magnification.",
979    "",
980    "  Use the arrow keys to move the image one pixel up, down,",
981    "  left, or right within the magnify window.  Be sure to first",
982    "  map the magnify window by pressing button 2.",
983    "",
984    "  Press ALT and one of the arrow keys to trim off one pixel",
985    "  from any side of the image.",
986    (char *) NULL,
987  },
988  *ImageMatteEditHelp[] =
989  {
990    "Matte information within an image is useful for some",
991    "operations such as image compositing (See IMAGE",
992    "COMPOSITING).  This extra channel usually defines a mask",
993    "which represents a sort of a cookie-cutter for the image.",
994    "This the case when matte is opaque (full coverage) for",
995    "pixels inside the shape, zero outside, and between 0 and",
996    "QuantumRange on the boundary.",
997    "",
998    "A small window appears showing the location of the cursor in",
999    "the image window. You are now in matte edit mode.  To exit",
1000    "immediately, press Dismiss.  In matte edit mode, the Command",
1001    "widget has these options:",
1002    "",
1003    "    Method",
1004    "      point",
1005    "      replace",
1006    "      floodfill",
1007    "      filltoborder",
1008    "      reset",
1009    "    Border Color",
1010    "      black",
1011    "      blue",
1012    "      cyan",
1013    "      green",
1014    "      gray",
1015    "      red",
1016    "      magenta",
1017    "      yellow",
1018    "      white",
1019    "      Browser...",
1020    "    Fuzz",
1021    "      0%",
1022    "      2%",
1023    "      5%",
1024    "      10%",
1025    "      15%",
1026    "      Dialog...",
1027    "    Matte",
1028    "      Opaque",
1029    "      Transparent",
1030    "      Dialog...",
1031    "    Undo",
1032    "    Help",
1033    "    Dismiss",
1034    "",
1035    "Choose a matte editing method from the Method sub-menu of",
1036    "the Command widget.  The point method changes the matte value",
1037    "of any pixel selected with the pointer until the button is",
1038    "is released.  The replace method changes the matte value of",
1039    "any pixel that matches the color of the pixel you select with",
1040    "a button press.  Floodfill changes the matte value of any pixel",
1041    "that matches the color of the pixel you select with a button",
1042    "press and is a neighbor.  Whereas filltoborder changes the matte",
1043    "value any neighbor pixel that is not the border color.  Finally",
1044    "reset changes the entire image to the designated matte value.",
1045    "",
1046    "Choose Matte Value and pick Opaque or Transarent.  For other values",
1047    "select the Dialog entry.  Here a dialog appears requesting a matte",
1048    "value.  The value you select is assigned as the opacity value of the",
1049    "selected pixel or pixels.",
1050    "",
1051    "Now, press any button to select a pixel within the image",
1052    "window to change its matte value.",
1053    "",
1054    "If the Magnify widget is mapped, it can be helpful in positioning",
1055    "your pointer within the image (refer to button 2).",
1056    "",
1057    "Matte information is only valid in a DirectClass image.",
1058    "Therefore, any PseudoClass image is promoted to DirectClass",
1059    "(see miff(5)).  Note that matte information for PseudoClass",
1060    "is not retained for colormapped X server visuals (e.g.",
1061    "StaticColor, StaticColor, GrayScale, PseudoColor) unless you",
1062    "immediately save your image to a file (refer to Write).",
1063    "Correct matte editing behavior may require a TrueColor or",
1064    "DirectColor visual or a Standard Colormap.",
1065    (char *) NULL,
1066  },
1067  *ImagePanHelp[] =
1068  {
1069    "When an image exceeds the width or height of the X server",
1070    "screen, display maps a small panning icon.  The rectangle",
1071    "within the panning icon shows the area that is currently",
1072    "displayed in the image window.  To pan about the image,",
1073    "press any button and drag the pointer within the panning",
1074    "icon.  The pan rectangle moves with the pointer and the",
1075    "image window is updated to reflect the location of the",
1076    "rectangle within the panning icon.  When you have selected",
1077    "the area of the image you wish to view, release the button.",
1078    "",
1079    "Use the arrow keys to pan the image one pixel up, down,",
1080    "left, or right within the image window.",
1081    "",
1082    "The panning icon is withdrawn if the image becomes smaller",
1083    "than the dimensions of the X server screen.",
1084    (char *) NULL,
1085  },
1086  *ImagePasteHelp[] =
1087  {
1088    "A small window appears showing the location of the cursor in",
1089    "the image window. You are now in paste mode.  To exit",
1090    "immediately, press Dismiss.  In paste mode, the Command",
1091    "widget has these options:",
1092    "",
1093    "    Operators",
1094    "      over",
1095    "      in",
1096    "      out",
1097    "      atop",
1098    "      xor",
1099    "      plus",
1100    "      minus",
1101    "      add",
1102    "      subtract",
1103    "      difference",
1104    "      replace",
1105    "    Help",
1106    "    Dismiss",
1107    "",
1108    "Choose a composite operation from the Operators sub-menu of",
1109    "the Command widget.  How each operator behaves is described",
1110    "below.  Image window is the image currently displayed on",
1111    "your X server and image is the image obtained with the File",
1112    "Browser widget.",
1113    "",
1114    "Over     The result is the union of the two image shapes,",
1115    "         with image obscuring image window in the region of",
1116    "         overlap.",
1117    "",
1118    "In       The result is simply image cut by the shape of",
1119    "         image window.  None of the image data of image",
1120    "         window is in the result.",
1121    "",
1122    "Out      The resulting image is image with the shape of",
1123    "         image window cut out.",
1124    "",
1125    "Atop     The result is the same shape as image image window,",
1126    "         with image obscuring image window where the image",
1127    "         shapes overlap.  Note this differs from over",
1128    "         because the portion of image outside image window's",
1129    "         shape does not appear in the result.",
1130    "",
1131    "Xor      The result is the image data from both image and",
1132    "         image window that is outside the overlap region.",
1133    "         The overlap region is blank.",
1134    "",
1135    "Plus     The result is just the sum of the image data.",
1136    "         Output values are cropped to QuantumRange (no overflow).",
1137    "         This operation is independent of the matte",
1138    "         channels.",
1139    "",
1140    "Minus    The result of image - image window, with underflow",
1141    "         cropped to zero.",
1142    "",
1143    "Add      The result of image + image window, with overflow",
1144    "         wrapping around (mod 256).",
1145    "",
1146    "Subtract The result of image - image window, with underflow",
1147    "         wrapping around (mod 256).  The add and subtract",
1148    "         operators can be used to perform reversible",
1149    "         transformations.",
1150    "",
1151    "Difference",
1152    "         The result of abs(image - image window).  This",
1153    "         useful for comparing two very similar images.",
1154    "",
1155    "Copy     The resulting image is image window replaced with",
1156    "         image.  Here the matte information is ignored.",
1157    "",
1158    "CopyRed  The red layer of the image window is replace with",
1159    "         the red layer of the image.  The other layers are",
1160    "         untouched.",
1161    "",
1162    "CopyGreen",
1163    "         The green layer of the image window is replace with",
1164    "         the green layer of the image.  The other layers are",
1165    "         untouched.",
1166    "",
1167    "CopyBlue The blue layer of the image window is replace with",
1168    "         the blue layer of the image.  The other layers are",
1169    "         untouched.",
1170    "",
1171    "CopyOpacity",
1172    "         The matte layer of the image window is replace with",
1173    "         the matte layer of the image.  The other layers are",
1174    "         untouched.",
1175    "",
1176    "The image compositor requires a matte, or alpha channel in",
1177    "the image for some operations.  This extra channel usually",
1178    "defines a mask which represents a sort of a cookie-cutter",
1179    "for the image.  This the case when matte is opaque (full",
1180    "coverage) for pixels inside the shape, zero outside, and",
1181    "between 0 and QuantumRange on the boundary.  If image does not",
1182    "have a matte channel, it is initialized with 0 for any pixel",
1183    "matching in color to pixel location (0,0), otherwise QuantumRange.",
1184    "",
1185    "Note that matte information for image window is not retained",
1186    "for colormapped X server visuals (e.g. StaticColor,",
1187    "StaticColor, GrayScale, PseudoColor).  Correct compositing",
1188    "behavior may require a TrueColor or DirectColor visual or a",
1189    "Standard Colormap.",
1190    "",
1191    "Choosing a composite operator is optional.  The default",
1192    "operator is replace.  However, you must choose a location to",
1193    "paste your image and press button 1.  Press and hold the",
1194    "button before releasing and an outline of the image will",
1195    "appear to help you identify your location.",
1196    "",
1197    "The actual colors of the pasted image is saved.  However,",
1198    "the color that appears in image window may be different.",
1199    "For example, on a monochrome screen image window will appear",
1200    "black or white even though your pasted image may have",
1201    "many colors.  If the image is saved to a file it is written",
1202    "with the correct colors.  To assure the correct colors are",
1203    "saved in the final image, any PseudoClass image is promoted",
1204    "to DirectClass (see miff(5)).  To force a PseudoClass image",
1205    "to remain PseudoClass, use -colors.",
1206    (char *) NULL,
1207  },
1208  *ImageROIHelp[] =
1209  {
1210    "In region of interest mode, the Command widget has these",
1211    "options:",
1212    "",
1213    "    Help",
1214    "    Dismiss",
1215    "",
1216    "To define a region of interest, press button 1 and drag.",
1217    "The region of interest is defined by a highlighted rectangle",
1218    "that expands or contracts as it follows the pointer.  Once",
1219    "you are satisfied with the region of interest, release the",
1220    "button.  You are now in apply mode.  In apply mode the",
1221    "Command widget has these options:",
1222    "",
1223    "      File",
1224    "        Save...",
1225    "        Print...",
1226    "      Edit",
1227    "        Undo",
1228    "        Redo",
1229    "      Transform",
1230    "        Flop",
1231    "        Flip",
1232    "        Rotate Right",
1233    "        Rotate Left",
1234    "      Enhance",
1235    "        Hue...",
1236    "        Saturation...",
1237    "        Brightness...",
1238    "        Gamma...",
1239    "        Spiff",
1240    "        Dull",
1241    "        Contrast Stretch",
1242    "        Sigmoidal Contrast...",
1243    "        Normalize",
1244    "        Equalize",
1245    "        Negate",
1246    "        Grayscale",
1247    "        Map...",
1248    "        Quantize...",
1249    "      Effects",
1250    "        Despeckle",
1251    "        Emboss",
1252    "        Reduce Noise",
1253    "        Sharpen...",
1254    "        Blur...",
1255    "        Threshold...",
1256    "        Edge Detect...",
1257    "        Spread...",
1258    "        Shade...",
1259    "        Raise...",
1260    "        Segment...",
1261    "      F/X",
1262    "        Solarize...",
1263    "        Sepia Tone...",
1264    "        Swirl...",
1265    "        Implode...",
1266    "        Vignette...",
1267    "        Wave...",
1268    "        Oil Painting...",
1269    "        Charcoal Drawing...",
1270    "      Miscellany",
1271    "        Image Info",
1272    "        Zoom Image",
1273    "        Show Preview...",
1274    "        Show Histogram",
1275    "        Show Matte",
1276    "      Help",
1277    "      Dismiss",
1278    "",
1279    "You can make adjustments to the region of interest by moving",
1280    "the pointer to one of the rectangle corners, pressing a",
1281    "button, and dragging.  Finally, choose an image processing",
1282    "technique from the Command widget.  You can choose more than",
1283    "one image processing technique to apply to an area.",
1284    "Alternatively, you can move the region of interest before",
1285    "applying another image processing technique.  To exit, press",
1286    "Dismiss.",
1287    (char *) NULL,
1288  },
1289  *ImageRotateHelp[] =
1290  {
1291    "In rotate mode, the Command widget has these options:",
1292    "",
1293    "    Pixel Color",
1294    "      black",
1295    "      blue",
1296    "      cyan",
1297    "      green",
1298    "      gray",
1299    "      red",
1300    "      magenta",
1301    "      yellow",
1302    "      white",
1303    "      Browser...",
1304    "    Direction",
1305    "      horizontal",
1306    "      vertical",
1307    "    Help",
1308    "    Dismiss",
1309    "",
1310    "Choose a background color from the Pixel Color sub-menu.",
1311    "Additional background colors can be specified with the color",
1312    "browser.  You can change the menu colors by setting the X",
1313    "resources pen1 through pen9.",
1314    "",
1315    "If you choose the color browser and press Grab, you can",
1316    "select the background color by moving the pointer to the",
1317    "desired color on the screen and press any button.",
1318    "",
1319    "Choose a point in the image window and press this button and",
1320    "hold.  Next, move the pointer to another location in the",
1321    "image.  As you move a line connects the initial location and",
1322    "the pointer.  When you release the button, the degree of",
1323    "image rotation is determined by the slope of the line you",
1324    "just drew.  The slope is relative to the direction you",
1325    "choose from the Direction sub-menu of the Command widget.",
1326    "",
1327    "To cancel the image rotation, move the pointer back to the",
1328    "starting point of the line and release the button.",
1329    (char *) NULL,
1330  };
1331
1332/*
1333  Enumeration declarations.
1334*/
1335typedef enum
1336{
1337  CopyMode,
1338  CropMode,
1339  CutMode
1340} ClipboardMode;
1341
1342typedef enum
1343{
1344  OpenCommand,
1345  NextCommand,
1346  FormerCommand,
1347  SelectCommand,
1348  SaveCommand,
1349  PrintCommand,
1350  DeleteCommand,
1351  NewCommand,
1352  VisualDirectoryCommand,
1353  QuitCommand,
1354  UndoCommand,
1355  RedoCommand,
1356  CutCommand,
1357  CopyCommand,
1358  PasteCommand,
1359  HalfSizeCommand,
1360  OriginalSizeCommand,
1361  DoubleSizeCommand,
1362  ResizeCommand,
1363  ApplyCommand,
1364  RefreshCommand,
1365  RestoreCommand,
1366  CropCommand,
1367  ChopCommand,
1368  FlopCommand,
1369  FlipCommand,
1370  RotateRightCommand,
1371  RotateLeftCommand,
1372  RotateCommand,
1373  ShearCommand,
1374  RollCommand,
1375  TrimCommand,
1376  HueCommand,
1377  SaturationCommand,
1378  BrightnessCommand,
1379  GammaCommand,
1380  SpiffCommand,
1381  DullCommand,
1382  ContrastStretchCommand,
1383  SigmoidalContrastCommand,
1384  NormalizeCommand,
1385  EqualizeCommand,
1386  NegateCommand,
1387  GrayscaleCommand,
1388  MapCommand,
1389  QuantizeCommand,
1390  DespeckleCommand,
1391  EmbossCommand,
1392  ReduceNoiseCommand,
1393  AddNoiseCommand,
1394  SharpenCommand,
1395  BlurCommand,
1396  ThresholdCommand,
1397  EdgeDetectCommand,
1398  SpreadCommand,
1399  ShadeCommand,
1400  RaiseCommand,
1401  SegmentCommand,
1402  SolarizeCommand,
1403  SepiaToneCommand,
1404  SwirlCommand,
1405  ImplodeCommand,
1406  VignetteCommand,
1407  WaveCommand,
1408  OilPaintCommand,
1409  CharcoalDrawCommand,
1410  AnnotateCommand,
1411  DrawCommand,
1412  ColorCommand,
1413  MatteCommand,
1414  CompositeCommand,
1415  AddBorderCommand,
1416  AddFrameCommand,
1417  CommentCommand,
1418  LaunchCommand,
1419  RegionofInterestCommand,
1420  ROIHelpCommand,
1421  ROIDismissCommand,
1422  InfoCommand,
1423  ZoomCommand,
1424  ShowPreviewCommand,
1425  ShowHistogramCommand,
1426  ShowMatteCommand,
1427  BackgroundCommand,
1428  SlideShowCommand,
1429  PreferencesCommand,
1430  HelpCommand,
1431  BrowseDocumentationCommand,
1432  VersionCommand,
1433  SaveToUndoBufferCommand,
1434  FreeBuffersCommand,
1435  NullCommand
1436} CommandType;
1437
1438typedef enum
1439{
1440  AnnotateNameCommand,
1441  AnnotateFontColorCommand,
1442  AnnotateBackgroundColorCommand,
1443  AnnotateRotateCommand,
1444  AnnotateHelpCommand,
1445  AnnotateDismissCommand,
1446  TextHelpCommand,
1447  TextApplyCommand,
1448  ChopDirectionCommand,
1449  ChopHelpCommand,
1450  ChopDismissCommand,
1451  HorizontalChopCommand,
1452  VerticalChopCommand,
1453  ColorEditMethodCommand,
1454  ColorEditColorCommand,
1455  ColorEditBorderCommand,
1456  ColorEditFuzzCommand,
1457  ColorEditUndoCommand,
1458  ColorEditHelpCommand,
1459  ColorEditDismissCommand,
1460  CompositeOperatorsCommand,
1461  CompositeDissolveCommand,
1462  CompositeDisplaceCommand,
1463  CompositeHelpCommand,
1464  CompositeDismissCommand,
1465  CropHelpCommand,
1466  CropDismissCommand,
1467  RectifyCopyCommand,
1468  RectifyHelpCommand,
1469  RectifyDismissCommand,
1470  DrawElementCommand,
1471  DrawColorCommand,
1472  DrawStippleCommand,
1473  DrawWidthCommand,
1474  DrawUndoCommand,
1475  DrawHelpCommand,
1476  DrawDismissCommand,
1477  MatteEditMethod,
1478  MatteEditBorderCommand,
1479  MatteEditFuzzCommand,
1480  MatteEditValueCommand,
1481  MatteEditUndoCommand,
1482  MatteEditHelpCommand,
1483  MatteEditDismissCommand,
1484  PasteOperatorsCommand,
1485  PasteHelpCommand,
1486  PasteDismissCommand,
1487  RotateColorCommand,
1488  RotateDirectionCommand,
1489  RotateCropCommand,
1490  RotateSharpenCommand,
1491  RotateHelpCommand,
1492  RotateDismissCommand,
1493  HorizontalRotateCommand,
1494  VerticalRotateCommand,
1495  TileLoadCommand,
1496  TileNextCommand,
1497  TileFormerCommand,
1498  TileDeleteCommand,
1499  TileUpdateCommand
1500} ModeType;
1501
1502/*
1503  Stipples.
1504*/
1505#define BricksWidth  20
1506#define BricksHeight  20
1507#define DiagonalWidth  16
1508#define DiagonalHeight  16
1509#define HighlightWidth  8
1510#define HighlightHeight  8
1511#define OpaqueWidth  8
1512#define OpaqueHeight  8
1513#define ScalesWidth  16
1514#define ScalesHeight  16
1515#define ShadowWidth  8
1516#define ShadowHeight  8
1517#define VerticalWidth  16
1518#define VerticalHeight  16
1519#define WavyWidth  16
1520#define WavyHeight  16
1521
1522/*
1523  Constant declaration.
1524*/
1525static const int
1526  RoiDelta = 8;
1527
1528static const unsigned char
1529  BricksBitmap[] =
1530  {
1531    0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1532    0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1533    0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1534    0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1535    0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1536  },
1537  DiagonalBitmap[] =
1538  {
1539    0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1540    0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1541    0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1542  },
1543  ScalesBitmap[] =
1544  {
1545    0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1546    0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1547    0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1548  },
1549  VerticalBitmap[] =
1550  {
1551    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1552    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1553    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1554  },
1555  WavyBitmap[] =
1556  {
1557    0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1558    0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1559    0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1560  };
1561
1562/*
1563  Function prototypes.
1564*/
1565static CommandType
1566  XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1567    const MagickStatusType,KeySym,Image **,ExceptionInfo *);
1568
1569static Image
1570  *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
1571    Image **,ExceptionInfo *),
1572  *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1573  *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *,
1574    ExceptionInfo *),
1575  *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *,
1576    ExceptionInfo *);
1577
1578static MagickBooleanType
1579  XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *,
1580    ExceptionInfo *),
1581  XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **,
1582    ExceptionInfo *),
1583  XChopImage(Display *,XResourceInfo *,XWindows *,Image **,
1584    ExceptionInfo *),
1585  XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode,
1586    ExceptionInfo *),
1587  XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1588    ExceptionInfo *),
1589  XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *,
1590    ExceptionInfo *),
1591  XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1592  XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1593    ExceptionInfo *),
1594  XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1595    ExceptionInfo *),
1596  XPasteImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1597  XPrintImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1598  XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **,
1599    ExceptionInfo *),
1600  XROIImage(Display *,XResourceInfo *,XWindows *,Image **,ExceptionInfo *),
1601  XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1602  XTrimImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
1603
1604static void
1605  XDrawPanRectangle(Display *,XWindows *),
1606  XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **,
1607    ExceptionInfo *),
1608  XMagnifyImage(Display *,XWindows *,XEvent *),
1609  XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1610  XPanImage(Display *,XWindows *,XEvent *),
1611  XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1612    const KeySym),
1613  XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1614  XScreenEvent(Display *,XWindows *,XEvent *),
1615  XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1616
1617/*
1618%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1619%                                                                             %
1620%                                                                             %
1621%                                                                             %
1622%   D i s p l a y I m a g e s                                                 %
1623%                                                                             %
1624%                                                                             %
1625%                                                                             %
1626%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1627%
1628%  DisplayImages() displays an image sequence to any X window screen.  It
1629%  returns a value other than 0 if successful.  Check the exception member
1630%  of image to determine the reason for any failure.
1631%
1632%  The format of the DisplayImages method is:
1633%
1634%      MagickBooleanType DisplayImages(const ImageInfo *image_info,
1635%        Image *images,ExceptionInfo *exception)
1636%
1637%  A description of each parameter follows:
1638%
1639%    o image_info: the image info.
1640%
1641%    o image: the image.
1642%
1643%    o exception: return any errors or warnings in this structure.
1644%
1645*/
1646MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1647  Image *images,ExceptionInfo *exception)
1648{
1649  char
1650    *argv[1];
1651
1652  Display
1653    *display;
1654
1655  Image
1656    *image;
1657
1658  register ssize_t
1659    i;
1660
1661  size_t
1662    state;
1663
1664  XrmDatabase
1665    resource_database;
1666
1667  XResourceInfo
1668    resource_info;
1669
1670  assert(image_info != (const ImageInfo *) NULL);
1671  assert(image_info->signature == MagickSignature);
1672  assert(images != (Image *) NULL);
1673  assert(images->signature == MagickSignature);
1674  if (images->debug != MagickFalse)
1675    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1676  display=XOpenDisplay(image_info->server_name);
1677  if (display == (Display *) NULL)
1678    {
1679      (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1680        "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1681      return(MagickFalse);
1682    }
1683  if (exception->severity != UndefinedException)
1684    CatchException(exception);
1685  (void) XSetErrorHandler(XError);
1686  resource_database=XGetResourceDatabase(display,GetClientName());
1687  (void) ResetMagickMemory(&resource_info,0,sizeof(resource_info));
1688  XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1689  if (image_info->page != (char *) NULL)
1690    resource_info.image_geometry=AcquireString(image_info->page);
1691  resource_info.immutable=MagickTrue;
1692  argv[0]=AcquireString(GetClientName());
1693  state=DefaultState;
1694  for (i=0; (state & ExitState) == 0; i++)
1695  {
1696    if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1697      break;
1698    image=GetImageFromList(images,i % GetImageListLength(images));
1699    (void) XDisplayImage(display,&resource_info,argv,1,&image,&state,exception);
1700  }
1701  SetErrorHandler((ErrorHandler) NULL);
1702  SetWarningHandler((WarningHandler) NULL);
1703  argv[0]=DestroyString(argv[0]);
1704  (void) XCloseDisplay(display);
1705  XDestroyResourceInfo(&resource_info);
1706  if (exception->severity != UndefinedException)
1707    return(MagickFalse);
1708  return(MagickTrue);
1709}
1710
1711/*
1712%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1713%                                                                             %
1714%                                                                             %
1715%                                                                             %
1716%   R e m o t e D i s p l a y C o m m a n d                                   %
1717%                                                                             %
1718%                                                                             %
1719%                                                                             %
1720%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1721%
1722%  RemoteDisplayCommand() encourages a remote display program to display the
1723%  specified image filename.
1724%
1725%  The format of the RemoteDisplayCommand method is:
1726%
1727%      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1728%        const char *window,const char *filename,ExceptionInfo *exception)
1729%
1730%  A description of each parameter follows:
1731%
1732%    o image_info: the image info.
1733%
1734%    o window: Specifies the name or id of an X window.
1735%
1736%    o filename: the name of the image filename to display.
1737%
1738%    o exception: return any errors or warnings in this structure.
1739%
1740*/
1741MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1742  const char *window,const char *filename,ExceptionInfo *exception)
1743{
1744  Display
1745    *display;
1746
1747  MagickStatusType
1748    status;
1749
1750  assert(image_info != (const ImageInfo *) NULL);
1751  assert(image_info->signature == MagickSignature);
1752  assert(filename != (char *) NULL);
1753  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1754  display=XOpenDisplay(image_info->server_name);
1755  if (display == (Display *) NULL)
1756    {
1757      (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1758        "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1759      return(MagickFalse);
1760    }
1761  (void) XSetErrorHandler(XError);
1762  status=XRemoteCommand(display,window,filename);
1763  (void) XCloseDisplay(display);
1764  return(status != 0 ? MagickTrue : MagickFalse);
1765}
1766
1767/*
1768%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1769%                                                                             %
1770%                                                                             %
1771%                                                                             %
1772+   X A n n o t a t e E d i t I m a g e                                       %
1773%                                                                             %
1774%                                                                             %
1775%                                                                             %
1776%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1777%
1778%  XAnnotateEditImage() annotates the image with text.
1779%
1780%  The format of the XAnnotateEditImage method is:
1781%
1782%      MagickBooleanType XAnnotateEditImage(Display *display,
1783%        XResourceInfo *resource_info,XWindows *windows,Image *image,
1784%        ExceptionInfo *exception)
1785%
1786%  A description of each parameter follows:
1787%
1788%    o display: Specifies a connection to an X server;  returned from
1789%      XOpenDisplay.
1790%
1791%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1792%
1793%    o windows: Specifies a pointer to a XWindows structure.
1794%
1795%    o image: the image; returned from ReadImage.
1796%
1797*/
1798
1799static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
1800{
1801  if (x > y)
1802    return(x);
1803  return(y);
1804}
1805
1806static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
1807{
1808  if (x < y)
1809    return(x);
1810  return(y);
1811}
1812
1813static MagickBooleanType XAnnotateEditImage(Display *display,
1814  XResourceInfo *resource_info,XWindows *windows,Image *image,
1815  ExceptionInfo *exception)
1816{
1817  static const char
1818    *AnnotateMenu[] =
1819    {
1820      "Font Name",
1821      "Font Color",
1822      "Box Color",
1823      "Rotate Text",
1824      "Help",
1825      "Dismiss",
1826      (char *) NULL
1827    },
1828    *TextMenu[] =
1829    {
1830      "Help",
1831      "Apply",
1832      (char *) NULL
1833    };
1834
1835  static const ModeType
1836    AnnotateCommands[] =
1837    {
1838      AnnotateNameCommand,
1839      AnnotateFontColorCommand,
1840      AnnotateBackgroundColorCommand,
1841      AnnotateRotateCommand,
1842      AnnotateHelpCommand,
1843      AnnotateDismissCommand
1844    },
1845    TextCommands[] =
1846    {
1847      TextHelpCommand,
1848      TextApplyCommand
1849    };
1850
1851  static MagickBooleanType
1852    transparent_box = MagickTrue,
1853    transparent_pen = MagickFalse;
1854
1855  static MagickRealType
1856    degrees = 0.0;
1857
1858  static unsigned int
1859    box_id = MaxNumberPens-2,
1860    font_id = 0,
1861    pen_id = 0;
1862
1863  char
1864    command[MaxTextExtent],
1865    text[MaxTextExtent];
1866
1867  const char
1868    *ColorMenu[MaxNumberPens+1];
1869
1870  Cursor
1871    cursor;
1872
1873  GC
1874    annotate_context;
1875
1876  int
1877    id,
1878    pen_number,
1879    status,
1880    x,
1881    y;
1882
1883  KeySym
1884    key_symbol;
1885
1886  register char
1887    *p;
1888
1889  register ssize_t
1890    i;
1891
1892  unsigned int
1893    height,
1894    width;
1895
1896  size_t
1897    state;
1898
1899  XAnnotateInfo
1900    *annotate_info,
1901    *previous_info;
1902
1903  XColor
1904    color;
1905
1906  XFontStruct
1907    *font_info;
1908
1909  XEvent
1910    event,
1911    text_event;
1912
1913  /*
1914    Map Command widget.
1915  */
1916  (void) CloneString(&windows->command.name,"Annotate");
1917  windows->command.data=4;
1918  (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1919  (void) XMapRaised(display,windows->command.id);
1920  XClientMessage(display,windows->image.id,windows->im_protocols,
1921    windows->im_update_widget,CurrentTime);
1922  /*
1923    Track pointer until button 1 is pressed.
1924  */
1925  XQueryPosition(display,windows->image.id,&x,&y);
1926  (void) XSelectInput(display,windows->image.id,
1927    windows->image.attributes.event_mask | PointerMotionMask);
1928  cursor=XCreateFontCursor(display,XC_left_side);
1929  (void) XCheckDefineCursor(display,windows->image.id,cursor);
1930  state=DefaultState;
1931  do
1932  {
1933    if (windows->info.mapped != MagickFalse)
1934      {
1935        /*
1936          Display pointer position.
1937        */
1938        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
1939          x+windows->image.x,y+windows->image.y);
1940        XInfoWidget(display,windows,text);
1941      }
1942    /*
1943      Wait for next event.
1944    */
1945    XScreenEvent(display,windows,&event);
1946    if (event.xany.window == windows->command.id)
1947      {
1948        /*
1949          Select a command from the Command widget.
1950        */
1951        id=XCommandWidget(display,windows,AnnotateMenu,&event);
1952        (void) XCheckDefineCursor(display,windows->image.id,cursor);
1953        if (id < 0)
1954          continue;
1955        switch (AnnotateCommands[id])
1956        {
1957          case AnnotateNameCommand:
1958          {
1959            const char
1960              *FontMenu[MaxNumberFonts];
1961
1962            int
1963              font_number;
1964
1965            /*
1966              Initialize menu selections.
1967            */
1968            for (i=0; i < MaxNumberFonts; i++)
1969              FontMenu[i]=resource_info->font_name[i];
1970            FontMenu[MaxNumberFonts-2]="Browser...";
1971            FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1972            /*
1973              Select a font name from the pop-up menu.
1974            */
1975            font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1976              (const char **) FontMenu,command);
1977            if (font_number < 0)
1978              break;
1979            if (font_number == (MaxNumberFonts-2))
1980              {
1981                static char
1982                  font_name[MaxTextExtent] = "fixed";
1983
1984                /*
1985                  Select a font name from a browser.
1986                */
1987                resource_info->font_name[font_number]=font_name;
1988                XFontBrowserWidget(display,windows,"Select",font_name);
1989                if (*font_name == '\0')
1990                  break;
1991              }
1992            /*
1993              Initialize font info.
1994            */
1995            font_info=XLoadQueryFont(display,resource_info->font_name[
1996              font_number]);
1997            if (font_info == (XFontStruct *) NULL)
1998              {
1999                XNoticeWidget(display,windows,"Unable to load font:",
2000                  resource_info->font_name[font_number]);
2001                break;
2002              }
2003            font_id=(unsigned int) font_number;
2004            (void) XFreeFont(display,font_info);
2005            break;
2006          }
2007          case AnnotateFontColorCommand:
2008          {
2009            /*
2010              Initialize menu selections.
2011            */
2012            for (i=0; i < (int) (MaxNumberPens-2); i++)
2013              ColorMenu[i]=resource_info->pen_colors[i];
2014            ColorMenu[MaxNumberPens-2]="transparent";
2015            ColorMenu[MaxNumberPens-1]="Browser...";
2016            ColorMenu[MaxNumberPens]=(const char *) NULL;
2017            /*
2018              Select a pen color from the pop-up menu.
2019            */
2020            pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2021              (const char **) ColorMenu,command);
2022            if (pen_number < 0)
2023              break;
2024            transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
2025              MagickFalse;
2026            if (transparent_pen != MagickFalse)
2027              break;
2028            if (pen_number == (MaxNumberPens-1))
2029              {
2030                static char
2031                  color_name[MaxTextExtent] = "gray";
2032
2033                /*
2034                  Select a pen color from a dialog.
2035                */
2036                resource_info->pen_colors[pen_number]=color_name;
2037                XColorBrowserWidget(display,windows,"Select",color_name);
2038                if (*color_name == '\0')
2039                  break;
2040              }
2041            /*
2042              Set pen color.
2043            */
2044            (void) XParseColor(display,windows->map_info->colormap,
2045              resource_info->pen_colors[pen_number],&color);
2046            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2047              (unsigned int) MaxColors,&color);
2048            windows->pixel_info->pen_colors[pen_number]=color;
2049            pen_id=(unsigned int) pen_number;
2050            break;
2051          }
2052          case AnnotateBackgroundColorCommand:
2053          {
2054            /*
2055              Initialize menu selections.
2056            */
2057            for (i=0; i < (int) (MaxNumberPens-2); i++)
2058              ColorMenu[i]=resource_info->pen_colors[i];
2059            ColorMenu[MaxNumberPens-2]="transparent";
2060            ColorMenu[MaxNumberPens-1]="Browser...";
2061            ColorMenu[MaxNumberPens]=(const char *) NULL;
2062            /*
2063              Select a pen color from the pop-up menu.
2064            */
2065            pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2066              (const char **) ColorMenu,command);
2067            if (pen_number < 0)
2068              break;
2069            transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2070              MagickFalse;
2071            if (transparent_box != MagickFalse)
2072              break;
2073            if (pen_number == (MaxNumberPens-1))
2074              {
2075                static char
2076                  color_name[MaxTextExtent] = "gray";
2077
2078                /*
2079                  Select a pen color from a dialog.
2080                */
2081                resource_info->pen_colors[pen_number]=color_name;
2082                XColorBrowserWidget(display,windows,"Select",color_name);
2083                if (*color_name == '\0')
2084                  break;
2085              }
2086            /*
2087              Set pen color.
2088            */
2089            (void) XParseColor(display,windows->map_info->colormap,
2090              resource_info->pen_colors[pen_number],&color);
2091            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2092              (unsigned int) MaxColors,&color);
2093            windows->pixel_info->pen_colors[pen_number]=color;
2094            box_id=(unsigned int) pen_number;
2095            break;
2096          }
2097          case AnnotateRotateCommand:
2098          {
2099            int
2100              entry;
2101
2102            static char
2103              angle[MaxTextExtent] = "30.0";
2104
2105            static const char
2106              *RotateMenu[] =
2107              {
2108                "-90",
2109                "-45",
2110                "-30",
2111                "0",
2112                "30",
2113                "45",
2114                "90",
2115                "180",
2116                "Dialog...",
2117                (char *) NULL,
2118              };
2119
2120            /*
2121              Select a command from the pop-up menu.
2122            */
2123            entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2124              command);
2125            if (entry < 0)
2126              break;
2127            if (entry != 8)
2128              {
2129                degrees=InterpretLocaleValue(RotateMenu[entry],(char **) NULL);
2130                break;
2131              }
2132            (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2133              angle);
2134            if (*angle == '\0')
2135              break;
2136            degrees=InterpretLocaleValue(angle,(char **) NULL);
2137            break;
2138          }
2139          case AnnotateHelpCommand:
2140          {
2141            XTextViewWidget(display,resource_info,windows,MagickFalse,
2142              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2143            break;
2144          }
2145          case AnnotateDismissCommand:
2146          {
2147            /*
2148              Prematurely exit.
2149            */
2150            state|=EscapeState;
2151            state|=ExitState;
2152            break;
2153          }
2154          default:
2155            break;
2156        }
2157        continue;
2158      }
2159    switch (event.type)
2160    {
2161      case ButtonPress:
2162      {
2163        if (event.xbutton.button != Button1)
2164          break;
2165        if (event.xbutton.window != windows->image.id)
2166          break;
2167        /*
2168          Change to text entering mode.
2169        */
2170        x=event.xbutton.x;
2171        y=event.xbutton.y;
2172        state|=ExitState;
2173        break;
2174      }
2175      case ButtonRelease:
2176        break;
2177      case Expose:
2178        break;
2179      case KeyPress:
2180      {
2181        if (event.xkey.window != windows->image.id)
2182          break;
2183        /*
2184          Respond to a user key press.
2185        */
2186        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2187          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2188        switch ((int) key_symbol)
2189        {
2190          case XK_Escape:
2191          case XK_F20:
2192          {
2193            /*
2194              Prematurely exit.
2195            */
2196            state|=EscapeState;
2197            state|=ExitState;
2198            break;
2199          }
2200          case XK_F1:
2201          case XK_Help:
2202          {
2203            XTextViewWidget(display,resource_info,windows,MagickFalse,
2204              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2205            break;
2206          }
2207          default:
2208          {
2209            (void) XBell(display,0);
2210            break;
2211          }
2212        }
2213        break;
2214      }
2215      case MotionNotify:
2216      {
2217        /*
2218          Map and unmap Info widget as cursor crosses its boundaries.
2219        */
2220        x=event.xmotion.x;
2221        y=event.xmotion.y;
2222        if (windows->info.mapped != MagickFalse)
2223          {
2224            if ((x < (int) (windows->info.x+windows->info.width)) &&
2225                (y < (int) (windows->info.y+windows->info.height)))
2226              (void) XWithdrawWindow(display,windows->info.id,
2227                windows->info.screen);
2228          }
2229        else
2230          if ((x > (int) (windows->info.x+windows->info.width)) ||
2231              (y > (int) (windows->info.y+windows->info.height)))
2232            (void) XMapWindow(display,windows->info.id);
2233        break;
2234      }
2235      default:
2236        break;
2237    }
2238  } while ((state & ExitState) == 0);
2239  (void) XSelectInput(display,windows->image.id,
2240    windows->image.attributes.event_mask);
2241  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2242  if ((state & EscapeState) != 0)
2243    return(MagickTrue);
2244  /*
2245    Set font info and check boundary conditions.
2246  */
2247  font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2248  if (font_info == (XFontStruct *) NULL)
2249    {
2250      XNoticeWidget(display,windows,"Unable to load font:",
2251        resource_info->font_name[font_id]);
2252      font_info=windows->font_info;
2253    }
2254  if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2255    x=(int) windows->image.width-font_info->max_bounds.width;
2256  if (y < (int) (font_info->ascent+font_info->descent))
2257    y=(int) font_info->ascent+font_info->descent;
2258  if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2259      ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2260    return(MagickFalse);
2261  /*
2262    Initialize annotate structure.
2263  */
2264  annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2265  if (annotate_info == (XAnnotateInfo *) NULL)
2266    return(MagickFalse);
2267  XGetAnnotateInfo(annotate_info);
2268  annotate_info->x=x;
2269  annotate_info->y=y;
2270  if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
2271    annotate_info->stencil=OpaqueStencil;
2272  else
2273    if (transparent_box == MagickFalse)
2274      annotate_info->stencil=BackgroundStencil;
2275    else
2276      annotate_info->stencil=ForegroundStencil;
2277  annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2278  annotate_info->degrees=degrees;
2279  annotate_info->font_info=font_info;
2280  annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2281    windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
2282    sizeof(*annotate_info->text));
2283  if (annotate_info->text == (char *) NULL)
2284    return(MagickFalse);
2285  /*
2286    Create cursor and set graphic context.
2287  */
2288  cursor=XCreateFontCursor(display,XC_pencil);
2289  (void) XCheckDefineCursor(display,windows->image.id,cursor);
2290  annotate_context=windows->image.annotate_context;
2291  (void) XSetFont(display,annotate_context,font_info->fid);
2292  (void) XSetBackground(display,annotate_context,
2293    windows->pixel_info->pen_colors[box_id].pixel);
2294  (void) XSetForeground(display,annotate_context,
2295    windows->pixel_info->pen_colors[pen_id].pixel);
2296  /*
2297    Begin annotating the image with text.
2298  */
2299  (void) CloneString(&windows->command.name,"Text");
2300  windows->command.data=0;
2301  (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2302  state=DefaultState;
2303  (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2304  text_event.xexpose.width=(int) font_info->max_bounds.width;
2305  text_event.xexpose.height=font_info->max_bounds.ascent+
2306    font_info->max_bounds.descent;
2307  p=annotate_info->text;
2308  do
2309  {
2310    /*
2311      Display text cursor.
2312    */
2313    *p='\0';
2314    (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2315    /*
2316      Wait for next event.
2317    */
2318    XScreenEvent(display,windows,&event);
2319    if (event.xany.window == windows->command.id)
2320      {
2321        /*
2322          Select a command from the Command widget.
2323        */
2324        (void) XSetBackground(display,annotate_context,
2325          windows->pixel_info->background_color.pixel);
2326        (void) XSetForeground(display,annotate_context,
2327          windows->pixel_info->foreground_color.pixel);
2328        id=XCommandWidget(display,windows,AnnotateMenu,&event);
2329        (void) XSetBackground(display,annotate_context,
2330          windows->pixel_info->pen_colors[box_id].pixel);
2331        (void) XSetForeground(display,annotate_context,
2332          windows->pixel_info->pen_colors[pen_id].pixel);
2333        if (id < 0)
2334          continue;
2335        switch (TextCommands[id])
2336        {
2337          case TextHelpCommand:
2338          {
2339            XTextViewWidget(display,resource_info,windows,MagickFalse,
2340              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2341            (void) XCheckDefineCursor(display,windows->image.id,cursor);
2342            break;
2343          }
2344          case TextApplyCommand:
2345          {
2346            /*
2347              Finished annotating.
2348            */
2349            annotate_info->width=(unsigned int) XTextWidth(font_info,
2350              annotate_info->text,(int) strlen(annotate_info->text));
2351            XRefreshWindow(display,&windows->image,&text_event);
2352            state|=ExitState;
2353            break;
2354          }
2355          default:
2356            break;
2357        }
2358        continue;
2359      }
2360    /*
2361      Erase text cursor.
2362    */
2363    text_event.xexpose.x=x;
2364    text_event.xexpose.y=y-font_info->max_bounds.ascent;
2365    (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2366      (unsigned int) text_event.xexpose.width,(unsigned int)
2367      text_event.xexpose.height,MagickFalse);
2368    XRefreshWindow(display,&windows->image,&text_event);
2369    switch (event.type)
2370    {
2371      case ButtonPress:
2372      {
2373        if (event.xbutton.window != windows->image.id)
2374          break;
2375        if (event.xbutton.button == Button2)
2376          {
2377            /*
2378              Request primary selection.
2379            */
2380            (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2381              windows->image.id,CurrentTime);
2382            break;
2383          }
2384        break;
2385      }
2386      case Expose:
2387      {
2388        if (event.xexpose.count == 0)
2389          {
2390            XAnnotateInfo
2391              *text_info;
2392
2393            /*
2394              Refresh Image window.
2395            */
2396            XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2397            text_info=annotate_info;
2398            while (text_info != (XAnnotateInfo *) NULL)
2399            {
2400              if (annotate_info->stencil == ForegroundStencil)
2401                (void) XDrawString(display,windows->image.id,annotate_context,
2402                  text_info->x,text_info->y,text_info->text,
2403                  (int) strlen(text_info->text));
2404              else
2405                (void) XDrawImageString(display,windows->image.id,
2406                  annotate_context,text_info->x,text_info->y,text_info->text,
2407                  (int) strlen(text_info->text));
2408              text_info=text_info->previous;
2409            }
2410            (void) XDrawString(display,windows->image.id,annotate_context,
2411              x,y,"_",1);
2412          }
2413        break;
2414      }
2415      case KeyPress:
2416      {
2417        int
2418          length;
2419
2420        if (event.xkey.window != windows->image.id)
2421          break;
2422        /*
2423          Respond to a user key press.
2424        */
2425        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2426          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2427        *(command+length)='\0';
2428        if (((event.xkey.state & ControlMask) != 0) ||
2429            ((event.xkey.state & Mod1Mask) != 0))
2430          state|=ModifierState;
2431        if ((state & ModifierState) != 0)
2432          switch ((int) key_symbol)
2433          {
2434            case XK_u:
2435            case XK_U:
2436            {
2437              key_symbol=DeleteCommand;
2438              break;
2439            }
2440            default:
2441              break;
2442          }
2443        switch ((int) key_symbol)
2444        {
2445          case XK_BackSpace:
2446          {
2447            /*
2448              Erase one character.
2449            */
2450            if (p == annotate_info->text)
2451              {
2452                if (annotate_info->previous == (XAnnotateInfo *) NULL)
2453                  break;
2454                else
2455                  {
2456                    /*
2457                      Go to end of the previous line of text.
2458                    */
2459                    annotate_info=annotate_info->previous;
2460                    p=annotate_info->text;
2461                    x=annotate_info->x+annotate_info->width;
2462                    y=annotate_info->y;
2463                    if (annotate_info->width != 0)
2464                      p+=strlen(annotate_info->text);
2465                    break;
2466                  }
2467              }
2468            p--;
2469            x-=XTextWidth(font_info,p,1);
2470            text_event.xexpose.x=x;
2471            text_event.xexpose.y=y-font_info->max_bounds.ascent;
2472            XRefreshWindow(display,&windows->image,&text_event);
2473            break;
2474          }
2475          case XK_bracketleft:
2476          {
2477            key_symbol=XK_Escape;
2478            break;
2479          }
2480          case DeleteCommand:
2481          {
2482            /*
2483              Erase the entire line of text.
2484            */
2485            while (p != annotate_info->text)
2486            {
2487              p--;
2488              x-=XTextWidth(font_info,p,1);
2489              text_event.xexpose.x=x;
2490              XRefreshWindow(display,&windows->image,&text_event);
2491            }
2492            break;
2493          }
2494          case XK_Escape:
2495          case XK_F20:
2496          {
2497            /*
2498              Finished annotating.
2499            */
2500            annotate_info->width=(unsigned int) XTextWidth(font_info,
2501              annotate_info->text,(int) strlen(annotate_info->text));
2502            XRefreshWindow(display,&windows->image,&text_event);
2503            state|=ExitState;
2504            break;
2505          }
2506          default:
2507          {
2508            /*
2509              Draw a single character on the Image window.
2510            */
2511            if ((state & ModifierState) != 0)
2512              break;
2513            if (*command == '\0')
2514              break;
2515            *p=(*command);
2516            if (annotate_info->stencil == ForegroundStencil)
2517              (void) XDrawString(display,windows->image.id,annotate_context,
2518                x,y,p,1);
2519            else
2520              (void) XDrawImageString(display,windows->image.id,
2521                annotate_context,x,y,p,1);
2522            x+=XTextWidth(font_info,p,1);
2523            p++;
2524            if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2525              break;
2526          }
2527          case XK_Return:
2528          case XK_KP_Enter:
2529          {
2530            /*
2531              Advance to the next line of text.
2532            */
2533            *p='\0';
2534            annotate_info->width=(unsigned int) XTextWidth(font_info,
2535              annotate_info->text,(int) strlen(annotate_info->text));
2536            if (annotate_info->next != (XAnnotateInfo *) NULL)
2537              {
2538                /*
2539                  Line of text already exists.
2540                */
2541                annotate_info=annotate_info->next;
2542                x=annotate_info->x;
2543                y=annotate_info->y;
2544                p=annotate_info->text;
2545                break;
2546              }
2547            annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2548              sizeof(*annotate_info->next));
2549            if (annotate_info->next == (XAnnotateInfo *) NULL)
2550              return(MagickFalse);
2551            *annotate_info->next=(*annotate_info);
2552            annotate_info->next->previous=annotate_info;
2553            annotate_info=annotate_info->next;
2554            annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2555              windows->image.width/MagickMax((ssize_t)
2556              font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2557            if (annotate_info->text == (char *) NULL)
2558              return(MagickFalse);
2559            annotate_info->y+=annotate_info->height;
2560            if (annotate_info->y > (int) windows->image.height)
2561              annotate_info->y=(int) annotate_info->height;
2562            annotate_info->next=(XAnnotateInfo *) NULL;
2563            x=annotate_info->x;
2564            y=annotate_info->y;
2565            p=annotate_info->text;
2566            break;
2567          }
2568        }
2569        break;
2570      }
2571      case KeyRelease:
2572      {
2573        /*
2574          Respond to a user key release.
2575        */
2576        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2577          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2578        state&=(~ModifierState);
2579        break;
2580      }
2581      case SelectionNotify:
2582      {
2583        Atom
2584          type;
2585
2586        int
2587          format;
2588
2589        unsigned char
2590          *data;
2591
2592        unsigned long
2593          after,
2594          length;
2595
2596        /*
2597          Obtain response from primary selection.
2598        */
2599        if (event.xselection.property == (Atom) None)
2600          break;
2601        status=XGetWindowProperty(display,event.xselection.requestor,
2602          event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
2603          &type,&format,&length,&after,&data);
2604        if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2605            (length == 0))
2606          break;
2607        /*
2608          Annotate Image window with primary selection.
2609        */
2610        for (i=0; i < (ssize_t) length; i++)
2611        {
2612          if ((char) data[i] != '\n')
2613            {
2614              /*
2615                Draw a single character on the Image window.
2616              */
2617              *p=(char) data[i];
2618              (void) XDrawString(display,windows->image.id,annotate_context,
2619                x,y,p,1);
2620              x+=XTextWidth(font_info,p,1);
2621              p++;
2622              if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2623                continue;
2624            }
2625          /*
2626            Advance to the next line of text.
2627          */
2628          *p='\0';
2629          annotate_info->width=(unsigned int) XTextWidth(font_info,
2630            annotate_info->text,(int) strlen(annotate_info->text));
2631          if (annotate_info->next != (XAnnotateInfo *) NULL)
2632            {
2633              /*
2634                Line of text already exists.
2635              */
2636              annotate_info=annotate_info->next;
2637              x=annotate_info->x;
2638              y=annotate_info->y;
2639              p=annotate_info->text;
2640              continue;
2641            }
2642          annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2643            sizeof(*annotate_info->next));
2644          if (annotate_info->next == (XAnnotateInfo *) NULL)
2645            return(MagickFalse);
2646          *annotate_info->next=(*annotate_info);
2647          annotate_info->next->previous=annotate_info;
2648          annotate_info=annotate_info->next;
2649          annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2650            windows->image.width/MagickMax((ssize_t)
2651            font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2652          if (annotate_info->text == (char *) NULL)
2653            return(MagickFalse);
2654          annotate_info->y+=annotate_info->height;
2655          if (annotate_info->y > (int) windows->image.height)
2656            annotate_info->y=(int) annotate_info->height;
2657          annotate_info->next=(XAnnotateInfo *) NULL;
2658          x=annotate_info->x;
2659          y=annotate_info->y;
2660          p=annotate_info->text;
2661        }
2662        (void) XFree((void *) data);
2663        break;
2664      }
2665      default:
2666        break;
2667    }
2668  } while ((state & ExitState) == 0);
2669  (void) XFreeCursor(display,cursor);
2670  /*
2671    Annotation is relative to image configuration.
2672  */
2673  width=(unsigned int) image->columns;
2674  height=(unsigned int) image->rows;
2675  x=0;
2676  y=0;
2677  if (windows->image.crop_geometry != (char *) NULL)
2678    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2679  /*
2680    Initialize annotated image.
2681  */
2682  XSetCursorState(display,windows,MagickTrue);
2683  XCheckRefreshWindows(display,windows);
2684  while (annotate_info != (XAnnotateInfo *) NULL)
2685  {
2686    if (annotate_info->width == 0)
2687      {
2688        /*
2689          No text on this line--  go to the next line of text.
2690        */
2691        previous_info=annotate_info->previous;
2692        annotate_info->text=(char *)
2693          RelinquishMagickMemory(annotate_info->text);
2694        annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2695        annotate_info=previous_info;
2696        continue;
2697      }
2698    /*
2699      Determine pixel index for box and pen color.
2700    */
2701    windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2702    if (windows->pixel_info->colors != 0)
2703      for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2704        if (windows->pixel_info->pixels[i] ==
2705            windows->pixel_info->pen_colors[box_id].pixel)
2706          {
2707            windows->pixel_info->box_index=(unsigned short) i;
2708            break;
2709          }
2710    windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2711    if (windows->pixel_info->colors != 0)
2712      for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2713        if (windows->pixel_info->pixels[i] ==
2714            windows->pixel_info->pen_colors[pen_id].pixel)
2715          {
2716            windows->pixel_info->pen_index=(unsigned short) i;
2717            break;
2718          }
2719    /*
2720      Define the annotate geometry string.
2721    */
2722    annotate_info->x=(int)
2723      width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2724    annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2725      windows->image.y)/windows->image.ximage->height;
2726    (void) FormatLocaleString(annotate_info->geometry,MaxTextExtent,
2727      "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2728      height*annotate_info->height/windows->image.ximage->height,
2729      annotate_info->x+x,annotate_info->y+y);
2730    /*
2731      Annotate image with text.
2732    */
2733    status=XAnnotateImage(display,windows->pixel_info,annotate_info,image);
2734    if (status == 0)
2735      return(MagickFalse);
2736    /*
2737      Free up memory.
2738    */
2739    previous_info=annotate_info->previous;
2740    annotate_info->text=DestroyString(annotate_info->text);
2741    annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2742    annotate_info=previous_info;
2743  }
2744  (void) XSetForeground(display,annotate_context,
2745    windows->pixel_info->foreground_color.pixel);
2746  (void) XSetBackground(display,annotate_context,
2747    windows->pixel_info->background_color.pixel);
2748  (void) XSetFont(display,annotate_context,windows->font_info->fid);
2749  XSetCursorState(display,windows,MagickFalse);
2750  (void) XFreeFont(display,font_info);
2751  /*
2752    Update image configuration.
2753  */
2754  XConfigureImageColormap(display,resource_info,windows,image);
2755  (void) XConfigureImage(display,resource_info,windows,image,exception);
2756  return(MagickTrue);
2757}
2758
2759/*
2760%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2761%                                                                             %
2762%                                                                             %
2763%                                                                             %
2764+   X B a c k g r o u n d I m a g e                                           %
2765%                                                                             %
2766%                                                                             %
2767%                                                                             %
2768%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2769%
2770%  XBackgroundImage() displays the image in the background of a window.
2771%
2772%  The format of the XBackgroundImage method is:
2773%
2774%      MagickBooleanType XBackgroundImage(Display *display,
2775%        XResourceInfo *resource_info,XWindows *windows,Image **image,
2776%        ExceptionInfo *exception)
2777%
2778%  A description of each parameter follows:
2779%
2780%    o display: Specifies a connection to an X server; returned from
2781%      XOpenDisplay.
2782%
2783%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2784%
2785%    o windows: Specifies a pointer to a XWindows structure.
2786%
2787%    o image: the image.
2788%
2789%    o exception: return any errors or warnings in this structure.
2790%
2791*/
2792static MagickBooleanType XBackgroundImage(Display *display,
2793  XResourceInfo *resource_info,XWindows *windows,Image **image,
2794  ExceptionInfo *exception)
2795{
2796#define BackgroundImageTag  "Background/Image"
2797
2798  int
2799    status;
2800
2801  static char
2802    window_id[MaxTextExtent] = "root";
2803
2804  XResourceInfo
2805    background_resources;
2806
2807  /*
2808    Put image in background.
2809  */
2810  status=XDialogWidget(display,windows,"Background",
2811    "Enter window id (id 0x00 selects window with pointer):",window_id);
2812  if (*window_id == '\0')
2813    return(MagickFalse);
2814  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
2815    exception);
2816  XInfoWidget(display,windows,BackgroundImageTag);
2817  XSetCursorState(display,windows,MagickTrue);
2818  XCheckRefreshWindows(display,windows);
2819  background_resources=(*resource_info);
2820  background_resources.window_id=window_id;
2821  background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2822  status=XDisplayBackgroundImage(display,&background_resources,*image,
2823    exception);
2824  if (status != MagickFalse)
2825    XClientMessage(display,windows->image.id,windows->im_protocols,
2826      windows->im_retain_colors,CurrentTime);
2827  XSetCursorState(display,windows,MagickFalse);
2828  (void) XMagickCommand(display,resource_info,windows,UndoCommand,image,
2829    exception);
2830  return(MagickTrue);
2831}
2832
2833/*
2834%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2835%                                                                             %
2836%                                                                             %
2837%                                                                             %
2838+   X C h o p I m a g e                                                       %
2839%                                                                             %
2840%                                                                             %
2841%                                                                             %
2842%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2843%
2844%  XChopImage() chops the X image.
2845%
2846%  The format of the XChopImage method is:
2847%
2848%    MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2849%      XWindows *windows,Image **image,ExceptionInfo *exception)
2850%
2851%  A description of each parameter follows:
2852%
2853%    o display: Specifies a connection to an X server; returned from
2854%      XOpenDisplay.
2855%
2856%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2857%
2858%    o windows: Specifies a pointer to a XWindows structure.
2859%
2860%    o image: the image.
2861%
2862%    o exception: return any errors or warnings in this structure.
2863%
2864*/
2865static MagickBooleanType XChopImage(Display *display,
2866  XResourceInfo *resource_info,XWindows *windows,Image **image,
2867  ExceptionInfo *exception)
2868{
2869  static const char
2870    *ChopMenu[] =
2871    {
2872      "Direction",
2873      "Help",
2874      "Dismiss",
2875      (char *) NULL
2876    };
2877
2878  static ModeType
2879    direction = HorizontalChopCommand;
2880
2881  static const ModeType
2882    ChopCommands[] =
2883    {
2884      ChopDirectionCommand,
2885      ChopHelpCommand,
2886      ChopDismissCommand
2887    },
2888    DirectionCommands[] =
2889    {
2890      HorizontalChopCommand,
2891      VerticalChopCommand
2892    };
2893
2894  char
2895    text[MaxTextExtent];
2896
2897  Image
2898    *chop_image;
2899
2900  int
2901    id,
2902    x,
2903    y;
2904
2905  MagickRealType
2906    scale_factor;
2907
2908  RectangleInfo
2909    chop_info;
2910
2911  unsigned int
2912    distance,
2913    height,
2914    width;
2915
2916  size_t
2917    state;
2918
2919  XEvent
2920    event;
2921
2922  XSegment
2923    segment_info;
2924
2925  /*
2926    Map Command widget.
2927  */
2928  (void) CloneString(&windows->command.name,"Chop");
2929  windows->command.data=1;
2930  (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2931  (void) XMapRaised(display,windows->command.id);
2932  XClientMessage(display,windows->image.id,windows->im_protocols,
2933    windows->im_update_widget,CurrentTime);
2934  /*
2935    Track pointer until button 1 is pressed.
2936  */
2937  XQueryPosition(display,windows->image.id,&x,&y);
2938  (void) XSelectInput(display,windows->image.id,
2939    windows->image.attributes.event_mask | PointerMotionMask);
2940  state=DefaultState;
2941  do
2942  {
2943    if (windows->info.mapped != MagickFalse)
2944      {
2945        /*
2946          Display pointer position.
2947        */
2948        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
2949          x+windows->image.x,y+windows->image.y);
2950        XInfoWidget(display,windows,text);
2951      }
2952    /*
2953      Wait for next event.
2954    */
2955    XScreenEvent(display,windows,&event);
2956    if (event.xany.window == windows->command.id)
2957      {
2958        /*
2959          Select a command from the Command widget.
2960        */
2961        id=XCommandWidget(display,windows,ChopMenu,&event);
2962        if (id < 0)
2963          continue;
2964        switch (ChopCommands[id])
2965        {
2966          case ChopDirectionCommand:
2967          {
2968            char
2969              command[MaxTextExtent];
2970
2971            static const char
2972              *Directions[] =
2973              {
2974                "horizontal",
2975                "vertical",
2976                (char *) NULL,
2977              };
2978
2979            /*
2980              Select a command from the pop-up menu.
2981            */
2982            id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2983            if (id >= 0)
2984              direction=DirectionCommands[id];
2985            break;
2986          }
2987          case ChopHelpCommand:
2988          {
2989            XTextViewWidget(display,resource_info,windows,MagickFalse,
2990              "Help Viewer - Image Chop",ImageChopHelp);
2991            break;
2992          }
2993          case ChopDismissCommand:
2994          {
2995            /*
2996              Prematurely exit.
2997            */
2998            state|=EscapeState;
2999            state|=ExitState;
3000            break;
3001          }
3002          default:
3003            break;
3004        }
3005        continue;
3006      }
3007    switch (event.type)
3008    {
3009      case ButtonPress:
3010      {
3011        if (event.xbutton.button != Button1)
3012          break;
3013        if (event.xbutton.window != windows->image.id)
3014          break;
3015        /*
3016          User has committed to start point of chopping line.
3017        */
3018        segment_info.x1=(short int) event.xbutton.x;
3019        segment_info.x2=(short int) event.xbutton.x;
3020        segment_info.y1=(short int) event.xbutton.y;
3021        segment_info.y2=(short int) event.xbutton.y;
3022        state|=ExitState;
3023        break;
3024      }
3025      case ButtonRelease:
3026        break;
3027      case Expose:
3028        break;
3029      case KeyPress:
3030      {
3031        char
3032          command[MaxTextExtent];
3033
3034        KeySym
3035          key_symbol;
3036
3037        if (event.xkey.window != windows->image.id)
3038          break;
3039        /*
3040          Respond to a user key press.
3041        */
3042        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3043          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3044        switch ((int) key_symbol)
3045        {
3046          case XK_Escape:
3047          case XK_F20:
3048          {
3049            /*
3050              Prematurely exit.
3051            */
3052            state|=EscapeState;
3053            state|=ExitState;
3054            break;
3055          }
3056          case XK_F1:
3057          case XK_Help:
3058          {
3059            (void) XSetFunction(display,windows->image.highlight_context,
3060              GXcopy);
3061            XTextViewWidget(display,resource_info,windows,MagickFalse,
3062              "Help Viewer - Image Chop",ImageChopHelp);
3063            (void) XSetFunction(display,windows->image.highlight_context,
3064              GXinvert);
3065            break;
3066          }
3067          default:
3068          {
3069            (void) XBell(display,0);
3070            break;
3071          }
3072        }
3073        break;
3074      }
3075      case MotionNotify:
3076      {
3077        /*
3078          Map and unmap Info widget as text cursor crosses its boundaries.
3079        */
3080        x=event.xmotion.x;
3081        y=event.xmotion.y;
3082        if (windows->info.mapped != MagickFalse)
3083          {
3084            if ((x < (int) (windows->info.x+windows->info.width)) &&
3085                (y < (int) (windows->info.y+windows->info.height)))
3086              (void) XWithdrawWindow(display,windows->info.id,
3087                windows->info.screen);
3088          }
3089        else
3090          if ((x > (int) (windows->info.x+windows->info.width)) ||
3091              (y > (int) (windows->info.y+windows->info.height)))
3092            (void) XMapWindow(display,windows->info.id);
3093      }
3094    }
3095  } while ((state & ExitState) == 0);
3096  (void) XSelectInput(display,windows->image.id,
3097    windows->image.attributes.event_mask);
3098  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3099  if ((state & EscapeState) != 0)
3100    return(MagickTrue);
3101  /*
3102    Draw line as pointer moves until the mouse button is released.
3103  */
3104  chop_info.width=0;
3105  chop_info.height=0;
3106  chop_info.x=0;
3107  chop_info.y=0;
3108  distance=0;
3109  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3110  state=DefaultState;
3111  do
3112  {
3113    if (distance > 9)
3114      {
3115        /*
3116          Display info and draw chopping line.
3117        */
3118        if (windows->info.mapped == MagickFalse)
3119          (void) XMapWindow(display,windows->info.id);
3120        (void) FormatLocaleString(text,MaxTextExtent,
3121          " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3122          chop_info.height,(double) chop_info.x,(double) chop_info.y);
3123        XInfoWidget(display,windows,text);
3124        XHighlightLine(display,windows->image.id,
3125          windows->image.highlight_context,&segment_info);
3126      }
3127    else
3128      if (windows->info.mapped != MagickFalse)
3129        (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3130    /*
3131      Wait for next event.
3132    */
3133    XScreenEvent(display,windows,&event);
3134    if (distance > 9)
3135      XHighlightLine(display,windows->image.id,
3136        windows->image.highlight_context,&segment_info);
3137    switch (event.type)
3138    {
3139      case ButtonPress:
3140      {
3141        segment_info.x2=(short int) event.xmotion.x;
3142        segment_info.y2=(short int) event.xmotion.y;
3143        break;
3144      }
3145      case ButtonRelease:
3146      {
3147        /*
3148          User has committed to chopping line.
3149        */
3150        segment_info.x2=(short int) event.xbutton.x;
3151        segment_info.y2=(short int) event.xbutton.y;
3152        state|=ExitState;
3153        break;
3154      }
3155      case Expose:
3156        break;
3157      case MotionNotify:
3158      {
3159        segment_info.x2=(short int) event.xmotion.x;
3160        segment_info.y2=(short int) event.xmotion.y;
3161      }
3162      default:
3163        break;
3164    }
3165    /*
3166      Check boundary conditions.
3167    */
3168    if (segment_info.x2 < 0)
3169      segment_info.x2=0;
3170    else
3171      if (segment_info.x2 > windows->image.ximage->width)
3172        segment_info.x2=windows->image.ximage->width;
3173    if (segment_info.y2 < 0)
3174      segment_info.y2=0;
3175    else
3176      if (segment_info.y2 > windows->image.ximage->height)
3177        segment_info.y2=windows->image.ximage->height;
3178    distance=(unsigned int)
3179      (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3180       ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3181    /*
3182      Compute chopping geometry.
3183    */
3184    if (direction == HorizontalChopCommand)
3185      {
3186        chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3187        chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3188        chop_info.height=0;
3189        chop_info.y=0;
3190        if (segment_info.x1 > (int) segment_info.x2)
3191          {
3192            chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3193            chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3194          }
3195      }
3196    else
3197      {
3198        chop_info.width=0;
3199        chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3200        chop_info.x=0;
3201        chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3202        if (segment_info.y1 > segment_info.y2)
3203          {
3204            chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3205            chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3206          }
3207      }
3208  } while ((state & ExitState) == 0);
3209  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3210  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3211  if (distance <= 9)
3212    return(MagickTrue);
3213  /*
3214    Image chopping is relative to image configuration.
3215  */
3216  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
3217    exception);
3218  XSetCursorState(display,windows,MagickTrue);
3219  XCheckRefreshWindows(display,windows);
3220  windows->image.window_changes.width=windows->image.ximage->width-
3221    (unsigned int) chop_info.width;
3222  windows->image.window_changes.height=windows->image.ximage->height-
3223    (unsigned int) chop_info.height;
3224  width=(unsigned int) (*image)->columns;
3225  height=(unsigned int) (*image)->rows;
3226  x=0;
3227  y=0;
3228  if (windows->image.crop_geometry != (char *) NULL)
3229    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3230  scale_factor=(MagickRealType) width/windows->image.ximage->width;
3231  chop_info.x+=x;
3232  chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3233  chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3234  scale_factor=(MagickRealType) height/windows->image.ximage->height;
3235  chop_info.y+=y;
3236  chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3237  chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3238  /*
3239    Chop image.
3240  */
3241  chop_image=ChopImage(*image,&chop_info,exception);
3242  XSetCursorState(display,windows,MagickFalse);
3243  if (chop_image == (Image *) NULL)
3244    return(MagickFalse);
3245  *image=DestroyImage(*image);
3246  *image=chop_image;
3247  /*
3248    Update image configuration.
3249  */
3250  XConfigureImageColormap(display,resource_info,windows,*image);
3251  (void) XConfigureImage(display,resource_info,windows,*image,exception);
3252  return(MagickTrue);
3253}
3254
3255/*
3256%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3257%                                                                             %
3258%                                                                             %
3259%                                                                             %
3260+   X C o l o r E d i t I m a g e                                             %
3261%                                                                             %
3262%                                                                             %
3263%                                                                             %
3264%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3265%
3266%  XColorEditImage() allows the user to interactively change the color of one
3267%  pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3268%
3269%  The format of the XColorEditImage method is:
3270%
3271%      MagickBooleanType XColorEditImage(Display *display,
3272%        XResourceInfo *resource_info,XWindows *windows,Image **image,
3273%          ExceptionInfo *exception)
3274%
3275%  A description of each parameter follows:
3276%
3277%    o display: Specifies a connection to an X server;  returned from
3278%      XOpenDisplay.
3279%
3280%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3281%
3282%    o windows: Specifies a pointer to a XWindows structure.
3283%
3284%    o image: the image; returned from ReadImage.
3285%
3286%    o exception: return any errors or warnings in this structure.
3287%
3288*/
3289static MagickBooleanType XColorEditImage(Display *display,
3290  XResourceInfo *resource_info,XWindows *windows,Image **image,
3291  ExceptionInfo *exception)
3292{
3293  static const char
3294    *ColorEditMenu[] =
3295    {
3296      "Method",
3297      "Pixel Color",
3298      "Border Color",
3299      "Fuzz",
3300      "Undo",
3301      "Help",
3302      "Dismiss",
3303      (char *) NULL
3304    };
3305
3306  static const ModeType
3307    ColorEditCommands[] =
3308    {
3309      ColorEditMethodCommand,
3310      ColorEditColorCommand,
3311      ColorEditBorderCommand,
3312      ColorEditFuzzCommand,
3313      ColorEditUndoCommand,
3314      ColorEditHelpCommand,
3315      ColorEditDismissCommand
3316    };
3317
3318  static PaintMethod
3319    method = PointMethod;
3320
3321  static unsigned int
3322    pen_id = 0;
3323
3324  static XColor
3325    border_color = { 0, 0, 0, 0, 0, 0 };
3326
3327  char
3328    command[MaxTextExtent],
3329    text[MaxTextExtent];
3330
3331  Cursor
3332    cursor;
3333
3334  int
3335    entry,
3336    id,
3337    x,
3338    x_offset,
3339    y,
3340    y_offset;
3341
3342  register Quantum
3343    *q;
3344
3345  register ssize_t
3346    i;
3347
3348  unsigned int
3349    height,
3350    width;
3351
3352  size_t
3353    state;
3354
3355  XColor
3356    color;
3357
3358  XEvent
3359    event;
3360
3361  /*
3362    Map Command widget.
3363  */
3364  (void) CloneString(&windows->command.name,"Color Edit");
3365  windows->command.data=4;
3366  (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3367  (void) XMapRaised(display,windows->command.id);
3368  XClientMessage(display,windows->image.id,windows->im_protocols,
3369    windows->im_update_widget,CurrentTime);
3370  /*
3371    Make cursor.
3372  */
3373  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3374    resource_info->background_color,resource_info->foreground_color);
3375  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3376  /*
3377    Track pointer until button 1 is pressed.
3378  */
3379  XQueryPosition(display,windows->image.id,&x,&y);
3380  (void) XSelectInput(display,windows->image.id,
3381    windows->image.attributes.event_mask | PointerMotionMask);
3382  state=DefaultState;
3383  do
3384  {
3385    if (windows->info.mapped != MagickFalse)
3386      {
3387        /*
3388          Display pointer position.
3389        */
3390        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
3391          x+windows->image.x,y+windows->image.y);
3392        XInfoWidget(display,windows,text);
3393      }
3394    /*
3395      Wait for next event.
3396    */
3397    XScreenEvent(display,windows,&event);
3398    if (event.xany.window == windows->command.id)
3399      {
3400        /*
3401          Select a command from the Command widget.
3402        */
3403        id=XCommandWidget(display,windows,ColorEditMenu,&event);
3404        if (id < 0)
3405          {
3406            (void) XCheckDefineCursor(display,windows->image.id,cursor);
3407            continue;
3408          }
3409        switch (ColorEditCommands[id])
3410        {
3411          case ColorEditMethodCommand:
3412          {
3413            char
3414              **methods;
3415
3416            /*
3417              Select a method from the pop-up menu.
3418            */
3419            methods=(char **) GetCommandOptions(MagickMethodOptions);
3420            if (methods == (char **) NULL)
3421              break;
3422            entry=XMenuWidget(display,windows,ColorEditMenu[id],
3423              (const char **) methods,command);
3424            if (entry >= 0)
3425              method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3426                MagickFalse,methods[entry]);
3427            methods=DestroyStringList(methods);
3428            break;
3429          }
3430          case ColorEditColorCommand:
3431          {
3432            const char
3433              *ColorMenu[MaxNumberPens];
3434
3435            int
3436              pen_number;
3437
3438            /*
3439              Initialize menu selections.
3440            */
3441            for (i=0; i < (int) (MaxNumberPens-2); i++)
3442              ColorMenu[i]=resource_info->pen_colors[i];
3443            ColorMenu[MaxNumberPens-2]="Browser...";
3444            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3445            /*
3446              Select a pen color from the pop-up menu.
3447            */
3448            pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3449              (const char **) ColorMenu,command);
3450            if (pen_number < 0)
3451              break;
3452            if (pen_number == (MaxNumberPens-2))
3453              {
3454                static char
3455                  color_name[MaxTextExtent] = "gray";
3456
3457                /*
3458                  Select a pen color from a dialog.
3459                */
3460                resource_info->pen_colors[pen_number]=color_name;
3461                XColorBrowserWidget(display,windows,"Select",color_name);
3462                if (*color_name == '\0')
3463                  break;
3464              }
3465            /*
3466              Set pen color.
3467            */
3468            (void) XParseColor(display,windows->map_info->colormap,
3469              resource_info->pen_colors[pen_number],&color);
3470            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3471              (unsigned int) MaxColors,&color);
3472            windows->pixel_info->pen_colors[pen_number]=color;
3473            pen_id=(unsigned int) pen_number;
3474            break;
3475          }
3476          case ColorEditBorderCommand:
3477          {
3478            const char
3479              *ColorMenu[MaxNumberPens];
3480
3481            int
3482              pen_number;
3483
3484            /*
3485              Initialize menu selections.
3486            */
3487            for (i=0; i < (int) (MaxNumberPens-2); i++)
3488              ColorMenu[i]=resource_info->pen_colors[i];
3489            ColorMenu[MaxNumberPens-2]="Browser...";
3490            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3491            /*
3492              Select a pen color from the pop-up menu.
3493            */
3494            pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3495              (const char **) ColorMenu,command);
3496            if (pen_number < 0)
3497              break;
3498            if (pen_number == (MaxNumberPens-2))
3499              {
3500                static char
3501                  color_name[MaxTextExtent] = "gray";
3502
3503                /*
3504                  Select a pen color from a dialog.
3505                */
3506                resource_info->pen_colors[pen_number]=color_name;
3507                XColorBrowserWidget(display,windows,"Select",color_name);
3508                if (*color_name == '\0')
3509                  break;
3510              }
3511            /*
3512              Set border color.
3513            */
3514            (void) XParseColor(display,windows->map_info->colormap,
3515              resource_info->pen_colors[pen_number],&border_color);
3516            break;
3517          }
3518          case ColorEditFuzzCommand:
3519          {
3520            static char
3521              fuzz[MaxTextExtent];
3522
3523            static const char
3524              *FuzzMenu[] =
3525              {
3526                "0%",
3527                "2%",
3528                "5%",
3529                "10%",
3530                "15%",
3531                "Dialog...",
3532                (char *) NULL,
3533              };
3534
3535            /*
3536              Select a command from the pop-up menu.
3537            */
3538            entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3539              command);
3540            if (entry < 0)
3541              break;
3542            if (entry != 5)
3543              {
3544                (*image)->fuzz=SiPrefixToDouble(FuzzMenu[entry],1.0*
3545                  QuantumRange+1.0);
3546                break;
3547              }
3548            (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
3549            (void) XDialogWidget(display,windows,"Ok",
3550              "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3551            if (*fuzz == '\0')
3552              break;
3553            (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
3554            (*image)->fuzz=SiPrefixToDouble(fuzz,1.0*QuantumRange+1.0);
3555            break;
3556          }
3557          case ColorEditUndoCommand:
3558          {
3559            (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3560              image,exception);
3561            break;
3562          }
3563          case ColorEditHelpCommand:
3564          default:
3565          {
3566            XTextViewWidget(display,resource_info,windows,MagickFalse,
3567              "Help Viewer - Image Annotation",ImageColorEditHelp);
3568            break;
3569          }
3570          case ColorEditDismissCommand:
3571          {
3572            /*
3573              Prematurely exit.
3574            */
3575            state|=EscapeState;
3576            state|=ExitState;
3577            break;
3578          }
3579        }
3580        (void) XCheckDefineCursor(display,windows->image.id,cursor);
3581        continue;
3582      }
3583    switch (event.type)
3584    {
3585      case ButtonPress:
3586      {
3587        if (event.xbutton.button != Button1)
3588          break;
3589        if ((event.xbutton.window != windows->image.id) &&
3590            (event.xbutton.window != windows->magnify.id))
3591          break;
3592        /*
3593          exit loop.
3594        */
3595        x=event.xbutton.x;
3596        y=event.xbutton.y;
3597        (void) XMagickCommand(display,resource_info,windows,
3598          SaveToUndoBufferCommand,image,exception);
3599        state|=UpdateConfigurationState;
3600        break;
3601      }
3602      case ButtonRelease:
3603      {
3604        if (event.xbutton.button != Button1)
3605          break;
3606        if ((event.xbutton.window != windows->image.id) &&
3607            (event.xbutton.window != windows->magnify.id))
3608          break;
3609        /*
3610          Update colormap information.
3611        */
3612        x=event.xbutton.x;
3613        y=event.xbutton.y;
3614        XConfigureImageColormap(display,resource_info,windows,*image);
3615        (void) XConfigureImage(display,resource_info,windows,*image,exception);
3616        XInfoWidget(display,windows,text);
3617        (void) XCheckDefineCursor(display,windows->image.id,cursor);
3618        state&=(~UpdateConfigurationState);
3619        break;
3620      }
3621      case Expose:
3622        break;
3623      case KeyPress:
3624      {
3625        KeySym
3626          key_symbol;
3627
3628        if (event.xkey.window == windows->magnify.id)
3629          {
3630            Window
3631              window;
3632
3633            window=windows->magnify.id;
3634            while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3635          }
3636        if (event.xkey.window != windows->image.id)
3637          break;
3638        /*
3639          Respond to a user key press.
3640        */
3641        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3642          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3643        switch ((int) key_symbol)
3644        {
3645          case XK_Escape:
3646          case XK_F20:
3647          {
3648            /*
3649              Prematurely exit.
3650            */
3651            state|=ExitState;
3652            break;
3653          }
3654          case XK_F1:
3655          case XK_Help:
3656          {
3657            XTextViewWidget(display,resource_info,windows,MagickFalse,
3658              "Help Viewer - Image Annotation",ImageColorEditHelp);
3659            break;
3660          }
3661          default:
3662          {
3663            (void) XBell(display,0);
3664            break;
3665          }
3666        }
3667        break;
3668      }
3669      case MotionNotify:
3670      {
3671        /*
3672          Map and unmap Info widget as cursor crosses its boundaries.
3673        */
3674        x=event.xmotion.x;
3675        y=event.xmotion.y;
3676        if (windows->info.mapped != MagickFalse)
3677          {
3678            if ((x < (int) (windows->info.x+windows->info.width)) &&
3679                (y < (int) (windows->info.y+windows->info.height)))
3680              (void) XWithdrawWindow(display,windows->info.id,
3681                windows->info.screen);
3682          }
3683        else
3684          if ((x > (int) (windows->info.x+windows->info.width)) ||
3685              (y > (int) (windows->info.y+windows->info.height)))
3686            (void) XMapWindow(display,windows->info.id);
3687        break;
3688      }
3689      default:
3690        break;
3691    }
3692    if (event.xany.window == windows->magnify.id)
3693      {
3694        x=windows->magnify.x-windows->image.x;
3695        y=windows->magnify.y-windows->image.y;
3696      }
3697    x_offset=x;
3698    y_offset=y;
3699    if ((state & UpdateConfigurationState) != 0)
3700      {
3701        CacheView
3702          *image_view;
3703
3704        int
3705          x,
3706          y;
3707
3708        /*
3709          Pixel edit is relative to image configuration.
3710        */
3711        (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3712          MagickTrue);
3713        color=windows->pixel_info->pen_colors[pen_id];
3714        XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3715        width=(unsigned int) (*image)->columns;
3716        height=(unsigned int) (*image)->rows;
3717        x=0;
3718        y=0;
3719        if (windows->image.crop_geometry != (char *) NULL)
3720          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3721            &width,&height);
3722        x_offset=(int)
3723          (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3724        y_offset=(int)
3725          (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3726        if ((x_offset < 0) || (y_offset < 0))
3727          continue;
3728        if ((x_offset >= (int) (*image)->columns) ||
3729            (y_offset >= (int) (*image)->rows))
3730          continue;
3731        image_view=AcquireCacheView(*image);
3732        switch (method)
3733        {
3734          case PointMethod:
3735          default:
3736          {
3737            /*
3738              Update color information using point algorithm.
3739            */
3740            if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3741              return(MagickFalse);
3742            q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3743              (ssize_t) y_offset,1,1,exception);
3744            if (q == (Quantum *) NULL)
3745              break;
3746            SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3747            SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3748            SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3749            (void) SyncCacheViewAuthenticPixels(image_view,exception);
3750            break;
3751          }
3752          case ReplaceMethod:
3753          {
3754            PixelPacket
3755              pixel,
3756              target;
3757
3758            /*
3759              Update color information using replace algorithm.
3760            */
3761            (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
3762              (ssize_t) y_offset,&target,exception);
3763            if ((*image)->storage_class == DirectClass)
3764              {
3765                for (y=0; y < (int) (*image)->rows; y++)
3766                {
3767                  q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3768                    (*image)->columns,1,exception);
3769                  if (q == (Quantum *) NULL)
3770                    break;
3771                  for (x=0; x < (int) (*image)->columns; x++)
3772                  {
3773                    GetPixelPacket(*image,q,&pixel);
3774                    if (IsFuzzyEquivalencePixelPacket(*image,&pixel,&target))
3775                      {
3776                        SetPixelRed(*image,ScaleShortToQuantum(
3777                          color.red),q);
3778                        SetPixelGreen(*image,ScaleShortToQuantum(
3779                          color.green),q);
3780                        SetPixelBlue(*image,ScaleShortToQuantum(
3781                          color.blue),q);
3782                      }
3783                    q+=GetPixelChannels(*image);
3784                  }
3785                  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3786                    break;
3787                }
3788              }
3789            else
3790              {
3791                for (i=0; i < (ssize_t) (*image)->colors; i++)
3792                  if (IsFuzzyEquivalencePixelPacket(*image,(*image)->colormap+i,&target))
3793                    {
3794                      (*image)->colormap[i].red=ScaleShortToQuantum(
3795                        color.red);
3796                      (*image)->colormap[i].green=ScaleShortToQuantum(
3797                        color.green);
3798                      (*image)->colormap[i].blue=ScaleShortToQuantum(
3799                        color.blue);
3800                    }
3801                (void) SyncImage(*image);
3802              }
3803            break;
3804          }
3805          case FloodfillMethod:
3806          case FillToBorderMethod:
3807          {
3808            DrawInfo
3809              *draw_info;
3810
3811            PixelInfo
3812              target;
3813
3814            /*
3815              Update color information using floodfill algorithm.
3816            */
3817            (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
3818              (ssize_t) y_offset,&target,exception);
3819            if (method == FillToBorderMethod)
3820              {
3821                target.red=(MagickRealType)
3822                  ScaleShortToQuantum(border_color.red);
3823                target.green=(MagickRealType)
3824                  ScaleShortToQuantum(border_color.green);
3825                target.blue=(MagickRealType)
3826                  ScaleShortToQuantum(border_color.blue);
3827              }
3828            draw_info=CloneDrawInfo(resource_info->image_info,
3829              (DrawInfo *) NULL);
3830            (void) QueryColorDatabase(resource_info->pen_colors[pen_id],
3831              &draw_info->fill,exception);
3832            (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
3833              x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
3834              MagickFalse : MagickTrue,exception);
3835            draw_info=DestroyDrawInfo(draw_info);
3836            break;
3837          }
3838          case ResetMethod:
3839          {
3840            /*
3841              Update color information using reset algorithm.
3842            */
3843            if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3844              return(MagickFalse);
3845            for (y=0; y < (int) (*image)->rows; y++)
3846            {
3847              q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3848                (*image)->columns,1,exception);
3849              if (q == (Quantum *) NULL)
3850                break;
3851              for (x=0; x < (int) (*image)->columns; x++)
3852              {
3853                SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3854                SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3855                SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3856                q+=GetPixelChannels(*image);
3857              }
3858              if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3859                break;
3860            }
3861            break;
3862          }
3863        }
3864        image_view=DestroyCacheView(image_view);
3865        state&=(~UpdateConfigurationState);
3866      }
3867  } while ((state & ExitState) == 0);
3868  (void) XSelectInput(display,windows->image.id,
3869    windows->image.attributes.event_mask);
3870  XSetCursorState(display,windows,MagickFalse);
3871  (void) XFreeCursor(display,cursor);
3872  return(MagickTrue);
3873}
3874
3875/*
3876%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3877%                                                                             %
3878%                                                                             %
3879%                                                                             %
3880+   X C o m p o s i t e I m a g e                                             %
3881%                                                                             %
3882%                                                                             %
3883%                                                                             %
3884%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3885%
3886%  XCompositeImage() requests an image name from the user, reads the image and
3887%  composites it with the X window image at a location the user chooses with
3888%  the pointer.
3889%
3890%  The format of the XCompositeImage method is:
3891%
3892%      MagickBooleanType XCompositeImage(Display *display,
3893%        XResourceInfo *resource_info,XWindows *windows,Image *image,
3894%        ExceptionInfo *exception)
3895%
3896%  A description of each parameter follows:
3897%
3898%    o display: Specifies a connection to an X server;  returned from
3899%      XOpenDisplay.
3900%
3901%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3902%
3903%    o windows: Specifies a pointer to a XWindows structure.
3904%
3905%    o image: the image; returned from ReadImage.
3906%
3907%    o exception: return any errors or warnings in this structure.
3908%
3909*/
3910static MagickBooleanType XCompositeImage(Display *display,
3911  XResourceInfo *resource_info,XWindows *windows,Image *image,
3912  ExceptionInfo *exception)
3913{
3914  static char
3915    displacement_geometry[MaxTextExtent] = "30x30",
3916    filename[MaxTextExtent] = "\0";
3917
3918  static const char
3919    *CompositeMenu[] =
3920    {
3921      "Operators",
3922      "Dissolve",
3923      "Displace",
3924      "Help",
3925      "Dismiss",
3926      (char *) NULL
3927    };
3928
3929  static CompositeOperator
3930    compose = CopyCompositeOp;
3931
3932  static const ModeType
3933    CompositeCommands[] =
3934    {
3935      CompositeOperatorsCommand,
3936      CompositeDissolveCommand,
3937      CompositeDisplaceCommand,
3938      CompositeHelpCommand,
3939      CompositeDismissCommand
3940    };
3941
3942  char
3943    text[MaxTextExtent];
3944
3945  Cursor
3946    cursor;
3947
3948  Image
3949    *composite_image;
3950
3951  int
3952    entry,
3953    id,
3954    x,
3955    y;
3956
3957  MagickRealType
3958    blend,
3959    scale_factor;
3960
3961  RectangleInfo
3962    highlight_info,
3963    composite_info;
3964
3965  unsigned int
3966    height,
3967    width;
3968
3969  size_t
3970    state;
3971
3972  XEvent
3973    event;
3974
3975  /*
3976    Request image file name from user.
3977  */
3978  XFileBrowserWidget(display,windows,"Composite",filename);
3979  if (*filename == '\0')
3980    return(MagickTrue);
3981  /*
3982    Read image.
3983  */
3984  XSetCursorState(display,windows,MagickTrue);
3985  XCheckRefreshWindows(display,windows);
3986  (void) CopyMagickString(resource_info->image_info->filename,filename,
3987    MaxTextExtent);
3988  composite_image=ReadImage(resource_info->image_info,exception);
3989  CatchException(exception);
3990  XSetCursorState(display,windows,MagickFalse);
3991  if (composite_image == (Image *) NULL)
3992    return(MagickFalse);
3993  /*
3994    Map Command widget.
3995  */
3996  (void) CloneString(&windows->command.name,"Composite");
3997  windows->command.data=1;
3998  (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
3999  (void) XMapRaised(display,windows->command.id);
4000  XClientMessage(display,windows->image.id,windows->im_protocols,
4001    windows->im_update_widget,CurrentTime);
4002  /*
4003    Track pointer until button 1 is pressed.
4004  */
4005  XQueryPosition(display,windows->image.id,&x,&y);
4006  (void) XSelectInput(display,windows->image.id,
4007    windows->image.attributes.event_mask | PointerMotionMask);
4008  composite_info.x=(ssize_t) windows->image.x+x;
4009  composite_info.y=(ssize_t) windows->image.y+y;
4010  composite_info.width=0;
4011  composite_info.height=0;
4012  cursor=XCreateFontCursor(display,XC_ul_angle);
4013  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4014  blend=0.0;
4015  state=DefaultState;
4016  do
4017  {
4018    if (windows->info.mapped != MagickFalse)
4019      {
4020        /*
4021          Display pointer position.
4022        */
4023        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4024          (long) composite_info.x,(long) composite_info.y);
4025        XInfoWidget(display,windows,text);
4026      }
4027    highlight_info=composite_info;
4028    highlight_info.x=composite_info.x-windows->image.x;
4029    highlight_info.y=composite_info.y-windows->image.y;
4030    XHighlightRectangle(display,windows->image.id,
4031      windows->image.highlight_context,&highlight_info);
4032    /*
4033      Wait for next event.
4034    */
4035    XScreenEvent(display,windows,&event);
4036    XHighlightRectangle(display,windows->image.id,
4037      windows->image.highlight_context,&highlight_info);
4038    if (event.xany.window == windows->command.id)
4039      {
4040        /*
4041          Select a command from the Command widget.
4042        */
4043        id=XCommandWidget(display,windows,CompositeMenu,&event);
4044        if (id < 0)
4045          continue;
4046        switch (CompositeCommands[id])
4047        {
4048          case CompositeOperatorsCommand:
4049          {
4050            char
4051              command[MaxTextExtent],
4052              **operators;
4053
4054            /*
4055              Select a command from the pop-up menu.
4056            */
4057            operators=GetCommandOptions(MagickComposeOptions);
4058            if (operators == (char **) NULL)
4059              break;
4060            entry=XMenuWidget(display,windows,CompositeMenu[id],
4061              (const char **) operators,command);
4062            if (entry >= 0)
4063              compose=(CompositeOperator) ParseCommandOption(
4064                MagickComposeOptions,MagickFalse,operators[entry]);
4065            operators=DestroyStringList(operators);
4066            break;
4067          }
4068          case CompositeDissolveCommand:
4069          {
4070            static char
4071              factor[MaxTextExtent] = "20.0";
4072
4073            /*
4074              Dissolve the two images a given percent.
4075            */
4076            (void) XSetFunction(display,windows->image.highlight_context,
4077              GXcopy);
4078            (void) XDialogWidget(display,windows,"Dissolve",
4079              "Enter the blend factor (0.0 - 99.9%):",factor);
4080            (void) XSetFunction(display,windows->image.highlight_context,
4081              GXinvert);
4082            if (*factor == '\0')
4083              break;
4084            blend=InterpretLocaleValue(factor,(char **) NULL);
4085            compose=DissolveCompositeOp;
4086            break;
4087          }
4088          case CompositeDisplaceCommand:
4089          {
4090            /*
4091              Get horizontal and vertical scale displacement geometry.
4092            */
4093            (void) XSetFunction(display,windows->image.highlight_context,
4094              GXcopy);
4095            (void) XDialogWidget(display,windows,"Displace",
4096              "Enter the horizontal and vertical scale:",displacement_geometry);
4097            (void) XSetFunction(display,windows->image.highlight_context,
4098              GXinvert);
4099            if (*displacement_geometry == '\0')
4100              break;
4101            compose=DisplaceCompositeOp;
4102            break;
4103          }
4104          case CompositeHelpCommand:
4105          {
4106            (void) XSetFunction(display,windows->image.highlight_context,
4107              GXcopy);
4108            XTextViewWidget(display,resource_info,windows,MagickFalse,
4109              "Help Viewer - Image Composite",ImageCompositeHelp);
4110            (void) XSetFunction(display,windows->image.highlight_context,
4111              GXinvert);
4112            break;
4113          }
4114          case CompositeDismissCommand:
4115          {
4116            /*
4117              Prematurely exit.
4118            */
4119            state|=EscapeState;
4120            state|=ExitState;
4121            break;
4122          }
4123          default:
4124            break;
4125        }
4126        continue;
4127      }
4128    switch (event.type)
4129    {
4130      case ButtonPress:
4131      {
4132        if (image->debug != MagickFalse)
4133          (void) LogMagickEvent(X11Event,GetMagickModule(),
4134            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4135            event.xbutton.button,event.xbutton.x,event.xbutton.y);
4136        if (event.xbutton.button != Button1)
4137          break;
4138        if (event.xbutton.window != windows->image.id)
4139          break;
4140        /*
4141          Change cursor.
4142        */
4143        composite_info.width=composite_image->columns;
4144        composite_info.height=composite_image->rows;
4145        (void) XCheckDefineCursor(display,windows->image.id,cursor);
4146        composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4147        composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4148        break;
4149      }
4150      case ButtonRelease:
4151      {
4152        if (image->debug != MagickFalse)
4153          (void) LogMagickEvent(X11Event,GetMagickModule(),
4154            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4155            event.xbutton.button,event.xbutton.x,event.xbutton.y);
4156        if (event.xbutton.button != Button1)
4157          break;
4158        if (event.xbutton.window != windows->image.id)
4159          break;
4160        if ((composite_info.width != 0) && (composite_info.height != 0))
4161          {
4162            /*
4163              User has selected the location of the composite image.
4164            */
4165            composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4166            composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4167            state|=ExitState;
4168          }
4169        break;
4170      }
4171      case Expose:
4172        break;
4173      case KeyPress:
4174      {
4175        char
4176          command[MaxTextExtent];
4177
4178        KeySym
4179          key_symbol;
4180
4181        int
4182          length;
4183
4184        if (event.xkey.window != windows->image.id)
4185          break;
4186        /*
4187          Respond to a user key press.
4188        */
4189        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4190          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4191        *(command+length)='\0';
4192        if (image->debug != MagickFalse)
4193          (void) LogMagickEvent(X11Event,GetMagickModule(),
4194            "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4195        switch ((int) key_symbol)
4196        {
4197          case XK_Escape:
4198          case XK_F20:
4199          {
4200            /*
4201              Prematurely exit.
4202            */
4203            composite_image=DestroyImage(composite_image);
4204            state|=EscapeState;
4205            state|=ExitState;
4206            break;
4207          }
4208          case XK_F1:
4209          case XK_Help:
4210          {
4211            (void) XSetFunction(display,windows->image.highlight_context,
4212              GXcopy);
4213            XTextViewWidget(display,resource_info,windows,MagickFalse,
4214              "Help Viewer - Image Composite",ImageCompositeHelp);
4215            (void) XSetFunction(display,windows->image.highlight_context,
4216              GXinvert);
4217            break;
4218          }
4219          default:
4220          {
4221            (void) XBell(display,0);
4222            break;
4223          }
4224        }
4225        break;
4226      }
4227      case MotionNotify:
4228      {
4229        /*
4230          Map and unmap Info widget as text cursor crosses its boundaries.
4231        */
4232        x=event.xmotion.x;
4233        y=event.xmotion.y;
4234        if (windows->info.mapped != MagickFalse)
4235          {
4236            if ((x < (int) (windows->info.x+windows->info.width)) &&
4237                (y < (int) (windows->info.y+windows->info.height)))
4238              (void) XWithdrawWindow(display,windows->info.id,
4239                windows->info.screen);
4240          }
4241        else
4242          if ((x > (int) (windows->info.x+windows->info.width)) ||
4243              (y > (int) (windows->info.y+windows->info.height)))
4244            (void) XMapWindow(display,windows->info.id);
4245        composite_info.x=(ssize_t) windows->image.x+x;
4246        composite_info.y=(ssize_t) windows->image.y+y;
4247        break;
4248      }
4249      default:
4250      {
4251        if (image->debug != MagickFalse)
4252          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4253            event.type);
4254        break;
4255      }
4256    }
4257  } while ((state & ExitState) == 0);
4258  (void) XSelectInput(display,windows->image.id,
4259    windows->image.attributes.event_mask);
4260  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4261  XSetCursorState(display,windows,MagickFalse);
4262  (void) XFreeCursor(display,cursor);
4263  if ((state & EscapeState) != 0)
4264    return(MagickTrue);
4265  /*
4266    Image compositing is relative to image configuration.
4267  */
4268  XSetCursorState(display,windows,MagickTrue);
4269  XCheckRefreshWindows(display,windows);
4270  width=(unsigned int) image->columns;
4271  height=(unsigned int) image->rows;
4272  x=0;
4273  y=0;
4274  if (windows->image.crop_geometry != (char *) NULL)
4275    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4276  scale_factor=(MagickRealType) width/windows->image.ximage->width;
4277  composite_info.x+=x;
4278  composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4279  composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4280  scale_factor=(MagickRealType) height/windows->image.ximage->height;
4281  composite_info.y+=y;
4282  composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4283  composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4284  if ((composite_info.width != composite_image->columns) ||
4285      (composite_info.height != composite_image->rows))
4286    {
4287      Image
4288        *resize_image;
4289
4290      /*
4291        Scale composite image.
4292      */
4293      resize_image=ResizeImage(composite_image,composite_info.width,
4294        composite_info.height,composite_image->filter,composite_image->blur,
4295        exception);
4296      composite_image=DestroyImage(composite_image);
4297      if (resize_image == (Image *) NULL)
4298        {
4299          XSetCursorState(display,windows,MagickFalse);
4300          return(MagickFalse);
4301        }
4302      composite_image=resize_image;
4303    }
4304  if (compose == DisplaceCompositeOp)
4305    (void) SetImageArtifact(composite_image,"compose:args",
4306      displacement_geometry);
4307  if (blend != 0.0)
4308    {
4309      CacheView
4310        *image_view;
4311
4312      int
4313        y;
4314
4315      Quantum
4316        opacity;
4317
4318      register int
4319        x;
4320
4321      register Quantum
4322        *q;
4323
4324      /*
4325        Create mattes for blending.
4326      */
4327      (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,exception);
4328      opacity=(Quantum) (ScaleQuantumToChar((Quantum) QuantumRange)-
4329        ((ssize_t) ScaleQuantumToChar((Quantum) QuantumRange)*blend)/100);
4330      if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
4331        return(MagickFalse);
4332      image->matte=MagickTrue;
4333      image_view=AcquireCacheView(image);
4334      for (y=0; y < (int) image->rows; y++)
4335      {
4336        q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4337          exception);
4338        if (q == (Quantum *) NULL)
4339          break;
4340        for (x=0; x < (int) image->columns; x++)
4341        {
4342          SetPixelAlpha(image,opacity,q);
4343          q+=GetPixelChannels(image);
4344        }
4345        if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4346          break;
4347      }
4348      image_view=DestroyCacheView(image_view);
4349    }
4350  /*
4351    Composite image with X Image window.
4352  */
4353  (void) CompositeImage(image,compose,composite_image,composite_info.x,
4354    composite_info.y);
4355  composite_image=DestroyImage(composite_image);
4356  XSetCursorState(display,windows,MagickFalse);
4357  /*
4358    Update image configuration.
4359  */
4360  XConfigureImageColormap(display,resource_info,windows,image);
4361  (void) XConfigureImage(display,resource_info,windows,image,exception);
4362  return(MagickTrue);
4363}
4364
4365/*
4366%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4367%                                                                             %
4368%                                                                             %
4369%                                                                             %
4370+   X C o n f i g u r e I m a g e                                             %
4371%                                                                             %
4372%                                                                             %
4373%                                                                             %
4374%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4375%
4376%  XConfigureImage() creates a new X image.  It also notifies the window
4377%  manager of the new image size and configures the transient widows.
4378%
4379%  The format of the XConfigureImage method is:
4380%
4381%      MagickBooleanType XConfigureImage(Display *display,
4382%        XResourceInfo *resource_info,XWindows *windows,Image *image,
4383%        ExceptionInfo *exception)
4384%
4385%  A description of each parameter follows:
4386%
4387%    o display: Specifies a connection to an X server; returned from
4388%      XOpenDisplay.
4389%
4390%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4391%
4392%    o windows: Specifies a pointer to a XWindows structure.
4393%
4394%    o image: the image.
4395%
4396%    o exception: return any errors or warnings in this structure.
4397%
4398%    o exception: return any errors or warnings in this structure.
4399%
4400*/
4401static MagickBooleanType XConfigureImage(Display *display,
4402  XResourceInfo *resource_info,XWindows *windows,Image *image,
4403  ExceptionInfo *exception)
4404{
4405  char
4406    geometry[MaxTextExtent];
4407
4408  MagickStatusType
4409    status;
4410
4411  size_t
4412    mask,
4413    height,
4414    width;
4415
4416  ssize_t
4417    x,
4418    y;
4419
4420  XSizeHints
4421    *size_hints;
4422
4423  XWindowChanges
4424    window_changes;
4425
4426  /*
4427    Dismiss if window dimensions are zero.
4428  */
4429  width=(unsigned int) windows->image.window_changes.width;
4430  height=(unsigned int) windows->image.window_changes.height;
4431  if (image->debug != MagickFalse)
4432    (void) LogMagickEvent(X11Event,GetMagickModule(),
4433      "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4434      windows->image.ximage->height,(double) width,(double) height);
4435  if ((width*height) == 0)
4436    return(MagickTrue);
4437  x=0;
4438  y=0;
4439  /*
4440    Resize image to fit Image window dimensions.
4441  */
4442  XSetCursorState(display,windows,MagickTrue);
4443  (void) XFlush(display);
4444  if (((int) width != windows->image.ximage->width) ||
4445      ((int) height != windows->image.ximage->height))
4446    image->taint=MagickTrue;
4447  windows->magnify.x=(int)
4448    width*windows->magnify.x/windows->image.ximage->width;
4449  windows->magnify.y=(int)
4450    height*windows->magnify.y/windows->image.ximage->height;
4451  windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4452  windows->image.y=(int)
4453    (height*windows->image.y/windows->image.ximage->height);
4454  status=XMakeImage(display,resource_info,&windows->image,image,
4455    (unsigned int) width,(unsigned int) height,exception);
4456  if (status == MagickFalse)
4457    XNoticeWidget(display,windows,"Unable to configure X image:",
4458      windows->image.name);
4459  /*
4460    Notify window manager of the new configuration.
4461  */
4462  if (resource_info->image_geometry != (char *) NULL)
4463    (void) FormatLocaleString(geometry,MaxTextExtent,"%s>!",
4464      resource_info->image_geometry);
4465  else
4466    (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
4467      XDisplayWidth(display,windows->image.screen),
4468      XDisplayHeight(display,windows->image.screen));
4469  (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4470  window_changes.width=(int) width;
4471  if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4472    window_changes.width=XDisplayWidth(display,windows->image.screen);
4473  window_changes.height=(int) height;
4474  if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4475    window_changes.height=XDisplayHeight(display,windows->image.screen);
4476  mask=(size_t) (CWWidth | CWHeight);
4477  if (resource_info->backdrop)
4478    {
4479      mask|=CWX | CWY;
4480      window_changes.x=(int)
4481        ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4482      window_changes.y=(int)
4483        ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4484    }
4485  (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4486    (unsigned int) mask,&window_changes);
4487  (void) XClearWindow(display,windows->image.id);
4488  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4489  /*
4490    Update Magnify window configuration.
4491  */
4492  if (windows->magnify.mapped != MagickFalse)
4493    XMakeMagnifyImage(display,windows);
4494  windows->pan.crop_geometry=windows->image.crop_geometry;
4495  XBestIconSize(display,&windows->pan,image);
4496  while (((windows->pan.width << 1) < MaxIconSize) &&
4497         ((windows->pan.height << 1) < MaxIconSize))
4498  {
4499    windows->pan.width<<=1;
4500    windows->pan.height<<=1;
4501  }
4502  if (windows->pan.geometry != (char *) NULL)
4503    (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4504      &windows->pan.width,&windows->pan.height);
4505  window_changes.width=(int) windows->pan.width;
4506  window_changes.height=(int) windows->pan.height;
4507  size_hints=XAllocSizeHints();
4508  if (size_hints != (XSizeHints *) NULL)
4509    {
4510      /*
4511        Set new size hints.
4512      */
4513      size_hints->flags=PSize | PMinSize | PMaxSize;
4514      size_hints->width=window_changes.width;
4515      size_hints->height=window_changes.height;
4516      size_hints->min_width=size_hints->width;
4517      size_hints->min_height=size_hints->height;
4518      size_hints->max_width=size_hints->width;
4519      size_hints->max_height=size_hints->height;
4520      (void) XSetNormalHints(display,windows->pan.id,size_hints);
4521      (void) XFree((void *) size_hints);
4522    }
4523  (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4524    (unsigned int) (CWWidth | CWHeight),&window_changes);
4525  /*
4526    Update icon window configuration.
4527  */
4528  windows->icon.crop_geometry=windows->image.crop_geometry;
4529  XBestIconSize(display,&windows->icon,image);
4530  window_changes.width=(int) windows->icon.width;
4531  window_changes.height=(int) windows->icon.height;
4532  (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4533    (unsigned int) (CWWidth | CWHeight),&window_changes);
4534  XSetCursorState(display,windows,MagickFalse);
4535  return(status != 0 ? MagickTrue : MagickFalse);
4536}
4537
4538/*
4539%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4540%                                                                             %
4541%                                                                             %
4542%                                                                             %
4543+   X C r o p I m a g e                                                       %
4544%                                                                             %
4545%                                                                             %
4546%                                                                             %
4547%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4548%
4549%  XCropImage() allows the user to select a region of the image and crop, copy,
4550%  or cut it.  For copy or cut, the image can subsequently be composited onto
4551%  the image with XPasteImage.
4552%
4553%  The format of the XCropImage method is:
4554%
4555%      MagickBooleanType XCropImage(Display *display,
4556%        XResourceInfo *resource_info,XWindows *windows,Image *image,
4557%        const ClipboardMode mode,ExceptionInfo *exception)
4558%
4559%  A description of each parameter follows:
4560%
4561%    o display: Specifies a connection to an X server; returned from
4562%      XOpenDisplay.
4563%
4564%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4565%
4566%    o windows: Specifies a pointer to a XWindows structure.
4567%
4568%    o image: the image; returned from ReadImage.
4569%
4570%    o mode: This unsigned value specified whether the image should be
4571%      cropped, copied, or cut.
4572%
4573%    o exception: return any errors or warnings in this structure.
4574%
4575*/
4576static MagickBooleanType XCropImage(Display *display,
4577  XResourceInfo *resource_info,XWindows *windows,Image *image,
4578  const ClipboardMode mode,ExceptionInfo *exception)
4579{
4580  static const char
4581    *CropModeMenu[] =
4582    {
4583      "Help",
4584      "Dismiss",
4585      (char *) NULL
4586    },
4587    *RectifyModeMenu[] =
4588    {
4589      "Crop",
4590      "Help",
4591      "Dismiss",
4592      (char *) NULL
4593    };
4594
4595  static const ModeType
4596    CropCommands[] =
4597    {
4598      CropHelpCommand,
4599      CropDismissCommand
4600    },
4601    RectifyCommands[] =
4602    {
4603      RectifyCopyCommand,
4604      RectifyHelpCommand,
4605      RectifyDismissCommand
4606    };
4607
4608  CacheView
4609    *image_view;
4610
4611  char
4612    command[MaxTextExtent],
4613    text[MaxTextExtent];
4614
4615  Cursor
4616    cursor;
4617
4618  int
4619    id,
4620    x,
4621    y;
4622
4623  KeySym
4624    key_symbol;
4625
4626  Image
4627    *crop_image;
4628
4629  MagickRealType
4630    scale_factor;
4631
4632  RectangleInfo
4633    crop_info,
4634    highlight_info;
4635
4636  register Quantum
4637    *q;
4638
4639  unsigned int
4640    height,
4641    width;
4642
4643  size_t
4644    state;
4645
4646  XEvent
4647    event;
4648
4649  /*
4650    Map Command widget.
4651  */
4652  switch (mode)
4653  {
4654    case CopyMode:
4655    {
4656      (void) CloneString(&windows->command.name,"Copy");
4657      break;
4658    }
4659    case CropMode:
4660    {
4661      (void) CloneString(&windows->command.name,"Crop");
4662      break;
4663    }
4664    case CutMode:
4665    {
4666      (void) CloneString(&windows->command.name,"Cut");
4667      break;
4668    }
4669  }
4670  RectifyModeMenu[0]=windows->command.name;
4671  windows->command.data=0;
4672  (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4673  (void) XMapRaised(display,windows->command.id);
4674  XClientMessage(display,windows->image.id,windows->im_protocols,
4675    windows->im_update_widget,CurrentTime);
4676  /*
4677    Track pointer until button 1 is pressed.
4678  */
4679  XQueryPosition(display,windows->image.id,&x,&y);
4680  (void) XSelectInput(display,windows->image.id,
4681    windows->image.attributes.event_mask | PointerMotionMask);
4682  crop_info.x=(ssize_t) windows->image.x+x;
4683  crop_info.y=(ssize_t) windows->image.y+y;
4684  crop_info.width=0;
4685  crop_info.height=0;
4686  cursor=XCreateFontCursor(display,XC_fleur);
4687  state=DefaultState;
4688  do
4689  {
4690    if (windows->info.mapped != MagickFalse)
4691      {
4692        /*
4693          Display pointer position.
4694        */
4695        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4696          (long) crop_info.x,(long) crop_info.y);
4697        XInfoWidget(display,windows,text);
4698      }
4699    /*
4700      Wait for next event.
4701    */
4702    XScreenEvent(display,windows,&event);
4703    if (event.xany.window == windows->command.id)
4704      {
4705        /*
4706          Select a command from the Command widget.
4707        */
4708        id=XCommandWidget(display,windows,CropModeMenu,&event);
4709        if (id < 0)
4710          continue;
4711        switch (CropCommands[id])
4712        {
4713          case CropHelpCommand:
4714          {
4715            switch (mode)
4716            {
4717              case CopyMode:
4718              {
4719                XTextViewWidget(display,resource_info,windows,MagickFalse,
4720                  "Help Viewer - Image Copy",ImageCopyHelp);
4721                break;
4722              }
4723              case CropMode:
4724              {
4725                XTextViewWidget(display,resource_info,windows,MagickFalse,
4726                  "Help Viewer - Image Crop",ImageCropHelp);
4727                break;
4728              }
4729              case CutMode:
4730              {
4731                XTextViewWidget(display,resource_info,windows,MagickFalse,
4732                  "Help Viewer - Image Cut",ImageCutHelp);
4733                break;
4734              }
4735            }
4736            break;
4737          }
4738          case CropDismissCommand:
4739          {
4740            /*
4741              Prematurely exit.
4742            */
4743            state|=EscapeState;
4744            state|=ExitState;
4745            break;
4746          }
4747          default:
4748            break;
4749        }
4750        continue;
4751      }
4752    switch (event.type)
4753    {
4754      case ButtonPress:
4755      {
4756        if (event.xbutton.button != Button1)
4757          break;
4758        if (event.xbutton.window != windows->image.id)
4759          break;
4760        /*
4761          Note first corner of cropping rectangle-- exit loop.
4762        */
4763        (void) XCheckDefineCursor(display,windows->image.id,cursor);
4764        crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4765        crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4766        state|=ExitState;
4767        break;
4768      }
4769      case ButtonRelease:
4770        break;
4771      case Expose:
4772        break;
4773      case KeyPress:
4774      {
4775        if (event.xkey.window != windows->image.id)
4776          break;
4777        /*
4778          Respond to a user key press.
4779        */
4780        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4781          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4782        switch ((int) key_symbol)
4783        {
4784          case XK_Escape:
4785          case XK_F20:
4786          {
4787            /*
4788              Prematurely exit.
4789            */
4790            state|=EscapeState;
4791            state|=ExitState;
4792            break;
4793          }
4794          case XK_F1:
4795          case XK_Help:
4796          {
4797            switch (mode)
4798            {
4799              case CopyMode:
4800              {
4801                XTextViewWidget(display,resource_info,windows,MagickFalse,
4802                  "Help Viewer - Image Copy",ImageCopyHelp);
4803                break;
4804              }
4805              case CropMode:
4806              {
4807                XTextViewWidget(display,resource_info,windows,MagickFalse,
4808                  "Help Viewer - Image Crop",ImageCropHelp);
4809                break;
4810              }
4811              case CutMode:
4812              {
4813                XTextViewWidget(display,resource_info,windows,MagickFalse,
4814                  "Help Viewer - Image Cut",ImageCutHelp);
4815                break;
4816              }
4817            }
4818            break;
4819          }
4820          default:
4821          {
4822            (void) XBell(display,0);
4823            break;
4824          }
4825        }
4826        break;
4827      }
4828      case MotionNotify:
4829      {
4830        if (event.xmotion.window != windows->image.id)
4831          break;
4832        /*
4833          Map and unmap Info widget as text cursor crosses its boundaries.
4834        */
4835        x=event.xmotion.x;
4836        y=event.xmotion.y;
4837        if (windows->info.mapped != MagickFalse)
4838          {
4839            if ((x < (int) (windows->info.x+windows->info.width)) &&
4840                (y < (int) (windows->info.y+windows->info.height)))
4841              (void) XWithdrawWindow(display,windows->info.id,
4842                windows->info.screen);
4843          }
4844        else
4845          if ((x > (int) (windows->info.x+windows->info.width)) ||
4846              (y > (int) (windows->info.y+windows->info.height)))
4847            (void) XMapWindow(display,windows->info.id);
4848        crop_info.x=(ssize_t) windows->image.x+x;
4849        crop_info.y=(ssize_t) windows->image.y+y;
4850        break;
4851      }
4852      default:
4853        break;
4854    }
4855  } while ((state & ExitState) == 0);
4856  (void) XSelectInput(display,windows->image.id,
4857    windows->image.attributes.event_mask);
4858  if ((state & EscapeState) != 0)
4859    {
4860      /*
4861        User want to exit without cropping.
4862      */
4863      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4864      (void) XFreeCursor(display,cursor);
4865      return(MagickTrue);
4866    }
4867  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4868  do
4869  {
4870    /*
4871      Size rectangle as pointer moves until the mouse button is released.
4872    */
4873    x=(int) crop_info.x;
4874    y=(int) crop_info.y;
4875    crop_info.width=0;
4876    crop_info.height=0;
4877    state=DefaultState;
4878    do
4879    {
4880      highlight_info=crop_info;
4881      highlight_info.x=crop_info.x-windows->image.x;
4882      highlight_info.y=crop_info.y-windows->image.y;
4883      if ((highlight_info.width > 3) && (highlight_info.height > 3))
4884        {
4885          /*
4886            Display info and draw cropping rectangle.
4887          */
4888          if (windows->info.mapped == MagickFalse)
4889            (void) XMapWindow(display,windows->info.id);
4890          (void) FormatLocaleString(text,MaxTextExtent,
4891            " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4892            crop_info.height,(double) crop_info.x,(double) crop_info.y);
4893          XInfoWidget(display,windows,text);
4894          XHighlightRectangle(display,windows->image.id,
4895            windows->image.highlight_context,&highlight_info);
4896        }
4897      else
4898        if (windows->info.mapped != MagickFalse)
4899          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4900      /*
4901        Wait for next event.
4902      */
4903      XScreenEvent(display,windows,&event);
4904      if ((highlight_info.width > 3) && (highlight_info.height > 3))
4905        XHighlightRectangle(display,windows->image.id,
4906          windows->image.highlight_context,&highlight_info);
4907      switch (event.type)
4908      {
4909        case ButtonPress:
4910        {
4911          crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4912          crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4913          break;
4914        }
4915        case ButtonRelease:
4916        {
4917          /*
4918            User has committed to cropping rectangle.
4919          */
4920          crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4921          crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4922          XSetCursorState(display,windows,MagickFalse);
4923          state|=ExitState;
4924          windows->command.data=0;
4925          (void) XCommandWidget(display,windows,RectifyModeMenu,
4926            (XEvent *) NULL);
4927          break;
4928        }
4929        case Expose:
4930          break;
4931        case MotionNotify:
4932        {
4933          crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4934          crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4935        }
4936        default:
4937          break;
4938      }
4939      if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4940          ((state & ExitState) != 0))
4941        {
4942          /*
4943            Check boundary conditions.
4944          */
4945          if (crop_info.x < 0)
4946            crop_info.x=0;
4947          else
4948            if (crop_info.x > (ssize_t) windows->image.ximage->width)
4949              crop_info.x=(ssize_t) windows->image.ximage->width;
4950          if ((int) crop_info.x < x)
4951            crop_info.width=(unsigned int) (x-crop_info.x);
4952          else
4953            {
4954              crop_info.width=(unsigned int) (crop_info.x-x);
4955              crop_info.x=(ssize_t) x;
4956            }
4957          if (crop_info.y < 0)
4958            crop_info.y=0;
4959          else
4960            if (crop_info.y > (ssize_t) windows->image.ximage->height)
4961              crop_info.y=(ssize_t) windows->image.ximage->height;
4962          if ((int) crop_info.y < y)
4963            crop_info.height=(unsigned int) (y-crop_info.y);
4964          else
4965            {
4966              crop_info.height=(unsigned int) (crop_info.y-y);
4967              crop_info.y=(ssize_t) y;
4968            }
4969        }
4970    } while ((state & ExitState) == 0);
4971    /*
4972      Wait for user to grab a corner of the rectangle or press return.
4973    */
4974    state=DefaultState;
4975    (void) XMapWindow(display,windows->info.id);
4976    do
4977    {
4978      if (windows->info.mapped != MagickFalse)
4979        {
4980          /*
4981            Display pointer position.
4982          */
4983          (void) FormatLocaleString(text,MaxTextExtent,
4984            " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4985            crop_info.height,(double) crop_info.x,(double) crop_info.y);
4986          XInfoWidget(display,windows,text);
4987        }
4988      highlight_info=crop_info;
4989      highlight_info.x=crop_info.x-windows->image.x;
4990      highlight_info.y=crop_info.y-windows->image.y;
4991      if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4992        {
4993          state|=EscapeState;
4994          state|=ExitState;
4995          break;
4996        }
4997      XHighlightRectangle(display,windows->image.id,
4998        windows->image.highlight_context,&highlight_info);
4999      XScreenEvent(display,windows,&event);
5000      if (event.xany.window == windows->command.id)
5001        {
5002          /*
5003            Select a command from the Command widget.
5004          */
5005          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5006          id=XCommandWidget(display,windows,RectifyModeMenu,&event);
5007          (void) XSetFunction(display,windows->image.highlight_context,
5008            GXinvert);
5009          XHighlightRectangle(display,windows->image.id,
5010            windows->image.highlight_context,&highlight_info);
5011          if (id >= 0)
5012            switch (RectifyCommands[id])
5013            {
5014              case RectifyCopyCommand:
5015              {
5016                state|=ExitState;
5017                break;
5018              }
5019              case RectifyHelpCommand:
5020              {
5021                (void) XSetFunction(display,windows->image.highlight_context,
5022                  GXcopy);
5023                switch (mode)
5024                {
5025                  case CopyMode:
5026                  {
5027                    XTextViewWidget(display,resource_info,windows,MagickFalse,
5028                      "Help Viewer - Image Copy",ImageCopyHelp);
5029                    break;
5030                  }
5031                  case CropMode:
5032                  {
5033                    XTextViewWidget(display,resource_info,windows,MagickFalse,
5034                      "Help Viewer - Image Crop",ImageCropHelp);
5035                    break;
5036                  }
5037                  case CutMode:
5038                  {
5039                    XTextViewWidget(display,resource_info,windows,MagickFalse,
5040                      "Help Viewer - Image Cut",ImageCutHelp);
5041                    break;
5042                  }
5043                }
5044                (void) XSetFunction(display,windows->image.highlight_context,
5045                  GXinvert);
5046                break;
5047              }
5048              case RectifyDismissCommand:
5049              {
5050                /*
5051                  Prematurely exit.
5052                */
5053                state|=EscapeState;
5054                state|=ExitState;
5055                break;
5056              }
5057              default:
5058                break;
5059            }
5060          continue;
5061        }
5062      XHighlightRectangle(display,windows->image.id,
5063        windows->image.highlight_context,&highlight_info);
5064      switch (event.type)
5065      {
5066        case ButtonPress:
5067        {
5068          if (event.xbutton.button != Button1)
5069            break;
5070          if (event.xbutton.window != windows->image.id)
5071            break;
5072          x=windows->image.x+event.xbutton.x;
5073          y=windows->image.y+event.xbutton.y;
5074          if ((x < (int) (crop_info.x+RoiDelta)) &&
5075              (x > (int) (crop_info.x-RoiDelta)) &&
5076              (y < (int) (crop_info.y+RoiDelta)) &&
5077              (y > (int) (crop_info.y-RoiDelta)))
5078            {
5079              crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5080              crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5081              state|=UpdateConfigurationState;
5082              break;
5083            }
5084          if ((x < (int) (crop_info.x+RoiDelta)) &&
5085              (x > (int) (crop_info.x-RoiDelta)) &&
5086              (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5087              (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5088            {
5089              crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5090              state|=UpdateConfigurationState;
5091              break;
5092            }
5093          if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5094              (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5095              (y < (int) (crop_info.y+RoiDelta)) &&
5096              (y > (int) (crop_info.y-RoiDelta)))
5097            {
5098              crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5099              state|=UpdateConfigurationState;
5100              break;
5101            }
5102          if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5103              (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5104              (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5105              (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5106            {
5107              state|=UpdateConfigurationState;
5108              break;
5109            }
5110        }
5111        case ButtonRelease:
5112        {
5113          if (event.xbutton.window == windows->pan.id)
5114            if ((highlight_info.x != crop_info.x-windows->image.x) ||
5115                (highlight_info.y != crop_info.y-windows->image.y))
5116              XHighlightRectangle(display,windows->image.id,
5117                windows->image.highlight_context,&highlight_info);
5118          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5119            event.xbutton.time);
5120          break;
5121        }
5122        case Expose:
5123        {
5124          if (event.xexpose.window == windows->image.id)
5125            if (event.xexpose.count == 0)
5126              {
5127                event.xexpose.x=(int) highlight_info.x;
5128                event.xexpose.y=(int) highlight_info.y;
5129                event.xexpose.width=(int) highlight_info.width;
5130                event.xexpose.height=(int) highlight_info.height;
5131                XRefreshWindow(display,&windows->image,&event);
5132              }
5133          if (event.xexpose.window == windows->info.id)
5134            if (event.xexpose.count == 0)
5135              XInfoWidget(display,windows,text);
5136          break;
5137        }
5138        case KeyPress:
5139        {
5140          if (event.xkey.window != windows->image.id)
5141            break;
5142          /*
5143            Respond to a user key press.
5144          */
5145          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5146            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5147          switch ((int) key_symbol)
5148          {
5149            case XK_Escape:
5150            case XK_F20:
5151              state|=EscapeState;
5152            case XK_Return:
5153            {
5154              state|=ExitState;
5155              break;
5156            }
5157            case XK_Home:
5158            case XK_KP_Home:
5159            {
5160              crop_info.x=(ssize_t) (windows->image.width/2L-
5161                crop_info.width/2L);
5162              crop_info.y=(ssize_t) (windows->image.height/2L-
5163                crop_info.height/2L);
5164              break;
5165            }
5166            case XK_Left:
5167            case XK_KP_Left:
5168            {
5169              crop_info.x--;
5170              break;
5171            }
5172            case XK_Up:
5173            case XK_KP_Up:
5174            case XK_Next:
5175            {
5176              crop_info.y--;
5177              break;
5178            }
5179            case XK_Right:
5180            case XK_KP_Right:
5181            {
5182              crop_info.x++;
5183              break;
5184            }
5185            case XK_Prior:
5186            case XK_Down:
5187            case XK_KP_Down:
5188            {
5189              crop_info.y++;
5190              break;
5191            }
5192            case XK_F1:
5193            case XK_Help:
5194            {
5195              (void) XSetFunction(display,windows->image.highlight_context,
5196                GXcopy);
5197              switch (mode)
5198              {
5199                case CopyMode:
5200                {
5201                  XTextViewWidget(display,resource_info,windows,MagickFalse,
5202                    "Help Viewer - Image Copy",ImageCopyHelp);
5203                  break;
5204                }
5205                case CropMode:
5206                {
5207                  XTextViewWidget(display,resource_info,windows,MagickFalse,
5208                    "Help Viewer - Image Cropg",ImageCropHelp);
5209                  break;
5210                }
5211                case CutMode:
5212                {
5213                  XTextViewWidget(display,resource_info,windows,MagickFalse,
5214                    "Help Viewer - Image Cutg",ImageCutHelp);
5215                  break;
5216                }
5217              }
5218              (void) XSetFunction(display,windows->image.highlight_context,
5219                GXinvert);
5220              break;
5221            }
5222            default:
5223            {
5224              (void) XBell(display,0);
5225              break;
5226            }
5227          }
5228          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5229            event.xkey.time);
5230          break;
5231        }
5232        case KeyRelease:
5233          break;
5234        case MotionNotify:
5235        {
5236          if (event.xmotion.window != windows->image.id)
5237            break;
5238          /*
5239            Map and unmap Info widget as text cursor crosses its boundaries.
5240          */
5241          x=event.xmotion.x;
5242          y=event.xmotion.y;
5243          if (windows->info.mapped != MagickFalse)
5244            {
5245              if ((x < (int) (windows->info.x+windows->info.width)) &&
5246                  (y < (int) (windows->info.y+windows->info.height)))
5247                (void) XWithdrawWindow(display,windows->info.id,
5248                  windows->info.screen);
5249            }
5250          else
5251            if ((x > (int) (windows->info.x+windows->info.width)) ||
5252                (y > (int) (windows->info.y+windows->info.height)))
5253              (void) XMapWindow(display,windows->info.id);
5254          crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5255          crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5256          break;
5257        }
5258        case SelectionRequest:
5259        {
5260          XSelectionEvent
5261            notify;
5262
5263          XSelectionRequestEvent
5264            *request;
5265
5266          /*
5267            Set primary selection.
5268          */
5269          (void) FormatLocaleString(text,MaxTextExtent,
5270            "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5271            crop_info.height,(double) crop_info.x,(double) crop_info.y);
5272          request=(&(event.xselectionrequest));
5273          (void) XChangeProperty(request->display,request->requestor,
5274            request->property,request->target,8,PropModeReplace,
5275            (unsigned char *) text,(int) strlen(text));
5276          notify.type=SelectionNotify;
5277          notify.display=request->display;
5278          notify.requestor=request->requestor;
5279          notify.selection=request->selection;
5280          notify.target=request->target;
5281          notify.time=request->time;
5282          if (request->property == None)
5283            notify.property=request->target;
5284          else
5285            notify.property=request->property;
5286          (void) XSendEvent(request->display,request->requestor,False,0,
5287            (XEvent *) &notify);
5288        }
5289        default:
5290          break;
5291      }
5292      if ((state & UpdateConfigurationState) != 0)
5293        {
5294          (void) XPutBackEvent(display,&event);
5295          (void) XCheckDefineCursor(display,windows->image.id,cursor);
5296          break;
5297        }
5298    } while ((state & ExitState) == 0);
5299  } while ((state & ExitState) == 0);
5300  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5301  XSetCursorState(display,windows,MagickFalse);
5302  if ((state & EscapeState) != 0)
5303    return(MagickTrue);
5304  if (mode == CropMode)
5305    if (((int) crop_info.width != windows->image.ximage->width) ||
5306        ((int) crop_info.height != windows->image.ximage->height))
5307      {
5308        /*
5309          Reconfigure Image window as defined by cropping rectangle.
5310        */
5311        XSetCropGeometry(display,windows,&crop_info,image);
5312        windows->image.window_changes.width=(int) crop_info.width;
5313        windows->image.window_changes.height=(int) crop_info.height;
5314        (void) XConfigureImage(display,resource_info,windows,image,exception);
5315        return(MagickTrue);
5316      }
5317  /*
5318    Copy image before applying image transforms.
5319  */
5320  XSetCursorState(display,windows,MagickTrue);
5321  XCheckRefreshWindows(display,windows);
5322  width=(unsigned int) image->columns;
5323  height=(unsigned int) image->rows;
5324  x=0;
5325  y=0;
5326  if (windows->image.crop_geometry != (char *) NULL)
5327    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5328  scale_factor=(MagickRealType) width/windows->image.ximage->width;
5329  crop_info.x+=x;
5330  crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5331  crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5332  scale_factor=(MagickRealType) height/windows->image.ximage->height;
5333  crop_info.y+=y;
5334  crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5335  crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5336  crop_image=CropImage(image,&crop_info,exception);
5337  XSetCursorState(display,windows,MagickFalse);
5338  if (crop_image == (Image *) NULL)
5339    return(MagickFalse);
5340  if (resource_info->copy_image != (Image *) NULL)
5341    resource_info->copy_image=DestroyImage(resource_info->copy_image);
5342  resource_info->copy_image=crop_image;
5343  if (mode == CopyMode)
5344    {
5345      (void) XConfigureImage(display,resource_info,windows,image,exception);
5346      return(MagickTrue);
5347    }
5348  /*
5349    Cut image.
5350  */
5351  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
5352    return(MagickFalse);
5353  image->matte=MagickTrue;
5354  image_view=AcquireCacheView(image);
5355  for (y=0; y < (int) crop_info.height; y++)
5356  {
5357    q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5358      crop_info.width,1,exception);
5359    if (q == (Quantum *) NULL)
5360      break;
5361    for (x=0; x < (int) crop_info.width; x++)
5362    {
5363      SetPixelAlpha(image,TransparentAlpha,q);
5364      q+=GetPixelChannels(image);
5365    }
5366    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5367      break;
5368  }
5369  image_view=DestroyCacheView(image_view);
5370  /*
5371    Update image configuration.
5372  */
5373  XConfigureImageColormap(display,resource_info,windows,image);
5374  (void) XConfigureImage(display,resource_info,windows,image,exception);
5375  return(MagickTrue);
5376}
5377
5378/*
5379%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5380%                                                                             %
5381%                                                                             %
5382%                                                                             %
5383+   X D r a w I m a g e                                                       %
5384%                                                                             %
5385%                                                                             %
5386%                                                                             %
5387%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5388%
5389%  XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5390%  the image.
5391%
5392%  The format of the XDrawEditImage method is:
5393%
5394%      MagickBooleanType XDrawEditImage(Display *display,
5395%        XResourceInfo *resource_info,XWindows *windows,Image **image,
5396%        ExceptionInfo *exception)
5397%
5398%  A description of each parameter follows:
5399%
5400%    o display: Specifies a connection to an X server; returned from
5401%      XOpenDisplay.
5402%
5403%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5404%
5405%    o windows: Specifies a pointer to a XWindows structure.
5406%
5407%    o image: the image.
5408%
5409%    o exception: return any errors or warnings in this structure.
5410%
5411*/
5412static MagickBooleanType XDrawEditImage(Display *display,
5413  XResourceInfo *resource_info,XWindows *windows,Image **image,
5414  ExceptionInfo *exception)
5415{
5416  static const char
5417    *DrawMenu[] =
5418    {
5419      "Element",
5420      "Color",
5421      "Stipple",
5422      "Width",
5423      "Undo",
5424      "Help",
5425      "Dismiss",
5426      (char *) NULL
5427    };
5428
5429  static ElementType
5430    element = PointElement;
5431
5432  static const ModeType
5433    DrawCommands[] =
5434    {
5435      DrawElementCommand,
5436      DrawColorCommand,
5437      DrawStippleCommand,
5438      DrawWidthCommand,
5439      DrawUndoCommand,
5440      DrawHelpCommand,
5441      DrawDismissCommand
5442    };
5443
5444  static Pixmap
5445    stipple = (Pixmap) NULL;
5446
5447  static unsigned int
5448    pen_id = 0,
5449    line_width = 1;
5450
5451  char
5452    command[MaxTextExtent],
5453    text[MaxTextExtent];
5454
5455  Cursor
5456    cursor;
5457
5458  int
5459    entry,
5460    id,
5461    number_coordinates,
5462    x,
5463    y;
5464
5465  MagickRealType
5466    degrees;
5467
5468  MagickStatusType
5469    status;
5470
5471  RectangleInfo
5472    rectangle_info;
5473
5474  register int
5475    i;
5476
5477  unsigned int
5478    distance,
5479    height,
5480    max_coordinates,
5481    width;
5482
5483  size_t
5484    state;
5485
5486  Window
5487    root_window;
5488
5489  XDrawInfo
5490    draw_info;
5491
5492  XEvent
5493    event;
5494
5495  XPoint
5496    *coordinate_info;
5497
5498  XSegment
5499    line_info;
5500
5501  /*
5502    Allocate polygon info.
5503  */
5504  max_coordinates=2048;
5505  coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5506    sizeof(*coordinate_info));
5507  if (coordinate_info == (XPoint *) NULL)
5508    {
5509      (void) ThrowMagickException(exception,GetMagickModule(),
5510        ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5511      return(MagickFalse);
5512    }
5513  /*
5514    Map Command widget.
5515  */
5516  (void) CloneString(&windows->command.name,"Draw");
5517  windows->command.data=4;
5518  (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5519  (void) XMapRaised(display,windows->command.id);
5520  XClientMessage(display,windows->image.id,windows->im_protocols,
5521    windows->im_update_widget,CurrentTime);
5522  /*
5523    Wait for first button press.
5524  */
5525  root_window=XRootWindow(display,XDefaultScreen(display));
5526  draw_info.stencil=OpaqueStencil;
5527  status=MagickTrue;
5528  cursor=XCreateFontCursor(display,XC_tcross);
5529  for ( ; ; )
5530  {
5531    XQueryPosition(display,windows->image.id,&x,&y);
5532    (void) XSelectInput(display,windows->image.id,
5533      windows->image.attributes.event_mask | PointerMotionMask);
5534    (void) XCheckDefineCursor(display,windows->image.id,cursor);
5535    state=DefaultState;
5536    do
5537    {
5538      if (windows->info.mapped != MagickFalse)
5539        {
5540          /*
5541            Display pointer position.
5542          */
5543          (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
5544            x+windows->image.x,y+windows->image.y);
5545          XInfoWidget(display,windows,text);
5546        }
5547      /*
5548        Wait for next event.
5549      */
5550      XScreenEvent(display,windows,&event);
5551      if (event.xany.window == windows->command.id)
5552        {
5553          /*
5554            Select a command from the Command widget.
5555          */
5556          id=XCommandWidget(display,windows,DrawMenu,&event);
5557          if (id < 0)
5558            continue;
5559          switch (DrawCommands[id])
5560          {
5561            case DrawElementCommand:
5562            {
5563              static const char
5564                *Elements[] =
5565                {
5566                  "point",
5567                  "line",
5568                  "rectangle",
5569                  "fill rectangle",
5570                  "circle",
5571                  "fill circle",
5572                  "ellipse",
5573                  "fill ellipse",
5574                  "polygon",
5575                  "fill polygon",
5576                  (char *) NULL,
5577                };
5578
5579              /*
5580                Select a command from the pop-up menu.
5581              */
5582              element=(ElementType) (XMenuWidget(display,windows,
5583                DrawMenu[id],Elements,command)+1);
5584              break;
5585            }
5586            case DrawColorCommand:
5587            {
5588              const char
5589                *ColorMenu[MaxNumberPens+1];
5590
5591              int
5592                pen_number;
5593
5594              MagickBooleanType
5595                transparent;
5596
5597              XColor
5598                color;
5599
5600              /*
5601                Initialize menu selections.
5602              */
5603              for (i=0; i < (int) (MaxNumberPens-2); i++)
5604                ColorMenu[i]=resource_info->pen_colors[i];
5605              ColorMenu[MaxNumberPens-2]="transparent";
5606              ColorMenu[MaxNumberPens-1]="Browser...";
5607              ColorMenu[MaxNumberPens]=(char *) NULL;
5608              /*
5609                Select a pen color from the pop-up menu.
5610              */
5611              pen_number=XMenuWidget(display,windows,DrawMenu[id],
5612                (const char **) ColorMenu,command);
5613              if (pen_number < 0)
5614                break;
5615              transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5616                MagickFalse;
5617              if (transparent != MagickFalse)
5618                {
5619                  draw_info.stencil=TransparentStencil;
5620                  break;
5621                }
5622              if (pen_number == (MaxNumberPens-1))
5623                {
5624                  static char
5625                    color_name[MaxTextExtent] = "gray";
5626
5627                  /*
5628                    Select a pen color from a dialog.
5629                  */
5630                  resource_info->pen_colors[pen_number]=color_name;
5631                  XColorBrowserWidget(display,windows,"Select",color_name);
5632                  if (*color_name == '\0')
5633                    break;
5634                }
5635              /*
5636                Set pen color.
5637              */
5638              (void) XParseColor(display,windows->map_info->colormap,
5639                resource_info->pen_colors[pen_number],&color);
5640              XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5641                (unsigned int) MaxColors,&color);
5642              windows->pixel_info->pen_colors[pen_number]=color;
5643              pen_id=(unsigned int) pen_number;
5644              draw_info.stencil=OpaqueStencil;
5645              break;
5646            }
5647            case DrawStippleCommand:
5648            {
5649              Image
5650                *stipple_image;
5651
5652              ImageInfo
5653                *image_info;
5654
5655              int
5656                status;
5657
5658              static char
5659                filename[MaxTextExtent] = "\0";
5660
5661              static const char
5662                *StipplesMenu[] =
5663                {
5664                  "Brick",
5665                  "Diagonal",
5666                  "Scales",
5667                  "Vertical",
5668                  "Wavy",
5669                  "Translucent",
5670                  "Opaque",
5671                  (char *) NULL,
5672                  (char *) NULL,
5673                };
5674
5675              /*
5676                Select a command from the pop-up menu.
5677              */
5678              StipplesMenu[7]="Open...";
5679              entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5680                command);
5681              if (entry < 0)
5682                break;
5683              if (stipple != (Pixmap) NULL)
5684                (void) XFreePixmap(display,stipple);
5685              stipple=(Pixmap) NULL;
5686              if (entry != 7)
5687                {
5688                  switch (entry)
5689                  {
5690                    case 0:
5691                    {
5692                      stipple=XCreateBitmapFromData(display,root_window,
5693                        (char *) BricksBitmap,BricksWidth,BricksHeight);
5694                      break;
5695                    }
5696                    case 1:
5697                    {
5698                      stipple=XCreateBitmapFromData(display,root_window,
5699                        (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5700                      break;
5701                    }
5702                    case 2:
5703                    {
5704                      stipple=XCreateBitmapFromData(display,root_window,
5705                        (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5706                      break;
5707                    }
5708                    case 3:
5709                    {
5710                      stipple=XCreateBitmapFromData(display,root_window,
5711                        (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5712                      break;
5713                    }
5714                    case 4:
5715                    {
5716                      stipple=XCreateBitmapFromData(display,root_window,
5717                        (char *) WavyBitmap,WavyWidth,WavyHeight);
5718                      break;
5719                    }
5720                    case 5:
5721                    {
5722                      stipple=XCreateBitmapFromData(display,root_window,
5723                        (char *) HighlightBitmap,HighlightWidth,
5724                        HighlightHeight);
5725                      break;
5726                    }
5727                    case 6:
5728                    default:
5729                    {
5730                      stipple=XCreateBitmapFromData(display,root_window,
5731                        (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5732                      break;
5733                    }
5734                  }
5735                  break;
5736                }
5737              XFileBrowserWidget(display,windows,"Stipple",filename);
5738              if (*filename == '\0')
5739                break;
5740              /*
5741                Read image.
5742              */
5743              XSetCursorState(display,windows,MagickTrue);
5744              XCheckRefreshWindows(display,windows);
5745              image_info=AcquireImageInfo();
5746              (void) CopyMagickString(image_info->filename,filename,
5747                MaxTextExtent);
5748              stipple_image=ReadImage(image_info,exception);
5749              CatchException(exception);
5750              XSetCursorState(display,windows,MagickFalse);
5751              if (stipple_image == (Image *) NULL)
5752                break;
5753              (void) AcquireUniqueFileResource(filename);
5754              (void) FormatLocaleString(stipple_image->filename,MaxTextExtent,
5755                "xbm:%s",filename);
5756              (void) WriteImage(image_info,stipple_image,exception);
5757              stipple_image=DestroyImage(stipple_image);
5758              image_info=DestroyImageInfo(image_info);
5759              status=XReadBitmapFile(display,root_window,filename,&width,
5760                &height,&stipple,&x,&y);
5761              (void) RelinquishUniqueFileResource(filename);
5762              if ((status != BitmapSuccess) != 0)
5763                XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5764                  filename);
5765              break;
5766            }
5767            case DrawWidthCommand:
5768            {
5769              static char
5770                width[MaxTextExtent] = "0";
5771
5772              static const char
5773                *WidthsMenu[] =
5774                {
5775                  "1",
5776                  "2",
5777                  "4",
5778                  "8",
5779                  "16",
5780                  "Dialog...",
5781                  (char *) NULL,
5782                };
5783
5784              /*
5785                Select a command from the pop-up menu.
5786              */
5787              entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5788                command);
5789              if (entry < 0)
5790                break;
5791              if (entry != 5)
5792                {
5793                  line_width=(unsigned int) StringToUnsignedLong(
5794                    WidthsMenu[entry]);
5795                  break;
5796                }
5797              (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5798                width);
5799              if (*width == '\0')
5800                break;
5801              line_width=(unsigned int) StringToUnsignedLong(width);
5802              break;
5803            }
5804            case DrawUndoCommand:
5805            {
5806              (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5807                image,exception);
5808              break;
5809            }
5810            case DrawHelpCommand:
5811            {
5812              XTextViewWidget(display,resource_info,windows,MagickFalse,
5813                "Help Viewer - Image Rotation",ImageDrawHelp);
5814              (void) XCheckDefineCursor(display,windows->image.id,cursor);
5815              break;
5816            }
5817            case DrawDismissCommand:
5818            {
5819              /*
5820                Prematurely exit.
5821              */
5822              state|=EscapeState;
5823              state|=ExitState;
5824              break;
5825            }
5826            default:
5827              break;
5828          }
5829          (void) XCheckDefineCursor(display,windows->image.id,cursor);
5830          continue;
5831        }
5832      switch (event.type)
5833      {
5834        case ButtonPress:
5835        {
5836          if (event.xbutton.button != Button1)
5837            break;
5838          if (event.xbutton.window != windows->image.id)
5839            break;
5840          /*
5841            exit loop.
5842          */
5843          x=event.xbutton.x;
5844          y=event.xbutton.y;
5845          state|=ExitState;
5846          break;
5847        }
5848        case ButtonRelease:
5849          break;
5850        case Expose:
5851          break;
5852        case KeyPress:
5853        {
5854          KeySym
5855            key_symbol;
5856
5857          if (event.xkey.window != windows->image.id)
5858            break;
5859          /*
5860            Respond to a user key press.
5861          */
5862          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5863            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5864          switch ((int) key_symbol)
5865          {
5866            case XK_Escape:
5867            case XK_F20:
5868            {
5869              /*
5870                Prematurely exit.
5871              */
5872              state|=EscapeState;
5873              state|=ExitState;
5874              break;
5875            }
5876            case XK_F1:
5877            case XK_Help:
5878            {
5879              XTextViewWidget(display,resource_info,windows,MagickFalse,
5880                "Help Viewer - Image Rotation",ImageDrawHelp);
5881              break;
5882            }
5883            default:
5884            {
5885              (void) XBell(display,0);
5886              break;
5887            }
5888          }
5889          break;
5890        }
5891        case MotionNotify:
5892        {
5893          /*
5894            Map and unmap Info widget as text cursor crosses its boundaries.
5895          */
5896          x=event.xmotion.x;
5897          y=event.xmotion.y;
5898          if (windows->info.mapped != MagickFalse)
5899            {
5900              if ((x < (int) (windows->info.x+windows->info.width)) &&
5901                  (y < (int) (windows->info.y+windows->info.height)))
5902                (void) XWithdrawWindow(display,windows->info.id,
5903                  windows->info.screen);
5904            }
5905          else
5906            if ((x > (int) (windows->info.x+windows->info.width)) ||
5907                (y > (int) (windows->info.y+windows->info.height)))
5908              (void) XMapWindow(display,windows->info.id);
5909          break;
5910        }
5911      }
5912    } while ((state & ExitState) == 0);
5913    (void) XSelectInput(display,windows->image.id,
5914      windows->image.attributes.event_mask);
5915    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5916    if ((state & EscapeState) != 0)
5917      break;
5918    /*
5919      Draw element as pointer moves until the button is released.
5920    */
5921    distance=0;
5922    degrees=0.0;
5923    line_info.x1=x;
5924    line_info.y1=y;
5925    line_info.x2=x;
5926    line_info.y2=y;
5927    rectangle_info.x=(ssize_t) x;
5928    rectangle_info.y=(ssize_t) y;
5929    rectangle_info.width=0;
5930    rectangle_info.height=0;
5931    number_coordinates=1;
5932    coordinate_info->x=x;
5933    coordinate_info->y=y;
5934    (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5935    state=DefaultState;
5936    do
5937    {
5938      switch (element)
5939      {
5940        case PointElement:
5941        default:
5942        {
5943          if (number_coordinates > 1)
5944            {
5945              (void) XDrawLines(display,windows->image.id,
5946                windows->image.highlight_context,coordinate_info,
5947                number_coordinates,CoordModeOrigin);
5948              (void) FormatLocaleString(text,MaxTextExtent," %+d%+d",
5949                coordinate_info[number_coordinates-1].x,
5950                coordinate_info[number_coordinates-1].y);
5951              XInfoWidget(display,windows,text);
5952            }
5953          break;
5954        }
5955        case LineElement:
5956        {
5957          if (distance > 9)
5958            {
5959              /*
5960                Display angle of the line.
5961              */
5962              degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5963                line_info.y1),(double) (line_info.x2-line_info.x1)));
5964              (void) FormatLocaleString(text,MaxTextExtent," %g",
5965                (double) degrees);
5966              XInfoWidget(display,windows,text);
5967              XHighlightLine(display,windows->image.id,
5968                windows->image.highlight_context,&line_info);
5969            }
5970          else
5971            if (windows->info.mapped != MagickFalse)
5972              (void) XWithdrawWindow(display,windows->info.id,
5973                windows->info.screen);
5974          break;
5975        }
5976        case RectangleElement:
5977        case FillRectangleElement:
5978        {
5979          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5980            {
5981              /*
5982                Display info and draw drawing rectangle.
5983              */
5984              (void) FormatLocaleString(text,MaxTextExtent,
5985                " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5986                (double) rectangle_info.height,(double) rectangle_info.x,
5987                (double) rectangle_info.y);
5988              XInfoWidget(display,windows,text);
5989              XHighlightRectangle(display,windows->image.id,
5990                windows->image.highlight_context,&rectangle_info);
5991            }
5992          else
5993            if (windows->info.mapped != MagickFalse)
5994              (void) XWithdrawWindow(display,windows->info.id,
5995                windows->info.screen);
5996          break;
5997        }
5998        case CircleElement:
5999        case FillCircleElement:
6000        case EllipseElement:
6001        case FillEllipseElement:
6002        {
6003          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6004            {
6005              /*
6006                Display info and draw drawing rectangle.
6007              */
6008              (void) FormatLocaleString(text,MaxTextExtent,
6009                " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
6010                (double) rectangle_info.height,(double) rectangle_info.x,
6011                (double) rectangle_info.y);
6012              XInfoWidget(display,windows,text);
6013              XHighlightEllipse(display,windows->image.id,
6014                windows->image.highlight_context,&rectangle_info);
6015            }
6016          else
6017            if (windows->info.mapped != MagickFalse)
6018              (void) XWithdrawWindow(display,windows->info.id,
6019                windows->info.screen);
6020          break;
6021        }
6022        case PolygonElement:
6023        case FillPolygonElement:
6024        {
6025          if (number_coordinates > 1)
6026            (void) XDrawLines(display,windows->image.id,
6027              windows->image.highlight_context,coordinate_info,
6028              number_coordinates,CoordModeOrigin);
6029          if (distance > 9)
6030            {
6031              /*
6032                Display angle of the line.
6033              */
6034              degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
6035                line_info.y1),(double) (line_info.x2-line_info.x1)));
6036              (void) FormatLocaleString(text,MaxTextExtent," %g",
6037                (double) degrees);
6038              XInfoWidget(display,windows,text);
6039              XHighlightLine(display,windows->image.id,
6040                windows->image.highlight_context,&line_info);
6041            }
6042          else
6043            if (windows->info.mapped != MagickFalse)
6044              (void) XWithdrawWindow(display,windows->info.id,
6045                windows->info.screen);
6046          break;
6047        }
6048      }
6049      /*
6050        Wait for next event.
6051      */
6052      XScreenEvent(display,windows,&event);
6053      switch (element)
6054      {
6055        case PointElement:
6056        default:
6057        {
6058          if (number_coordinates > 1)
6059            (void) XDrawLines(display,windows->image.id,
6060              windows->image.highlight_context,coordinate_info,
6061              number_coordinates,CoordModeOrigin);
6062          break;
6063        }
6064        case LineElement:
6065        {
6066          if (distance > 9)
6067            XHighlightLine(display,windows->image.id,
6068              windows->image.highlight_context,&line_info);
6069          break;
6070        }
6071        case RectangleElement:
6072        case FillRectangleElement:
6073        {
6074          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6075            XHighlightRectangle(display,windows->image.id,
6076              windows->image.highlight_context,&rectangle_info);
6077          break;
6078        }
6079        case CircleElement:
6080        case FillCircleElement:
6081        case EllipseElement:
6082        case FillEllipseElement:
6083        {
6084          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6085            XHighlightEllipse(display,windows->image.id,
6086              windows->image.highlight_context,&rectangle_info);
6087          break;
6088        }
6089        case PolygonElement:
6090        case FillPolygonElement:
6091        {
6092          if (number_coordinates > 1)
6093            (void) XDrawLines(display,windows->image.id,
6094              windows->image.highlight_context,coordinate_info,
6095              number_coordinates,CoordModeOrigin);
6096          if (distance > 9)
6097            XHighlightLine(display,windows->image.id,
6098              windows->image.highlight_context,&line_info);
6099          break;
6100        }
6101      }
6102      switch (event.type)
6103      {
6104        case ButtonPress:
6105          break;
6106        case ButtonRelease:
6107        {
6108          /*
6109            User has committed to element.
6110          */
6111          line_info.x2=event.xbutton.x;
6112          line_info.y2=event.xbutton.y;
6113          rectangle_info.x=(ssize_t) event.xbutton.x;
6114          rectangle_info.y=(ssize_t) event.xbutton.y;
6115          coordinate_info[number_coordinates].x=event.xbutton.x;
6116          coordinate_info[number_coordinates].y=event.xbutton.y;
6117          if (((element != PolygonElement) &&
6118               (element != FillPolygonElement)) || (distance <= 9))
6119            {
6120              state|=ExitState;
6121              break;
6122            }
6123          number_coordinates++;
6124          if (number_coordinates < (int) max_coordinates)
6125            {
6126              line_info.x1=event.xbutton.x;
6127              line_info.y1=event.xbutton.y;
6128              break;
6129            }
6130          max_coordinates<<=1;
6131          coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6132            max_coordinates,sizeof(*coordinate_info));
6133          if (coordinate_info == (XPoint *) NULL)
6134            (void) ThrowMagickException(exception,GetMagickModule(),
6135              ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6136          break;
6137        }
6138        case Expose:
6139          break;
6140        case MotionNotify:
6141        {
6142          if (event.xmotion.window != windows->image.id)
6143            break;
6144          if (element != PointElement)
6145            {
6146              line_info.x2=event.xmotion.x;
6147              line_info.y2=event.xmotion.y;
6148              rectangle_info.x=(ssize_t) event.xmotion.x;
6149              rectangle_info.y=(ssize_t) event.xmotion.y;
6150              break;
6151            }
6152          coordinate_info[number_coordinates].x=event.xbutton.x;
6153          coordinate_info[number_coordinates].y=event.xbutton.y;
6154          number_coordinates++;
6155          if (number_coordinates < (int) max_coordinates)
6156            break;
6157          max_coordinates<<=1;
6158          coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6159            max_coordinates,sizeof(*coordinate_info));
6160          if (coordinate_info == (XPoint *) NULL)
6161            (void) ThrowMagickException(exception,GetMagickModule(),
6162              ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6163          break;
6164        }
6165        default:
6166          break;
6167      }
6168      /*
6169        Check boundary conditions.
6170      */
6171      if (line_info.x2 < 0)
6172        line_info.x2=0;
6173      else
6174        if (line_info.x2 > (int) windows->image.width)
6175          line_info.x2=(short) windows->image.width;
6176      if (line_info.y2 < 0)
6177        line_info.y2=0;
6178      else
6179        if (line_info.y2 > (int) windows->image.height)
6180          line_info.y2=(short) windows->image.height;
6181      distance=(unsigned int)
6182        (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6183         ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6184      if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6185          ((state & ExitState) != 0))
6186        {
6187          if (rectangle_info.x < 0)
6188            rectangle_info.x=0;
6189          else
6190            if (rectangle_info.x > (ssize_t) windows->image.width)
6191              rectangle_info.x=(ssize_t) windows->image.width;
6192          if ((int) rectangle_info.x < x)
6193            rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6194          else
6195            {
6196              rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6197              rectangle_info.x=(ssize_t) x;
6198            }
6199          if (rectangle_info.y < 0)
6200            rectangle_info.y=0;
6201          else
6202            if (rectangle_info.y > (ssize_t) windows->image.height)
6203              rectangle_info.y=(ssize_t) windows->image.height;
6204          if ((int) rectangle_info.y < y)
6205            rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6206          else
6207            {
6208              rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6209              rectangle_info.y=(ssize_t) y;
6210            }
6211        }
6212    } while ((state & ExitState) == 0);
6213    (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6214    if ((element == PointElement) || (element == PolygonElement) ||
6215        (element == FillPolygonElement))
6216      {
6217        /*
6218          Determine polygon bounding box.
6219        */
6220        rectangle_info.x=(ssize_t) coordinate_info->x;
6221        rectangle_info.y=(ssize_t) coordinate_info->y;
6222        x=coordinate_info->x;
6223        y=coordinate_info->y;
6224        for (i=1; i < number_coordinates; i++)
6225        {
6226          if (coordinate_info[i].x > x)
6227            x=coordinate_info[i].x;
6228          if (coordinate_info[i].y > y)
6229            y=coordinate_info[i].y;
6230          if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6231            rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6232          if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6233            rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6234        }
6235        rectangle_info.width=(size_t) (x-rectangle_info.x);
6236        rectangle_info.height=(size_t) (y-rectangle_info.y);
6237        for (i=0; i < number_coordinates; i++)
6238        {
6239          coordinate_info[i].x-=rectangle_info.x;
6240          coordinate_info[i].y-=rectangle_info.y;
6241        }
6242      }
6243    else
6244      if (distance <= 9)
6245        continue;
6246      else
6247        if ((element == RectangleElement) ||
6248            (element == CircleElement) || (element == EllipseElement))
6249          {
6250            rectangle_info.width--;
6251            rectangle_info.height--;
6252          }
6253    /*
6254      Drawing is relative to image configuration.
6255    */
6256    draw_info.x=(int) rectangle_info.x;
6257    draw_info.y=(int) rectangle_info.y;
6258    (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6259      image,exception);
6260    width=(unsigned int) (*image)->columns;
6261    height=(unsigned int) (*image)->rows;
6262    x=0;
6263    y=0;
6264    if (windows->image.crop_geometry != (char *) NULL)
6265      (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6266    draw_info.x+=windows->image.x-(line_width/2);
6267    if (draw_info.x < 0)
6268      draw_info.x=0;
6269    draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6270    draw_info.y+=windows->image.y-(line_width/2);
6271    if (draw_info.y < 0)
6272      draw_info.y=0;
6273    draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6274    draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6275    if (draw_info.width > (unsigned int) (*image)->columns)
6276      draw_info.width=(unsigned int) (*image)->columns;
6277    draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6278    if (draw_info.height > (unsigned int) (*image)->rows)
6279      draw_info.height=(unsigned int) (*image)->rows;
6280    (void) FormatLocaleString(draw_info.geometry,MaxTextExtent,"%ux%u%+d%+d",
6281      width*draw_info.width/windows->image.ximage->width,
6282      height*draw_info.height/windows->image.ximage->height,
6283      draw_info.x+x,draw_info.y+y);
6284    /*
6285      Initialize drawing attributes.
6286    */
6287    draw_info.degrees=0.0;
6288    draw_info.element=element;
6289    draw_info.stipple=stipple;
6290    draw_info.line_width=line_width;
6291    draw_info.line_info=line_info;
6292    if (line_info.x1 > (int) (line_width/2))
6293      draw_info.line_info.x1=(short) line_width/2;
6294    if (line_info.y1 > (int) (line_width/2))
6295      draw_info.line_info.y1=(short) line_width/2;
6296    draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6297    draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6298    if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6299      {
6300        draw_info.line_info.x2=(-draw_info.line_info.x2);
6301        draw_info.line_info.y2=(-draw_info.line_info.y2);
6302      }
6303    if (draw_info.line_info.x2 < 0)
6304      {
6305        draw_info.line_info.x2=(-draw_info.line_info.x2);
6306        Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6307      }
6308    if (draw_info.line_info.y2 < 0)
6309      {
6310        draw_info.line_info.y2=(-draw_info.line_info.y2);
6311        Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6312      }
6313    draw_info.rectangle_info=rectangle_info;
6314    if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6315      draw_info.rectangle_info.x=(ssize_t) line_width/2;
6316    if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6317      draw_info.rectangle_info.y=(ssize_t) line_width/2;
6318    draw_info.number_coordinates=(unsigned int) number_coordinates;
6319    draw_info.coordinate_info=coordinate_info;
6320    windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6321    /*
6322      Draw element on image.
6323    */
6324    XSetCursorState(display,windows,MagickTrue);
6325    XCheckRefreshWindows(display,windows);
6326    status=XDrawImage(display,windows->pixel_info,&draw_info,*image);
6327    XSetCursorState(display,windows,MagickFalse);
6328    /*
6329      Update image colormap and return to image drawing.
6330    */
6331    XConfigureImageColormap(display,resource_info,windows,*image);
6332    (void) XConfigureImage(display,resource_info,windows,*image,exception);
6333  }
6334  XSetCursorState(display,windows,MagickFalse);
6335  coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6336  return(status != 0 ? MagickTrue : MagickFalse);
6337}
6338
6339/*
6340%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6341%                                                                             %
6342%                                                                             %
6343%                                                                             %
6344+   X D r a w P a n R e c t a n g l e                                         %
6345%                                                                             %
6346%                                                                             %
6347%                                                                             %
6348%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6349%
6350%  XDrawPanRectangle() draws a rectangle in the pan window.  The pan window
6351%  displays a zoom image and the rectangle shows which portion of the image is
6352%  displayed in the Image window.
6353%
6354%  The format of the XDrawPanRectangle method is:
6355%
6356%      XDrawPanRectangle(Display *display,XWindows *windows)
6357%
6358%  A description of each parameter follows:
6359%
6360%    o display: Specifies a connection to an X server;  returned from
6361%      XOpenDisplay.
6362%
6363%    o windows: Specifies a pointer to a XWindows structure.
6364%
6365*/
6366static void XDrawPanRectangle(Display *display,XWindows *windows)
6367{
6368  MagickRealType
6369    scale_factor;
6370
6371  RectangleInfo
6372    highlight_info;
6373
6374  /*
6375    Determine dimensions of the panning rectangle.
6376  */
6377  scale_factor=(MagickRealType) windows->pan.width/windows->image.ximage->width;
6378  highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6379  highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6380  scale_factor=(MagickRealType)
6381    windows->pan.height/windows->image.ximage->height;
6382  highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6383  highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6384  /*
6385    Display the panning rectangle.
6386  */
6387  (void) XClearWindow(display,windows->pan.id);
6388  XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6389    &highlight_info);
6390}
6391
6392/*
6393%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6394%                                                                             %
6395%                                                                             %
6396%                                                                             %
6397+   X I m a g e C a c h e                                                     %
6398%                                                                             %
6399%                                                                             %
6400%                                                                             %
6401%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6402%
6403%  XImageCache() handles the creation, manipulation, and destruction of the
6404%  image cache (undo and redo buffers).
6405%
6406%  The format of the XImageCache method is:
6407%
6408%      void XImageCache(Display *display,XResourceInfo *resource_info,
6409%        XWindows *windows,const CommandType command,Image **image,
6410%        ExceptionInfo *exception)
6411%
6412%  A description of each parameter follows:
6413%
6414%    o display: Specifies a connection to an X server; returned from
6415%      XOpenDisplay.
6416%
6417%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6418%
6419%    o windows: Specifies a pointer to a XWindows structure.
6420%
6421%    o command: Specifies a command to perform.
6422%
6423%    o image: the image;  XImageCache may transform the image and return a new
6424%      image pointer.
6425%
6426%    o exception: return any errors or warnings in this structure.
6427%
6428*/
6429static void XImageCache(Display *display,XResourceInfo *resource_info,
6430  XWindows *windows,const CommandType command,Image **image,
6431  ExceptionInfo *exception)
6432{
6433  Image
6434    *cache_image;
6435
6436  static Image
6437    *redo_image = (Image *) NULL,
6438    *undo_image = (Image *) NULL;
6439
6440  switch (command)
6441  {
6442    case FreeBuffersCommand:
6443    {
6444      /*
6445        Free memory from the undo and redo cache.
6446      */
6447      while (undo_image != (Image *) NULL)
6448      {
6449        cache_image=undo_image;
6450        undo_image=GetPreviousImageInList(undo_image);
6451        cache_image->list=DestroyImage(cache_image->list);
6452        cache_image=DestroyImage(cache_image);
6453      }
6454      undo_image=NewImageList();
6455      if (redo_image != (Image *) NULL)
6456        redo_image=DestroyImage(redo_image);
6457      redo_image=NewImageList();
6458      return;
6459    }
6460    case UndoCommand:
6461    {
6462      char
6463        image_geometry[MaxTextExtent];
6464
6465      /*
6466        Undo the last image transformation.
6467      */
6468      if (undo_image == (Image *) NULL)
6469        {
6470          (void) XBell(display,0);
6471          return;
6472        }
6473      cache_image=undo_image;
6474      undo_image=GetPreviousImageInList(undo_image);
6475      windows->image.window_changes.width=(int) cache_image->columns;
6476      windows->image.window_changes.height=(int) cache_image->rows;
6477      (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
6478        windows->image.ximage->width,windows->image.ximage->height);
6479      (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
6480      if (windows->image.crop_geometry != (char *) NULL)
6481        windows->image.crop_geometry=(char *)
6482          RelinquishMagickMemory(windows->image.crop_geometry);
6483      windows->image.crop_geometry=cache_image->geometry;
6484      if (redo_image != (Image *) NULL)
6485        redo_image=DestroyImage(redo_image);
6486      redo_image=(*image);
6487      *image=cache_image->list;
6488      cache_image=DestroyImage(cache_image);
6489      if (windows->image.orphan != MagickFalse)
6490        return;
6491      XConfigureImageColormap(display,resource_info,windows,*image);
6492      (void) XConfigureImage(display,resource_info,windows,*image,exception);
6493      return;
6494    }
6495    case CutCommand:
6496    case PasteCommand:
6497    case ApplyCommand:
6498    case HalfSizeCommand:
6499    case OriginalSizeCommand:
6500    case DoubleSizeCommand:
6501    case ResizeCommand:
6502    case TrimCommand:
6503    case CropCommand:
6504    case ChopCommand:
6505    case FlipCommand:
6506    case FlopCommand:
6507    case RotateRightCommand:
6508    case RotateLeftCommand:
6509    case RotateCommand:
6510    case ShearCommand:
6511    case RollCommand:
6512    case NegateCommand:
6513    case ContrastStretchCommand:
6514    case SigmoidalContrastCommand:
6515    case NormalizeCommand:
6516    case EqualizeCommand:
6517    case HueCommand:
6518    case SaturationCommand:
6519    case BrightnessCommand:
6520    case GammaCommand:
6521    case SpiffCommand:
6522    case DullCommand:
6523    case GrayscaleCommand:
6524    case MapCommand:
6525    case QuantizeCommand:
6526    case DespeckleCommand:
6527    case EmbossCommand:
6528    case ReduceNoiseCommand:
6529    case AddNoiseCommand:
6530    case SharpenCommand:
6531    case BlurCommand:
6532    case ThresholdCommand:
6533    case EdgeDetectCommand:
6534    case SpreadCommand:
6535    case ShadeCommand:
6536    case RaiseCommand:
6537    case SegmentCommand:
6538    case SolarizeCommand:
6539    case SepiaToneCommand:
6540    case SwirlCommand:
6541    case ImplodeCommand:
6542    case VignetteCommand:
6543    case WaveCommand:
6544    case OilPaintCommand:
6545    case CharcoalDrawCommand:
6546    case AnnotateCommand:
6547    case AddBorderCommand:
6548    case AddFrameCommand:
6549    case CompositeCommand:
6550    case CommentCommand:
6551    case LaunchCommand:
6552    case RegionofInterestCommand:
6553    case SaveToUndoBufferCommand:
6554    case RedoCommand:
6555    {
6556      Image
6557        *previous_image;
6558
6559      ssize_t
6560        bytes;
6561
6562      bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelPacket));
6563      if (undo_image != (Image *) NULL)
6564        {
6565          /*
6566            Ensure the undo cache has enough memory available.
6567          */
6568          previous_image=undo_image;
6569          while (previous_image != (Image *) NULL)
6570          {
6571            bytes+=previous_image->list->columns*previous_image->list->rows*
6572              sizeof(PixelPacket);
6573            if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6574              {
6575                previous_image=GetPreviousImageInList(previous_image);
6576                continue;
6577              }
6578            bytes-=previous_image->list->columns*previous_image->list->rows*
6579              sizeof(PixelPacket);
6580            if (previous_image == undo_image)
6581              undo_image=NewImageList();
6582            else
6583              previous_image->next->previous=NewImageList();
6584            break;
6585          }
6586          while (previous_image != (Image *) NULL)
6587          {
6588            /*
6589              Delete any excess memory from undo cache.
6590            */
6591            cache_image=previous_image;
6592            previous_image=GetPreviousImageInList(previous_image);
6593            cache_image->list=DestroyImage(cache_image->list);
6594            cache_image=DestroyImage(cache_image);
6595          }
6596        }
6597      if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6598        break;
6599      /*
6600        Save image before transformations are applied.
6601      */
6602      cache_image=AcquireImage((ImageInfo *) NULL);
6603      if (cache_image == (Image *) NULL)
6604        break;
6605      XSetCursorState(display,windows,MagickTrue);
6606      XCheckRefreshWindows(display,windows);
6607      cache_image->list=CloneImage(*image,0,0,MagickTrue,exception);
6608      XSetCursorState(display,windows,MagickFalse);
6609      if (cache_image->list == (Image *) NULL)
6610        {
6611          cache_image=DestroyImage(cache_image);
6612          break;
6613        }
6614      cache_image->columns=(size_t) windows->image.ximage->width;
6615      cache_image->rows=(size_t) windows->image.ximage->height;
6616      cache_image->geometry=windows->image.crop_geometry;
6617      if (windows->image.crop_geometry != (char *) NULL)
6618        {
6619          cache_image->geometry=AcquireString((char *) NULL);
6620          (void) CopyMagickString(cache_image->geometry,
6621            windows->image.crop_geometry,MaxTextExtent);
6622        }
6623      if (undo_image == (Image *) NULL)
6624        {
6625          undo_image=cache_image;
6626          break;
6627        }
6628      undo_image->next=cache_image;
6629      undo_image->next->previous=undo_image;
6630      undo_image=undo_image->next;
6631      break;
6632    }
6633    default:
6634      break;
6635  }
6636  if (command == RedoCommand)
6637    {
6638      /*
6639        Redo the last image transformation.
6640      */
6641      if (redo_image == (Image *) NULL)
6642        {
6643          (void) XBell(display,0);
6644          return;
6645        }
6646      windows->image.window_changes.width=(int) redo_image->columns;
6647      windows->image.window_changes.height=(int) redo_image->rows;
6648      if (windows->image.crop_geometry != (char *) NULL)
6649        windows->image.crop_geometry=(char *)
6650          RelinquishMagickMemory(windows->image.crop_geometry);
6651      windows->image.crop_geometry=redo_image->geometry;
6652      *image=DestroyImage(*image);
6653      *image=redo_image;
6654      redo_image=NewImageList();
6655      if (windows->image.orphan != MagickFalse)
6656        return;
6657      XConfigureImageColormap(display,resource_info,windows,*image);
6658      (void) XConfigureImage(display,resource_info,windows,*image,exception);
6659      return;
6660    }
6661  if (command != InfoCommand)
6662    return;
6663  /*
6664    Display image info.
6665  */
6666  XSetCursorState(display,windows,MagickTrue);
6667  XCheckRefreshWindows(display,windows);
6668  XDisplayImageInfo(display,resource_info,windows,undo_image,*image);
6669  XSetCursorState(display,windows,MagickFalse);
6670}
6671
6672/*
6673%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6674%                                                                             %
6675%                                                                             %
6676%                                                                             %
6677+   X I m a g e W i n d o w C o m m a n d                                     %
6678%                                                                             %
6679%                                                                             %
6680%                                                                             %
6681%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6682%
6683%  XImageWindowCommand() makes a transform to the image or Image window as
6684%  specified by a user menu button or keyboard command.
6685%
6686%  The format of the XImageWindowCommand method is:
6687%
6688%      CommandType XImageWindowCommand(Display *display,
6689%        XResourceInfo *resource_info,XWindows *windows,
6690%        const MagickStatusType state,KeySym key_symbol,Image **image,
6691%        ExceptionInfo *exception)
6692%
6693%  A description of each parameter follows:
6694%
6695%    o nexus:  Method XImageWindowCommand returns an image when the
6696%      user chooses 'Open Image' from the command menu.  Otherwise a null
6697%      image is returned.
6698%
6699%    o display: Specifies a connection to an X server; returned from
6700%      XOpenDisplay.
6701%
6702%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6703%
6704%    o windows: Specifies a pointer to a XWindows structure.
6705%
6706%    o state: key mask.
6707%
6708%    o key_symbol: Specifies a command to perform.
6709%
6710%    o image: the image;  XImageWIndowCommand may transform the image and
6711%      return a new image pointer.
6712%
6713%    o exception: return any errors or warnings in this structure.
6714%
6715*/
6716static CommandType XImageWindowCommand(Display *display,
6717  XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6718  KeySym key_symbol,Image **image,ExceptionInfo *exception)
6719{
6720  static char
6721    delta[MaxTextExtent] = "";
6722
6723  static const char
6724    Digits[] = "01234567890";
6725
6726  static KeySym
6727    last_symbol = XK_0;
6728
6729  if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6730    {
6731      if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6732        {
6733          *delta='\0';
6734          resource_info->quantum=1;
6735        }
6736      last_symbol=key_symbol;
6737      delta[strlen(delta)+1]='\0';
6738      delta[strlen(delta)]=Digits[key_symbol-XK_0];
6739      resource_info->quantum=StringToLong(delta);
6740      return(NullCommand);
6741    }
6742  last_symbol=key_symbol;
6743  if (resource_info->immutable)
6744    {
6745      /*
6746        Virtual image window has a restricted command set.
6747      */
6748      switch (key_symbol)
6749      {
6750        case XK_question:
6751          return(InfoCommand);
6752        case XK_p:
6753        case XK_Print:
6754          return(PrintCommand);
6755        case XK_space:
6756          return(NextCommand);
6757        case XK_q:
6758        case XK_Escape:
6759          return(QuitCommand);
6760        default:
6761          break;
6762      }
6763      return(NullCommand);
6764    }
6765  switch ((int) key_symbol)
6766  {
6767    case XK_o:
6768    {
6769      if ((state & ControlMask) == 0)
6770        break;
6771      return(OpenCommand);
6772    }
6773    case XK_space:
6774      return(NextCommand);
6775    case XK_BackSpace:
6776      return(FormerCommand);
6777    case XK_s:
6778    {
6779      if ((state & Mod1Mask) != 0)
6780        return(SwirlCommand);
6781      if ((state & ControlMask) == 0)
6782        return(ShearCommand);
6783      return(SaveCommand);
6784    }
6785    case XK_p:
6786    case XK_Print:
6787    {
6788      if ((state & Mod1Mask) != 0)
6789        return(OilPaintCommand);
6790      if ((state & Mod4Mask) != 0)
6791        return(ColorCommand);
6792      if ((state & ControlMask) == 0)
6793        return(NullCommand);
6794      return(PrintCommand);
6795    }
6796    case XK_d:
6797    {
6798      if ((state & Mod4Mask) != 0)
6799        return(DrawCommand);
6800      if ((state & ControlMask) == 0)
6801        return(NullCommand);
6802      return(DeleteCommand);
6803    }
6804    case XK_Select:
6805    {
6806      if ((state & ControlMask) == 0)
6807        return(NullCommand);
6808      return(SelectCommand);
6809    }
6810    case XK_n:
6811    {
6812      if ((state & ControlMask) == 0)
6813        return(NullCommand);
6814      return(NewCommand);
6815    }
6816    case XK_q:
6817    case XK_Escape:
6818      return(QuitCommand);
6819    case XK_z:
6820    case XK_Undo:
6821    {
6822      if ((state & ControlMask) == 0)
6823        return(NullCommand);
6824      return(UndoCommand);
6825    }
6826    case XK_r:
6827    case XK_Redo:
6828    {
6829      if ((state & ControlMask) == 0)
6830        return(RollCommand);
6831      return(RedoCommand);
6832    }
6833    case XK_x:
6834    {
6835      if ((state & ControlMask) == 0)
6836        return(NullCommand);
6837      return(CutCommand);
6838    }
6839    case XK_c:
6840    {
6841      if ((state & Mod1Mask) != 0)
6842        return(CharcoalDrawCommand);
6843      if ((state & ControlMask) == 0)
6844        return(CropCommand);
6845      return(CopyCommand);
6846    }
6847    case XK_v:
6848    case XK_Insert:
6849    {
6850      if ((state & Mod4Mask) != 0)
6851        return(CompositeCommand);
6852      if ((state & ControlMask) == 0)
6853        return(FlipCommand);
6854      return(PasteCommand);
6855    }
6856    case XK_less:
6857      return(HalfSizeCommand);
6858    case XK_minus:
6859      return(OriginalSizeCommand);
6860    case XK_greater:
6861      return(DoubleSizeCommand);
6862    case XK_percent:
6863      return(ResizeCommand);
6864    case XK_at:
6865      return(RefreshCommand);
6866    case XK_bracketleft:
6867      return(ChopCommand);
6868    case XK_h:
6869      return(FlopCommand);
6870    case XK_slash:
6871      return(RotateRightCommand);
6872    case XK_backslash:
6873      return(RotateLeftCommand);
6874    case XK_asterisk:
6875      return(RotateCommand);
6876    case XK_t:
6877      return(TrimCommand);
6878    case XK_H:
6879      return(HueCommand);
6880    case XK_S:
6881      return(SaturationCommand);
6882    case XK_L:
6883      return(BrightnessCommand);
6884    case XK_G:
6885      return(GammaCommand);
6886    case XK_C:
6887      return(SpiffCommand);
6888    case XK_Z:
6889      return(DullCommand);
6890    case XK_N:
6891      return(NormalizeCommand);
6892    case XK_equal:
6893      return(EqualizeCommand);
6894    case XK_asciitilde:
6895      return(NegateCommand);
6896    case XK_period:
6897      return(GrayscaleCommand);
6898    case XK_numbersign:
6899      return(QuantizeCommand);
6900    case XK_F2:
6901      return(DespeckleCommand);
6902    case XK_F3:
6903      return(EmbossCommand);
6904    case XK_F4:
6905      return(ReduceNoiseCommand);
6906    case XK_F5:
6907      return(AddNoiseCommand);
6908    case XK_F6:
6909      return(SharpenCommand);
6910    case XK_F7:
6911      return(BlurCommand);
6912    case XK_F8:
6913      return(ThresholdCommand);
6914    case XK_F9:
6915      return(EdgeDetectCommand);
6916    case XK_F10:
6917      return(SpreadCommand);
6918    case XK_F11:
6919      return(ShadeCommand);
6920    case XK_F12:
6921      return(RaiseCommand);
6922    case XK_F13:
6923      return(SegmentCommand);
6924    case XK_i:
6925    {
6926      if ((state & Mod1Mask) == 0)
6927        return(NullCommand);
6928      return(ImplodeCommand);
6929    }
6930    case XK_w:
6931    {
6932      if ((state & Mod1Mask) == 0)
6933        return(NullCommand);
6934      return(WaveCommand);
6935    }
6936    case XK_m:
6937    {
6938      if ((state & Mod4Mask) == 0)
6939        return(NullCommand);
6940      return(MatteCommand);
6941    }
6942    case XK_b:
6943    {
6944      if ((state & Mod4Mask) == 0)
6945        return(NullCommand);
6946      return(AddBorderCommand);
6947    }
6948    case XK_f:
6949    {
6950      if ((state & Mod4Mask) == 0)
6951        return(NullCommand);
6952      return(AddFrameCommand);
6953    }
6954    case XK_exclam:
6955    {
6956      if ((state & Mod4Mask) == 0)
6957        return(NullCommand);
6958      return(CommentCommand);
6959    }
6960    case XK_a:
6961    {
6962      if ((state & Mod1Mask) != 0)
6963        return(ApplyCommand);
6964      if ((state & Mod4Mask) != 0)
6965        return(AnnotateCommand);
6966      if ((state & ControlMask) == 0)
6967        return(NullCommand);
6968      return(RegionofInterestCommand);
6969    }
6970    case XK_question:
6971      return(InfoCommand);
6972    case XK_plus:
6973      return(ZoomCommand);
6974    case XK_P:
6975    {
6976      if ((state & ShiftMask) == 0)
6977        return(NullCommand);
6978      return(ShowPreviewCommand);
6979    }
6980    case XK_Execute:
6981      return(LaunchCommand);
6982    case XK_F1:
6983      return(HelpCommand);
6984    case XK_Find:
6985      return(BrowseDocumentationCommand);
6986    case XK_Menu:
6987    {
6988      (void) XMapRaised(display,windows->command.id);
6989      return(NullCommand);
6990    }
6991    case XK_Next:
6992    case XK_Prior:
6993    case XK_Home:
6994    case XK_KP_Home:
6995    {
6996      XTranslateImage(display,windows,*image,key_symbol);
6997      return(NullCommand);
6998    }
6999    case XK_Up:
7000    case XK_KP_Up:
7001    case XK_Down:
7002    case XK_KP_Down:
7003    case XK_Left:
7004    case XK_KP_Left:
7005    case XK_Right:
7006    case XK_KP_Right:
7007    {
7008      if ((state & Mod1Mask) != 0)
7009        {
7010          RectangleInfo
7011            crop_info;
7012
7013          /*
7014            Trim one pixel from edge of image.
7015          */
7016          crop_info.x=0;
7017          crop_info.y=0;
7018          crop_info.width=(size_t) windows->image.ximage->width;
7019          crop_info.height=(size_t) windows->image.ximage->height;
7020          if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
7021            {
7022              if (resource_info->quantum >= (int) crop_info.height)
7023                resource_info->quantum=(int) crop_info.height-1;
7024              crop_info.height-=resource_info->quantum;
7025            }
7026          if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
7027            {
7028              if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
7029                resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
7030              crop_info.y+=resource_info->quantum;
7031              crop_info.height-=resource_info->quantum;
7032            }
7033          if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
7034            {
7035              if (resource_info->quantum >= (int) crop_info.width)
7036                resource_info->quantum=(int) crop_info.width-1;
7037              crop_info.width-=resource_info->quantum;
7038            }
7039          if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
7040            {
7041              if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
7042                resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
7043              crop_info.x+=resource_info->quantum;
7044              crop_info.width-=resource_info->quantum;
7045            }
7046          if ((int) (windows->image.x+windows->image.width) >
7047              (int) crop_info.width)
7048            windows->image.x=(int) (crop_info.width-windows->image.width);
7049          if ((int) (windows->image.y+windows->image.height) >
7050              (int) crop_info.height)
7051            windows->image.y=(int) (crop_info.height-windows->image.height);
7052          XSetCropGeometry(display,windows,&crop_info,*image);
7053          windows->image.window_changes.width=(int) crop_info.width;
7054          windows->image.window_changes.height=(int) crop_info.height;
7055          (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
7056          (void) XConfigureImage(display,resource_info,windows,*image,
7057            exception);
7058          return(NullCommand);
7059        }
7060      XTranslateImage(display,windows,*image,key_symbol);
7061      return(NullCommand);
7062    }
7063    default:
7064      return(NullCommand);
7065  }
7066  return(NullCommand);
7067}
7068
7069/*
7070%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7071%                                                                             %
7072%                                                                             %
7073%                                                                             %
7074+   X M a g i c k C o m m a n d                                               %
7075%                                                                             %
7076%                                                                             %
7077%                                                                             %
7078%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7079%
7080%  XMagickCommand() makes a transform to the image or Image window as
7081%  specified by a user menu button or keyboard command.
7082%
7083%  The format of the XMagickCommand method is:
7084%
7085%      Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7086%        XWindows *windows,const CommandType command,Image **image,
7087%        ExceptionInfo *exception)
7088%
7089%  A description of each parameter follows:
7090%
7091%    o display: Specifies a connection to an X server; returned from
7092%      XOpenDisplay.
7093%
7094%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7095%
7096%    o windows: Specifies a pointer to a XWindows structure.
7097%
7098%    o command: Specifies a command to perform.
7099%
7100%    o image: the image;  XMagickCommand may transform the image and return a
7101%      new image pointer.
7102%
7103%    o exception: return any errors or warnings in this structure.
7104%
7105*/
7106static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7107  XWindows *windows,const CommandType command,Image **image,
7108  ExceptionInfo *exception)
7109{
7110  char
7111    filename[MaxTextExtent],
7112    geometry[MaxTextExtent],
7113    modulate_factors[MaxTextExtent];
7114
7115  GeometryInfo
7116    geometry_info;
7117
7118  Image
7119    *nexus;
7120
7121  ImageInfo
7122    *image_info;
7123
7124  int
7125    x,
7126    y;
7127
7128  MagickStatusType
7129    flags,
7130    status;
7131
7132  QuantizeInfo
7133    quantize_info;
7134
7135  RectangleInfo
7136    page_geometry;
7137
7138  register int
7139    i;
7140
7141  static char
7142    color[MaxTextExtent] = "gray";
7143
7144  unsigned int
7145    height,
7146    width;
7147
7148  /*
7149    Process user command.
7150  */
7151  XCheckRefreshWindows(display,windows);
7152  XImageCache(display,resource_info,windows,command,image,exception);
7153  nexus=NewImageList();
7154  windows->image.window_changes.width=windows->image.ximage->width;
7155  windows->image.window_changes.height=windows->image.ximage->height;
7156  image_info=CloneImageInfo(resource_info->image_info);
7157  SetGeometryInfo(&geometry_info);
7158  GetQuantizeInfo(&quantize_info);
7159  switch (command)
7160  {
7161    case OpenCommand:
7162    {
7163      /*
7164        Load image.
7165      */
7166      nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7167      break;
7168    }
7169    case NextCommand:
7170    {
7171      /*
7172        Display next image.
7173      */
7174      for (i=0; i < resource_info->quantum; i++)
7175        XClientMessage(display,windows->image.id,windows->im_protocols,
7176          windows->im_next_image,CurrentTime);
7177      break;
7178    }
7179    case FormerCommand:
7180    {
7181      /*
7182        Display former image.
7183      */
7184      for (i=0; i < resource_info->quantum; i++)
7185        XClientMessage(display,windows->image.id,windows->im_protocols,
7186          windows->im_former_image,CurrentTime);
7187      break;
7188    }
7189    case SelectCommand:
7190    {
7191      int
7192        status;
7193
7194      /*
7195        Select image.
7196      */
7197      status=chdir(resource_info->home_directory);
7198      if (status == -1)
7199        (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
7200          "UnableToOpenFile","%s",resource_info->home_directory);
7201      nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7202      break;
7203    }
7204    case SaveCommand:
7205    {
7206      /*
7207        Save image.
7208      */
7209      status=XSaveImage(display,resource_info,windows,*image,exception);
7210      if (status == MagickFalse)
7211        {
7212          XNoticeWidget(display,windows,"Unable to write X image:",
7213            (*image)->filename);
7214          break;
7215        }
7216      break;
7217    }
7218    case PrintCommand:
7219    {
7220      /*
7221        Print image.
7222      */
7223      status=XPrintImage(display,resource_info,windows,*image,exception);
7224      if (status == MagickFalse)
7225        {
7226          XNoticeWidget(display,windows,"Unable to print X image:",
7227            (*image)->filename);
7228          break;
7229        }
7230      break;
7231    }
7232    case DeleteCommand:
7233    {
7234      static char
7235        filename[MaxTextExtent] = "\0";
7236
7237      /*
7238        Delete image file.
7239      */
7240      XFileBrowserWidget(display,windows,"Delete",filename);
7241      if (*filename == '\0')
7242        break;
7243      status=remove(filename) != 0 ? MagickTrue : MagickFalse;
7244      if (status != MagickFalse)
7245        XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7246      break;
7247    }
7248    case NewCommand:
7249    {
7250      int
7251        status;
7252
7253      static char
7254        color[MaxTextExtent] = "gray",
7255        geometry[MaxTextExtent] = "640x480";
7256
7257      static const char
7258        *format = "gradient";
7259
7260      /*
7261        Query user for canvas geometry.
7262      */
7263      status=XDialogWidget(display,windows,"New","Enter image geometry:",
7264        geometry);
7265      if (*geometry == '\0')
7266        break;
7267      if (status == 0)
7268        format="xc";
7269      XColorBrowserWidget(display,windows,"Select",color);
7270      if (*color == '\0')
7271        break;
7272      /*
7273        Create canvas.
7274      */
7275      (void) FormatLocaleString(image_info->filename,MaxTextExtent,
7276        "%s:%s",format,color);
7277      (void) CloneString(&image_info->size,geometry);
7278      nexus=ReadImage(image_info,exception);
7279      CatchException(exception);
7280      XClientMessage(display,windows->image.id,windows->im_protocols,
7281        windows->im_next_image,CurrentTime);
7282      break;
7283    }
7284    case VisualDirectoryCommand:
7285    {
7286      /*
7287        Visual Image directory.
7288      */
7289      nexus=XVisualDirectoryImage(display,resource_info,windows,exception);
7290      break;
7291    }
7292    case QuitCommand:
7293    {
7294      /*
7295        exit program.
7296      */
7297      if (resource_info->confirm_exit == MagickFalse)
7298        XClientMessage(display,windows->image.id,windows->im_protocols,
7299          windows->im_exit,CurrentTime);
7300      else
7301        {
7302          int
7303            status;
7304
7305          /*
7306            Confirm program exit.
7307          */
7308          status=XConfirmWidget(display,windows,"Do you really want to exit",
7309            resource_info->client_name);
7310          if (status > 0)
7311            XClientMessage(display,windows->image.id,windows->im_protocols,
7312              windows->im_exit,CurrentTime);
7313        }
7314      break;
7315    }
7316    case CutCommand:
7317    {
7318      /*
7319        Cut image.
7320      */
7321      (void) XCropImage(display,resource_info,windows,*image,CutMode,exception);
7322      break;
7323    }
7324    case CopyCommand:
7325    {
7326      /*
7327        Copy image.
7328      */
7329      (void) XCropImage(display,resource_info,windows,*image,CopyMode,
7330        exception);
7331      break;
7332    }
7333    case PasteCommand:
7334    {
7335      /*
7336        Paste image.
7337      */
7338      status=XPasteImage(display,resource_info,windows,*image,exception);
7339      if (status == MagickFalse)
7340        {
7341          XNoticeWidget(display,windows,"Unable to paste X image",
7342            (*image)->filename);
7343          break;
7344        }
7345      break;
7346    }
7347    case HalfSizeCommand:
7348    {
7349      /*
7350        Half image size.
7351      */
7352      windows->image.window_changes.width=windows->image.ximage->width/2;
7353      windows->image.window_changes.height=windows->image.ximage->height/2;
7354      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7355      break;
7356    }
7357    case OriginalSizeCommand:
7358    {
7359      /*
7360        Original image size.
7361      */
7362      windows->image.window_changes.width=(int) (*image)->columns;
7363      windows->image.window_changes.height=(int) (*image)->rows;
7364      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7365      break;
7366    }
7367    case DoubleSizeCommand:
7368    {
7369      /*
7370        Double the image size.
7371      */
7372      windows->image.window_changes.width=windows->image.ximage->width << 1;
7373      windows->image.window_changes.height=windows->image.ximage->height << 1;
7374      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7375      break;
7376    }
7377    case ResizeCommand:
7378    {
7379      int
7380        status;
7381
7382      size_t
7383        height,
7384        width;
7385
7386      ssize_t
7387        x,
7388        y;
7389
7390      /*
7391        Resize image.
7392      */
7393      width=(size_t) windows->image.ximage->width;
7394      height=(size_t) windows->image.ximage->height;
7395      x=0;
7396      y=0;
7397      (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g+0+0",
7398        (double) width,(double) height);
7399      status=XDialogWidget(display,windows,"Resize",
7400        "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7401      if (*geometry == '\0')
7402        break;
7403      if (status == 0)
7404        (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7405      (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7406      windows->image.window_changes.width=(int) width;
7407      windows->image.window_changes.height=(int) height;
7408      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7409      break;
7410    }
7411    case ApplyCommand:
7412    {
7413      char
7414        image_geometry[MaxTextExtent];
7415
7416      if ((windows->image.crop_geometry == (char *) NULL) &&
7417          ((int) (*image)->columns == windows->image.ximage->width) &&
7418          ((int) (*image)->rows == windows->image.ximage->height))
7419        break;
7420      /*
7421        Apply size transforms to image.
7422      */
7423      XSetCursorState(display,windows,MagickTrue);
7424      XCheckRefreshWindows(display,windows);
7425      /*
7426        Crop and/or scale displayed image.
7427      */
7428      (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
7429        windows->image.ximage->width,windows->image.ximage->height);
7430      (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
7431      if (windows->image.crop_geometry != (char *) NULL)
7432        windows->image.crop_geometry=(char *)
7433          RelinquishMagickMemory(windows->image.crop_geometry);
7434      windows->image.x=0;
7435      windows->image.y=0;
7436      XConfigureImageColormap(display,resource_info,windows,*image);
7437      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7438      break;
7439    }
7440    case RefreshCommand:
7441    {
7442      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7443      break;
7444    }
7445    case RestoreCommand:
7446    {
7447      /*
7448        Restore Image window to its original size.
7449      */
7450      if ((windows->image.width == (unsigned int) (*image)->columns) &&
7451          (windows->image.height == (unsigned int) (*image)->rows) &&
7452          (windows->image.crop_geometry == (char *) NULL))
7453        {
7454          (void) XBell(display,0);
7455          break;
7456        }
7457      windows->image.window_changes.width=(int) (*image)->columns;
7458      windows->image.window_changes.height=(int) (*image)->rows;
7459      if (windows->image.crop_geometry != (char *) NULL)
7460        {
7461          windows->image.crop_geometry=(char *)
7462            RelinquishMagickMemory(windows->image.crop_geometry);
7463          windows->image.crop_geometry=(char *) NULL;
7464          windows->image.x=0;
7465          windows->image.y=0;
7466        }
7467      XConfigureImageColormap(display,resource_info,windows,*image);
7468      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7469      break;
7470    }
7471    case CropCommand:
7472    {
7473      /*
7474        Crop image.
7475      */
7476      (void) XCropImage(display,resource_info,windows,*image,CropMode,
7477        exception);
7478      break;
7479    }
7480    case ChopCommand:
7481    {
7482      /*
7483        Chop image.
7484      */
7485      status=XChopImage(display,resource_info,windows,image,exception);
7486      if (status == MagickFalse)
7487        {
7488          XNoticeWidget(display,windows,"Unable to cut X image",
7489            (*image)->filename);
7490          break;
7491        }
7492      break;
7493    }
7494    case FlopCommand:
7495    {
7496      Image
7497        *flop_image;
7498
7499      /*
7500        Flop image scanlines.
7501      */
7502      XSetCursorState(display,windows,MagickTrue);
7503      XCheckRefreshWindows(display,windows);
7504      flop_image=FlopImage(*image,exception);
7505      if (flop_image != (Image *) NULL)
7506        {
7507          *image=DestroyImage(*image);
7508          *image=flop_image;
7509        }
7510      CatchException(exception);
7511      XSetCursorState(display,windows,MagickFalse);
7512      if (windows->image.crop_geometry != (char *) NULL)
7513        {
7514          /*
7515            Flop crop geometry.
7516          */
7517          width=(unsigned int) (*image)->columns;
7518          height=(unsigned int) (*image)->rows;
7519          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7520            &width,&height);
7521          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7522            "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7523        }
7524      if (windows->image.orphan != MagickFalse)
7525        break;
7526      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7527      break;
7528    }
7529    case FlipCommand:
7530    {
7531      Image
7532        *flip_image;
7533
7534      /*
7535        Flip image scanlines.
7536      */
7537      XSetCursorState(display,windows,MagickTrue);
7538      XCheckRefreshWindows(display,windows);
7539      flip_image=FlipImage(*image,exception);
7540      if (flip_image != (Image *) NULL)
7541        {
7542          *image=DestroyImage(*image);
7543          *image=flip_image;
7544        }
7545      CatchException(exception);
7546      XSetCursorState(display,windows,MagickFalse);
7547      if (windows->image.crop_geometry != (char *) NULL)
7548        {
7549          /*
7550            Flip crop geometry.
7551          */
7552          width=(unsigned int) (*image)->columns;
7553          height=(unsigned int) (*image)->rows;
7554          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7555            &width,&height);
7556          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7557            "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7558        }
7559      if (windows->image.orphan != MagickFalse)
7560        break;
7561      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7562      break;
7563    }
7564    case RotateRightCommand:
7565    {
7566      /*
7567        Rotate image 90 degrees clockwise.
7568      */
7569      status=XRotateImage(display,resource_info,windows,90.0,image,exception);
7570      if (status == MagickFalse)
7571        {
7572          XNoticeWidget(display,windows,"Unable to rotate X image",
7573            (*image)->filename);
7574          break;
7575        }
7576      break;
7577    }
7578    case RotateLeftCommand:
7579    {
7580      /*
7581        Rotate image 90 degrees counter-clockwise.
7582      */
7583      status=XRotateImage(display,resource_info,windows,-90.0,image,exception);
7584      if (status == MagickFalse)
7585        {
7586          XNoticeWidget(display,windows,"Unable to rotate X image",
7587            (*image)->filename);
7588          break;
7589        }
7590      break;
7591    }
7592    case RotateCommand:
7593    {
7594      /*
7595        Rotate image.
7596      */
7597      status=XRotateImage(display,resource_info,windows,0.0,image,exception);
7598      if (status == MagickFalse)
7599        {
7600          XNoticeWidget(display,windows,"Unable to rotate X image",
7601            (*image)->filename);
7602          break;
7603        }
7604      break;
7605    }
7606    case ShearCommand:
7607    {
7608      Image
7609        *shear_image;
7610
7611      static char
7612        geometry[MaxTextExtent] = "45.0x45.0";
7613
7614      /*
7615        Query user for shear color and geometry.
7616      */
7617      XColorBrowserWidget(display,windows,"Select",color);
7618      if (*color == '\0')
7619        break;
7620      (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7621        geometry);
7622      if (*geometry == '\0')
7623        break;
7624      /*
7625        Shear image.
7626      */
7627      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7628        exception);
7629      XSetCursorState(display,windows,MagickTrue);
7630      XCheckRefreshWindows(display,windows);
7631      (void) QueryColorDatabase(color,&(*image)->background_color,
7632        exception);
7633      flags=ParseGeometry(geometry,&geometry_info);
7634      if ((flags & SigmaValue) == 0)
7635        geometry_info.sigma=geometry_info.rho;
7636      shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7637        exception);
7638      if (shear_image != (Image *) NULL)
7639        {
7640          *image=DestroyImage(*image);
7641          *image=shear_image;
7642        }
7643      CatchException(exception);
7644      XSetCursorState(display,windows,MagickFalse);
7645      if (windows->image.orphan != MagickFalse)
7646        break;
7647      windows->image.window_changes.width=(int) (*image)->columns;
7648      windows->image.window_changes.height=(int) (*image)->rows;
7649      XConfigureImageColormap(display,resource_info,windows,*image);
7650      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7651      break;
7652    }
7653    case RollCommand:
7654    {
7655      Image
7656        *roll_image;
7657
7658      static char
7659        geometry[MaxTextExtent] = "+2+2";
7660
7661      /*
7662        Query user for the roll geometry.
7663      */
7664      (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7665        geometry);
7666      if (*geometry == '\0')
7667        break;
7668      /*
7669        Roll image.
7670      */
7671      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7672        exception);
7673      XSetCursorState(display,windows,MagickTrue);
7674      XCheckRefreshWindows(display,windows);
7675      (void) ParsePageGeometry(*image,geometry,&page_geometry,
7676        exception);
7677      roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7678        exception);
7679      if (roll_image != (Image *) NULL)
7680        {
7681          *image=DestroyImage(*image);
7682          *image=roll_image;
7683        }
7684      CatchException(exception);
7685      XSetCursorState(display,windows,MagickFalse);
7686      if (windows->image.orphan != MagickFalse)
7687        break;
7688      windows->image.window_changes.width=(int) (*image)->columns;
7689      windows->image.window_changes.height=(int) (*image)->rows;
7690      XConfigureImageColormap(display,resource_info,windows,*image);
7691      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7692      break;
7693    }
7694    case TrimCommand:
7695    {
7696      static char
7697        fuzz[MaxTextExtent];
7698
7699      /*
7700        Query user for the fuzz factor.
7701      */
7702      (void) FormatLocaleString(fuzz,MaxTextExtent,"%g%%",100.0*
7703        (*image)->fuzz/(QuantumRange+1.0));
7704      (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7705      if (*fuzz == '\0')
7706        break;
7707      (*image)->fuzz=SiPrefixToDouble(fuzz,(double) QuantumRange+1.0);
7708      /*
7709        Trim image.
7710      */
7711      status=XTrimImage(display,resource_info,windows,*image,exception);
7712      if (status == MagickFalse)
7713        {
7714          XNoticeWidget(display,windows,"Unable to trim X image",
7715            (*image)->filename);
7716          break;
7717        }
7718      break;
7719    }
7720    case HueCommand:
7721    {
7722      static char
7723        hue_percent[MaxTextExtent] = "110";
7724
7725      /*
7726        Query user for percent hue change.
7727      */
7728      (void) XDialogWidget(display,windows,"Apply",
7729        "Enter percent change in image hue (0-200):",hue_percent);
7730      if (*hue_percent == '\0')
7731        break;
7732      /*
7733        Vary the image hue.
7734      */
7735      XSetCursorState(display,windows,MagickTrue);
7736      XCheckRefreshWindows(display,windows);
7737      (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7738      (void) ConcatenateMagickString(modulate_factors,hue_percent,
7739        MaxTextExtent);
7740      (void) ModulateImage(*image,modulate_factors,exception);
7741      XSetCursorState(display,windows,MagickFalse);
7742      if (windows->image.orphan != MagickFalse)
7743        break;
7744      XConfigureImageColormap(display,resource_info,windows,*image);
7745      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7746      break;
7747    }
7748    case SaturationCommand:
7749    {
7750      static char
7751        saturation_percent[MaxTextExtent] = "110";
7752
7753      /*
7754        Query user for percent saturation change.
7755      */
7756      (void) XDialogWidget(display,windows,"Apply",
7757        "Enter percent change in color saturation (0-200):",saturation_percent);
7758      if (*saturation_percent == '\0')
7759        break;
7760      /*
7761        Vary color saturation.
7762      */
7763      XSetCursorState(display,windows,MagickTrue);
7764      XCheckRefreshWindows(display,windows);
7765      (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7766      (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7767        MaxTextExtent);
7768      (void) ModulateImage(*image,modulate_factors,exception);
7769      XSetCursorState(display,windows,MagickFalse);
7770      if (windows->image.orphan != MagickFalse)
7771        break;
7772      XConfigureImageColormap(display,resource_info,windows,*image);
7773      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7774      break;
7775    }
7776    case BrightnessCommand:
7777    {
7778      static char
7779        brightness_percent[MaxTextExtent] = "110";
7780
7781      /*
7782        Query user for percent brightness change.
7783      */
7784      (void) XDialogWidget(display,windows,"Apply",
7785        "Enter percent change in color brightness (0-200):",brightness_percent);
7786      if (*brightness_percent == '\0')
7787        break;
7788      /*
7789        Vary the color brightness.
7790      */
7791      XSetCursorState(display,windows,MagickTrue);
7792      XCheckRefreshWindows(display,windows);
7793      (void) CopyMagickString(modulate_factors,brightness_percent,
7794        MaxTextExtent);
7795      (void) ModulateImage(*image,modulate_factors,exception);
7796      XSetCursorState(display,windows,MagickFalse);
7797      if (windows->image.orphan != MagickFalse)
7798        break;
7799      XConfigureImageColormap(display,resource_info,windows,*image);
7800      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7801      break;
7802    }
7803    case GammaCommand:
7804    {
7805      static char
7806        factor[MaxTextExtent] = "1.6";
7807
7808      /*
7809        Query user for gamma value.
7810      */
7811      (void) XDialogWidget(display,windows,"Gamma",
7812        "Enter gamma value (e.g. 1.2):",factor);
7813      if (*factor == '\0')
7814        break;
7815      /*
7816        Gamma correct image.
7817      */
7818      XSetCursorState(display,windows,MagickTrue);
7819      XCheckRefreshWindows(display,windows);
7820      (void) GammaImage(*image,atof(factor),exception);
7821      XSetCursorState(display,windows,MagickFalse);
7822      if (windows->image.orphan != MagickFalse)
7823        break;
7824      XConfigureImageColormap(display,resource_info,windows,*image);
7825      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7826      break;
7827    }
7828    case SpiffCommand:
7829    {
7830      /*
7831        Sharpen the image contrast.
7832      */
7833      XSetCursorState(display,windows,MagickTrue);
7834      XCheckRefreshWindows(display,windows);
7835      (void) ContrastImage(*image,MagickTrue,exception);
7836      XSetCursorState(display,windows,MagickFalse);
7837      if (windows->image.orphan != MagickFalse)
7838        break;
7839      XConfigureImageColormap(display,resource_info,windows,*image);
7840      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7841      break;
7842    }
7843    case DullCommand:
7844    {
7845      /*
7846        Dull the image contrast.
7847      */
7848      XSetCursorState(display,windows,MagickTrue);
7849      XCheckRefreshWindows(display,windows);
7850      (void) ContrastImage(*image,MagickFalse,exception);
7851      XSetCursorState(display,windows,MagickFalse);
7852      if (windows->image.orphan != MagickFalse)
7853        break;
7854      XConfigureImageColormap(display,resource_info,windows,*image);
7855      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7856      break;
7857    }
7858    case ContrastStretchCommand:
7859    {
7860      double
7861        black_point,
7862        white_point;
7863
7864      static char
7865        levels[MaxTextExtent] = "1%";
7866
7867      /*
7868        Query user for gamma value.
7869      */
7870      (void) XDialogWidget(display,windows,"Contrast Stretch",
7871        "Enter black and white points:",levels);
7872      if (*levels == '\0')
7873        break;
7874      /*
7875        Contrast stretch image.
7876      */
7877      XSetCursorState(display,windows,MagickTrue);
7878      XCheckRefreshWindows(display,windows);
7879      flags=ParseGeometry(levels,&geometry_info);
7880      black_point=geometry_info.rho;
7881      white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7882      if ((flags & PercentValue) != 0)
7883        {
7884          black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7885          white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7886        }
7887      white_point=(MagickRealType) (*image)->columns*(*image)->rows-white_point;
7888      (void) ContrastStretchImage(*image,black_point,white_point,
7889        exception);
7890      XSetCursorState(display,windows,MagickFalse);
7891      if (windows->image.orphan != MagickFalse)
7892        break;
7893      XConfigureImageColormap(display,resource_info,windows,*image);
7894      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7895      break;
7896    }
7897    case SigmoidalContrastCommand:
7898    {
7899      GeometryInfo
7900        geometry_info;
7901
7902      MagickStatusType
7903        flags;
7904
7905      static char
7906        levels[MaxTextExtent] = "3x50%";
7907
7908      /*
7909        Query user for gamma value.
7910      */
7911      (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7912        "Enter contrast and midpoint:",levels);
7913      if (*levels == '\0')
7914        break;
7915      /*
7916        Contrast stretch image.
7917      */
7918      XSetCursorState(display,windows,MagickTrue);
7919      XCheckRefreshWindows(display,windows);
7920      flags=ParseGeometry(levels,&geometry_info);
7921      if ((flags & SigmaValue) == 0)
7922        geometry_info.sigma=1.0*QuantumRange/2.0;
7923      if ((flags & PercentValue) != 0)
7924        geometry_info.sigma=1.0*QuantumRange*geometry_info.sigma/100.0;
7925      (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
7926        geometry_info.sigma,exception);
7927      XSetCursorState(display,windows,MagickFalse);
7928      if (windows->image.orphan != MagickFalse)
7929        break;
7930      XConfigureImageColormap(display,resource_info,windows,*image);
7931      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7932      break;
7933    }
7934    case NormalizeCommand:
7935    {
7936      /*
7937        Perform histogram normalization on the image.
7938      */
7939      XSetCursorState(display,windows,MagickTrue);
7940      XCheckRefreshWindows(display,windows);
7941      (void) NormalizeImage(*image,exception);
7942      XSetCursorState(display,windows,MagickFalse);
7943      if (windows->image.orphan != MagickFalse)
7944        break;
7945      XConfigureImageColormap(display,resource_info,windows,*image);
7946      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7947      break;
7948    }
7949    case EqualizeCommand:
7950    {
7951      /*
7952        Perform histogram equalization on the image.
7953      */
7954      XSetCursorState(display,windows,MagickTrue);
7955      XCheckRefreshWindows(display,windows);
7956      (void) EqualizeImage(*image,exception);
7957      XSetCursorState(display,windows,MagickFalse);
7958      if (windows->image.orphan != MagickFalse)
7959        break;
7960      XConfigureImageColormap(display,resource_info,windows,*image);
7961      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7962      break;
7963    }
7964    case NegateCommand:
7965    {
7966      /*
7967        Negate colors in image.
7968      */
7969      XSetCursorState(display,windows,MagickTrue);
7970      XCheckRefreshWindows(display,windows);
7971      (void) NegateImage(*image,MagickFalse,exception);
7972      XSetCursorState(display,windows,MagickFalse);
7973      if (windows->image.orphan != MagickFalse)
7974        break;
7975      XConfigureImageColormap(display,resource_info,windows,*image);
7976      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7977      break;
7978    }
7979    case GrayscaleCommand:
7980    {
7981      /*
7982        Convert image to grayscale.
7983      */
7984      XSetCursorState(display,windows,MagickTrue);
7985      XCheckRefreshWindows(display,windows);
7986      (void) SetImageType(*image,(*image)->matte == MagickFalse ?
7987        GrayscaleType : GrayscaleMatteType);
7988      XSetCursorState(display,windows,MagickFalse);
7989      if (windows->image.orphan != MagickFalse)
7990        break;
7991      XConfigureImageColormap(display,resource_info,windows,*image);
7992      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7993      break;
7994    }
7995    case MapCommand:
7996    {
7997      Image
7998        *affinity_image;
7999
8000      static char
8001        filename[MaxTextExtent] = "\0";
8002
8003      /*
8004        Request image file name from user.
8005      */
8006      XFileBrowserWidget(display,windows,"Map",filename);
8007      if (*filename == '\0')
8008        break;
8009      /*
8010        Map image.
8011      */
8012      XSetCursorState(display,windows,MagickTrue);
8013      XCheckRefreshWindows(display,windows);
8014      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
8015      affinity_image=ReadImage(image_info,exception);
8016      if (affinity_image != (Image *) NULL)
8017        {
8018          (void) RemapImage(&quantize_info,*image,affinity_image);
8019          affinity_image=DestroyImage(affinity_image);
8020        }
8021      CatchException(exception);
8022      XSetCursorState(display,windows,MagickFalse);
8023      if (windows->image.orphan != MagickFalse)
8024        break;
8025      XConfigureImageColormap(display,resource_info,windows,*image);
8026      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8027      break;
8028    }
8029    case QuantizeCommand:
8030    {
8031      int
8032        status;
8033
8034      static char
8035        colors[MaxTextExtent] = "256";
8036
8037      /*
8038        Query user for maximum number of colors.
8039      */
8040      status=XDialogWidget(display,windows,"Quantize",
8041        "Maximum number of colors:",colors);
8042      if (*colors == '\0')
8043        break;
8044      /*
8045        Color reduce the image.
8046      */
8047      XSetCursorState(display,windows,MagickTrue);
8048      XCheckRefreshWindows(display,windows);
8049      quantize_info.number_colors=StringToUnsignedLong(colors);
8050      quantize_info.dither=status != 0 ? MagickTrue : MagickFalse;
8051      (void) QuantizeImage(&quantize_info,*image);
8052      XSetCursorState(display,windows,MagickFalse);
8053      if (windows->image.orphan != MagickFalse)
8054        break;
8055      XConfigureImageColormap(display,resource_info,windows,*image);
8056      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8057      break;
8058    }
8059    case DespeckleCommand:
8060    {
8061      Image
8062        *despeckle_image;
8063
8064      /*
8065        Despeckle image.
8066      */
8067      XSetCursorState(display,windows,MagickTrue);
8068      XCheckRefreshWindows(display,windows);
8069      despeckle_image=DespeckleImage(*image,exception);
8070      if (despeckle_image != (Image *) NULL)
8071        {
8072          *image=DestroyImage(*image);
8073          *image=despeckle_image;
8074        }
8075      CatchException(exception);
8076      XSetCursorState(display,windows,MagickFalse);
8077      if (windows->image.orphan != MagickFalse)
8078        break;
8079      XConfigureImageColormap(display,resource_info,windows,*image);
8080      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8081      break;
8082    }
8083    case EmbossCommand:
8084    {
8085      Image
8086        *emboss_image;
8087
8088      static char
8089        radius[MaxTextExtent] = "0.0x1.0";
8090
8091      /*
8092        Query user for emboss radius.
8093      */
8094      (void) XDialogWidget(display,windows,"Emboss",
8095        "Enter the emboss radius and standard deviation:",radius);
8096      if (*radius == '\0')
8097        break;
8098      /*
8099        Reduce noise in the image.
8100      */
8101      XSetCursorState(display,windows,MagickTrue);
8102      XCheckRefreshWindows(display,windows);
8103      flags=ParseGeometry(radius,&geometry_info);
8104      if ((flags & SigmaValue) == 0)
8105        geometry_info.sigma=1.0;
8106      emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8107        exception);
8108      if (emboss_image != (Image *) NULL)
8109        {
8110          *image=DestroyImage(*image);
8111          *image=emboss_image;
8112        }
8113      CatchException(exception);
8114      XSetCursorState(display,windows,MagickFalse);
8115      if (windows->image.orphan != MagickFalse)
8116        break;
8117      XConfigureImageColormap(display,resource_info,windows,*image);
8118      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8119      break;
8120    }
8121    case ReduceNoiseCommand:
8122    {
8123      Image
8124        *noise_image;
8125
8126      static char
8127        radius[MaxTextExtent] = "0";
8128
8129      /*
8130        Query user for noise radius.
8131      */
8132      (void) XDialogWidget(display,windows,"Reduce Noise",
8133        "Enter the noise radius:",radius);
8134      if (*radius == '\0')
8135        break;
8136      /*
8137        Reduce noise in the image.
8138      */
8139      XSetCursorState(display,windows,MagickTrue);
8140      XCheckRefreshWindows(display,windows);
8141      flags=ParseGeometry(radius,&geometry_info);
8142      noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8143        geometry_info.rho,(size_t) geometry_info.rho,exception);
8144      if (noise_image != (Image *) NULL)
8145        {
8146          *image=DestroyImage(*image);
8147          *image=noise_image;
8148        }
8149      CatchException(exception);
8150      XSetCursorState(display,windows,MagickFalse);
8151      if (windows->image.orphan != MagickFalse)
8152        break;
8153      XConfigureImageColormap(display,resource_info,windows,*image);
8154      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8155      break;
8156    }
8157    case AddNoiseCommand:
8158    {
8159      char
8160        **noises;
8161
8162      Image
8163        *noise_image;
8164
8165      static char
8166        noise_type[MaxTextExtent] = "Gaussian";
8167
8168      /*
8169        Add noise to the image.
8170      */
8171      noises=GetCommandOptions(MagickNoiseOptions);
8172      if (noises == (char **) NULL)
8173        break;
8174      XListBrowserWidget(display,windows,&windows->widget,
8175        (const char **) noises,"Add Noise",
8176        "Select a type of noise to add to your image:",noise_type);
8177      noises=DestroyStringList(noises);
8178      if (*noise_type == '\0')
8179        break;
8180      XSetCursorState(display,windows,MagickTrue);
8181      XCheckRefreshWindows(display,windows);
8182      noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8183        MagickNoiseOptions,MagickFalse,noise_type),exception);
8184      if (noise_image != (Image *) NULL)
8185        {
8186          *image=DestroyImage(*image);
8187          *image=noise_image;
8188        }
8189      CatchException(exception);
8190      XSetCursorState(display,windows,MagickFalse);
8191      if (windows->image.orphan != MagickFalse)
8192        break;
8193      XConfigureImageColormap(display,resource_info,windows,*image);
8194      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8195      break;
8196    }
8197    case SharpenCommand:
8198    {
8199      Image
8200        *sharp_image;
8201
8202      static char
8203        radius[MaxTextExtent] = "0.0x1.0";
8204
8205      /*
8206        Query user for sharpen radius.
8207      */
8208      (void) XDialogWidget(display,windows,"Sharpen",
8209        "Enter the sharpen radius and standard deviation:",radius);
8210      if (*radius == '\0')
8211        break;
8212      /*
8213        Sharpen image scanlines.
8214      */
8215      XSetCursorState(display,windows,MagickTrue);
8216      XCheckRefreshWindows(display,windows);
8217      flags=ParseGeometry(radius,&geometry_info);
8218      sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8219        exception);
8220      if (sharp_image != (Image *) NULL)
8221        {
8222          *image=DestroyImage(*image);
8223          *image=sharp_image;
8224        }
8225      CatchException(exception);
8226      XSetCursorState(display,windows,MagickFalse);
8227      if (windows->image.orphan != MagickFalse)
8228        break;
8229      XConfigureImageColormap(display,resource_info,windows,*image);
8230      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8231      break;
8232    }
8233    case BlurCommand:
8234    {
8235      Image
8236        *blur_image;
8237
8238      static char
8239        radius[MaxTextExtent] = "0.0x1.0";
8240
8241      /*
8242        Query user for blur radius.
8243      */
8244      (void) XDialogWidget(display,windows,"Blur",
8245        "Enter the blur radius and standard deviation:",radius);
8246      if (*radius == '\0')
8247        break;
8248      /*
8249        Blur an image.
8250      */
8251      XSetCursorState(display,windows,MagickTrue);
8252      XCheckRefreshWindows(display,windows);
8253      flags=ParseGeometry(radius,&geometry_info);
8254      blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8255        exception);
8256      if (blur_image != (Image *) NULL)
8257        {
8258          *image=DestroyImage(*image);
8259          *image=blur_image;
8260        }
8261      CatchException(exception);
8262      XSetCursorState(display,windows,MagickFalse);
8263      if (windows->image.orphan != MagickFalse)
8264        break;
8265      XConfigureImageColormap(display,resource_info,windows,*image);
8266      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8267      break;
8268    }
8269    case ThresholdCommand:
8270    {
8271      double
8272        threshold;
8273
8274      static char
8275        factor[MaxTextExtent] = "128";
8276
8277      /*
8278        Query user for threshold value.
8279      */
8280      (void) XDialogWidget(display,windows,"Threshold",
8281        "Enter threshold value:",factor);
8282      if (*factor == '\0')
8283        break;
8284      /*
8285        Gamma correct image.
8286      */
8287      XSetCursorState(display,windows,MagickTrue);
8288      XCheckRefreshWindows(display,windows);
8289      threshold=SiPrefixToDouble(factor,QuantumRange);
8290      (void) BilevelImage(*image,threshold);
8291      XSetCursorState(display,windows,MagickFalse);
8292      if (windows->image.orphan != MagickFalse)
8293        break;
8294      XConfigureImageColormap(display,resource_info,windows,*image);
8295      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8296      break;
8297    }
8298    case EdgeDetectCommand:
8299    {
8300      Image
8301        *edge_image;
8302
8303      static char
8304        radius[MaxTextExtent] = "0";
8305
8306      /*
8307        Query user for edge factor.
8308      */
8309      (void) XDialogWidget(display,windows,"Detect Edges",
8310        "Enter the edge detect radius:",radius);
8311      if (*radius == '\0')
8312        break;
8313      /*
8314        Detect edge in image.
8315      */
8316      XSetCursorState(display,windows,MagickTrue);
8317      XCheckRefreshWindows(display,windows);
8318      flags=ParseGeometry(radius,&geometry_info);
8319      edge_image=EdgeImage(*image,geometry_info.rho,exception);
8320      if (edge_image != (Image *) NULL)
8321        {
8322          *image=DestroyImage(*image);
8323          *image=edge_image;
8324        }
8325      CatchException(exception);
8326      XSetCursorState(display,windows,MagickFalse);
8327      if (windows->image.orphan != MagickFalse)
8328        break;
8329      XConfigureImageColormap(display,resource_info,windows,*image);
8330      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8331      break;
8332    }
8333    case SpreadCommand:
8334    {
8335      Image
8336        *spread_image;
8337
8338      static char
8339        amount[MaxTextExtent] = "2";
8340
8341      /*
8342        Query user for spread amount.
8343      */
8344      (void) XDialogWidget(display,windows,"Spread",
8345        "Enter the displacement amount:",amount);
8346      if (*amount == '\0')
8347        break;
8348      /*
8349        Displace image pixels by a random amount.
8350      */
8351      XSetCursorState(display,windows,MagickTrue);
8352      XCheckRefreshWindows(display,windows);
8353      flags=ParseGeometry(amount,&geometry_info);
8354      spread_image=EdgeImage(*image,geometry_info.rho,exception);
8355      if (spread_image != (Image *) NULL)
8356        {
8357          *image=DestroyImage(*image);
8358          *image=spread_image;
8359        }
8360      CatchException(exception);
8361      XSetCursorState(display,windows,MagickFalse);
8362      if (windows->image.orphan != MagickFalse)
8363        break;
8364      XConfigureImageColormap(display,resource_info,windows,*image);
8365      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8366      break;
8367    }
8368    case ShadeCommand:
8369    {
8370      Image
8371        *shade_image;
8372
8373      int
8374        status;
8375
8376      static char
8377        geometry[MaxTextExtent] = "30x30";
8378
8379      /*
8380        Query user for the shade geometry.
8381      */
8382      status=XDialogWidget(display,windows,"Shade",
8383        "Enter the azimuth and elevation of the light source:",geometry);
8384      if (*geometry == '\0')
8385        break;
8386      /*
8387        Shade image pixels.
8388      */
8389      XSetCursorState(display,windows,MagickTrue);
8390      XCheckRefreshWindows(display,windows);
8391      flags=ParseGeometry(geometry,&geometry_info);
8392      if ((flags & SigmaValue) == 0)
8393        geometry_info.sigma=1.0;
8394      shade_image=ShadeImage(*image,status != 0 ? MagickFalse : MagickTrue,
8395        geometry_info.rho,geometry_info.sigma,exception);
8396      if (shade_image != (Image *) NULL)
8397        {
8398          *image=DestroyImage(*image);
8399          *image=shade_image;
8400        }
8401      CatchException(exception);
8402      XSetCursorState(display,windows,MagickFalse);
8403      if (windows->image.orphan != MagickFalse)
8404        break;
8405      XConfigureImageColormap(display,resource_info,windows,*image);
8406      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8407      break;
8408    }
8409    case RaiseCommand:
8410    {
8411      static char
8412        bevel_width[MaxTextExtent] = "10";
8413
8414      /*
8415        Query user for bevel width.
8416      */
8417      (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8418      if (*bevel_width == '\0')
8419        break;
8420      /*
8421        Raise an image.
8422      */
8423      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8424        exception);
8425      XSetCursorState(display,windows,MagickTrue);
8426      XCheckRefreshWindows(display,windows);
8427      (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8428        exception);
8429      (void) RaiseImage(*image,&page_geometry,MagickTrue,exception);
8430      XSetCursorState(display,windows,MagickFalse);
8431      if (windows->image.orphan != MagickFalse)
8432        break;
8433      XConfigureImageColormap(display,resource_info,windows,*image);
8434      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8435      break;
8436    }
8437    case SegmentCommand:
8438    {
8439      static char
8440        threshold[MaxTextExtent] = "1.0x1.5";
8441
8442      /*
8443        Query user for smoothing threshold.
8444      */
8445      (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8446        threshold);
8447      if (*threshold == '\0')
8448        break;
8449      /*
8450        Segment an image.
8451      */
8452      XSetCursorState(display,windows,MagickTrue);
8453      XCheckRefreshWindows(display,windows);
8454      flags=ParseGeometry(threshold,&geometry_info);
8455      if ((flags & SigmaValue) == 0)
8456        geometry_info.sigma=1.0;
8457      (void) SegmentImage(*image,RGBColorspace,MagickFalse,geometry_info.rho,
8458        geometry_info.sigma);
8459      XSetCursorState(display,windows,MagickFalse);
8460      if (windows->image.orphan != MagickFalse)
8461        break;
8462      XConfigureImageColormap(display,resource_info,windows,*image);
8463      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8464      break;
8465    }
8466    case SepiaToneCommand:
8467    {
8468      double
8469        threshold;
8470
8471      Image
8472        *sepia_image;
8473
8474      static char
8475        factor[MaxTextExtent] = "80%";
8476
8477      /*
8478        Query user for sepia-tone factor.
8479      */
8480      (void) XDialogWidget(display,windows,"Sepia Tone",
8481        "Enter the sepia tone factor (0 - 99.9%):",factor);
8482      if (*factor == '\0')
8483        break;
8484      /*
8485        Sepia tone image pixels.
8486      */
8487      XSetCursorState(display,windows,MagickTrue);
8488      XCheckRefreshWindows(display,windows);
8489      threshold=SiPrefixToDouble(factor,QuantumRange);
8490      sepia_image=SepiaToneImage(*image,threshold,exception);
8491      if (sepia_image != (Image *) NULL)
8492        {
8493          *image=DestroyImage(*image);
8494          *image=sepia_image;
8495        }
8496      CatchException(exception);
8497      XSetCursorState(display,windows,MagickFalse);
8498      if (windows->image.orphan != MagickFalse)
8499        break;
8500      XConfigureImageColormap(display,resource_info,windows,*image);
8501      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8502      break;
8503    }
8504    case SolarizeCommand:
8505    {
8506      double
8507        threshold;
8508
8509      static char
8510        factor[MaxTextExtent] = "60%";
8511
8512      /*
8513        Query user for solarize factor.
8514      */
8515      (void) XDialogWidget(display,windows,"Solarize",
8516        "Enter the solarize factor (0 - 99.9%):",factor);
8517      if (*factor == '\0')
8518        break;
8519      /*
8520        Solarize image pixels.
8521      */
8522      XSetCursorState(display,windows,MagickTrue);
8523      XCheckRefreshWindows(display,windows);
8524      threshold=SiPrefixToDouble(factor,QuantumRange);
8525      (void) SolarizeImage(*image,threshold,exception);
8526      XSetCursorState(display,windows,MagickFalse);
8527      if (windows->image.orphan != MagickFalse)
8528        break;
8529      XConfigureImageColormap(display,resource_info,windows,*image);
8530      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8531      break;
8532    }
8533    case SwirlCommand:
8534    {
8535      Image
8536        *swirl_image;
8537
8538      static char
8539        degrees[MaxTextExtent] = "60";
8540
8541      /*
8542        Query user for swirl angle.
8543      */
8544      (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8545        degrees);
8546      if (*degrees == '\0')
8547        break;
8548      /*
8549        Swirl image pixels about the center.
8550      */
8551      XSetCursorState(display,windows,MagickTrue);
8552      XCheckRefreshWindows(display,windows);
8553      flags=ParseGeometry(degrees,&geometry_info);
8554      swirl_image=SwirlImage(*image,geometry_info.rho,exception);
8555      if (swirl_image != (Image *) NULL)
8556        {
8557          *image=DestroyImage(*image);
8558          *image=swirl_image;
8559        }
8560      CatchException(exception);
8561      XSetCursorState(display,windows,MagickFalse);
8562      if (windows->image.orphan != MagickFalse)
8563        break;
8564      XConfigureImageColormap(display,resource_info,windows,*image);
8565      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8566      break;
8567    }
8568    case ImplodeCommand:
8569    {
8570      Image
8571        *implode_image;
8572
8573      static char
8574        factor[MaxTextExtent] = "0.3";
8575
8576      /*
8577        Query user for implode factor.
8578      */
8579      (void) XDialogWidget(display,windows,"Implode",
8580        "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8581      if (*factor == '\0')
8582        break;
8583      /*
8584        Implode image pixels about the center.
8585      */
8586      XSetCursorState(display,windows,MagickTrue);
8587      XCheckRefreshWindows(display,windows);
8588      flags=ParseGeometry(factor,&geometry_info);
8589      implode_image=ImplodeImage(*image,geometry_info.rho,exception);
8590      if (implode_image != (Image *) NULL)
8591        {
8592          *image=DestroyImage(*image);
8593          *image=implode_image;
8594        }
8595      CatchException(exception);
8596      XSetCursorState(display,windows,MagickFalse);
8597      if (windows->image.orphan != MagickFalse)
8598        break;
8599      XConfigureImageColormap(display,resource_info,windows,*image);
8600      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8601      break;
8602    }
8603    case VignetteCommand:
8604    {
8605      Image
8606        *vignette_image;
8607
8608      static char
8609        geometry[MaxTextExtent] = "0x20";
8610
8611      /*
8612        Query user for the vignette geometry.
8613      */
8614      (void) XDialogWidget(display,windows,"Vignette",
8615        "Enter the radius, sigma, and x and y offsets:",geometry);
8616      if (*geometry == '\0')
8617        break;
8618      /*
8619        Soften the edges of the image in vignette style
8620      */
8621      XSetCursorState(display,windows,MagickTrue);
8622      XCheckRefreshWindows(display,windows);
8623      flags=ParseGeometry(geometry,&geometry_info);
8624      if ((flags & SigmaValue) == 0)
8625        geometry_info.sigma=1.0;
8626      if ((flags & XiValue) == 0)
8627        geometry_info.xi=0.1*(*image)->columns;
8628      if ((flags & PsiValue) == 0)
8629        geometry_info.psi=0.1*(*image)->rows;
8630      vignette_image=VignetteImage(*image,geometry_info.rho,geometry_info.sigma,
8631        (ssize_t) ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-
8632        0.5),exception);
8633      if (vignette_image != (Image *) NULL)
8634        {
8635          *image=DestroyImage(*image);
8636          *image=vignette_image;
8637        }
8638      CatchException(exception);
8639      XSetCursorState(display,windows,MagickFalse);
8640      if (windows->image.orphan != MagickFalse)
8641        break;
8642      XConfigureImageColormap(display,resource_info,windows,*image);
8643      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8644      break;
8645    }
8646    case WaveCommand:
8647    {
8648      Image
8649        *wave_image;
8650
8651      static char
8652        geometry[MaxTextExtent] = "25x150";
8653
8654      /*
8655        Query user for the wave geometry.
8656      */
8657      (void) XDialogWidget(display,windows,"Wave",
8658        "Enter the amplitude and length of the wave:",geometry);
8659      if (*geometry == '\0')
8660        break;
8661      /*
8662        Alter an image along a sine wave.
8663      */
8664      XSetCursorState(display,windows,MagickTrue);
8665      XCheckRefreshWindows(display,windows);
8666      flags=ParseGeometry(geometry,&geometry_info);
8667      if ((flags & SigmaValue) == 0)
8668        geometry_info.sigma=1.0;
8669      wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8670        exception);
8671      if (wave_image != (Image *) NULL)
8672        {
8673          *image=DestroyImage(*image);
8674          *image=wave_image;
8675        }
8676      CatchException(exception);
8677      XSetCursorState(display,windows,MagickFalse);
8678      if (windows->image.orphan != MagickFalse)
8679        break;
8680      XConfigureImageColormap(display,resource_info,windows,*image);
8681      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8682      break;
8683    }
8684    case OilPaintCommand:
8685    {
8686      Image
8687        *paint_image;
8688
8689      static char
8690        radius[MaxTextExtent] = "0";
8691
8692      /*
8693        Query user for circular neighborhood radius.
8694      */
8695      (void) XDialogWidget(display,windows,"Oil Paint",
8696        "Enter the mask radius:",radius);
8697      if (*radius == '\0')
8698        break;
8699      /*
8700        OilPaint image scanlines.
8701      */
8702      XSetCursorState(display,windows,MagickTrue);
8703      XCheckRefreshWindows(display,windows);
8704      flags=ParseGeometry(radius,&geometry_info);
8705      paint_image=OilPaintImage(*image,geometry_info.rho,geometry_info.sigma,
8706        exception);
8707      if (paint_image != (Image *) NULL)
8708        {
8709          *image=DestroyImage(*image);
8710          *image=paint_image;
8711        }
8712      CatchException(exception);
8713      XSetCursorState(display,windows,MagickFalse);
8714      if (windows->image.orphan != MagickFalse)
8715        break;
8716      XConfigureImageColormap(display,resource_info,windows,*image);
8717      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8718      break;
8719    }
8720    case CharcoalDrawCommand:
8721    {
8722      Image
8723        *charcoal_image;
8724
8725      static char
8726        radius[MaxTextExtent] = "0x1";
8727
8728      /*
8729        Query user for charcoal radius.
8730      */
8731      (void) XDialogWidget(display,windows,"Charcoal Draw",
8732        "Enter the charcoal radius and sigma:",radius);
8733      if (*radius == '\0')
8734        break;
8735      /*
8736        Charcoal the image.
8737      */
8738      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8739        exception);
8740      XSetCursorState(display,windows,MagickTrue);
8741      XCheckRefreshWindows(display,windows);
8742      flags=ParseGeometry(radius,&geometry_info);
8743      if ((flags & SigmaValue) == 0)
8744        geometry_info.sigma=geometry_info.rho;
8745      charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8746        exception);
8747      if (charcoal_image != (Image *) NULL)
8748        {
8749          *image=DestroyImage(*image);
8750          *image=charcoal_image;
8751        }
8752      CatchException(exception);
8753      XSetCursorState(display,windows,MagickFalse);
8754      if (windows->image.orphan != MagickFalse)
8755        break;
8756      XConfigureImageColormap(display,resource_info,windows,*image);
8757      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8758      break;
8759    }
8760    case AnnotateCommand:
8761    {
8762      /*
8763        Annotate the image with text.
8764      */
8765      status=XAnnotateEditImage(display,resource_info,windows,*image,exception);
8766      if (status == MagickFalse)
8767        {
8768          XNoticeWidget(display,windows,"Unable to annotate X image",
8769            (*image)->filename);
8770          break;
8771        }
8772      break;
8773    }
8774    case DrawCommand:
8775    {
8776      /*
8777        Draw image.
8778      */
8779      status=XDrawEditImage(display,resource_info,windows,image,exception);
8780      if (status == MagickFalse)
8781        {
8782          XNoticeWidget(display,windows,"Unable to draw on the X image",
8783            (*image)->filename);
8784          break;
8785        }
8786      break;
8787    }
8788    case ColorCommand:
8789    {
8790      /*
8791        Color edit.
8792      */
8793      status=XColorEditImage(display,resource_info,windows,image,exception);
8794      if (status == MagickFalse)
8795        {
8796          XNoticeWidget(display,windows,"Unable to pixel edit X image",
8797            (*image)->filename);
8798          break;
8799        }
8800      break;
8801    }
8802    case MatteCommand:
8803    {
8804      /*
8805        Matte edit.
8806      */
8807      status=XMatteEditImage(display,resource_info,windows,image,exception);
8808      if (status == MagickFalse)
8809        {
8810          XNoticeWidget(display,windows,"Unable to matte edit X image",
8811            (*image)->filename);
8812          break;
8813        }
8814      break;
8815    }
8816    case CompositeCommand:
8817    {
8818      /*
8819        Composite image.
8820      */
8821      status=XCompositeImage(display,resource_info,windows,*image,
8822        exception);
8823      if (status == MagickFalse)
8824        {
8825          XNoticeWidget(display,windows,"Unable to composite X image",
8826            (*image)->filename);
8827          break;
8828        }
8829      break;
8830    }
8831    case AddBorderCommand:
8832    {
8833      Image
8834        *border_image;
8835
8836      static char
8837        geometry[MaxTextExtent] = "6x6";
8838
8839      /*
8840        Query user for border color and geometry.
8841      */
8842      XColorBrowserWidget(display,windows,"Select",color);
8843      if (*color == '\0')
8844        break;
8845      (void) XDialogWidget(display,windows,"Add Border",
8846        "Enter border geometry:",geometry);
8847      if (*geometry == '\0')
8848        break;
8849      /*
8850        Add a border to the image.
8851      */
8852      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8853        exception);
8854      XSetCursorState(display,windows,MagickTrue);
8855      XCheckRefreshWindows(display,windows);
8856      (void) QueryColorDatabase(color,&(*image)->border_color,
8857        exception);
8858      (void) ParsePageGeometry(*image,geometry,&page_geometry,
8859        exception);
8860      border_image=BorderImage(*image,&page_geometry,exception);
8861      if (border_image != (Image *) NULL)
8862        {
8863          *image=DestroyImage(*image);
8864          *image=border_image;
8865        }
8866      CatchException(exception);
8867      XSetCursorState(display,windows,MagickFalse);
8868      if (windows->image.orphan != MagickFalse)
8869        break;
8870      windows->image.window_changes.width=(int) (*image)->columns;
8871      windows->image.window_changes.height=(int) (*image)->rows;
8872      XConfigureImageColormap(display,resource_info,windows,*image);
8873      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8874      break;
8875    }
8876    case AddFrameCommand:
8877    {
8878      FrameInfo
8879        frame_info;
8880
8881      Image
8882        *frame_image;
8883
8884      static char
8885        geometry[MaxTextExtent] = "6x6";
8886
8887      /*
8888        Query user for frame color and geometry.
8889      */
8890      XColorBrowserWidget(display,windows,"Select",color);
8891      if (*color == '\0')
8892        break;
8893      (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8894        geometry);
8895      if (*geometry == '\0')
8896        break;
8897      /*
8898        Surround image with an ornamental border.
8899      */
8900      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8901        exception);
8902      XSetCursorState(display,windows,MagickTrue);
8903      XCheckRefreshWindows(display,windows);
8904      (void) QueryColorDatabase(color,&(*image)->matte_color,
8905        exception);
8906      (void) ParsePageGeometry(*image,geometry,&page_geometry,
8907        exception);
8908      frame_info.width=page_geometry.width;
8909      frame_info.height=page_geometry.height;
8910      frame_info.outer_bevel=page_geometry.x;
8911      frame_info.inner_bevel=page_geometry.y;
8912      frame_info.x=(ssize_t) frame_info.width;
8913      frame_info.y=(ssize_t) frame_info.height;
8914      frame_info.width=(*image)->columns+2*frame_info.width;
8915      frame_info.height=(*image)->rows+2*frame_info.height;
8916      frame_image=FrameImage(*image,&frame_info,exception);
8917      if (frame_image != (Image *) NULL)
8918        {
8919          *image=DestroyImage(*image);
8920          *image=frame_image;
8921        }
8922      CatchException(exception);
8923      XSetCursorState(display,windows,MagickFalse);
8924      if (windows->image.orphan != MagickFalse)
8925        break;
8926      windows->image.window_changes.width=(int) (*image)->columns;
8927      windows->image.window_changes.height=(int) (*image)->rows;
8928      XConfigureImageColormap(display,resource_info,windows,*image);
8929      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8930      break;
8931    }
8932    case CommentCommand:
8933    {
8934      const char
8935        *value;
8936
8937      FILE
8938        *file;
8939
8940      int
8941        unique_file;
8942
8943      /*
8944        Edit image comment.
8945      */
8946      unique_file=AcquireUniqueFileResource(image_info->filename);
8947      if (unique_file == -1)
8948        XNoticeWidget(display,windows,"Unable to edit image comment",
8949          image_info->filename);
8950      value=GetImageProperty(*image,"comment");
8951      if (value == (char *) NULL)
8952        unique_file=close(unique_file)-1;
8953      else
8954        {
8955          register const char
8956            *p;
8957
8958          file=fdopen(unique_file,"w");
8959          if (file == (FILE *) NULL)
8960            {
8961              XNoticeWidget(display,windows,"Unable to edit image comment",
8962                image_info->filename);
8963              break;
8964            }
8965          for (p=value; *p != '\0'; p++)
8966            (void) fputc((int) *p,file);
8967          (void) fputc('\n',file);
8968          (void) fclose(file);
8969        }
8970      XSetCursorState(display,windows,MagickTrue);
8971      XCheckRefreshWindows(display,windows);
8972      status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
8973        exception);
8974      if (status == MagickFalse)
8975        XNoticeWidget(display,windows,"Unable to edit image comment",
8976          (char *) NULL);
8977      else
8978        {
8979          char
8980            *comment;
8981
8982          comment=FileToString(image_info->filename,~0UL,exception);
8983          if (comment != (char *) NULL)
8984            {
8985              (void) SetImageProperty(*image,"comment",comment);
8986              (*image)->taint=MagickTrue;
8987            }
8988        }
8989      (void) RelinquishUniqueFileResource(image_info->filename);
8990      XSetCursorState(display,windows,MagickFalse);
8991      break;
8992    }
8993    case LaunchCommand:
8994    {
8995      /*
8996        Launch program.
8997      */
8998      XSetCursorState(display,windows,MagickTrue);
8999      XCheckRefreshWindows(display,windows);
9000      (void) AcquireUniqueFilename(filename);
9001      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"launch:%s",
9002        filename);
9003      status=WriteImage(image_info,*image,exception);
9004      if (status == MagickFalse)
9005        XNoticeWidget(display,windows,"Unable to launch image editor",
9006          (char *) NULL);
9007      else
9008        {
9009          nexus=ReadImage(resource_info->image_info,exception);
9010          CatchException(exception);
9011          XClientMessage(display,windows->image.id,windows->im_protocols,
9012            windows->im_next_image,CurrentTime);
9013        }
9014      (void) RelinquishUniqueFileResource(filename);
9015      XSetCursorState(display,windows,MagickFalse);
9016      break;
9017    }
9018    case RegionofInterestCommand:
9019    {
9020      /*
9021        Apply an image processing technique to a region of interest.
9022      */
9023      (void) XROIImage(display,resource_info,windows,image,exception);
9024      break;
9025    }
9026    case InfoCommand:
9027      break;
9028    case ZoomCommand:
9029    {
9030      /*
9031        Zoom image.
9032      */
9033      if (windows->magnify.mapped != MagickFalse)
9034        (void) XRaiseWindow(display,windows->magnify.id);
9035      else
9036        {
9037          /*
9038            Make magnify image.
9039          */
9040          XSetCursorState(display,windows,MagickTrue);
9041          (void) XMapRaised(display,windows->magnify.id);
9042          XSetCursorState(display,windows,MagickFalse);
9043        }
9044      break;
9045    }
9046    case ShowPreviewCommand:
9047    {
9048      char
9049        **previews;
9050
9051      Image
9052        *preview_image;
9053
9054      static char
9055        preview_type[MaxTextExtent] = "Gamma";
9056
9057      /*
9058        Select preview type from menu.
9059      */
9060      previews=GetCommandOptions(MagickPreviewOptions);
9061      if (previews == (char **) NULL)
9062        break;
9063      XListBrowserWidget(display,windows,&windows->widget,
9064        (const char **) previews,"Preview",
9065        "Select an enhancement, effect, or F/X:",preview_type);
9066      previews=DestroyStringList(previews);
9067      if (*preview_type == '\0')
9068        break;
9069      /*
9070        Show image preview.
9071      */
9072      XSetCursorState(display,windows,MagickTrue);
9073      XCheckRefreshWindows(display,windows);
9074      image_info->preview_type=(PreviewType)
9075        ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
9076      image_info->group=(ssize_t) windows->image.id;
9077      (void) DeleteImageProperty(*image,"label");
9078      (void) SetImageProperty(*image,"label","Preview");
9079      (void) AcquireUniqueFilename(filename);
9080      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"preview:%s",
9081        filename);
9082      status=WriteImage(image_info,*image,exception);
9083      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9084      preview_image=ReadImage(image_info,exception);
9085      (void) RelinquishUniqueFileResource(filename);
9086      if (preview_image == (Image *) NULL)
9087        break;
9088      (void) FormatLocaleString(preview_image->filename,MaxTextExtent,"show:%s",
9089        filename);
9090      status=WriteImage(image_info,preview_image,exception);
9091      preview_image=DestroyImage(preview_image);
9092      if (status == MagickFalse)
9093        XNoticeWidget(display,windows,"Unable to show image preview",
9094          (*image)->filename);
9095      XDelay(display,1500);
9096      XSetCursorState(display,windows,MagickFalse);
9097      break;
9098    }
9099    case ShowHistogramCommand:
9100    {
9101      Image
9102        *histogram_image;
9103
9104      /*
9105        Show image histogram.
9106      */
9107      XSetCursorState(display,windows,MagickTrue);
9108      XCheckRefreshWindows(display,windows);
9109      image_info->group=(ssize_t) windows->image.id;
9110      (void) DeleteImageProperty(*image,"label");
9111      (void) SetImageProperty(*image,"label","Histogram");
9112      (void) AcquireUniqueFilename(filename);
9113      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"histogram:%s",
9114        filename);
9115      status=WriteImage(image_info,*image,exception);
9116      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9117      histogram_image=ReadImage(image_info,exception);
9118      (void) RelinquishUniqueFileResource(filename);
9119      if (histogram_image == (Image *) NULL)
9120        break;
9121      (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
9122        "show:%s",filename);
9123      status=WriteImage(image_info,histogram_image,exception);
9124      histogram_image=DestroyImage(histogram_image);
9125      if (status == MagickFalse)
9126        XNoticeWidget(display,windows,"Unable to show histogram",
9127          (*image)->filename);
9128      XDelay(display,1500);
9129      XSetCursorState(display,windows,MagickFalse);
9130      break;
9131    }
9132    case ShowMatteCommand:
9133    {
9134      Image
9135        *matte_image;
9136
9137      if ((*image)->matte == MagickFalse)
9138        {
9139          XNoticeWidget(display,windows,
9140            "Image does not have any matte information",(*image)->filename);
9141          break;
9142        }
9143      /*
9144        Show image matte.
9145      */
9146      XSetCursorState(display,windows,MagickTrue);
9147      XCheckRefreshWindows(display,windows);
9148      image_info->group=(ssize_t) windows->image.id;
9149      (void) DeleteImageProperty(*image,"label");
9150      (void) SetImageProperty(*image,"label","Matte");
9151      (void) AcquireUniqueFilename(filename);
9152      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"matte:%s",
9153        filename);
9154      status=WriteImage(image_info,*image,exception);
9155      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9156      matte_image=ReadImage(image_info,exception);
9157      (void) RelinquishUniqueFileResource(filename);
9158      if (matte_image == (Image *) NULL)
9159        break;
9160      (void) FormatLocaleString(matte_image->filename,MaxTextExtent,"show:%s",
9161        filename);
9162      status=WriteImage(image_info,matte_image,exception);
9163      matte_image=DestroyImage(matte_image);
9164      if (status == MagickFalse)
9165        XNoticeWidget(display,windows,"Unable to show matte",
9166          (*image)->filename);
9167      XDelay(display,1500);
9168      XSetCursorState(display,windows,MagickFalse);
9169      break;
9170    }
9171    case BackgroundCommand:
9172    {
9173      /*
9174        Background image.
9175      */
9176      status=XBackgroundImage(display,resource_info,windows,image,exception);
9177      if (status == MagickFalse)
9178        break;
9179      nexus=CloneImage(*image,0,0,MagickTrue,exception);
9180      if (nexus != (Image *) NULL)
9181        XClientMessage(display,windows->image.id,windows->im_protocols,
9182          windows->im_next_image,CurrentTime);
9183      break;
9184    }
9185    case SlideShowCommand:
9186    {
9187      static char
9188        delay[MaxTextExtent] = "5";
9189
9190      /*
9191        Display next image after pausing.
9192      */
9193      (void) XDialogWidget(display,windows,"Slide Show",
9194        "Pause how many 1/100ths of a second between images:",delay);
9195      if (*delay == '\0')
9196        break;
9197      resource_info->delay=StringToUnsignedLong(delay);
9198      XClientMessage(display,windows->image.id,windows->im_protocols,
9199        windows->im_next_image,CurrentTime);
9200      break;
9201    }
9202    case PreferencesCommand:
9203    {
9204      /*
9205        Set user preferences.
9206      */
9207      status=XPreferencesWidget(display,resource_info,windows);
9208      if (status == MagickFalse)
9209        break;
9210      nexus=CloneImage(*image,0,0,MagickTrue,exception);
9211      if (nexus != (Image *) NULL)
9212        XClientMessage(display,windows->image.id,windows->im_protocols,
9213          windows->im_next_image,CurrentTime);
9214      break;
9215    }
9216    case HelpCommand:
9217    {
9218      /*
9219        User requested help.
9220      */
9221      XTextViewWidget(display,resource_info,windows,MagickFalse,
9222        "Help Viewer - Display",DisplayHelp);
9223      break;
9224    }
9225    case BrowseDocumentationCommand:
9226    {
9227      Atom
9228        mozilla_atom;
9229
9230      Window
9231        mozilla_window,
9232        root_window;
9233
9234      /*
9235        Browse the ImageMagick documentation.
9236      */
9237      root_window=XRootWindow(display,XDefaultScreen(display));
9238      mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9239      mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9240      if (mozilla_window != (Window) NULL)
9241        {
9242          char
9243            command[MaxTextExtent],
9244            *url;
9245
9246          /*
9247            Display documentation using Netscape remote control.
9248          */
9249          url=GetMagickHomeURL();
9250          (void) FormatLocaleString(command,MaxTextExtent,
9251            "openurl(%s,new-tab)",url);
9252          url=DestroyString(url);
9253          mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9254          (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9255            8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9256          XSetCursorState(display,windows,MagickFalse);
9257          break;
9258        }
9259      XSetCursorState(display,windows,MagickTrue);
9260      XCheckRefreshWindows(display,windows);
9261      status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9262        exception);
9263      if (status == MagickFalse)
9264        XNoticeWidget(display,windows,"Unable to browse documentation",
9265          (char *) NULL);
9266      XDelay(display,1500);
9267      XSetCursorState(display,windows,MagickFalse);
9268      break;
9269    }
9270    case VersionCommand:
9271    {
9272      XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9273        GetMagickCopyright());
9274      break;
9275    }
9276    case SaveToUndoBufferCommand:
9277      break;
9278    default:
9279    {
9280      (void) XBell(display,0);
9281      break;
9282    }
9283  }
9284  image_info=DestroyImageInfo(image_info);
9285  return(nexus);
9286}
9287
9288/*
9289%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9290%                                                                             %
9291%                                                                             %
9292%                                                                             %
9293+   X M a g n i f y I m a g e                                                 %
9294%                                                                             %
9295%                                                                             %
9296%                                                                             %
9297%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9298%
9299%  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9300%  The magnified portion is displayed in a separate window.
9301%
9302%  The format of the XMagnifyImage method is:
9303%
9304%      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9305%
9306%  A description of each parameter follows:
9307%
9308%    o display: Specifies a connection to an X server;  returned from
9309%      XOpenDisplay.
9310%
9311%    o windows: Specifies a pointer to a XWindows structure.
9312%
9313%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
9314%      the entire image is refreshed.
9315%
9316*/
9317static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9318{
9319  char
9320    text[MaxTextExtent];
9321
9322  register int
9323    x,
9324    y;
9325
9326  size_t
9327    state;
9328
9329  /*
9330    Update magnified image until the mouse button is released.
9331  */
9332  (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9333  state=DefaultState;
9334  x=event->xbutton.x;
9335  y=event->xbutton.y;
9336  windows->magnify.x=(int) windows->image.x+x;
9337  windows->magnify.y=(int) windows->image.y+y;
9338  do
9339  {
9340    /*
9341      Map and unmap Info widget as text cursor crosses its boundaries.
9342    */
9343    if (windows->info.mapped != MagickFalse)
9344      {
9345        if ((x < (int) (windows->info.x+windows->info.width)) &&
9346            (y < (int) (windows->info.y+windows->info.height)))
9347          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9348      }
9349    else
9350      if ((x > (int) (windows->info.x+windows->info.width)) ||
9351          (y > (int) (windows->info.y+windows->info.height)))
9352        (void) XMapWindow(display,windows->info.id);
9353    if (windows->info.mapped != MagickFalse)
9354      {
9355        /*
9356          Display pointer position.
9357        */
9358        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9359          windows->magnify.x,windows->magnify.y);
9360        XInfoWidget(display,windows,text);
9361      }
9362    /*
9363      Wait for next event.
9364    */
9365    XScreenEvent(display,windows,event);
9366    switch (event->type)
9367    {
9368      case ButtonPress:
9369        break;
9370      case ButtonRelease:
9371      {
9372        /*
9373          User has finished magnifying image.
9374        */
9375        x=event->xbutton.x;
9376        y=event->xbutton.y;
9377        state|=ExitState;
9378        break;
9379      }
9380      case Expose:
9381        break;
9382      case MotionNotify:
9383      {
9384        x=event->xmotion.x;
9385        y=event->xmotion.y;
9386        break;
9387      }
9388      default:
9389        break;
9390    }
9391    /*
9392      Check boundary conditions.
9393    */
9394    if (x < 0)
9395      x=0;
9396    else
9397      if (x >= (int) windows->image.width)
9398        x=(int) windows->image.width-1;
9399    if (y < 0)
9400      y=0;
9401    else
9402     if (y >= (int) windows->image.height)
9403       y=(int) windows->image.height-1;
9404  } while ((state & ExitState) == 0);
9405  /*
9406    Display magnified image.
9407  */
9408  XSetCursorState(display,windows,MagickFalse);
9409}
9410
9411/*
9412%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9413%                                                                             %
9414%                                                                             %
9415%                                                                             %
9416+   X M a g n i f y W i n d o w C o m m a n d                                 %
9417%                                                                             %
9418%                                                                             %
9419%                                                                             %
9420%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9421%
9422%  XMagnifyWindowCommand() moves the image within an Magnify window by one
9423%  pixel as specified by the key symbol.
9424%
9425%  The format of the XMagnifyWindowCommand method is:
9426%
9427%      void XMagnifyWindowCommand(Display *display,XWindows *windows,
9428%        const MagickStatusType state,const KeySym key_symbol)
9429%
9430%  A description of each parameter follows:
9431%
9432%    o display: Specifies a connection to an X server; returned from
9433%      XOpenDisplay.
9434%
9435%    o windows: Specifies a pointer to a XWindows structure.
9436%
9437%    o state: key mask.
9438%
9439%    o key_symbol: Specifies a KeySym which indicates which side of the image
9440%      to trim.
9441%
9442*/
9443static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9444  const MagickStatusType state,const KeySym key_symbol)
9445{
9446  unsigned int
9447    quantum;
9448
9449  /*
9450    User specified a magnify factor or position.
9451  */
9452  quantum=1;
9453  if ((state & Mod1Mask) != 0)
9454    quantum=10;
9455  switch ((int) key_symbol)
9456  {
9457    case QuitCommand:
9458    {
9459      (void) XWithdrawWindow(display,windows->magnify.id,
9460        windows->magnify.screen);
9461      break;
9462    }
9463    case XK_Home:
9464    case XK_KP_Home:
9465    {
9466      windows->magnify.x=(int) windows->image.width/2;
9467      windows->magnify.y=(int) windows->image.height/2;
9468      break;
9469    }
9470    case XK_Left:
9471    case XK_KP_Left:
9472    {
9473      if (windows->magnify.x > 0)
9474        windows->magnify.x-=quantum;
9475      break;
9476    }
9477    case XK_Up:
9478    case XK_KP_Up:
9479    {
9480      if (windows->magnify.y > 0)
9481        windows->magnify.y-=quantum;
9482      break;
9483    }
9484    case XK_Right:
9485    case XK_KP_Right:
9486    {
9487      if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9488        windows->magnify.x+=quantum;
9489      break;
9490    }
9491    case XK_Down:
9492    case XK_KP_Down:
9493    {
9494      if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9495        windows->magnify.y+=quantum;
9496      break;
9497    }
9498    case XK_0:
9499    case XK_1:
9500    case XK_2:
9501    case XK_3:
9502    case XK_4:
9503    case XK_5:
9504    case XK_6:
9505    case XK_7:
9506    case XK_8:
9507    case XK_9:
9508    {
9509      windows->magnify.data=(key_symbol-XK_0);
9510      break;
9511    }
9512    case XK_KP_0:
9513    case XK_KP_1:
9514    case XK_KP_2:
9515    case XK_KP_3:
9516    case XK_KP_4:
9517    case XK_KP_5:
9518    case XK_KP_6:
9519    case XK_KP_7:
9520    case XK_KP_8:
9521    case XK_KP_9:
9522    {
9523      windows->magnify.data=(key_symbol-XK_KP_0);
9524      break;
9525    }
9526    default:
9527      break;
9528  }
9529  XMakeMagnifyImage(display,windows);
9530}
9531
9532/*
9533%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9534%                                                                             %
9535%                                                                             %
9536%                                                                             %
9537+   X M a k e P a n I m a g e                                                 %
9538%                                                                             %
9539%                                                                             %
9540%                                                                             %
9541%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9542%
9543%  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9544%  icon window.
9545%
9546%  The format of the XMakePanImage method is:
9547%
9548%        void XMakePanImage(Display *display,XResourceInfo *resource_info,
9549%          XWindows *windows,Image *image,ExceptionInfo *exception)
9550%
9551%  A description of each parameter follows:
9552%
9553%    o display: Specifies a connection to an X server;  returned from
9554%      XOpenDisplay.
9555%
9556%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9557%
9558%    o windows: Specifies a pointer to a XWindows structure.
9559%
9560%    o image: the image.
9561%
9562%    o exception: return any errors or warnings in this structure.
9563%
9564*/
9565static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9566  XWindows *windows,Image *image,ExceptionInfo *exception)
9567{
9568  MagickStatusType
9569    status;
9570
9571  /*
9572    Create and display image for panning icon.
9573  */
9574  XSetCursorState(display,windows,MagickTrue);
9575  XCheckRefreshWindows(display,windows);
9576  windows->pan.x=(int) windows->image.x;
9577  windows->pan.y=(int) windows->image.y;
9578  status=XMakeImage(display,resource_info,&windows->pan,image,
9579    windows->pan.width,windows->pan.height,exception);
9580  if (status == MagickFalse)
9581    ThrowXWindowFatalException(ResourceLimitError,
9582     "MemoryAllocationFailed",image->filename);
9583  (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9584    windows->pan.pixmap);
9585  (void) XClearWindow(display,windows->pan.id);
9586  XDrawPanRectangle(display,windows);
9587  XSetCursorState(display,windows,MagickFalse);
9588}
9589
9590/*
9591%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9592%                                                                             %
9593%                                                                             %
9594%                                                                             %
9595+   X M a t t a E d i t I m a g e                                             %
9596%                                                                             %
9597%                                                                             %
9598%                                                                             %
9599%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9600%
9601%  XMatteEditImage() allows the user to interactively change the Matte channel
9602%  of an image.  If the image is PseudoClass it is promoted to DirectClass
9603%  before the matte information is stored.
9604%
9605%  The format of the XMatteEditImage method is:
9606%
9607%      MagickBooleanType XMatteEditImage(Display *display,
9608%        XResourceInfo *resource_info,XWindows *windows,Image **image,
9609%        ExceptionInfo *exception)
9610%
9611%  A description of each parameter follows:
9612%
9613%    o display: Specifies a connection to an X server;  returned from
9614%      XOpenDisplay.
9615%
9616%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9617%
9618%    o windows: Specifies a pointer to a XWindows structure.
9619%
9620%    o image: the image; returned from ReadImage.
9621%
9622%    o exception: return any errors or warnings in this structure.
9623%
9624*/
9625static MagickBooleanType XMatteEditImage(Display *display,
9626  XResourceInfo *resource_info,XWindows *windows,Image **image,
9627  ExceptionInfo *exception)
9628{
9629  static char
9630    matte[MaxTextExtent] = "0";
9631
9632  static const char
9633    *MatteEditMenu[] =
9634    {
9635      "Method",
9636      "Border Color",
9637      "Fuzz",
9638      "Matte Value",
9639      "Undo",
9640      "Help",
9641      "Dismiss",
9642      (char *) NULL
9643    };
9644
9645  static const ModeType
9646    MatteEditCommands[] =
9647    {
9648      MatteEditMethod,
9649      MatteEditBorderCommand,
9650      MatteEditFuzzCommand,
9651      MatteEditValueCommand,
9652      MatteEditUndoCommand,
9653      MatteEditHelpCommand,
9654      MatteEditDismissCommand
9655    };
9656
9657  static PaintMethod
9658    method = PointMethod;
9659
9660  static XColor
9661    border_color = { 0, 0, 0, 0, 0, 0 };
9662
9663  char
9664    command[MaxTextExtent],
9665    text[MaxTextExtent];
9666
9667  Cursor
9668    cursor;
9669
9670  int
9671    entry,
9672    id,
9673    x,
9674    x_offset,
9675    y,
9676    y_offset;
9677
9678  register int
9679    i;
9680
9681  register Quantum
9682    *q;
9683
9684  unsigned int
9685    height,
9686    width;
9687
9688  size_t
9689    state;
9690
9691  XEvent
9692    event;
9693
9694  /*
9695    Map Command widget.
9696  */
9697  (void) CloneString(&windows->command.name,"Matte Edit");
9698  windows->command.data=4;
9699  (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9700  (void) XMapRaised(display,windows->command.id);
9701  XClientMessage(display,windows->image.id,windows->im_protocols,
9702    windows->im_update_widget,CurrentTime);
9703  /*
9704    Make cursor.
9705  */
9706  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9707    resource_info->background_color,resource_info->foreground_color);
9708  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9709  /*
9710    Track pointer until button 1 is pressed.
9711  */
9712  XQueryPosition(display,windows->image.id,&x,&y);
9713  (void) XSelectInput(display,windows->image.id,
9714    windows->image.attributes.event_mask | PointerMotionMask);
9715  state=DefaultState;
9716  do
9717  {
9718    if (windows->info.mapped != MagickFalse)
9719      {
9720        /*
9721          Display pointer position.
9722        */
9723        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9724          x+windows->image.x,y+windows->image.y);
9725        XInfoWidget(display,windows,text);
9726      }
9727    /*
9728      Wait for next event.
9729    */
9730    XScreenEvent(display,windows,&event);
9731    if (event.xany.window == windows->command.id)
9732      {
9733        /*
9734          Select a command from the Command widget.
9735        */
9736        id=XCommandWidget(display,windows,MatteEditMenu,&event);
9737        if (id < 0)
9738          {
9739            (void) XCheckDefineCursor(display,windows->image.id,cursor);
9740            continue;
9741          }
9742        switch (MatteEditCommands[id])
9743        {
9744          case MatteEditMethod:
9745          {
9746            char
9747              **methods;
9748
9749            /*
9750              Select a method from the pop-up menu.
9751            */
9752            methods=GetCommandOptions(MagickMethodOptions);
9753            if (methods == (char **) NULL)
9754              break;
9755            entry=XMenuWidget(display,windows,MatteEditMenu[id],
9756              (const char **) methods,command);
9757            if (entry >= 0)
9758              method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9759                MagickFalse,methods[entry]);
9760            methods=DestroyStringList(methods);
9761            break;
9762          }
9763          case MatteEditBorderCommand:
9764          {
9765            const char
9766              *ColorMenu[MaxNumberPens];
9767
9768            int
9769              pen_number;
9770
9771            /*
9772              Initialize menu selections.
9773            */
9774            for (i=0; i < (int) (MaxNumberPens-2); i++)
9775              ColorMenu[i]=resource_info->pen_colors[i];
9776            ColorMenu[MaxNumberPens-2]="Browser...";
9777            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9778            /*
9779              Select a pen color from the pop-up menu.
9780            */
9781            pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9782              (const char **) ColorMenu,command);
9783            if (pen_number < 0)
9784              break;
9785            if (pen_number == (MaxNumberPens-2))
9786              {
9787                static char
9788                  color_name[MaxTextExtent] = "gray";
9789
9790                /*
9791                  Select a pen color from a dialog.
9792                */
9793                resource_info->pen_colors[pen_number]=color_name;
9794                XColorBrowserWidget(display,windows,"Select",color_name);
9795                if (*color_name == '\0')
9796                  break;
9797              }
9798            /*
9799              Set border color.
9800            */
9801            (void) XParseColor(display,windows->map_info->colormap,
9802              resource_info->pen_colors[pen_number],&border_color);
9803            break;
9804          }
9805          case MatteEditFuzzCommand:
9806          {
9807            static char
9808              fuzz[MaxTextExtent];
9809
9810            static const char
9811              *FuzzMenu[] =
9812              {
9813                "0%",
9814                "2%",
9815                "5%",
9816                "10%",
9817                "15%",
9818                "Dialog...",
9819                (char *) NULL,
9820              };
9821
9822            /*
9823              Select a command from the pop-up menu.
9824            */
9825            entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9826              command);
9827            if (entry < 0)
9828              break;
9829            if (entry != 5)
9830              {
9831                (*image)->fuzz=SiPrefixToDouble(FuzzMenu[entry],1.0*
9832                  QuantumRange+1.0);
9833                break;
9834              }
9835            (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9836            (void) XDialogWidget(display,windows,"Ok",
9837              "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9838            if (*fuzz == '\0')
9839              break;
9840            (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
9841            (*image)->fuzz=SiPrefixToDouble(fuzz,1.0*QuantumRange+1.0);
9842            break;
9843          }
9844          case MatteEditValueCommand:
9845          {
9846            static char
9847              message[MaxTextExtent];
9848
9849            static const char
9850              *MatteMenu[] =
9851              {
9852                "Opaque",
9853                "Transparent",
9854                "Dialog...",
9855                (char *) NULL,
9856              };
9857
9858            /*
9859              Select a command from the pop-up menu.
9860            */
9861            entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9862              command);
9863            if (entry < 0)
9864              break;
9865            if (entry != 2)
9866              {
9867                (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9868                  OpaqueAlpha);
9869                if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9870                  (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9871                    (Quantum) TransparentAlpha);
9872                break;
9873              }
9874            (void) FormatLocaleString(message,MaxTextExtent,
9875              "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9876              QuantumRange);
9877            (void) XDialogWidget(display,windows,"Matte",message,matte);
9878            if (*matte == '\0')
9879              break;
9880            break;
9881          }
9882          case MatteEditUndoCommand:
9883          {
9884            (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9885              image,exception);
9886            break;
9887          }
9888          case MatteEditHelpCommand:
9889          {
9890            XTextViewWidget(display,resource_info,windows,MagickFalse,
9891              "Help Viewer - Matte Edit",ImageMatteEditHelp);
9892            break;
9893          }
9894          case MatteEditDismissCommand:
9895          {
9896            /*
9897              Prematurely exit.
9898            */
9899            state|=EscapeState;
9900            state|=ExitState;
9901            break;
9902          }
9903          default:
9904            break;
9905        }
9906        (void) XCheckDefineCursor(display,windows->image.id,cursor);
9907        continue;
9908      }
9909    switch (event.type)
9910    {
9911      case ButtonPress:
9912      {
9913        if (event.xbutton.button != Button1)
9914          break;
9915        if ((event.xbutton.window != windows->image.id) &&
9916            (event.xbutton.window != windows->magnify.id))
9917          break;
9918        /*
9919          Update matte data.
9920        */
9921        x=event.xbutton.x;
9922        y=event.xbutton.y;
9923        (void) XMagickCommand(display,resource_info,windows,
9924          SaveToUndoBufferCommand,image,exception);
9925        state|=UpdateConfigurationState;
9926        break;
9927      }
9928      case ButtonRelease:
9929      {
9930        if (event.xbutton.button != Button1)
9931          break;
9932        if ((event.xbutton.window != windows->image.id) &&
9933            (event.xbutton.window != windows->magnify.id))
9934          break;
9935        /*
9936          Update colormap information.
9937        */
9938        x=event.xbutton.x;
9939        y=event.xbutton.y;
9940        XConfigureImageColormap(display,resource_info,windows,*image);
9941        (void) XConfigureImage(display,resource_info,windows,*image,exception);
9942        XInfoWidget(display,windows,text);
9943        (void) XCheckDefineCursor(display,windows->image.id,cursor);
9944        state&=(~UpdateConfigurationState);
9945        break;
9946      }
9947      case Expose:
9948        break;
9949      case KeyPress:
9950      {
9951        char
9952          command[MaxTextExtent];
9953
9954        KeySym
9955          key_symbol;
9956
9957        if (event.xkey.window == windows->magnify.id)
9958          {
9959            Window
9960              window;
9961
9962            window=windows->magnify.id;
9963            while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9964          }
9965        if (event.xkey.window != windows->image.id)
9966          break;
9967        /*
9968          Respond to a user key press.
9969        */
9970        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
9971          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
9972        switch ((int) key_symbol)
9973        {
9974          case XK_Escape:
9975          case XK_F20:
9976          {
9977            /*
9978              Prematurely exit.
9979            */
9980            state|=ExitState;
9981            break;
9982          }
9983          case XK_F1:
9984          case XK_Help:
9985          {
9986            XTextViewWidget(display,resource_info,windows,MagickFalse,
9987              "Help Viewer - Matte Edit",ImageMatteEditHelp);
9988            break;
9989          }
9990          default:
9991          {
9992            (void) XBell(display,0);
9993            break;
9994          }
9995        }
9996        break;
9997      }
9998      case MotionNotify:
9999      {
10000        /*
10001          Map and unmap Info widget as cursor crosses its boundaries.
10002        */
10003        x=event.xmotion.x;
10004        y=event.xmotion.y;
10005        if (windows->info.mapped != MagickFalse)
10006          {
10007            if ((x < (int) (windows->info.x+windows->info.width)) &&
10008                (y < (int) (windows->info.y+windows->info.height)))
10009              (void) XWithdrawWindow(display,windows->info.id,
10010                windows->info.screen);
10011          }
10012        else
10013          if ((x > (int) (windows->info.x+windows->info.width)) ||
10014              (y > (int) (windows->info.y+windows->info.height)))
10015            (void) XMapWindow(display,windows->info.id);
10016        break;
10017      }
10018      default:
10019        break;
10020    }
10021    if (event.xany.window == windows->magnify.id)
10022      {
10023        x=windows->magnify.x-windows->image.x;
10024        y=windows->magnify.y-windows->image.y;
10025      }
10026    x_offset=x;
10027    y_offset=y;
10028    if ((state & UpdateConfigurationState) != 0)
10029      {
10030        CacheView
10031          *image_view;
10032
10033        int
10034          x,
10035          y;
10036
10037        /*
10038          Matte edit is relative to image configuration.
10039        */
10040        (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
10041          MagickTrue);
10042        XPutPixel(windows->image.ximage,x_offset,y_offset,
10043          windows->pixel_info->background_color.pixel);
10044        width=(unsigned int) (*image)->columns;
10045        height=(unsigned int) (*image)->rows;
10046        x=0;
10047        y=0;
10048        if (windows->image.crop_geometry != (char *) NULL)
10049          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
10050            &height);
10051        x_offset=(int) (width*(windows->image.x+x_offset)/
10052          windows->image.ximage->width+x);
10053        y_offset=(int) (height*(windows->image.y+y_offset)/
10054          windows->image.ximage->height+y);
10055        if ((x_offset < 0) || (y_offset < 0))
10056          continue;
10057        if ((x_offset >= (int) (*image)->columns) ||
10058            (y_offset >= (int) (*image)->rows))
10059          continue;
10060        if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10061          return(MagickFalse);
10062        (*image)->matte=MagickTrue;
10063        image_view=AcquireCacheView(*image);
10064        switch (method)
10065        {
10066          case PointMethod:
10067          default:
10068          {
10069            /*
10070              Update matte information using point algorithm.
10071            */
10072            q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10073              (ssize_t) y_offset,1,1,exception);
10074            if (q == (Quantum *) NULL)
10075              break;
10076            SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10077            (void) SyncCacheViewAuthenticPixels(image_view,exception);
10078            break;
10079          }
10080          case ReplaceMethod:
10081          {
10082            PixelPacket
10083              pixel,
10084              target;
10085
10086            /*
10087              Update matte information using replace algorithm.
10088            */
10089            (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
10090              (ssize_t) y_offset,&target,exception);
10091            for (y=0; y < (int) (*image)->rows; y++)
10092            {
10093              q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10094                (*image)->columns,1,exception);
10095              if (q == (Quantum *) NULL)
10096                break;
10097              for (x=0; x < (int) (*image)->columns; x++)
10098              {
10099                GetPixelPacket(*image,q,&pixel);
10100                if (IsFuzzyEquivalencePixelPacket(*image,&pixel,&target))
10101                  SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10102                q+=GetPixelChannels(*image);
10103              }
10104              if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10105                break;
10106            }
10107            break;
10108          }
10109          case FloodfillMethod:
10110          case FillToBorderMethod:
10111          {
10112            ChannelType
10113              channel_mask;
10114
10115            DrawInfo
10116              *draw_info;
10117
10118            PixelInfo
10119              target;
10120
10121            /*
10122              Update matte information using floodfill algorithm.
10123            */
10124            (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
10125              (ssize_t) y_offset,&target,exception);
10126            if (method == FillToBorderMethod)
10127              {
10128                target.red=(MagickRealType) ScaleShortToQuantum(
10129                  border_color.red);
10130                target.green=(MagickRealType) ScaleShortToQuantum(
10131                  border_color.green);
10132                target.blue=(MagickRealType) ScaleShortToQuantum(
10133                  border_color.blue);
10134              }
10135            draw_info=CloneDrawInfo(resource_info->image_info,
10136              (DrawInfo *) NULL);
10137            draw_info->fill.alpha=ClampToQuantum(InterpretLocaleValue(matte,
10138              (char **) NULL));
10139            channel_mask=SetPixelChannelMask(*image,AlphaChannel);
10140            (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10141              x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
10142              MagickFalse : MagickTrue,exception);
10143            (void) SetPixelChannelMap(*image,channel_mask);
10144            draw_info=DestroyDrawInfo(draw_info);
10145            break;
10146          }
10147          case ResetMethod:
10148          {
10149            /*
10150              Update matte information using reset algorithm.
10151            */
10152            if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10153              return(MagickFalse);
10154            for (y=0; y < (int) (*image)->rows; y++)
10155            {
10156              q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10157                (*image)->columns,1,exception);
10158              if (q == (Quantum *) NULL)
10159                break;
10160              for (x=0; x < (int) (*image)->columns; x++)
10161              {
10162                SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10163                q+=GetPixelChannels(*image);
10164              }
10165              if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10166                break;
10167            }
10168            if (StringToLong(matte) == (long) OpaqueAlpha)
10169              (*image)->matte=MagickFalse;
10170            break;
10171          }
10172        }
10173        image_view=DestroyCacheView(image_view);
10174        state&=(~UpdateConfigurationState);
10175      }
10176  } while ((state & ExitState) == 0);
10177  (void) XSelectInput(display,windows->image.id,
10178    windows->image.attributes.event_mask);
10179  XSetCursorState(display,windows,MagickFalse);
10180  (void) XFreeCursor(display,cursor);
10181  return(MagickTrue);
10182}
10183
10184/*
10185%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10186%                                                                             %
10187%                                                                             %
10188%                                                                             %
10189+   X O p e n I m a g e                                                       %
10190%                                                                             %
10191%                                                                             %
10192%                                                                             %
10193%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10194%
10195%  XOpenImage() loads an image from a file.
10196%
10197%  The format of the XOpenImage method is:
10198%
10199%     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10200%       XWindows *windows,const unsigned int command)
10201%
10202%  A description of each parameter follows:
10203%
10204%    o display: Specifies a connection to an X server; returned from
10205%      XOpenDisplay.
10206%
10207%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10208%
10209%    o windows: Specifies a pointer to a XWindows structure.
10210%
10211%    o command: A value other than zero indicates that the file is selected
10212%      from the command line argument list.
10213%
10214*/
10215static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10216  XWindows *windows,const MagickBooleanType command)
10217{
10218  const MagickInfo
10219    *magick_info;
10220
10221  ExceptionInfo
10222    *exception;
10223
10224  Image
10225    *nexus;
10226
10227  ImageInfo
10228    *image_info;
10229
10230  static char
10231    filename[MaxTextExtent] = "\0";
10232
10233  /*
10234    Request file name from user.
10235  */
10236  if (command == MagickFalse)
10237    XFileBrowserWidget(display,windows,"Open",filename);
10238  else
10239    {
10240      char
10241        **filelist,
10242        **files;
10243
10244      int
10245        count,
10246        status;
10247
10248      register int
10249        i,
10250        j;
10251
10252      /*
10253        Select next image from the command line.
10254      */
10255      status=XGetCommand(display,windows->image.id,&files,&count);
10256      if (status == 0)
10257        {
10258          ThrowXWindowFatalException(XServerError,"UnableToGetProperty","...");
10259          return((Image *) NULL);
10260        }
10261      filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10262      if (filelist == (char **) NULL)
10263        {
10264          ThrowXWindowFatalException(ResourceLimitError,
10265            "MemoryAllocationFailed","...");
10266          (void) XFreeStringList(files);
10267          return((Image *) NULL);
10268        }
10269      j=0;
10270      for (i=1; i < count; i++)
10271        if (*files[i] != '-')
10272          filelist[j++]=files[i];
10273      filelist[j]=(char *) NULL;
10274      XListBrowserWidget(display,windows,&windows->widget,
10275        (const char **) filelist,"Load","Select Image to Load:",filename);
10276      filelist=(char **) RelinquishMagickMemory(filelist);
10277      (void) XFreeStringList(files);
10278    }
10279  if (*filename == '\0')
10280    return((Image *) NULL);
10281  image_info=CloneImageInfo(resource_info->image_info);
10282  (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10283    (void *) NULL);
10284  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10285  exception=AcquireExceptionInfo();
10286  (void) SetImageInfo(image_info,0,exception);
10287  if (LocaleCompare(image_info->magick,"X") == 0)
10288    {
10289      char
10290        seconds[MaxTextExtent];
10291
10292      /*
10293        User may want to delay the X server screen grab.
10294      */
10295      (void) CopyMagickString(seconds,"0",MaxTextExtent);
10296      (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10297        seconds);
10298      if (*seconds == '\0')
10299        return((Image *) NULL);
10300      XDelay(display,(size_t) (1000*StringToLong(seconds)));
10301    }
10302  magick_info=GetMagickInfo(image_info->magick,exception);
10303  if ((magick_info != (const MagickInfo *) NULL) &&
10304      (magick_info->raw != MagickFalse))
10305    {
10306      char
10307        geometry[MaxTextExtent];
10308
10309      /*
10310        Request image size from the user.
10311      */
10312      (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10313      if (image_info->size != (char *) NULL)
10314        (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10315      (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10316        geometry);
10317      (void) CloneString(&image_info->size,geometry);
10318    }
10319  /*
10320    Load the image.
10321  */
10322  XSetCursorState(display,windows,MagickTrue);
10323  XCheckRefreshWindows(display,windows);
10324  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10325  nexus=ReadImage(image_info,exception);
10326  CatchException(exception);
10327  XSetCursorState(display,windows,MagickFalse);
10328  if (nexus != (Image *) NULL)
10329    XClientMessage(display,windows->image.id,windows->im_protocols,
10330      windows->im_next_image,CurrentTime);
10331  else
10332    {
10333      char
10334        *text,
10335        **textlist;
10336
10337      /*
10338        Unknown image format.
10339      */
10340      text=FileToString(filename,~0,exception);
10341      if (text == (char *) NULL)
10342        return((Image *) NULL);
10343      textlist=StringToList(text);
10344      if (textlist != (char **) NULL)
10345        {
10346          char
10347            title[MaxTextExtent];
10348
10349          register int
10350            i;
10351
10352          (void) FormatLocaleString(title,MaxTextExtent,
10353            "Unknown format: %s",filename);
10354          XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10355            (const char **) textlist);
10356          for (i=0; textlist[i] != (char *) NULL; i++)
10357            textlist[i]=DestroyString(textlist[i]);
10358          textlist=(char **) RelinquishMagickMemory(textlist);
10359        }
10360      text=DestroyString(text);
10361    }
10362  exception=DestroyExceptionInfo(exception);
10363  image_info=DestroyImageInfo(image_info);
10364  return(nexus);
10365}
10366
10367/*
10368%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10369%                                                                             %
10370%                                                                             %
10371%                                                                             %
10372+   X P a n I m a g e                                                         %
10373%                                                                             %
10374%                                                                             %
10375%                                                                             %
10376%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10377%
10378%  XPanImage() pans the image until the mouse button is released.
10379%
10380%  The format of the XPanImage method is:
10381%
10382%      void XPanImage(Display *display,XWindows *windows,XEvent *event)
10383%
10384%  A description of each parameter follows:
10385%
10386%    o display: Specifies a connection to an X server;  returned from
10387%      XOpenDisplay.
10388%
10389%    o windows: Specifies a pointer to a XWindows structure.
10390%
10391%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10392%      the entire image is refreshed.
10393%
10394*/
10395static void XPanImage(Display *display,XWindows *windows,XEvent *event)
10396{
10397  char
10398    text[MaxTextExtent];
10399
10400  Cursor
10401    cursor;
10402
10403  MagickRealType
10404    x_factor,
10405    y_factor;
10406
10407  RectangleInfo
10408    pan_info;
10409
10410  size_t
10411    state;
10412
10413  /*
10414    Define cursor.
10415  */
10416  if ((windows->image.ximage->width > (int) windows->image.width) &&
10417      (windows->image.ximage->height > (int) windows->image.height))
10418    cursor=XCreateFontCursor(display,XC_fleur);
10419  else
10420    if (windows->image.ximage->width > (int) windows->image.width)
10421      cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10422    else
10423      if (windows->image.ximage->height > (int) windows->image.height)
10424        cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10425      else
10426        cursor=XCreateFontCursor(display,XC_arrow);
10427  (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10428  /*
10429    Pan image as pointer moves until the mouse button is released.
10430  */
10431  x_factor=(MagickRealType) windows->image.ximage->width/windows->pan.width;
10432  y_factor=(MagickRealType) windows->image.ximage->height/windows->pan.height;
10433  pan_info.width=windows->pan.width*windows->image.width/
10434    windows->image.ximage->width;
10435  pan_info.height=windows->pan.height*windows->image.height/
10436    windows->image.ximage->height;
10437  pan_info.x=0;
10438  pan_info.y=0;
10439  state=UpdateConfigurationState;
10440  do
10441  {
10442    switch (event->type)
10443    {
10444      case ButtonPress:
10445      {
10446        /*
10447          User choose an initial pan location.
10448        */
10449        pan_info.x=(ssize_t) event->xbutton.x;
10450        pan_info.y=(ssize_t) event->xbutton.y;
10451        state|=UpdateConfigurationState;
10452        break;
10453      }
10454      case ButtonRelease:
10455      {
10456        /*
10457          User has finished panning the image.
10458        */
10459        pan_info.x=(ssize_t) event->xbutton.x;
10460        pan_info.y=(ssize_t) event->xbutton.y;
10461        state|=UpdateConfigurationState | ExitState;
10462        break;
10463      }
10464      case MotionNotify:
10465      {
10466        pan_info.x=(ssize_t) event->xmotion.x;
10467        pan_info.y=(ssize_t) event->xmotion.y;
10468        state|=UpdateConfigurationState;
10469      }
10470      default:
10471        break;
10472    }
10473    if ((state & UpdateConfigurationState) != 0)
10474      {
10475        /*
10476          Check boundary conditions.
10477        */
10478        if (pan_info.x < (ssize_t) (pan_info.width/2))
10479          pan_info.x=0;
10480        else
10481          pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10482        if (pan_info.x < 0)
10483          pan_info.x=0;
10484        else
10485          if ((int) (pan_info.x+windows->image.width) >
10486              windows->image.ximage->width)
10487            pan_info.x=(ssize_t)
10488              (windows->image.ximage->width-windows->image.width);
10489        if (pan_info.y < (ssize_t) (pan_info.height/2))
10490          pan_info.y=0;
10491        else
10492          pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10493        if (pan_info.y < 0)
10494          pan_info.y=0;
10495        else
10496          if ((int) (pan_info.y+windows->image.height) >
10497              windows->image.ximage->height)
10498            pan_info.y=(ssize_t)
10499              (windows->image.ximage->height-windows->image.height);
10500        if ((windows->image.x != (int) pan_info.x) ||
10501            (windows->image.y != (int) pan_info.y))
10502          {
10503            /*
10504              Display image pan offset.
10505            */
10506            windows->image.x=(int) pan_info.x;
10507            windows->image.y=(int) pan_info.y;
10508            (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
10509              windows->image.width,windows->image.height,windows->image.x,
10510              windows->image.y);
10511            XInfoWidget(display,windows,text);
10512            /*
10513              Refresh Image window.
10514            */
10515            XDrawPanRectangle(display,windows);
10516            XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10517          }
10518        state&=(~UpdateConfigurationState);
10519      }
10520    /*
10521      Wait for next event.
10522    */
10523    if ((state & ExitState) == 0)
10524      XScreenEvent(display,windows,event);
10525  } while ((state & ExitState) == 0);
10526  /*
10527    Restore cursor.
10528  */
10529  (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10530  (void) XFreeCursor(display,cursor);
10531  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10532}
10533
10534/*
10535%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10536%                                                                             %
10537%                                                                             %
10538%                                                                             %
10539+   X P a s t e I m a g e                                                     %
10540%                                                                             %
10541%                                                                             %
10542%                                                                             %
10543%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10544%
10545%  XPasteImage() pastes an image previously saved with XCropImage in the X
10546%  window image at a location the user chooses with the pointer.
10547%
10548%  The format of the XPasteImage method is:
10549%
10550%      MagickBooleanType XPasteImage(Display *display,
10551%        XResourceInfo *resource_info,XWindows *windows,Image *image,
10552%        ExceptionInfo *exception)
10553%
10554%  A description of each parameter follows:
10555%
10556%    o display: Specifies a connection to an X server;  returned from
10557%      XOpenDisplay.
10558%
10559%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10560%
10561%    o windows: Specifies a pointer to a XWindows structure.
10562%
10563%    o image: the image; returned from ReadImage.
10564%
10565%    o exception: return any errors or warnings in this structure.
10566%
10567*/
10568static MagickBooleanType XPasteImage(Display *display,
10569  XResourceInfo *resource_info,XWindows *windows,Image *image,
10570  ExceptionInfo *exception)
10571{
10572  static const char
10573    *PasteMenu[] =
10574    {
10575      "Operator",
10576      "Help",
10577      "Dismiss",
10578      (char *) NULL
10579    };
10580
10581  static const ModeType
10582    PasteCommands[] =
10583    {
10584      PasteOperatorsCommand,
10585      PasteHelpCommand,
10586      PasteDismissCommand
10587    };
10588
10589  static CompositeOperator
10590    compose = CopyCompositeOp;
10591
10592  char
10593    text[MaxTextExtent];
10594
10595  Cursor
10596    cursor;
10597
10598  Image
10599    *paste_image;
10600
10601  int
10602    entry,
10603    id,
10604    x,
10605    y;
10606
10607  MagickRealType
10608    scale_factor;
10609
10610  RectangleInfo
10611    highlight_info,
10612    paste_info;
10613
10614  unsigned int
10615    height,
10616    width;
10617
10618  size_t
10619    state;
10620
10621  XEvent
10622    event;
10623
10624  /*
10625    Copy image.
10626  */
10627  if (resource_info->copy_image == (Image *) NULL)
10628    return(MagickFalse);
10629  paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,exception);
10630  /*
10631    Map Command widget.
10632  */
10633  (void) CloneString(&windows->command.name,"Paste");
10634  windows->command.data=1;
10635  (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10636  (void) XMapRaised(display,windows->command.id);
10637  XClientMessage(display,windows->image.id,windows->im_protocols,
10638    windows->im_update_widget,CurrentTime);
10639  /*
10640    Track pointer until button 1 is pressed.
10641  */
10642  XSetCursorState(display,windows,MagickFalse);
10643  XQueryPosition(display,windows->image.id,&x,&y);
10644  (void) XSelectInput(display,windows->image.id,
10645    windows->image.attributes.event_mask | PointerMotionMask);
10646  paste_info.x=(ssize_t) windows->image.x+x;
10647  paste_info.y=(ssize_t) windows->image.y+y;
10648  paste_info.width=0;
10649  paste_info.height=0;
10650  cursor=XCreateFontCursor(display,XC_ul_angle);
10651  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10652  state=DefaultState;
10653  do
10654  {
10655    if (windows->info.mapped != MagickFalse)
10656      {
10657        /*
10658          Display pointer position.
10659        */
10660        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
10661          (long) paste_info.x,(long) paste_info.y);
10662        XInfoWidget(display,windows,text);
10663      }
10664    highlight_info=paste_info;
10665    highlight_info.x=paste_info.x-windows->image.x;
10666    highlight_info.y=paste_info.y-windows->image.y;
10667    XHighlightRectangle(display,windows->image.id,
10668      windows->image.highlight_context,&highlight_info);
10669    /*
10670      Wait for next event.
10671    */
10672    XScreenEvent(display,windows,&event);
10673    XHighlightRectangle(display,windows->image.id,
10674      windows->image.highlight_context,&highlight_info);
10675    if (event.xany.window == windows->command.id)
10676      {
10677        /*
10678          Select a command from the Command widget.
10679        */
10680        id=XCommandWidget(display,windows,PasteMenu,&event);
10681        if (id < 0)
10682          continue;
10683        switch (PasteCommands[id])
10684        {
10685          case PasteOperatorsCommand:
10686          {
10687            char
10688              command[MaxTextExtent],
10689              **operators;
10690
10691            /*
10692              Select a command from the pop-up menu.
10693            */
10694            operators=GetCommandOptions(MagickComposeOptions);
10695            if (operators == (char **) NULL)
10696              break;
10697            entry=XMenuWidget(display,windows,PasteMenu[id],
10698              (const char **) operators,command);
10699            if (entry >= 0)
10700              compose=(CompositeOperator) ParseCommandOption(
10701                MagickComposeOptions,MagickFalse,operators[entry]);
10702            operators=DestroyStringList(operators);
10703            break;
10704          }
10705          case PasteHelpCommand:
10706          {
10707            XTextViewWidget(display,resource_info,windows,MagickFalse,
10708              "Help Viewer - Image Composite",ImagePasteHelp);
10709            break;
10710          }
10711          case PasteDismissCommand:
10712          {
10713            /*
10714              Prematurely exit.
10715            */
10716            state|=EscapeState;
10717            state|=ExitState;
10718            break;
10719          }
10720          default:
10721            break;
10722        }
10723        continue;
10724      }
10725    switch (event.type)
10726    {
10727      case ButtonPress:
10728      {
10729        if (image->debug != MagickFalse)
10730          (void) LogMagickEvent(X11Event,GetMagickModule(),
10731            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10732            event.xbutton.button,event.xbutton.x,event.xbutton.y);
10733        if (event.xbutton.button != Button1)
10734          break;
10735        if (event.xbutton.window != windows->image.id)
10736          break;
10737        /*
10738          Paste rectangle is relative to image configuration.
10739        */
10740        width=(unsigned int) image->columns;
10741        height=(unsigned int) image->rows;
10742        x=0;
10743        y=0;
10744        if (windows->image.crop_geometry != (char *) NULL)
10745          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10746            &width,&height);
10747        scale_factor=(MagickRealType) windows->image.ximage->width/width;
10748        paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10749        scale_factor=(MagickRealType) windows->image.ximage->height/height;
10750        paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10751        (void) XCheckDefineCursor(display,windows->image.id,cursor);
10752        paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10753        paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10754        break;
10755      }
10756      case ButtonRelease:
10757      {
10758        if (image->debug != MagickFalse)
10759          (void) LogMagickEvent(X11Event,GetMagickModule(),
10760            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10761            event.xbutton.button,event.xbutton.x,event.xbutton.y);
10762        if (event.xbutton.button != Button1)
10763          break;
10764        if (event.xbutton.window != windows->image.id)
10765          break;
10766        if ((paste_info.width != 0) && (paste_info.height != 0))
10767          {
10768            /*
10769              User has selected the location of the paste image.
10770            */
10771            paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10772            paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10773            state|=ExitState;
10774          }
10775        break;
10776      }
10777      case Expose:
10778        break;
10779      case KeyPress:
10780      {
10781        char
10782          command[MaxTextExtent];
10783
10784        KeySym
10785          key_symbol;
10786
10787        int
10788          length;
10789
10790        if (event.xkey.window != windows->image.id)
10791          break;
10792        /*
10793          Respond to a user key press.
10794        */
10795        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10796          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10797        *(command+length)='\0';
10798        if (image->debug != MagickFalse)
10799          (void) LogMagickEvent(X11Event,GetMagickModule(),
10800            "Key press: 0x%lx (%s)",(long) key_symbol,command);
10801        switch ((int) key_symbol)
10802        {
10803          case XK_Escape:
10804          case XK_F20:
10805          {
10806            /*
10807              Prematurely exit.
10808            */
10809            paste_image=DestroyImage(paste_image);
10810            state|=EscapeState;
10811            state|=ExitState;
10812            break;
10813          }
10814          case XK_F1:
10815          case XK_Help:
10816          {
10817            (void) XSetFunction(display,windows->image.highlight_context,
10818              GXcopy);
10819            XTextViewWidget(display,resource_info,windows,MagickFalse,
10820              "Help Viewer - Image Composite",ImagePasteHelp);
10821            (void) XSetFunction(display,windows->image.highlight_context,
10822              GXinvert);
10823            break;
10824          }
10825          default:
10826          {
10827            (void) XBell(display,0);
10828            break;
10829          }
10830        }
10831        break;
10832      }
10833      case MotionNotify:
10834      {
10835        /*
10836          Map and unmap Info widget as text cursor crosses its boundaries.
10837        */
10838        x=event.xmotion.x;
10839        y=event.xmotion.y;
10840        if (windows->info.mapped != MagickFalse)
10841          {
10842            if ((x < (int) (windows->info.x+windows->info.width)) &&
10843                (y < (int) (windows->info.y+windows->info.height)))
10844              (void) XWithdrawWindow(display,windows->info.id,
10845                windows->info.screen);
10846          }
10847        else
10848          if ((x > (int) (windows->info.x+windows->info.width)) ||
10849              (y > (int) (windows->info.y+windows->info.height)))
10850            (void) XMapWindow(display,windows->info.id);
10851        paste_info.x=(ssize_t) windows->image.x+x;
10852        paste_info.y=(ssize_t) windows->image.y+y;
10853        break;
10854      }
10855      default:
10856      {
10857        if (image->debug != MagickFalse)
10858          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10859            event.type);
10860        break;
10861      }
10862    }
10863  } while ((state & ExitState) == 0);
10864  (void) XSelectInput(display,windows->image.id,
10865    windows->image.attributes.event_mask);
10866  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10867  XSetCursorState(display,windows,MagickFalse);
10868  (void) XFreeCursor(display,cursor);
10869  if ((state & EscapeState) != 0)
10870    return(MagickTrue);
10871  /*
10872    Image pasting is relative to image configuration.
10873  */
10874  XSetCursorState(display,windows,MagickTrue);
10875  XCheckRefreshWindows(display,windows);
10876  width=(unsigned int) image->columns;
10877  height=(unsigned int) image->rows;
10878  x=0;
10879  y=0;
10880  if (windows->image.crop_geometry != (char *) NULL)
10881    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10882  scale_factor=(MagickRealType) width/windows->image.ximage->width;
10883  paste_info.x+=x;
10884  paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10885  paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10886  scale_factor=(MagickRealType) height/windows->image.ximage->height;
10887  paste_info.y+=y;
10888  paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10889  paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10890  /*
10891    Paste image with X Image window.
10892  */
10893  (void) CompositeImage(image,compose,paste_image,paste_info.x,paste_info.y);
10894  paste_image=DestroyImage(paste_image);
10895  XSetCursorState(display,windows,MagickFalse);
10896  /*
10897    Update image colormap.
10898  */
10899  XConfigureImageColormap(display,resource_info,windows,image);
10900  (void) XConfigureImage(display,resource_info,windows,image,exception);
10901  return(MagickTrue);
10902}
10903
10904/*
10905%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10906%                                                                             %
10907%                                                                             %
10908%                                                                             %
10909+   X P r i n t I m a g e                                                     %
10910%                                                                             %
10911%                                                                             %
10912%                                                                             %
10913%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10914%
10915%  XPrintImage() prints an image to a Postscript printer.
10916%
10917%  The format of the XPrintImage method is:
10918%
10919%      MagickBooleanType XPrintImage(Display *display,
10920%        XResourceInfo *resource_info,XWindows *windows,Image *image,
10921%        ExceptionInfo *exception)
10922%
10923%  A description of each parameter follows:
10924%
10925%    o display: Specifies a connection to an X server; returned from
10926%      XOpenDisplay.
10927%
10928%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10929%
10930%    o windows: Specifies a pointer to a XWindows structure.
10931%
10932%    o image: the image.
10933%
10934%    o exception: return any errors or warnings in this structure.
10935%
10936*/
10937static MagickBooleanType XPrintImage(Display *display,
10938  XResourceInfo *resource_info,XWindows *windows,Image *image,
10939  ExceptionInfo *exception)
10940{
10941  char
10942    filename[MaxTextExtent],
10943    geometry[MaxTextExtent];
10944
10945  Image
10946    *print_image;
10947
10948  ImageInfo
10949    *image_info;
10950
10951  MagickStatusType
10952    status;
10953
10954  /*
10955    Request Postscript page geometry from user.
10956  */
10957  image_info=CloneImageInfo(resource_info->image_info);
10958  (void) FormatLocaleString(geometry,MaxTextExtent,"Letter");
10959  if (image_info->page != (char *) NULL)
10960    (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
10961  XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
10962    "Select Postscript Page Geometry:",geometry);
10963  if (*geometry == '\0')
10964    return(MagickTrue);
10965  image_info->page=GetPageGeometry(geometry);
10966  /*
10967    Apply image transforms.
10968  */
10969  XSetCursorState(display,windows,MagickTrue);
10970  XCheckRefreshWindows(display,windows);
10971  print_image=CloneImage(image,0,0,MagickTrue,exception);
10972  if (print_image == (Image *) NULL)
10973    return(MagickFalse);
10974  (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
10975    windows->image.ximage->width,windows->image.ximage->height);
10976  (void) TransformImage(&print_image,windows->image.crop_geometry,geometry);
10977  /*
10978    Print image.
10979  */
10980  (void) AcquireUniqueFilename(filename);
10981  (void) FormatLocaleString(print_image->filename,MaxTextExtent,"print:%s",
10982    filename);
10983  status=WriteImage(image_info,print_image,exception);
10984  (void) RelinquishUniqueFileResource(filename);
10985  print_image=DestroyImage(print_image);
10986  image_info=DestroyImageInfo(image_info);
10987  XSetCursorState(display,windows,MagickFalse);
10988  return(status != 0 ? MagickTrue : MagickFalse);
10989}
10990
10991/*
10992%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10993%                                                                             %
10994%                                                                             %
10995%                                                                             %
10996+   X R O I I m a g e                                                         %
10997%                                                                             %
10998%                                                                             %
10999%                                                                             %
11000%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11001%
11002%  XROIImage() applies an image processing technique to a region of interest.
11003%
11004%  The format of the XROIImage method is:
11005%
11006%      MagickBooleanType XROIImage(Display *display,
11007%        XResourceInfo *resource_info,XWindows *windows,Image **image,
11008%        ExceptionInfo *exception)
11009%
11010%  A description of each parameter follows:
11011%
11012%    o display: Specifies a connection to an X server; returned from
11013%      XOpenDisplay.
11014%
11015%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11016%
11017%    o windows: Specifies a pointer to a XWindows structure.
11018%
11019%    o image: the image; returned from ReadImage.
11020%
11021%    o exception: return any errors or warnings in this structure.
11022%
11023*/
11024static MagickBooleanType XROIImage(Display *display,
11025  XResourceInfo *resource_info,XWindows *windows,Image **image,
11026  ExceptionInfo *exception)
11027{
11028#define ApplyMenus  7
11029
11030  static const char
11031    *ROIMenu[] =
11032    {
11033      "Help",
11034      "Dismiss",
11035      (char *) NULL
11036    },
11037    *ApplyMenu[] =
11038    {
11039      "File",
11040      "Edit",
11041      "Transform",
11042      "Enhance",
11043      "Effects",
11044      "F/X",
11045      "Miscellany",
11046      "Help",
11047      "Dismiss",
11048      (char *) NULL
11049    },
11050    *FileMenu[] =
11051    {
11052      "Save...",
11053      "Print...",
11054      (char *) NULL
11055    },
11056    *EditMenu[] =
11057    {
11058      "Undo",
11059      "Redo",
11060      (char *) NULL
11061    },
11062    *TransformMenu[] =
11063    {
11064      "Flop",
11065      "Flip",
11066      "Rotate Right",
11067      "Rotate Left",
11068      (char *) NULL
11069    },
11070    *EnhanceMenu[] =
11071    {
11072      "Hue...",
11073      "Saturation...",
11074      "Brightness...",
11075      "Gamma...",
11076      "Spiff",
11077      "Dull",
11078      "Contrast Stretch...",
11079      "Sigmoidal Contrast...",
11080      "Normalize",
11081      "Equalize",
11082      "Negate",
11083      "Grayscale",
11084      "Map...",
11085      "Quantize...",
11086      (char *) NULL
11087    },
11088    *EffectsMenu[] =
11089    {
11090      "Despeckle",
11091      "Emboss",
11092      "Reduce Noise",
11093      "Add Noise",
11094      "Sharpen...",
11095      "Blur...",
11096      "Threshold...",
11097      "Edge Detect...",
11098      "Spread...",
11099      "Shade...",
11100      "Raise...",
11101      "Segment...",
11102      (char *) NULL
11103    },
11104    *FXMenu[] =
11105    {
11106      "Solarize...",
11107      "Sepia Tone...",
11108      "Swirl...",
11109      "Implode...",
11110      "Vignette...",
11111      "Wave...",
11112      "Oil Paint...",
11113      "Charcoal Draw...",
11114      (char *) NULL
11115    },
11116    *MiscellanyMenu[] =
11117    {
11118      "Image Info",
11119      "Zoom Image",
11120      "Show Preview...",
11121      "Show Histogram",
11122      "Show Matte",
11123      (char *) NULL
11124    };
11125
11126  static const char
11127    **Menus[ApplyMenus] =
11128    {
11129      FileMenu,
11130      EditMenu,
11131      TransformMenu,
11132      EnhanceMenu,
11133      EffectsMenu,
11134      FXMenu,
11135      MiscellanyMenu
11136    };
11137
11138  static const CommandType
11139    ApplyCommands[] =
11140    {
11141      NullCommand,
11142      NullCommand,
11143      NullCommand,
11144      NullCommand,
11145      NullCommand,
11146      NullCommand,
11147      NullCommand,
11148      HelpCommand,
11149      QuitCommand
11150    },
11151    FileCommands[] =
11152    {
11153      SaveCommand,
11154      PrintCommand
11155    },
11156    EditCommands[] =
11157    {
11158      UndoCommand,
11159      RedoCommand
11160    },
11161    TransformCommands[] =
11162    {
11163      FlopCommand,
11164      FlipCommand,
11165      RotateRightCommand,
11166      RotateLeftCommand
11167    },
11168    EnhanceCommands[] =
11169    {
11170      HueCommand,
11171      SaturationCommand,
11172      BrightnessCommand,
11173      GammaCommand,
11174      SpiffCommand,
11175      DullCommand,
11176      ContrastStretchCommand,
11177      SigmoidalContrastCommand,
11178      NormalizeCommand,
11179      EqualizeCommand,
11180      NegateCommand,
11181      GrayscaleCommand,
11182      MapCommand,
11183      QuantizeCommand
11184    },
11185    EffectsCommands[] =
11186    {
11187      DespeckleCommand,
11188      EmbossCommand,
11189      ReduceNoiseCommand,
11190      AddNoiseCommand,
11191      SharpenCommand,
11192      BlurCommand,
11193      EdgeDetectCommand,
11194      SpreadCommand,
11195      ShadeCommand,
11196      RaiseCommand,
11197      SegmentCommand
11198    },
11199    FXCommands[] =
11200    {
11201      SolarizeCommand,
11202      SepiaToneCommand,
11203      SwirlCommand,
11204      ImplodeCommand,
11205      VignetteCommand,
11206      WaveCommand,
11207      OilPaintCommand,
11208      CharcoalDrawCommand
11209    },
11210    MiscellanyCommands[] =
11211    {
11212      InfoCommand,
11213      ZoomCommand,
11214      ShowPreviewCommand,
11215      ShowHistogramCommand,
11216      ShowMatteCommand
11217    },
11218    ROICommands[] =
11219    {
11220      ROIHelpCommand,
11221      ROIDismissCommand
11222    };
11223
11224  static const CommandType
11225    *Commands[ApplyMenus] =
11226    {
11227      FileCommands,
11228      EditCommands,
11229      TransformCommands,
11230      EnhanceCommands,
11231      EffectsCommands,
11232      FXCommands,
11233      MiscellanyCommands
11234    };
11235
11236  char
11237    command[MaxTextExtent],
11238    text[MaxTextExtent];
11239
11240  CommandType
11241    command_type;
11242
11243  Cursor
11244    cursor;
11245
11246  Image
11247    *roi_image;
11248
11249  int
11250    entry,
11251    id,
11252    x,
11253    y;
11254
11255  MagickRealType
11256    scale_factor;
11257
11258  MagickProgressMonitor
11259    progress_monitor;
11260
11261  RectangleInfo
11262    crop_info,
11263    highlight_info,
11264    roi_info;
11265
11266  unsigned int
11267    height,
11268    width;
11269
11270  size_t
11271    state;
11272
11273  XEvent
11274    event;
11275
11276  /*
11277    Map Command widget.
11278  */
11279  (void) CloneString(&windows->command.name,"ROI");
11280  windows->command.data=0;
11281  (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11282  (void) XMapRaised(display,windows->command.id);
11283  XClientMessage(display,windows->image.id,windows->im_protocols,
11284    windows->im_update_widget,CurrentTime);
11285  /*
11286    Track pointer until button 1 is pressed.
11287  */
11288  XQueryPosition(display,windows->image.id,&x,&y);
11289  (void) XSelectInput(display,windows->image.id,
11290    windows->image.attributes.event_mask | PointerMotionMask);
11291  roi_info.x=(ssize_t) windows->image.x+x;
11292  roi_info.y=(ssize_t) windows->image.y+y;
11293  roi_info.width=0;
11294  roi_info.height=0;
11295  cursor=XCreateFontCursor(display,XC_fleur);
11296  state=DefaultState;
11297  do
11298  {
11299    if (windows->info.mapped != MagickFalse)
11300      {
11301        /*
11302          Display pointer position.
11303        */
11304        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
11305          (long) roi_info.x,(long) roi_info.y);
11306        XInfoWidget(display,windows,text);
11307      }
11308    /*
11309      Wait for next event.
11310    */
11311    XScreenEvent(display,windows,&event);
11312    if (event.xany.window == windows->command.id)
11313      {
11314        /*
11315          Select a command from the Command widget.
11316        */
11317        id=XCommandWidget(display,windows,ROIMenu,&event);
11318        if (id < 0)
11319          continue;
11320        switch (ROICommands[id])
11321        {
11322          case ROIHelpCommand:
11323          {
11324            XTextViewWidget(display,resource_info,windows,MagickFalse,
11325              "Help Viewer - Region of Interest",ImageROIHelp);
11326            break;
11327          }
11328          case ROIDismissCommand:
11329          {
11330            /*
11331              Prematurely exit.
11332            */
11333            state|=EscapeState;
11334            state|=ExitState;
11335            break;
11336          }
11337          default:
11338            break;
11339        }
11340        continue;
11341      }
11342    switch (event.type)
11343    {
11344      case ButtonPress:
11345      {
11346        if (event.xbutton.button != Button1)
11347          break;
11348        if (event.xbutton.window != windows->image.id)
11349          break;
11350        /*
11351          Note first corner of region of interest rectangle-- exit loop.
11352        */
11353        (void) XCheckDefineCursor(display,windows->image.id,cursor);
11354        roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11355        roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11356        state|=ExitState;
11357        break;
11358      }
11359      case ButtonRelease:
11360        break;
11361      case Expose:
11362        break;
11363      case KeyPress:
11364      {
11365        KeySym
11366          key_symbol;
11367
11368        if (event.xkey.window != windows->image.id)
11369          break;
11370        /*
11371          Respond to a user key press.
11372        */
11373        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11374          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11375        switch ((int) key_symbol)
11376        {
11377          case XK_Escape:
11378          case XK_F20:
11379          {
11380            /*
11381              Prematurely exit.
11382            */
11383            state|=EscapeState;
11384            state|=ExitState;
11385            break;
11386          }
11387          case XK_F1:
11388          case XK_Help:
11389          {
11390            XTextViewWidget(display,resource_info,windows,MagickFalse,
11391              "Help Viewer - Region of Interest",ImageROIHelp);
11392            break;
11393          }
11394          default:
11395          {
11396            (void) XBell(display,0);
11397            break;
11398          }
11399        }
11400        break;
11401      }
11402      case MotionNotify:
11403      {
11404        /*
11405          Map and unmap Info widget as text cursor crosses its boundaries.
11406        */
11407        x=event.xmotion.x;
11408        y=event.xmotion.y;
11409        if (windows->info.mapped != MagickFalse)
11410          {
11411            if ((x < (int) (windows->info.x+windows->info.width)) &&
11412                (y < (int) (windows->info.y+windows->info.height)))
11413              (void) XWithdrawWindow(display,windows->info.id,
11414                windows->info.screen);
11415          }
11416        else
11417          if ((x > (int) (windows->info.x+windows->info.width)) ||
11418              (y > (int) (windows->info.y+windows->info.height)))
11419            (void) XMapWindow(display,windows->info.id);
11420        roi_info.x=(ssize_t) windows->image.x+x;
11421        roi_info.y=(ssize_t) windows->image.y+y;
11422        break;
11423      }
11424      default:
11425        break;
11426    }
11427  } while ((state & ExitState) == 0);
11428  (void) XSelectInput(display,windows->image.id,
11429    windows->image.attributes.event_mask);
11430  if ((state & EscapeState) != 0)
11431    {
11432      /*
11433        User want to exit without region of interest.
11434      */
11435      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11436      (void) XFreeCursor(display,cursor);
11437      return(MagickTrue);
11438    }
11439  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11440  do
11441  {
11442    /*
11443      Size rectangle as pointer moves until the mouse button is released.
11444    */
11445    x=(int) roi_info.x;
11446    y=(int) roi_info.y;
11447    roi_info.width=0;
11448    roi_info.height=0;
11449    state=DefaultState;
11450    do
11451    {
11452      highlight_info=roi_info;
11453      highlight_info.x=roi_info.x-windows->image.x;
11454      highlight_info.y=roi_info.y-windows->image.y;
11455      if ((highlight_info.width > 3) && (highlight_info.height > 3))
11456        {
11457          /*
11458            Display info and draw region of interest rectangle.
11459          */
11460          if (windows->info.mapped == MagickFalse)
11461            (void) XMapWindow(display,windows->info.id);
11462          (void) FormatLocaleString(text,MaxTextExtent,
11463            " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11464            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11465          XInfoWidget(display,windows,text);
11466          XHighlightRectangle(display,windows->image.id,
11467            windows->image.highlight_context,&highlight_info);
11468        }
11469      else
11470        if (windows->info.mapped != MagickFalse)
11471          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11472      /*
11473        Wait for next event.
11474      */
11475      XScreenEvent(display,windows,&event);
11476      if ((highlight_info.width > 3) && (highlight_info.height > 3))
11477        XHighlightRectangle(display,windows->image.id,
11478          windows->image.highlight_context,&highlight_info);
11479      switch (event.type)
11480      {
11481        case ButtonPress:
11482        {
11483          roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11484          roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11485          break;
11486        }
11487        case ButtonRelease:
11488        {
11489          /*
11490            User has committed to region of interest rectangle.
11491          */
11492          roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11493          roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11494          XSetCursorState(display,windows,MagickFalse);
11495          state|=ExitState;
11496          if (LocaleCompare(windows->command.name,"Apply") == 0)
11497            break;
11498          (void) CloneString(&windows->command.name,"Apply");
11499          windows->command.data=ApplyMenus;
11500          (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11501          break;
11502        }
11503        case Expose:
11504          break;
11505        case MotionNotify:
11506        {
11507          roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11508          roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11509        }
11510        default:
11511          break;
11512      }
11513      if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11514          ((state & ExitState) != 0))
11515        {
11516          /*
11517            Check boundary conditions.
11518          */
11519          if (roi_info.x < 0)
11520            roi_info.x=0;
11521          else
11522            if (roi_info.x > (ssize_t) windows->image.ximage->width)
11523              roi_info.x=(ssize_t) windows->image.ximage->width;
11524          if ((int) roi_info.x < x)
11525            roi_info.width=(unsigned int) (x-roi_info.x);
11526          else
11527            {
11528              roi_info.width=(unsigned int) (roi_info.x-x);
11529              roi_info.x=(ssize_t) x;
11530            }
11531          if (roi_info.y < 0)
11532            roi_info.y=0;
11533          else
11534            if (roi_info.y > (ssize_t) windows->image.ximage->height)
11535              roi_info.y=(ssize_t) windows->image.ximage->height;
11536          if ((int) roi_info.y < y)
11537            roi_info.height=(unsigned int) (y-roi_info.y);
11538          else
11539            {
11540              roi_info.height=(unsigned int) (roi_info.y-y);
11541              roi_info.y=(ssize_t) y;
11542            }
11543        }
11544    } while ((state & ExitState) == 0);
11545    /*
11546      Wait for user to grab a corner of the rectangle or press return.
11547    */
11548    state=DefaultState;
11549    command_type=NullCommand;
11550    (void) XMapWindow(display,windows->info.id);
11551    do
11552    {
11553      if (windows->info.mapped != MagickFalse)
11554        {
11555          /*
11556            Display pointer position.
11557          */
11558          (void) FormatLocaleString(text,MaxTextExtent,
11559            " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11560            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11561          XInfoWidget(display,windows,text);
11562        }
11563      highlight_info=roi_info;
11564      highlight_info.x=roi_info.x-windows->image.x;
11565      highlight_info.y=roi_info.y-windows->image.y;
11566      if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11567        {
11568          state|=EscapeState;
11569          state|=ExitState;
11570          break;
11571        }
11572      if ((state & UpdateRegionState) != 0)
11573        {
11574          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11575          switch (command_type)
11576          {
11577            case UndoCommand:
11578            case RedoCommand:
11579            {
11580              (void) XMagickCommand(display,resource_info,windows,command_type,
11581                image,exception);
11582              break;
11583            }
11584            default:
11585            {
11586              /*
11587                Region of interest is relative to image configuration.
11588              */
11589              progress_monitor=SetImageProgressMonitor(*image,
11590                (MagickProgressMonitor) NULL,(*image)->client_data);
11591              crop_info=roi_info;
11592              width=(unsigned int) (*image)->columns;
11593              height=(unsigned int) (*image)->rows;
11594              x=0;
11595              y=0;
11596              if (windows->image.crop_geometry != (char *) NULL)
11597                (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11598                  &width,&height);
11599              scale_factor=(MagickRealType) width/windows->image.ximage->width;
11600              crop_info.x+=x;
11601              crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11602              crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11603              scale_factor=(MagickRealType)
11604                height/windows->image.ximage->height;
11605              crop_info.y+=y;
11606              crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11607              crop_info.height=(unsigned int)
11608                (scale_factor*crop_info.height+0.5);
11609              roi_image=CropImage(*image,&crop_info,exception);
11610              (void) SetImageProgressMonitor(*image,progress_monitor,
11611                (*image)->client_data);
11612              if (roi_image == (Image *) NULL)
11613                continue;
11614              /*
11615                Apply image processing technique to the region of interest.
11616              */
11617              windows->image.orphan=MagickTrue;
11618              (void) XMagickCommand(display,resource_info,windows,command_type,
11619                &roi_image,exception);
11620              progress_monitor=SetImageProgressMonitor(*image,
11621                (MagickProgressMonitor) NULL,(*image)->client_data);
11622              (void) XMagickCommand(display,resource_info,windows,
11623                SaveToUndoBufferCommand,image,exception);
11624              windows->image.orphan=MagickFalse;
11625              (void) CompositeImage(*image,CopyCompositeOp,roi_image,
11626                crop_info.x,crop_info.y);
11627              roi_image=DestroyImage(roi_image);
11628              (void) SetImageProgressMonitor(*image,progress_monitor,
11629                (*image)->client_data);
11630              break;
11631            }
11632          }
11633          if (command_type != InfoCommand)
11634            {
11635              XConfigureImageColormap(display,resource_info,windows,*image);
11636              (void) XConfigureImage(display,resource_info,windows,*image,exception);
11637            }
11638          XCheckRefreshWindows(display,windows);
11639          XInfoWidget(display,windows,text);
11640          (void) XSetFunction(display,windows->image.highlight_context,
11641            GXinvert);
11642          state&=(~UpdateRegionState);
11643        }
11644      XHighlightRectangle(display,windows->image.id,
11645        windows->image.highlight_context,&highlight_info);
11646      XScreenEvent(display,windows,&event);
11647      if (event.xany.window == windows->command.id)
11648        {
11649          /*
11650            Select a command from the Command widget.
11651          */
11652          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11653          command_type=NullCommand;
11654          id=XCommandWidget(display,windows,ApplyMenu,&event);
11655          if (id >= 0)
11656            {
11657              (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11658              command_type=ApplyCommands[id];
11659              if (id < ApplyMenus)
11660                {
11661                  /*
11662                    Select a command from a pop-up menu.
11663                  */
11664                  entry=XMenuWidget(display,windows,ApplyMenu[id],
11665                    (const char **) Menus[id],command);
11666                  if (entry >= 0)
11667                    {
11668                      (void) CopyMagickString(command,Menus[id][entry],
11669                        MaxTextExtent);
11670                      command_type=Commands[id][entry];
11671                    }
11672                }
11673            }
11674          (void) XSetFunction(display,windows->image.highlight_context,
11675            GXinvert);
11676          XHighlightRectangle(display,windows->image.id,
11677            windows->image.highlight_context,&highlight_info);
11678          if (command_type == HelpCommand)
11679            {
11680              (void) XSetFunction(display,windows->image.highlight_context,
11681                GXcopy);
11682              XTextViewWidget(display,resource_info,windows,MagickFalse,
11683                "Help Viewer - Region of Interest",ImageROIHelp);
11684              (void) XSetFunction(display,windows->image.highlight_context,
11685                GXinvert);
11686              continue;
11687            }
11688          if (command_type == QuitCommand)
11689            {
11690              /*
11691                exit.
11692              */
11693              state|=EscapeState;
11694              state|=ExitState;
11695              continue;
11696            }
11697          if (command_type != NullCommand)
11698            state|=UpdateRegionState;
11699          continue;
11700        }
11701      XHighlightRectangle(display,windows->image.id,
11702        windows->image.highlight_context,&highlight_info);
11703      switch (event.type)
11704      {
11705        case ButtonPress:
11706        {
11707          x=windows->image.x;
11708          y=windows->image.y;
11709          if (event.xbutton.button != Button1)
11710            break;
11711          if (event.xbutton.window != windows->image.id)
11712            break;
11713          x=windows->image.x+event.xbutton.x;
11714          y=windows->image.y+event.xbutton.y;
11715          if ((x < (int) (roi_info.x+RoiDelta)) &&
11716              (x > (int) (roi_info.x-RoiDelta)) &&
11717              (y < (int) (roi_info.y+RoiDelta)) &&
11718              (y > (int) (roi_info.y-RoiDelta)))
11719            {
11720              roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11721              roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11722              state|=UpdateConfigurationState;
11723              break;
11724            }
11725          if ((x < (int) (roi_info.x+RoiDelta)) &&
11726              (x > (int) (roi_info.x-RoiDelta)) &&
11727              (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11728              (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11729            {
11730              roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11731              state|=UpdateConfigurationState;
11732              break;
11733            }
11734          if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11735              (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11736              (y < (int) (roi_info.y+RoiDelta)) &&
11737              (y > (int) (roi_info.y-RoiDelta)))
11738            {
11739              roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11740              state|=UpdateConfigurationState;
11741              break;
11742            }
11743          if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11744              (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11745              (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11746              (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11747            {
11748              state|=UpdateConfigurationState;
11749              break;
11750            }
11751        }
11752        case ButtonRelease:
11753        {
11754          if (event.xbutton.window == windows->pan.id)
11755            if ((highlight_info.x != crop_info.x-windows->image.x) ||
11756                (highlight_info.y != crop_info.y-windows->image.y))
11757              XHighlightRectangle(display,windows->image.id,
11758                windows->image.highlight_context,&highlight_info);
11759          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11760            event.xbutton.time);
11761          break;
11762        }
11763        case Expose:
11764        {
11765          if (event.xexpose.window == windows->image.id)
11766            if (event.xexpose.count == 0)
11767              {
11768                event.xexpose.x=(int) highlight_info.x;
11769                event.xexpose.y=(int) highlight_info.y;
11770                event.xexpose.width=(int) highlight_info.width;
11771                event.xexpose.height=(int) highlight_info.height;
11772                XRefreshWindow(display,&windows->image,&event);
11773              }
11774          if (event.xexpose.window == windows->info.id)
11775            if (event.xexpose.count == 0)
11776              XInfoWidget(display,windows,text);
11777          break;
11778        }
11779        case KeyPress:
11780        {
11781          KeySym
11782            key_symbol;
11783
11784          if (event.xkey.window != windows->image.id)
11785            break;
11786          /*
11787            Respond to a user key press.
11788          */
11789          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11790            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11791          switch ((int) key_symbol)
11792          {
11793            case XK_Shift_L:
11794            case XK_Shift_R:
11795              break;
11796            case XK_Escape:
11797            case XK_F20:
11798              state|=EscapeState;
11799            case XK_Return:
11800            {
11801              state|=ExitState;
11802              break;
11803            }
11804            case XK_Home:
11805            case XK_KP_Home:
11806            {
11807              roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11808              roi_info.y=(ssize_t) (windows->image.height/2L-
11809                roi_info.height/2L);
11810              break;
11811            }
11812            case XK_Left:
11813            case XK_KP_Left:
11814            {
11815              roi_info.x--;
11816              break;
11817            }
11818            case XK_Up:
11819            case XK_KP_Up:
11820            case XK_Next:
11821            {
11822              roi_info.y--;
11823              break;
11824            }
11825            case XK_Right:
11826            case XK_KP_Right:
11827            {
11828              roi_info.x++;
11829              break;
11830            }
11831            case XK_Prior:
11832            case XK_Down:
11833            case XK_KP_Down:
11834            {
11835              roi_info.y++;
11836              break;
11837            }
11838            case XK_F1:
11839            case XK_Help:
11840            {
11841              (void) XSetFunction(display,windows->image.highlight_context,
11842                GXcopy);
11843              XTextViewWidget(display,resource_info,windows,MagickFalse,
11844                "Help Viewer - Region of Interest",ImageROIHelp);
11845              (void) XSetFunction(display,windows->image.highlight_context,
11846                GXinvert);
11847              break;
11848            }
11849            default:
11850            {
11851              command_type=XImageWindowCommand(display,resource_info,windows,
11852                event.xkey.state,key_symbol,image,exception);
11853              if (command_type != NullCommand)
11854                state|=UpdateRegionState;
11855              break;
11856            }
11857          }
11858          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11859            event.xkey.time);
11860          break;
11861        }
11862        case KeyRelease:
11863          break;
11864        case MotionNotify:
11865        {
11866          if (event.xbutton.window != windows->image.id)
11867            break;
11868          /*
11869            Map and unmap Info widget as text cursor crosses its boundaries.
11870          */
11871          x=event.xmotion.x;
11872          y=event.xmotion.y;
11873          if (windows->info.mapped != MagickFalse)
11874            {
11875              if ((x < (int) (windows->info.x+windows->info.width)) &&
11876                  (y < (int) (windows->info.y+windows->info.height)))
11877                (void) XWithdrawWindow(display,windows->info.id,
11878                  windows->info.screen);
11879            }
11880          else
11881            if ((x > (int) (windows->info.x+windows->info.width)) ||
11882                (y > (int) (windows->info.y+windows->info.height)))
11883              (void) XMapWindow(display,windows->info.id);
11884          roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11885          roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11886          break;
11887        }
11888        case SelectionRequest:
11889        {
11890          XSelectionEvent
11891            notify;
11892
11893          XSelectionRequestEvent
11894            *request;
11895
11896          /*
11897            Set primary selection.
11898          */
11899          (void) FormatLocaleString(text,MaxTextExtent,
11900            "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11901            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11902          request=(&(event.xselectionrequest));
11903          (void) XChangeProperty(request->display,request->requestor,
11904            request->property,request->target,8,PropModeReplace,
11905            (unsigned char *) text,(int) strlen(text));
11906          notify.type=SelectionNotify;
11907          notify.display=request->display;
11908          notify.requestor=request->requestor;
11909          notify.selection=request->selection;
11910          notify.target=request->target;
11911          notify.time=request->time;
11912          if (request->property == None)
11913            notify.property=request->target;
11914          else
11915            notify.property=request->property;
11916          (void) XSendEvent(request->display,request->requestor,False,0,
11917            (XEvent *) &notify);
11918        }
11919        default:
11920          break;
11921      }
11922      if ((state & UpdateConfigurationState) != 0)
11923        {
11924          (void) XPutBackEvent(display,&event);
11925          (void) XCheckDefineCursor(display,windows->image.id,cursor);
11926          break;
11927        }
11928    } while ((state & ExitState) == 0);
11929  } while ((state & ExitState) == 0);
11930  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11931  XSetCursorState(display,windows,MagickFalse);
11932  if ((state & EscapeState) != 0)
11933    return(MagickTrue);
11934  return(MagickTrue);
11935}
11936
11937/*
11938%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11939%                                                                             %
11940%                                                                             %
11941%                                                                             %
11942+   X R o t a t e I m a g e                                                   %
11943%                                                                             %
11944%                                                                             %
11945%                                                                             %
11946%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11947%
11948%  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
11949%  rotation angle is computed from the slope of a line drawn by the user.
11950%
11951%  The format of the XRotateImage method is:
11952%
11953%      MagickBooleanType XRotateImage(Display *display,
11954%        XResourceInfo *resource_info,XWindows *windows,double degrees,
11955%        Image **image,ExceptionInfo *exception)
11956%
11957%  A description of each parameter follows:
11958%
11959%    o display: Specifies a connection to an X server; returned from
11960%      XOpenDisplay.
11961%
11962%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11963%
11964%    o windows: Specifies a pointer to a XWindows structure.
11965%
11966%    o degrees: Specifies the number of degrees to rotate the image.
11967%
11968%    o image: the image.
11969%
11970%    o exception: return any errors or warnings in this structure.
11971%
11972*/
11973static MagickBooleanType XRotateImage(Display *display,
11974  XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
11975  ExceptionInfo *exception)
11976{
11977  static const char
11978    *RotateMenu[] =
11979    {
11980      "Pixel Color",
11981      "Direction",
11982      "Help",
11983      "Dismiss",
11984      (char *) NULL
11985    };
11986
11987  static ModeType
11988    direction = HorizontalRotateCommand;
11989
11990  static const ModeType
11991    DirectionCommands[] =
11992    {
11993      HorizontalRotateCommand,
11994      VerticalRotateCommand
11995    },
11996    RotateCommands[] =
11997    {
11998      RotateColorCommand,
11999      RotateDirectionCommand,
12000      RotateHelpCommand,
12001      RotateDismissCommand
12002    };
12003
12004  static unsigned int
12005    pen_id = 0;
12006
12007  char
12008    command[MaxTextExtent],
12009    text[MaxTextExtent];
12010
12011  Image
12012    *rotate_image;
12013
12014  int
12015    id,
12016    x,
12017    y;
12018
12019  MagickRealType
12020    normalized_degrees;
12021
12022  register int
12023    i;
12024
12025  unsigned int
12026    height,
12027    rotations,
12028    width;
12029
12030  if (degrees == 0.0)
12031    {
12032      unsigned int
12033        distance;
12034
12035      size_t
12036        state;
12037
12038      XEvent
12039        event;
12040
12041      XSegment
12042        rotate_info;
12043
12044      /*
12045        Map Command widget.
12046      */
12047      (void) CloneString(&windows->command.name,"Rotate");
12048      windows->command.data=2;
12049      (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12050      (void) XMapRaised(display,windows->command.id);
12051      XClientMessage(display,windows->image.id,windows->im_protocols,
12052        windows->im_update_widget,CurrentTime);
12053      /*
12054        Wait for first button press.
12055      */
12056      (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12057      XQueryPosition(display,windows->image.id,&x,&y);
12058      rotate_info.x1=x;
12059      rotate_info.y1=y;
12060      rotate_info.x2=x;
12061      rotate_info.y2=y;
12062      state=DefaultState;
12063      do
12064      {
12065        XHighlightLine(display,windows->image.id,
12066          windows->image.highlight_context,&rotate_info);
12067        /*
12068          Wait for next event.
12069        */
12070        XScreenEvent(display,windows,&event);
12071        XHighlightLine(display,windows->image.id,
12072          windows->image.highlight_context,&rotate_info);
12073        if (event.xany.window == windows->command.id)
12074          {
12075            /*
12076              Select a command from the Command widget.
12077            */
12078            id=XCommandWidget(display,windows,RotateMenu,&event);
12079            if (id < 0)
12080              continue;
12081            (void) XSetFunction(display,windows->image.highlight_context,
12082              GXcopy);
12083            switch (RotateCommands[id])
12084            {
12085              case RotateColorCommand:
12086              {
12087                const char
12088                  *ColorMenu[MaxNumberPens];
12089
12090                int
12091                  pen_number;
12092
12093                XColor
12094                  color;
12095
12096                /*
12097                  Initialize menu selections.
12098                */
12099                for (i=0; i < (int) (MaxNumberPens-2); i++)
12100                  ColorMenu[i]=resource_info->pen_colors[i];
12101                ColorMenu[MaxNumberPens-2]="Browser...";
12102                ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12103                /*
12104                  Select a pen color from the pop-up menu.
12105                */
12106                pen_number=XMenuWidget(display,windows,RotateMenu[id],
12107                  (const char **) ColorMenu,command);
12108                if (pen_number < 0)
12109                  break;
12110                if (pen_number == (MaxNumberPens-2))
12111                  {
12112                    static char
12113                      color_name[MaxTextExtent] = "gray";
12114
12115                    /*
12116                      Select a pen color from a dialog.
12117                    */
12118                    resource_info->pen_colors[pen_number]=color_name;
12119                    XColorBrowserWidget(display,windows,"Select",color_name);
12120                    if (*color_name == '\0')
12121                      break;
12122                  }
12123                /*
12124                  Set pen color.
12125                */
12126                (void) XParseColor(display,windows->map_info->colormap,
12127                  resource_info->pen_colors[pen_number],&color);
12128                XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12129                  (unsigned int) MaxColors,&color);
12130                windows->pixel_info->pen_colors[pen_number]=color;
12131                pen_id=(unsigned int) pen_number;
12132                break;
12133              }
12134              case RotateDirectionCommand:
12135              {
12136                static const char
12137                  *Directions[] =
12138                  {
12139                    "horizontal",
12140                    "vertical",
12141                    (char *) NULL,
12142                  };
12143
12144                /*
12145                  Select a command from the pop-up menu.
12146                */
12147                id=XMenuWidget(display,windows,RotateMenu[id],
12148                  Directions,command);
12149                if (id >= 0)
12150                  direction=DirectionCommands[id];
12151                break;
12152              }
12153              case RotateHelpCommand:
12154              {
12155                XTextViewWidget(display,resource_info,windows,MagickFalse,
12156                  "Help Viewer - Image Rotation",ImageRotateHelp);
12157                break;
12158              }
12159              case RotateDismissCommand:
12160              {
12161                /*
12162                  Prematurely exit.
12163                */
12164                state|=EscapeState;
12165                state|=ExitState;
12166                break;
12167              }
12168              default:
12169                break;
12170            }
12171            (void) XSetFunction(display,windows->image.highlight_context,
12172              GXinvert);
12173            continue;
12174          }
12175        switch (event.type)
12176        {
12177          case ButtonPress:
12178          {
12179            if (event.xbutton.button != Button1)
12180              break;
12181            if (event.xbutton.window != windows->image.id)
12182              break;
12183            /*
12184              exit loop.
12185            */
12186            (void) XSetFunction(display,windows->image.highlight_context,
12187              GXcopy);
12188            rotate_info.x1=event.xbutton.x;
12189            rotate_info.y1=event.xbutton.y;
12190            state|=ExitState;
12191            break;
12192          }
12193          case ButtonRelease:
12194            break;
12195          case Expose:
12196            break;
12197          case KeyPress:
12198          {
12199            char
12200              command[MaxTextExtent];
12201
12202            KeySym
12203              key_symbol;
12204
12205            if (event.xkey.window != windows->image.id)
12206              break;
12207            /*
12208              Respond to a user key press.
12209            */
12210            (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12211              sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12212            switch ((int) key_symbol)
12213            {
12214              case XK_Escape:
12215              case XK_F20:
12216              {
12217                /*
12218                  Prematurely exit.
12219                */
12220                state|=EscapeState;
12221                state|=ExitState;
12222                break;
12223              }
12224              case XK_F1:
12225              case XK_Help:
12226              {
12227                (void) XSetFunction(display,windows->image.highlight_context,
12228                  GXcopy);
12229                XTextViewWidget(display,resource_info,windows,MagickFalse,
12230                  "Help Viewer - Image Rotation",ImageRotateHelp);
12231                (void) XSetFunction(display,windows->image.highlight_context,
12232                  GXinvert);
12233                break;
12234              }
12235              default:
12236              {
12237                (void) XBell(display,0);
12238                break;
12239              }
12240            }
12241            break;
12242          }
12243          case MotionNotify:
12244          {
12245            rotate_info.x1=event.xmotion.x;
12246            rotate_info.y1=event.xmotion.y;
12247          }
12248        }
12249        rotate_info.x2=rotate_info.x1;
12250        rotate_info.y2=rotate_info.y1;
12251        if (direction == HorizontalRotateCommand)
12252          rotate_info.x2+=32;
12253        else
12254          rotate_info.y2-=32;
12255      } while ((state & ExitState) == 0);
12256      (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12257      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12258      if ((state & EscapeState) != 0)
12259        return(MagickTrue);
12260      /*
12261        Draw line as pointer moves until the mouse button is released.
12262      */
12263      distance=0;
12264      (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12265      state=DefaultState;
12266      do
12267      {
12268        if (distance > 9)
12269          {
12270            /*
12271              Display info and draw rotation line.
12272            */
12273            if (windows->info.mapped == MagickFalse)
12274              (void) XMapWindow(display,windows->info.id);
12275            (void) FormatLocaleString(text,MaxTextExtent," %g",
12276              direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12277            XInfoWidget(display,windows,text);
12278            XHighlightLine(display,windows->image.id,
12279              windows->image.highlight_context,&rotate_info);
12280          }
12281        else
12282          if (windows->info.mapped != MagickFalse)
12283            (void) XWithdrawWindow(display,windows->info.id,
12284              windows->info.screen);
12285        /*
12286          Wait for next event.
12287        */
12288        XScreenEvent(display,windows,&event);
12289        if (distance > 9)
12290          XHighlightLine(display,windows->image.id,
12291            windows->image.highlight_context,&rotate_info);
12292        switch (event.type)
12293        {
12294          case ButtonPress:
12295            break;
12296          case ButtonRelease:
12297          {
12298            /*
12299              User has committed to rotation line.
12300            */
12301            rotate_info.x2=event.xbutton.x;
12302            rotate_info.y2=event.xbutton.y;
12303            state|=ExitState;
12304            break;
12305          }
12306          case Expose:
12307            break;
12308          case MotionNotify:
12309          {
12310            rotate_info.x2=event.xmotion.x;
12311            rotate_info.y2=event.xmotion.y;
12312          }
12313          default:
12314            break;
12315        }
12316        /*
12317          Check boundary conditions.
12318        */
12319        if (rotate_info.x2 < 0)
12320          rotate_info.x2=0;
12321        else
12322          if (rotate_info.x2 > (int) windows->image.width)
12323            rotate_info.x2=(short) windows->image.width;
12324        if (rotate_info.y2 < 0)
12325          rotate_info.y2=0;
12326        else
12327          if (rotate_info.y2 > (int) windows->image.height)
12328            rotate_info.y2=(short) windows->image.height;
12329        /*
12330          Compute rotation angle from the slope of the line.
12331        */
12332        degrees=0.0;
12333        distance=(unsigned int)
12334          ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12335          ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12336        if (distance > 9)
12337          degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12338            rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12339      } while ((state & ExitState) == 0);
12340      (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12341      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12342      if (distance <= 9)
12343        return(MagickTrue);
12344    }
12345  if (direction == VerticalRotateCommand)
12346    degrees-=90.0;
12347  if (degrees == 0.0)
12348    return(MagickTrue);
12349  /*
12350    Rotate image.
12351  */
12352  normalized_degrees=degrees;
12353  while (normalized_degrees < -45.0)
12354    normalized_degrees+=360.0;
12355  for (rotations=0; normalized_degrees > 45.0; rotations++)
12356    normalized_degrees-=90.0;
12357  if (normalized_degrees != 0.0)
12358    (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12359      exception);
12360  XSetCursorState(display,windows,MagickTrue);
12361  XCheckRefreshWindows(display,windows);
12362  (*image)->background_color.red=ScaleShortToQuantum(
12363    windows->pixel_info->pen_colors[pen_id].red);
12364  (*image)->background_color.green=ScaleShortToQuantum(
12365    windows->pixel_info->pen_colors[pen_id].green);
12366  (*image)->background_color.blue=ScaleShortToQuantum(
12367    windows->pixel_info->pen_colors[pen_id].blue);
12368  rotate_image=RotateImage(*image,degrees,exception);
12369  XSetCursorState(display,windows,MagickFalse);
12370  if (rotate_image == (Image *) NULL)
12371    return(MagickFalse);
12372  *image=DestroyImage(*image);
12373  *image=rotate_image;
12374  if (windows->image.crop_geometry != (char *) NULL)
12375    {
12376      /*
12377        Rotate crop geometry.
12378      */
12379      width=(unsigned int) (*image)->columns;
12380      height=(unsigned int) (*image)->rows;
12381      (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12382      switch (rotations % 4)
12383      {
12384        default:
12385        case 0:
12386          break;
12387        case 1:
12388        {
12389          /*
12390            Rotate 90 degrees.
12391          */
12392          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12393            "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12394            (int) height-y,x);
12395          break;
12396        }
12397        case 2:
12398        {
12399          /*
12400            Rotate 180 degrees.
12401          */
12402          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12403            "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12404          break;
12405        }
12406        case 3:
12407        {
12408          /*
12409            Rotate 270 degrees.
12410          */
12411          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12412            "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12413          break;
12414        }
12415      }
12416    }
12417  if (windows->image.orphan != MagickFalse)
12418    return(MagickTrue);
12419  if (normalized_degrees != 0.0)
12420    {
12421      /*
12422        Update image colormap.
12423      */
12424      windows->image.window_changes.width=(int) (*image)->columns;
12425      windows->image.window_changes.height=(int) (*image)->rows;
12426      if (windows->image.crop_geometry != (char *) NULL)
12427        {
12428          /*
12429            Obtain dimensions of image from crop geometry.
12430          */
12431          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12432            &width,&height);
12433          windows->image.window_changes.width=(int) width;
12434          windows->image.window_changes.height=(int) height;
12435        }
12436      XConfigureImageColormap(display,resource_info,windows,*image);
12437    }
12438  else
12439    if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12440      {
12441        windows->image.window_changes.width=windows->image.ximage->height;
12442        windows->image.window_changes.height=windows->image.ximage->width;
12443      }
12444  /*
12445    Update image configuration.
12446  */
12447  (void) XConfigureImage(display,resource_info,windows,*image,exception);
12448  return(MagickTrue);
12449}
12450
12451/*
12452%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12453%                                                                             %
12454%                                                                             %
12455%                                                                             %
12456+   X S a v e I m a g e                                                       %
12457%                                                                             %
12458%                                                                             %
12459%                                                                             %
12460%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12461%
12462%  XSaveImage() saves an image to a file.
12463%
12464%  The format of the XSaveImage method is:
12465%
12466%      MagickBooleanType XSaveImage(Display *display,
12467%        XResourceInfo *resource_info,XWindows *windows,Image *image,
12468%        ExceptionInfo *exception)
12469%
12470%  A description of each parameter follows:
12471%
12472%    o display: Specifies a connection to an X server; returned from
12473%      XOpenDisplay.
12474%
12475%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12476%
12477%    o windows: Specifies a pointer to a XWindows structure.
12478%
12479%    o image: the image.
12480%
12481%    o exception: return any errors or warnings in this structure.
12482%
12483*/
12484static MagickBooleanType XSaveImage(Display *display,
12485  XResourceInfo *resource_info,XWindows *windows,Image *image,
12486  ExceptionInfo *exception)
12487{
12488  char
12489    filename[MaxTextExtent],
12490    geometry[MaxTextExtent];
12491
12492  Image
12493    *save_image;
12494
12495  ImageInfo
12496    *image_info;
12497
12498  MagickStatusType
12499    status;
12500
12501  /*
12502    Request file name from user.
12503  */
12504  if (resource_info->write_filename != (char *) NULL)
12505    (void) CopyMagickString(filename,resource_info->write_filename,
12506      MaxTextExtent);
12507  else
12508    {
12509      char
12510        path[MaxTextExtent];
12511
12512      int
12513        status;
12514
12515      GetPathComponent(image->filename,HeadPath,path);
12516      GetPathComponent(image->filename,TailPath,filename);
12517      if (*path != '\0')
12518        {
12519          status=chdir(path);
12520          if (status == -1)
12521            (void) ThrowMagickException(exception,GetMagickModule(),
12522              FileOpenError,"UnableToOpenFile","%s",path);
12523        }
12524    }
12525  XFileBrowserWidget(display,windows,"Save",filename);
12526  if (*filename == '\0')
12527    return(MagickTrue);
12528  if (IsPathAccessible(filename) != MagickFalse)
12529    {
12530      int
12531        status;
12532
12533      /*
12534        File exists-- seek user's permission before overwriting.
12535      */
12536      status=XConfirmWidget(display,windows,"Overwrite",filename);
12537      if (status <= 0)
12538        return(MagickTrue);
12539    }
12540  image_info=CloneImageInfo(resource_info->image_info);
12541  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12542  (void) SetImageInfo(image_info,1,exception);
12543  if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12544      (LocaleCompare(image_info->magick,"JPG") == 0))
12545    {
12546      char
12547        quality[MaxTextExtent];
12548
12549      int
12550        status;
12551
12552      /*
12553        Request JPEG quality from user.
12554      */
12555      (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
12556        image->quality);
12557      status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12558        quality);
12559      if (*quality == '\0')
12560        return(MagickTrue);
12561      image->quality=StringToUnsignedLong(quality);
12562      image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12563    }
12564  if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12565      (LocaleCompare(image_info->magick,"PDF") == 0) ||
12566      (LocaleCompare(image_info->magick,"PS") == 0) ||
12567      (LocaleCompare(image_info->magick,"PS2") == 0))
12568    {
12569      char
12570        geometry[MaxTextExtent];
12571
12572      /*
12573        Request page geometry from user.
12574      */
12575      (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12576      if (LocaleCompare(image_info->magick,"PDF") == 0)
12577        (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12578      if (image_info->page != (char *) NULL)
12579        (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12580      XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12581        "Select page geometry:",geometry);
12582      if (*geometry != '\0')
12583        image_info->page=GetPageGeometry(geometry);
12584    }
12585  /*
12586    Apply image transforms.
12587  */
12588  XSetCursorState(display,windows,MagickTrue);
12589  XCheckRefreshWindows(display,windows);
12590  save_image=CloneImage(image,0,0,MagickTrue,exception);
12591  if (save_image == (Image *) NULL)
12592    return(MagickFalse);
12593  (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
12594    windows->image.ximage->width,windows->image.ximage->height);
12595  (void) TransformImage(&save_image,windows->image.crop_geometry,geometry);
12596  /*
12597    Write image.
12598  */
12599  (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12600  status=WriteImage(image_info,save_image,exception);
12601  if (status != MagickFalse)
12602    image->taint=MagickFalse;
12603  save_image=DestroyImage(save_image);
12604  image_info=DestroyImageInfo(image_info);
12605  XSetCursorState(display,windows,MagickFalse);
12606  return(status != 0 ? MagickTrue : MagickFalse);
12607}
12608
12609/*
12610%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12611%                                                                             %
12612%                                                                             %
12613%                                                                             %
12614+   X S c r e e n E v e n t                                                   %
12615%                                                                             %
12616%                                                                             %
12617%                                                                             %
12618%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12619%
12620%  XScreenEvent() handles global events associated with the Pan and Magnify
12621%  windows.
12622%
12623%  The format of the XScreenEvent function is:
12624%
12625%      void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12626%
12627%  A description of each parameter follows:
12628%
12629%    o display: Specifies a pointer to the Display structure;  returned from
12630%      XOpenDisplay.
12631%
12632%    o windows: Specifies a pointer to a XWindows structure.
12633%
12634%    o event: Specifies a pointer to a X11 XEvent structure.
12635%
12636%
12637*/
12638
12639#if defined(__cplusplus) || defined(c_plusplus)
12640extern "C" {
12641#endif
12642
12643static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12644{
12645  register XWindows
12646    *windows;
12647
12648  windows=(XWindows *) data;
12649  if ((event->type == ClientMessage) &&
12650      (event->xclient.window == windows->image.id))
12651    return(MagickFalse);
12652  return(MagickTrue);
12653}
12654
12655#if defined(__cplusplus) || defined(c_plusplus)
12656}
12657#endif
12658
12659static void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12660{
12661  register int
12662    x,
12663    y;
12664
12665  (void) XIfEvent(display,event,XPredicate,(char *) windows);
12666  if (event->xany.window == windows->command.id)
12667    return;
12668  switch (event->type)
12669  {
12670    case ButtonPress:
12671    case ButtonRelease:
12672    {
12673      if ((event->xbutton.button == Button3) &&
12674          (event->xbutton.state & Mod1Mask))
12675        {
12676          /*
12677            Convert Alt-Button3 to Button2.
12678          */
12679          event->xbutton.button=Button2;
12680          event->xbutton.state&=(~Mod1Mask);
12681        }
12682      if (event->xbutton.window == windows->backdrop.id)
12683        {
12684          (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12685            event->xbutton.time);
12686          break;
12687        }
12688      if (event->xbutton.window == windows->pan.id)
12689        {
12690          XPanImage(display,windows,event);
12691          break;
12692        }
12693      if (event->xbutton.window == windows->image.id)
12694        if (event->xbutton.button == Button2)
12695          {
12696            /*
12697              Update magnified image.
12698            */
12699            x=event->xbutton.x;
12700            y=event->xbutton.y;
12701            if (x < 0)
12702              x=0;
12703            else
12704              if (x >= (int) windows->image.width)
12705                x=(int) (windows->image.width-1);
12706            windows->magnify.x=(int) windows->image.x+x;
12707            if (y < 0)
12708              y=0;
12709            else
12710             if (y >= (int) windows->image.height)
12711               y=(int) (windows->image.height-1);
12712            windows->magnify.y=windows->image.y+y;
12713            if (windows->magnify.mapped == MagickFalse)
12714              (void) XMapRaised(display,windows->magnify.id);
12715            XMakeMagnifyImage(display,windows);
12716            if (event->type == ButtonRelease)
12717              (void) XWithdrawWindow(display,windows->info.id,
12718                windows->info.screen);
12719            break;
12720          }
12721      break;
12722    }
12723    case ClientMessage:
12724    {
12725      /*
12726        If client window delete message, exit.
12727      */
12728      if (event->xclient.message_type != windows->wm_protocols)
12729        break;
12730      if (*event->xclient.data.l != (long) windows->wm_delete_window)
12731        break;
12732      if (event->xclient.window == windows->magnify.id)
12733        {
12734          (void) XWithdrawWindow(display,windows->magnify.id,
12735            windows->magnify.screen);
12736          break;
12737        }
12738      break;
12739    }
12740    case ConfigureNotify:
12741    {
12742      if (event->xconfigure.window == windows->magnify.id)
12743        {
12744          unsigned int
12745            magnify;
12746
12747          /*
12748            Magnify window has a new configuration.
12749          */
12750          windows->magnify.width=(unsigned int) event->xconfigure.width;
12751          windows->magnify.height=(unsigned int) event->xconfigure.height;
12752          if (windows->magnify.mapped == MagickFalse)
12753            break;
12754          magnify=1;
12755          while ((int) magnify <= event->xconfigure.width)
12756            magnify<<=1;
12757          while ((int) magnify <= event->xconfigure.height)
12758            magnify<<=1;
12759          magnify>>=1;
12760          if (((int) magnify != event->xconfigure.width) ||
12761              ((int) magnify != event->xconfigure.height))
12762            {
12763              XWindowChanges
12764                window_changes;
12765
12766              window_changes.width=(int) magnify;
12767              window_changes.height=(int) magnify;
12768              (void) XReconfigureWMWindow(display,windows->magnify.id,
12769                windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12770                &window_changes);
12771              break;
12772            }
12773          XMakeMagnifyImage(display,windows);
12774          break;
12775        }
12776      break;
12777    }
12778    case Expose:
12779    {
12780      if (event->xexpose.window == windows->image.id)
12781        {
12782          XRefreshWindow(display,&windows->image,event);
12783          break;
12784        }
12785      if (event->xexpose.window == windows->pan.id)
12786        if (event->xexpose.count == 0)
12787          {
12788            XDrawPanRectangle(display,windows);
12789            break;
12790          }
12791      if (event->xexpose.window == windows->magnify.id)
12792        if (event->xexpose.count == 0)
12793          {
12794            XMakeMagnifyImage(display,windows);
12795            break;
12796          }
12797      break;
12798    }
12799    case KeyPress:
12800    {
12801      char
12802        command[MaxTextExtent];
12803
12804      KeySym
12805        key_symbol;
12806
12807      if (event->xkey.window != windows->magnify.id)
12808        break;
12809      /*
12810        Respond to a user key press.
12811      */
12812      (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12813        sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12814      XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol);
12815      break;
12816    }
12817    case MapNotify:
12818    {
12819      if (event->xmap.window == windows->magnify.id)
12820        {
12821          windows->magnify.mapped=MagickTrue;
12822          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12823          break;
12824        }
12825      if (event->xmap.window == windows->info.id)
12826        {
12827          windows->info.mapped=MagickTrue;
12828          break;
12829        }
12830      break;
12831    }
12832    case MotionNotify:
12833    {
12834      while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12835      if (event->xmotion.window == windows->image.id)
12836        if (windows->magnify.mapped != MagickFalse)
12837          {
12838            /*
12839              Update magnified image.
12840            */
12841            x=event->xmotion.x;
12842            y=event->xmotion.y;
12843            if (x < 0)
12844              x=0;
12845            else
12846              if (x >= (int) windows->image.width)
12847                x=(int) (windows->image.width-1);
12848            windows->magnify.x=(int) windows->image.x+x;
12849            if (y < 0)
12850              y=0;
12851            else
12852             if (y >= (int) windows->image.height)
12853               y=(int) (windows->image.height-1);
12854            windows->magnify.y=windows->image.y+y;
12855            XMakeMagnifyImage(display,windows);
12856          }
12857      break;
12858    }
12859    case UnmapNotify:
12860    {
12861      if (event->xunmap.window == windows->magnify.id)
12862        {
12863          windows->magnify.mapped=MagickFalse;
12864          break;
12865        }
12866      if (event->xunmap.window == windows->info.id)
12867        {
12868          windows->info.mapped=MagickFalse;
12869          break;
12870        }
12871      break;
12872    }
12873    default:
12874      break;
12875  }
12876}
12877
12878/*
12879%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12880%                                                                             %
12881%                                                                             %
12882%                                                                             %
12883+   X S e t C r o p G e o m e t r y                                           %
12884%                                                                             %
12885%                                                                             %
12886%                                                                             %
12887%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12888%
12889%  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12890%  and translates it to a cropping geometry relative to the image.
12891%
12892%  The format of the XSetCropGeometry method is:
12893%
12894%      void XSetCropGeometry(Display *display,XWindows *windows,
12895%        RectangleInfo *crop_info,Image *image)
12896%
12897%  A description of each parameter follows:
12898%
12899%    o display: Specifies a connection to an X server; returned from
12900%      XOpenDisplay.
12901%
12902%    o windows: Specifies a pointer to a XWindows structure.
12903%
12904%    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12905%      Image window to crop.
12906%
12907%    o image: the image.
12908%
12909*/
12910static void XSetCropGeometry(Display *display,XWindows *windows,
12911  RectangleInfo *crop_info,Image *image)
12912{
12913  char
12914    text[MaxTextExtent];
12915
12916  int
12917    x,
12918    y;
12919
12920  MagickRealType
12921    scale_factor;
12922
12923  unsigned int
12924    height,
12925    width;
12926
12927  if (windows->info.mapped != MagickFalse)
12928    {
12929      /*
12930        Display info on cropping rectangle.
12931      */
12932      (void) FormatLocaleString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
12933        (double) crop_info->width,(double) crop_info->height,(double)
12934        crop_info->x,(double) crop_info->y);
12935      XInfoWidget(display,windows,text);
12936    }
12937  /*
12938    Cropping geometry is relative to any previous crop geometry.
12939  */
12940  x=0;
12941  y=0;
12942  width=(unsigned int) image->columns;
12943  height=(unsigned int) image->rows;
12944  if (windows->image.crop_geometry != (char *) NULL)
12945    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12946  else
12947    windows->image.crop_geometry=AcquireString((char *) NULL);
12948  /*
12949    Define the crop geometry string from the cropping rectangle.
12950  */
12951  scale_factor=(MagickRealType) width/windows->image.ximage->width;
12952  if (crop_info->x > 0)
12953    x+=(int) (scale_factor*crop_info->x+0.5);
12954  width=(unsigned int) (scale_factor*crop_info->width+0.5);
12955  if (width == 0)
12956    width=1;
12957  scale_factor=(MagickRealType) height/windows->image.ximage->height;
12958  if (crop_info->y > 0)
12959    y+=(int) (scale_factor*crop_info->y+0.5);
12960  height=(unsigned int) (scale_factor*crop_info->height+0.5);
12961  if (height == 0)
12962    height=1;
12963  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12964    "%ux%u%+d%+d",width,height,x,y);
12965}
12966
12967/*
12968%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12969%                                                                             %
12970%                                                                             %
12971%                                                                             %
12972+   X T i l e I m a g e                                                       %
12973%                                                                             %
12974%                                                                             %
12975%                                                                             %
12976%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12977%
12978%  XTileImage() loads or deletes a selected tile from a visual image directory.
12979%  The load or delete command is chosen from a menu.
12980%
12981%  The format of the XTileImage method is:
12982%
12983%      Image *XTileImage(Display *display,XResourceInfo *resource_info,
12984%        XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
12985%
12986%  A description of each parameter follows:
12987%
12988%    o tile_image:  XTileImage reads or deletes the tile image
12989%      and returns it.  A null image is returned if an error occurs.
12990%
12991%    o display: Specifies a connection to an X server;  returned from
12992%      XOpenDisplay.
12993%
12994%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12995%
12996%    o windows: Specifies a pointer to a XWindows structure.
12997%
12998%    o image: the image; returned from ReadImage.
12999%
13000%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
13001%      the entire image is refreshed.
13002%
13003%    o exception: return any errors or warnings in this structure.
13004%
13005*/
13006static Image *XTileImage(Display *display,XResourceInfo *resource_info,
13007  XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13008{
13009  static const char
13010    *VerbMenu[] =
13011    {
13012      "Load",
13013      "Next",
13014      "Former",
13015      "Delete",
13016      "Update",
13017      (char *) NULL,
13018    };
13019
13020  static const ModeType
13021    TileCommands[] =
13022    {
13023      TileLoadCommand,
13024      TileNextCommand,
13025      TileFormerCommand,
13026      TileDeleteCommand,
13027      TileUpdateCommand
13028    };
13029
13030  char
13031    command[MaxTextExtent],
13032    filename[MaxTextExtent];
13033
13034  Image
13035    *tile_image;
13036
13037  int
13038    id,
13039    status,
13040    tile,
13041    x,
13042    y;
13043
13044  MagickRealType
13045    scale_factor;
13046
13047  register char
13048    *p,
13049    *q;
13050
13051  register int
13052    i;
13053
13054  unsigned int
13055    height,
13056    width;
13057
13058  /*
13059    Tile image is relative to montage image configuration.
13060  */
13061  x=0;
13062  y=0;
13063  width=(unsigned int) image->columns;
13064  height=(unsigned int) image->rows;
13065  if (windows->image.crop_geometry != (char *) NULL)
13066    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13067  scale_factor=(MagickRealType) width/windows->image.ximage->width;
13068  event->xbutton.x+=windows->image.x;
13069  event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13070  scale_factor=(MagickRealType) height/windows->image.ximage->height;
13071  event->xbutton.y+=windows->image.y;
13072  event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13073  /*
13074    Determine size and location of each tile in the visual image directory.
13075  */
13076  width=(unsigned int) image->columns;
13077  height=(unsigned int) image->rows;
13078  x=0;
13079  y=0;
13080  (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13081  tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13082    (event->xbutton.x-x)/width;
13083  if (tile < 0)
13084    {
13085      /*
13086        Button press is outside any tile.
13087      */
13088      (void) XBell(display,0);
13089      return((Image *) NULL);
13090    }
13091  /*
13092    Determine file name from the tile directory.
13093  */
13094  p=image->directory;
13095  for (i=tile; (i != 0) && (*p != '\0'); )
13096  {
13097    if (*p == '\n')
13098      i--;
13099    p++;
13100  }
13101  if (*p == '\0')
13102    {
13103      /*
13104        Button press is outside any tile.
13105      */
13106      (void) XBell(display,0);
13107      return((Image *) NULL);
13108    }
13109  /*
13110    Select a command from the pop-up menu.
13111  */
13112  id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13113  if (id < 0)
13114    return((Image *) NULL);
13115  q=p;
13116  while ((*q != '\n') && (*q != '\0'))
13117    q++;
13118  (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13119  /*
13120    Perform command for the selected tile.
13121  */
13122  XSetCursorState(display,windows,MagickTrue);
13123  XCheckRefreshWindows(display,windows);
13124  tile_image=NewImageList();
13125  switch (TileCommands[id])
13126  {
13127    case TileLoadCommand:
13128    {
13129      /*
13130        Load tile image.
13131      */
13132      XCheckRefreshWindows(display,windows);
13133      (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13134        MaxTextExtent);
13135      (void) CopyMagickString(resource_info->image_info->filename,filename,
13136        MaxTextExtent);
13137      tile_image=ReadImage(resource_info->image_info,exception);
13138      CatchException(exception);
13139      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13140      break;
13141    }
13142    case TileNextCommand:
13143    {
13144      /*
13145        Display next image.
13146      */
13147      XClientMessage(display,windows->image.id,windows->im_protocols,
13148        windows->im_next_image,CurrentTime);
13149      break;
13150    }
13151    case TileFormerCommand:
13152    {
13153      /*
13154        Display former image.
13155      */
13156      XClientMessage(display,windows->image.id,windows->im_protocols,
13157        windows->im_former_image,CurrentTime);
13158      break;
13159    }
13160    case TileDeleteCommand:
13161    {
13162      /*
13163        Delete tile image.
13164      */
13165      if (IsPathAccessible(filename) == MagickFalse)
13166        {
13167          XNoticeWidget(display,windows,"Image file does not exist:",filename);
13168          break;
13169        }
13170      status=XConfirmWidget(display,windows,"Really delete tile",filename);
13171      if (status <= 0)
13172        break;
13173      status=remove(filename) != 0 ? MagickTrue : MagickFalse;
13174      if (status != MagickFalse)
13175        {
13176          XNoticeWidget(display,windows,"Unable to delete image file:",
13177            filename);
13178          break;
13179        }
13180    }
13181    case TileUpdateCommand:
13182    {
13183      int
13184        x_offset,
13185        y_offset;
13186
13187      PixelPacket
13188        pixel;
13189
13190      register int
13191        j;
13192
13193      register Quantum
13194        *s;
13195
13196      /*
13197        Ensure all the images exist.
13198      */
13199      tile=0;
13200      for (p=image->directory; *p != '\0'; p++)
13201      {
13202        CacheView
13203          *image_view;
13204
13205        q=p;
13206        while ((*q != '\n') && (*q != '\0'))
13207          q++;
13208        (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13209        p=q;
13210        if (IsPathAccessible(filename) != MagickFalse)
13211          {
13212            tile++;
13213            continue;
13214          }
13215        /*
13216          Overwrite tile with background color.
13217        */
13218        x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13219        y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13220        image_view=AcquireCacheView(image);
13221        (void) GetOneCacheViewVirtualPixel(image_view,0,0,&pixel,exception);
13222        for (i=0; i < (int) height; i++)
13223        {
13224          s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13225            y_offset+i,width,1,exception);
13226          if (s == (Quantum *) NULL)
13227            break;
13228          for (j=0; j < (int) width; j++)
13229          {
13230            SetPixelPacket(image,&pixel,s);
13231            s+=GetPixelChannels(image);
13232          }
13233          if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13234            break;
13235        }
13236        image_view=DestroyCacheView(image_view);
13237        tile++;
13238      }
13239      windows->image.window_changes.width=(int) image->columns;
13240      windows->image.window_changes.height=(int) image->rows;
13241      XConfigureImageColormap(display,resource_info,windows,image);
13242      (void) XConfigureImage(display,resource_info,windows,image,exception);
13243      break;
13244    }
13245    default:
13246      break;
13247  }
13248  XSetCursorState(display,windows,MagickFalse);
13249  return(tile_image);
13250}
13251
13252/*
13253%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13254%                                                                             %
13255%                                                                             %
13256%                                                                             %
13257+   X T r a n s l a t e I m a g e                                             %
13258%                                                                             %
13259%                                                                             %
13260%                                                                             %
13261%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13262%
13263%  XTranslateImage() translates the image within an Image window by one pixel
13264%  as specified by the key symbol.  If the image has a `montage string the
13265%  translation is respect to the width and height contained within the string.
13266%
13267%  The format of the XTranslateImage method is:
13268%
13269%      void XTranslateImage(Display *display,XWindows *windows,
13270%        Image *image,const KeySym key_symbol)
13271%
13272%  A description of each parameter follows:
13273%
13274%    o display: Specifies a connection to an X server; returned from
13275%      XOpenDisplay.
13276%
13277%    o windows: Specifies a pointer to a XWindows structure.
13278%
13279%    o image: the image.
13280%
13281%    o key_symbol: Specifies a KeySym which indicates which side of the image
13282%      to trim.
13283%
13284*/
13285static void XTranslateImage(Display *display,XWindows *windows,
13286  Image *image,const KeySym key_symbol)
13287{
13288  char
13289    text[MaxTextExtent];
13290
13291  int
13292    x,
13293    y;
13294
13295  unsigned int
13296    x_offset,
13297    y_offset;
13298
13299  /*
13300    User specified a pan position offset.
13301  */
13302  x_offset=windows->image.width;
13303  y_offset=windows->image.height;
13304  if (image->montage != (char *) NULL)
13305    (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13306  switch ((int) key_symbol)
13307  {
13308    case XK_Home:
13309    case XK_KP_Home:
13310    {
13311      windows->image.x=(int) windows->image.width/2;
13312      windows->image.y=(int) windows->image.height/2;
13313      break;
13314    }
13315    case XK_Left:
13316    case XK_KP_Left:
13317    {
13318      windows->image.x-=x_offset;
13319      break;
13320    }
13321    case XK_Next:
13322    case XK_Up:
13323    case XK_KP_Up:
13324    {
13325      windows->image.y-=y_offset;
13326      break;
13327    }
13328    case XK_Right:
13329    case XK_KP_Right:
13330    {
13331      windows->image.x+=x_offset;
13332      break;
13333    }
13334    case XK_Prior:
13335    case XK_Down:
13336    case XK_KP_Down:
13337    {
13338      windows->image.y+=y_offset;
13339      break;
13340    }
13341    default:
13342      return;
13343  }
13344  /*
13345    Check boundary conditions.
13346  */
13347  if (windows->image.x < 0)
13348    windows->image.x=0;
13349  else
13350    if ((int) (windows->image.x+windows->image.width) >
13351        windows->image.ximage->width)
13352      windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13353  if (windows->image.y < 0)
13354    windows->image.y=0;
13355  else
13356    if ((int) (windows->image.y+windows->image.height) >
13357        windows->image.ximage->height)
13358      windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13359  /*
13360    Refresh Image window.
13361  */
13362  (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
13363    windows->image.width,windows->image.height,windows->image.x,
13364    windows->image.y);
13365  XInfoWidget(display,windows,text);
13366  XCheckRefreshWindows(display,windows);
13367  XDrawPanRectangle(display,windows);
13368  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13369  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13370}
13371
13372/*
13373%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13374%                                                                             %
13375%                                                                             %
13376%                                                                             %
13377+   X T r i m I m a g e                                                       %
13378%                                                                             %
13379%                                                                             %
13380%                                                                             %
13381%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13382%
13383%  XTrimImage() trims the edges from the Image window.
13384%
13385%  The format of the XTrimImage method is:
13386%
13387%      MagickBooleanType XTrimImage(Display *display,
13388%        XResourceInfo *resource_info,XWindows *windows,Image *image,
13389%        ExceptionInfo *exception)
13390%
13391%  A description of each parameter follows:
13392%
13393%    o display: Specifies a connection to an X server; returned from
13394%      XOpenDisplay.
13395%
13396%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13397%
13398%    o windows: Specifies a pointer to a XWindows structure.
13399%
13400%    o image: the image.
13401%
13402%    o exception: return any errors or warnings in this structure.
13403%
13404*/
13405static MagickBooleanType XTrimImage(Display *display,
13406  XResourceInfo *resource_info,XWindows *windows,Image *image,
13407  ExceptionInfo *exception)
13408{
13409  RectangleInfo
13410    trim_info;
13411
13412  register int
13413    x,
13414    y;
13415
13416  size_t
13417    background,
13418    pixel;
13419
13420  /*
13421    Trim edges from image.
13422  */
13423  XSetCursorState(display,windows,MagickTrue);
13424  XCheckRefreshWindows(display,windows);
13425  /*
13426    Crop the left edge.
13427  */
13428  background=XGetPixel(windows->image.ximage,0,0);
13429  trim_info.width=(size_t) windows->image.ximage->width;
13430  for (x=0; x < windows->image.ximage->width; x++)
13431  {
13432    for (y=0; y < windows->image.ximage->height; y++)
13433    {
13434      pixel=XGetPixel(windows->image.ximage,x,y);
13435      if (pixel != background)
13436        break;
13437    }
13438    if (y < windows->image.ximage->height)
13439      break;
13440  }
13441  trim_info.x=(ssize_t) x;
13442  if (trim_info.x == (ssize_t) windows->image.ximage->width)
13443    {
13444      XSetCursorState(display,windows,MagickFalse);
13445      return(MagickFalse);
13446    }
13447  /*
13448    Crop the right edge.
13449  */
13450  background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13451  for (x=windows->image.ximage->width-1; x != 0; x--)
13452  {
13453    for (y=0; y < windows->image.ximage->height; y++)
13454    {
13455      pixel=XGetPixel(windows->image.ximage,x,y);
13456      if (pixel != background)
13457        break;
13458    }
13459    if (y < windows->image.ximage->height)
13460      break;
13461  }
13462  trim_info.width=(size_t) (x-trim_info.x+1);
13463  /*
13464    Crop the top edge.
13465  */
13466  background=XGetPixel(windows->image.ximage,0,0);
13467  trim_info.height=(size_t) windows->image.ximage->height;
13468  for (y=0; y < windows->image.ximage->height; y++)
13469  {
13470    for (x=0; x < windows->image.ximage->width; x++)
13471    {
13472      pixel=XGetPixel(windows->image.ximage,x,y);
13473      if (pixel != background)
13474        break;
13475    }
13476    if (x < windows->image.ximage->width)
13477      break;
13478  }
13479  trim_info.y=(ssize_t) y;
13480  /*
13481    Crop the bottom edge.
13482  */
13483  background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13484  for (y=windows->image.ximage->height-1; y != 0; y--)
13485  {
13486    for (x=0; x < windows->image.ximage->width; x++)
13487    {
13488      pixel=XGetPixel(windows->image.ximage,x,y);
13489      if (pixel != background)
13490        break;
13491    }
13492    if (x < windows->image.ximage->width)
13493      break;
13494  }
13495  trim_info.height=(size_t) y-trim_info.y+1;
13496  if (((unsigned int) trim_info.width != windows->image.width) ||
13497      ((unsigned int) trim_info.height != windows->image.height))
13498    {
13499      /*
13500        Reconfigure Image window as defined by the trimming rectangle.
13501      */
13502      XSetCropGeometry(display,windows,&trim_info,image);
13503      windows->image.window_changes.width=(int) trim_info.width;
13504      windows->image.window_changes.height=(int) trim_info.height;
13505      (void) XConfigureImage(display,resource_info,windows,image,exception);
13506    }
13507  XSetCursorState(display,windows,MagickFalse);
13508  return(MagickTrue);
13509}
13510
13511/*
13512%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13513%                                                                             %
13514%                                                                             %
13515%                                                                             %
13516+   X V i s u a l D i r e c t o r y I m a g e                                 %
13517%                                                                             %
13518%                                                                             %
13519%                                                                             %
13520%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13521%
13522%  XVisualDirectoryImage() creates a Visual Image Directory.
13523%
13524%  The format of the XVisualDirectoryImage method is:
13525%
13526%      Image *XVisualDirectoryImage(Display *display,
13527%        XResourceInfo *resource_info,XWindows *windows,
13528%        ExceptionInfo *exception)
13529%
13530%  A description of each parameter follows:
13531%
13532%    o display: Specifies a connection to an X server; returned from
13533%      XOpenDisplay.
13534%
13535%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13536%
13537%    o windows: Specifies a pointer to a XWindows structure.
13538%
13539%    o exception: return any errors or warnings in this structure.
13540%
13541*/
13542static Image *XVisualDirectoryImage(Display *display,
13543  XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
13544{
13545#define TileImageTag  "Scale/Image"
13546#define XClientName  "montage"
13547
13548  char
13549    **filelist;
13550
13551  Image
13552    *images,
13553    *montage_image,
13554    *next_image,
13555    *thumbnail_image;
13556
13557  ImageInfo
13558    *read_info;
13559
13560  int
13561    number_files;
13562
13563  MagickBooleanType
13564    backdrop;
13565
13566  MagickStatusType
13567    status;
13568
13569  MontageInfo
13570    *montage_info;
13571
13572  RectangleInfo
13573    geometry;
13574
13575  register int
13576    i;
13577
13578  static char
13579    filename[MaxTextExtent] = "\0",
13580    filenames[MaxTextExtent] = "*";
13581
13582  XResourceInfo
13583    background_resources;
13584
13585  /*
13586    Request file name from user.
13587  */
13588  XFileBrowserWidget(display,windows,"Directory",filenames);
13589  if (*filenames == '\0')
13590    return((Image *) NULL);
13591  /*
13592    Expand the filenames.
13593  */
13594  filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13595  if (filelist == (char **) NULL)
13596    {
13597      ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13598        filenames);
13599      return((Image *) NULL);
13600    }
13601  number_files=1;
13602  filelist[0]=filenames;
13603  status=ExpandFilenames(&number_files,&filelist);
13604  if ((status == MagickFalse) || (number_files == 0))
13605    {
13606      if (number_files == 0)
13607        ThrowXWindowFatalException(ImageError,"NoImagesWereFound",filenames)
13608      else
13609        ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13610          filenames);
13611      return((Image *) NULL);
13612    }
13613  /*
13614    Set image background resources.
13615  */
13616  background_resources=(*resource_info);
13617  background_resources.window_id=AcquireString("");
13618  (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
13619    "0x%lx",windows->image.id);
13620  background_resources.backdrop=MagickTrue;
13621  /*
13622    Read each image and convert them to a tile.
13623  */
13624  backdrop=(windows->visual_info->klass == TrueColor) ||
13625    (windows->visual_info->klass == DirectColor) ? MagickTrue : MagickFalse;
13626  read_info=CloneImageInfo(resource_info->image_info);
13627  (void) SetImageOption(read_info,"jpeg:size","120x120");
13628  (void) CloneString(&read_info->size,DefaultTileGeometry);
13629  (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13630    (void *) NULL);
13631  images=NewImageList();
13632  XSetCursorState(display,windows,MagickTrue);
13633  XCheckRefreshWindows(display,windows);
13634  for (i=0; i < (int) number_files; i++)
13635  {
13636    (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13637    filelist[i]=DestroyString(filelist[i]);
13638    *read_info->magick='\0';
13639    next_image=ReadImage(read_info,exception);
13640    CatchException(exception);
13641    if (next_image != (Image *) NULL)
13642      {
13643        (void) DeleteImageProperty(next_image,"label");
13644        (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13645          read_info,next_image,DefaultTileLabel));
13646        (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13647          exception);
13648        thumbnail_image=ThumbnailImage(next_image,geometry.width,
13649          geometry.height,exception);
13650        if (thumbnail_image != (Image *) NULL)
13651          {
13652            next_image=DestroyImage(next_image);
13653            next_image=thumbnail_image;
13654          }
13655        if (backdrop)
13656          {
13657            (void) XDisplayBackgroundImage(display,&background_resources,
13658              next_image,exception);
13659            XSetCursorState(display,windows,MagickTrue);
13660          }
13661        AppendImageToList(&images,next_image);
13662        if (images->progress_monitor != (MagickProgressMonitor) NULL)
13663          {
13664            MagickBooleanType
13665              proceed;
13666
13667            proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13668              (MagickSizeType) number_files);
13669            if (proceed == MagickFalse)
13670              break;
13671          }
13672      }
13673  }
13674  filelist=(char **) RelinquishMagickMemory(filelist);
13675  if (images == (Image *) NULL)
13676    {
13677      read_info=DestroyImageInfo(read_info);
13678      XSetCursorState(display,windows,MagickFalse);
13679      ThrowXWindowFatalException(ImageError,"NoImagesWereLoaded",filenames);
13680      return((Image *) NULL);
13681    }
13682  /*
13683    Create the Visual Image Directory.
13684  */
13685  montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13686  montage_info->pointsize=10;
13687  if (resource_info->font != (char *) NULL)
13688    (void) CloneString(&montage_info->font,resource_info->font);
13689  (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13690  montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13691    images),exception);
13692  images=DestroyImageList(images);
13693  montage_info=DestroyMontageInfo(montage_info);
13694  read_info=DestroyImageInfo(read_info);
13695  XSetCursorState(display,windows,MagickFalse);
13696  if (montage_image == (Image *) NULL)
13697    return(montage_image);
13698  XClientMessage(display,windows->image.id,windows->im_protocols,
13699    windows->im_next_image,CurrentTime);
13700  return(montage_image);
13701}
13702
13703/*
13704%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13705%                                                                             %
13706%                                                                             %
13707%                                                                             %
13708%   X D i s p l a y B a c k g r o u n d I m a g e                             %
13709%                                                                             %
13710%                                                                             %
13711%                                                                             %
13712%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13713%
13714%  XDisplayBackgroundImage() displays an image in the background of a window.
13715%
13716%  The format of the XDisplayBackgroundImage method is:
13717%
13718%      MagickBooleanType XDisplayBackgroundImage(Display *display,
13719%        XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13720%
13721%  A description of each parameter follows:
13722%
13723%    o display: Specifies a connection to an X server;  returned from
13724%      XOpenDisplay.
13725%
13726%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13727%
13728%    o image: the image.
13729%
13730%    o exception: return any errors or warnings in this structure.
13731%
13732*/
13733MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13734  XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13735{
13736  char
13737    geometry[MaxTextExtent],
13738    visual_type[MaxTextExtent];
13739
13740  int
13741    height,
13742    status,
13743    width;
13744
13745  RectangleInfo
13746    geometry_info;
13747
13748  static XPixelInfo
13749    pixel;
13750
13751  static XStandardColormap
13752    *map_info;
13753
13754  static XVisualInfo
13755    *visual_info = (XVisualInfo *) NULL;
13756
13757  static XWindowInfo
13758    window_info;
13759
13760  size_t
13761    delay;
13762
13763  Window
13764    root_window;
13765
13766  XGCValues
13767    context_values;
13768
13769  XResourceInfo
13770    resources;
13771
13772  XWindowAttributes
13773    window_attributes;
13774
13775  /*
13776    Determine target window.
13777  */
13778  assert(image != (Image *) NULL);
13779  assert(image->signature == MagickSignature);
13780  if (image->debug != MagickFalse)
13781    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13782  resources=(*resource_info);
13783  window_info.id=(Window) NULL;
13784  root_window=XRootWindow(display,XDefaultScreen(display));
13785  if (LocaleCompare(resources.window_id,"root") == 0)
13786    window_info.id=root_window;
13787  else
13788    {
13789      if (isdigit((unsigned char) *resources.window_id) != 0)
13790        window_info.id=XWindowByID(display,root_window,
13791          (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13792      if (window_info.id == (Window) NULL)
13793        window_info.id=XWindowByName(display,root_window,resources.window_id);
13794    }
13795  if (window_info.id == (Window) NULL)
13796    {
13797      ThrowXWindowFatalException(XServerError,"NoWindowWithSpecifiedIDExists",
13798        resources.window_id);
13799      return(MagickFalse);
13800    }
13801  /*
13802    Determine window visual id.
13803  */
13804  window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13805  window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13806  (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13807  status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13808  if (status != 0)
13809    (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
13810      XVisualIDFromVisual(window_attributes.visual));
13811  if (visual_info == (XVisualInfo *) NULL)
13812    {
13813      /*
13814        Allocate standard colormap.
13815      */
13816      map_info=XAllocStandardColormap();
13817      if (map_info == (XStandardColormap *) NULL)
13818        ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13819          image->filename);
13820      map_info->colormap=(Colormap) NULL;
13821      pixel.pixels=(unsigned long *) NULL;
13822      /*
13823        Initialize visual info.
13824      */
13825      resources.map_type=(char *) NULL;
13826      resources.visual_type=visual_type;
13827      visual_info=XBestVisualInfo(display,map_info,&resources);
13828      if (visual_info == (XVisualInfo *) NULL)
13829        ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13830          resources.visual_type);
13831      /*
13832        Initialize window info.
13833      */
13834      window_info.ximage=(XImage *) NULL;
13835      window_info.matte_image=(XImage *) NULL;
13836      window_info.pixmap=(Pixmap) NULL;
13837      window_info.matte_pixmap=(Pixmap) NULL;
13838    }
13839  /*
13840    Free previous root colors.
13841  */
13842  if (window_info.id == root_window)
13843    (void) XDestroyWindowColors(display,root_window);
13844  /*
13845    Initialize Standard Colormap.
13846  */
13847  resources.colormap=SharedColormap;
13848  XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel);
13849  /*
13850    Graphic context superclass.
13851  */
13852  context_values.background=pixel.background_color.pixel;
13853  context_values.foreground=pixel.foreground_color.pixel;
13854  pixel.annotate_context=XCreateGC(display,window_info.id,
13855    (size_t) (GCBackground | GCForeground),&context_values);
13856  if (pixel.annotate_context == (GC) NULL)
13857    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13858      image->filename);
13859  /*
13860    Initialize Image window attributes.
13861  */
13862  window_info.name=AcquireString("\0");
13863  window_info.icon_name=AcquireString("\0");
13864  XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13865    &resources,&window_info);
13866  /*
13867    Create the X image.
13868  */
13869  window_info.width=(unsigned int) image->columns;
13870  window_info.height=(unsigned int) image->rows;
13871  if ((image->columns != window_info.width) ||
13872      (image->rows != window_info.height))
13873    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13874      image->filename);
13875  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
13876    window_attributes.width,window_attributes.height);
13877  geometry_info.width=window_info.width;
13878  geometry_info.height=window_info.height;
13879  geometry_info.x=(ssize_t) window_info.x;
13880  geometry_info.y=(ssize_t) window_info.y;
13881  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13882    &geometry_info.width,&geometry_info.height);
13883  window_info.width=(unsigned int) geometry_info.width;
13884  window_info.height=(unsigned int) geometry_info.height;
13885  window_info.x=(int) geometry_info.x;
13886  window_info.y=(int) geometry_info.y;
13887  status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13888    window_info.height,exception);
13889  if (status == MagickFalse)
13890    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13891      image->filename);
13892  window_info.x=0;
13893  window_info.y=0;
13894  if (image->debug != MagickFalse)
13895    {
13896      (void) LogMagickEvent(X11Event,GetMagickModule(),
13897        "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13898        (double) image->columns,(double) image->rows);
13899      if (image->colors != 0)
13900        (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13901          image->colors);
13902      (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13903    }
13904  /*
13905    Adjust image dimensions as specified by backdrop or geometry options.
13906  */
13907  width=(int) window_info.width;
13908  height=(int) window_info.height;
13909  if (resources.backdrop != MagickFalse)
13910    {
13911      /*
13912        Center image on window.
13913      */
13914      window_info.x=(window_attributes.width/2)-
13915        (window_info.ximage->width/2);
13916      window_info.y=(window_attributes.height/2)-
13917        (window_info.ximage->height/2);
13918      width=window_attributes.width;
13919      height=window_attributes.height;
13920    }
13921  if ((resources.image_geometry != (char *) NULL) &&
13922      (*resources.image_geometry != '\0'))
13923    {
13924      char
13925        default_geometry[MaxTextExtent];
13926
13927      int
13928        flags,
13929        gravity;
13930
13931      XSizeHints
13932        *size_hints;
13933
13934      /*
13935        User specified geometry.
13936      */
13937      size_hints=XAllocSizeHints();
13938      if (size_hints == (XSizeHints *) NULL)
13939        ThrowXWindowFatalException(ResourceLimitFatalError,
13940          "MemoryAllocationFailed",image->filename);
13941      size_hints->flags=0L;
13942      (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
13943        width,height);
13944      flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
13945        default_geometry,window_info.border_width,size_hints,&window_info.x,
13946        &window_info.y,&width,&height,&gravity);
13947      if (flags & (XValue | YValue))
13948        {
13949          width=window_attributes.width;
13950          height=window_attributes.height;
13951        }
13952      (void) XFree((void *) size_hints);
13953    }
13954  /*
13955    Create the X pixmap.
13956  */
13957  window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
13958    (unsigned int) height,window_info.depth);
13959  if (window_info.pixmap == (Pixmap) NULL)
13960    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
13961      image->filename);
13962  /*
13963    Display pixmap on the window.
13964  */
13965  if (((unsigned int) width > window_info.width) ||
13966      ((unsigned int) height > window_info.height))
13967    (void) XFillRectangle(display,window_info.pixmap,
13968      window_info.annotate_context,0,0,(unsigned int) width,
13969      (unsigned int) height);
13970  (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
13971    window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
13972    window_info.width,(unsigned int) window_info.height);
13973  (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
13974  (void) XClearWindow(display,window_info.id);
13975  delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
13976  XDelay(display,delay == 0UL ? 10UL : delay);
13977  (void) XSync(display,MagickFalse);
13978  return(window_info.id == root_window ? MagickTrue : MagickFalse);
13979}
13980
13981/*
13982%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13983%                                                                             %
13984%                                                                             %
13985%                                                                             %
13986+   X D i s p l a y I m a g e                                                 %
13987%                                                                             %
13988%                                                                             %
13989%                                                                             %
13990%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13991%
13992%  XDisplayImage() displays an image via X11.  A new image is created and
13993%  returned if the user interactively transforms the displayed image.
13994%
13995%  The format of the XDisplayImage method is:
13996%
13997%      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
13998%        char **argv,int argc,Image **image,size_t *state,
13999%        ExceptionInfo *exception)
14000%
14001%  A description of each parameter follows:
14002%
14003%    o nexus:  Method XDisplayImage returns an image when the
14004%      user chooses 'Open Image' from the command menu or picks a tile
14005%      from the image directory.  Otherwise a null image is returned.
14006%
14007%    o display: Specifies a connection to an X server;  returned from
14008%      XOpenDisplay.
14009%
14010%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14011%
14012%    o argv: Specifies the application's argument list.
14013%
14014%    o argc: Specifies the number of arguments.
14015%
14016%    o image: Specifies an address to an address of an Image structure;
14017%
14018%    o exception: return any errors or warnings in this structure.
14019%
14020*/
14021MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14022  char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
14023{
14024#define MagnifySize  256  /* must be a power of 2 */
14025#define MagickMenus  10
14026#define MagickTitle  "Commands"
14027
14028  static const char
14029    *CommandMenu[] =
14030    {
14031      "File",
14032      "Edit",
14033      "View",
14034      "Transform",
14035      "Enhance",
14036      "Effects",
14037      "F/X",
14038      "Image Edit",
14039      "Miscellany",
14040      "Help",
14041      (char *) NULL
14042    },
14043    *FileMenu[] =
14044    {
14045      "Open...",
14046      "Next",
14047      "Former",
14048      "Select...",
14049      "Save...",
14050      "Print...",
14051      "Delete...",
14052      "New...",
14053      "Visual Directory...",
14054      "Quit",
14055      (char *) NULL
14056    },
14057    *EditMenu[] =
14058    {
14059      "Undo",
14060      "Redo",
14061      "Cut",
14062      "Copy",
14063      "Paste",
14064      (char *) NULL
14065    },
14066    *ViewMenu[] =
14067    {
14068      "Half Size",
14069      "Original Size",
14070      "Double Size",
14071      "Resize...",
14072      "Apply",
14073      "Refresh",
14074      "Restore",
14075      (char *) NULL
14076    },
14077    *TransformMenu[] =
14078    {
14079      "Crop",
14080      "Chop",
14081      "Flop",
14082      "Flip",
14083      "Rotate Right",
14084      "Rotate Left",
14085      "Rotate...",
14086      "Shear...",
14087      "Roll...",
14088      "Trim Edges",
14089      (char *) NULL
14090    },
14091    *EnhanceMenu[] =
14092    {
14093      "Hue...",
14094      "Saturation...",
14095      "Brightness...",
14096      "Gamma...",
14097      "Spiff",
14098      "Dull",
14099      "Contrast Stretch...",
14100      "Sigmoidal Contrast...",
14101      "Normalize",
14102      "Equalize",
14103      "Negate",
14104      "Grayscale",
14105      "Map...",
14106      "Quantize...",
14107      (char *) NULL
14108    },
14109    *EffectsMenu[] =
14110    {
14111      "Despeckle",
14112      "Emboss",
14113      "Reduce Noise",
14114      "Add Noise...",
14115      "Sharpen...",
14116      "Blur...",
14117      "Threshold...",
14118      "Edge Detect...",
14119      "Spread...",
14120      "Shade...",
14121      "Raise...",
14122      "Segment...",
14123      (char *) NULL
14124    },
14125    *FXMenu[] =
14126    {
14127      "Solarize...",
14128      "Sepia Tone...",
14129      "Swirl...",
14130      "Implode...",
14131      "Vignette...",
14132      "Wave...",
14133      "Oil Paint...",
14134      "Charcoal Draw...",
14135      (char *) NULL
14136    },
14137    *ImageEditMenu[] =
14138    {
14139      "Annotate...",
14140      "Draw...",
14141      "Color...",
14142      "Matte...",
14143      "Composite...",
14144      "Add Border...",
14145      "Add Frame...",
14146      "Comment...",
14147      "Launch...",
14148      "Region of Interest...",
14149      (char *) NULL
14150    },
14151    *MiscellanyMenu[] =
14152    {
14153      "Image Info",
14154      "Zoom Image",
14155      "Show Preview...",
14156      "Show Histogram",
14157      "Show Matte",
14158      "Background...",
14159      "Slide Show...",
14160      "Preferences...",
14161      (char *) NULL
14162    },
14163    *HelpMenu[] =
14164    {
14165      "Overview",
14166      "Browse Documentation",
14167      "About Display",
14168      (char *) NULL
14169    },
14170    *ShortCutsMenu[] =
14171    {
14172      "Next",
14173      "Former",
14174      "Open...",
14175      "Save...",
14176      "Print...",
14177      "Undo",
14178      "Restore",
14179      "Image Info",
14180      "Quit",
14181      (char *) NULL
14182    },
14183    *VirtualMenu[] =
14184    {
14185      "Image Info",
14186      "Print",
14187      "Next",
14188      "Quit",
14189      (char *) NULL
14190    };
14191
14192  static const char
14193    **Menus[MagickMenus] =
14194    {
14195      FileMenu,
14196      EditMenu,
14197      ViewMenu,
14198      TransformMenu,
14199      EnhanceMenu,
14200      EffectsMenu,
14201      FXMenu,
14202      ImageEditMenu,
14203      MiscellanyMenu,
14204      HelpMenu
14205    };
14206
14207  static CommandType
14208    CommandMenus[] =
14209    {
14210      NullCommand,
14211      NullCommand,
14212      NullCommand,
14213      NullCommand,
14214      NullCommand,
14215      NullCommand,
14216      NullCommand,
14217      NullCommand,
14218      NullCommand,
14219      NullCommand,
14220    },
14221    FileCommands[] =
14222    {
14223      OpenCommand,
14224      NextCommand,
14225      FormerCommand,
14226      SelectCommand,
14227      SaveCommand,
14228      PrintCommand,
14229      DeleteCommand,
14230      NewCommand,
14231      VisualDirectoryCommand,
14232      QuitCommand
14233    },
14234    EditCommands[] =
14235    {
14236      UndoCommand,
14237      RedoCommand,
14238      CutCommand,
14239      CopyCommand,
14240      PasteCommand
14241    },
14242    ViewCommands[] =
14243    {
14244      HalfSizeCommand,
14245      OriginalSizeCommand,
14246      DoubleSizeCommand,
14247      ResizeCommand,
14248      ApplyCommand,
14249      RefreshCommand,
14250      RestoreCommand
14251    },
14252    TransformCommands[] =
14253    {
14254      CropCommand,
14255      ChopCommand,
14256      FlopCommand,
14257      FlipCommand,
14258      RotateRightCommand,
14259      RotateLeftCommand,
14260      RotateCommand,
14261      ShearCommand,
14262      RollCommand,
14263      TrimCommand
14264    },
14265    EnhanceCommands[] =
14266    {
14267      HueCommand,
14268      SaturationCommand,
14269      BrightnessCommand,
14270      GammaCommand,
14271      SpiffCommand,
14272      DullCommand,
14273      ContrastStretchCommand,
14274      SigmoidalContrastCommand,
14275      NormalizeCommand,
14276      EqualizeCommand,
14277      NegateCommand,
14278      GrayscaleCommand,
14279      MapCommand,
14280      QuantizeCommand
14281    },
14282    EffectsCommands[] =
14283    {
14284      DespeckleCommand,
14285      EmbossCommand,
14286      ReduceNoiseCommand,
14287      AddNoiseCommand,
14288      SharpenCommand,
14289      BlurCommand,
14290      ThresholdCommand,
14291      EdgeDetectCommand,
14292      SpreadCommand,
14293      ShadeCommand,
14294      RaiseCommand,
14295      SegmentCommand
14296    },
14297    FXCommands[] =
14298    {
14299      SolarizeCommand,
14300      SepiaToneCommand,
14301      SwirlCommand,
14302      ImplodeCommand,
14303      VignetteCommand,
14304      WaveCommand,
14305      OilPaintCommand,
14306      CharcoalDrawCommand
14307    },
14308    ImageEditCommands[] =
14309    {
14310      AnnotateCommand,
14311      DrawCommand,
14312      ColorCommand,
14313      MatteCommand,
14314      CompositeCommand,
14315      AddBorderCommand,
14316      AddFrameCommand,
14317      CommentCommand,
14318      LaunchCommand,
14319      RegionofInterestCommand
14320    },
14321    MiscellanyCommands[] =
14322    {
14323      InfoCommand,
14324      ZoomCommand,
14325      ShowPreviewCommand,
14326      ShowHistogramCommand,
14327      ShowMatteCommand,
14328      BackgroundCommand,
14329      SlideShowCommand,
14330      PreferencesCommand
14331    },
14332    HelpCommands[] =
14333    {
14334      HelpCommand,
14335      BrowseDocumentationCommand,
14336      VersionCommand
14337    },
14338    ShortCutsCommands[] =
14339    {
14340      NextCommand,
14341      FormerCommand,
14342      OpenCommand,
14343      SaveCommand,
14344      PrintCommand,
14345      UndoCommand,
14346      RestoreCommand,
14347      InfoCommand,
14348      QuitCommand
14349    },
14350    VirtualCommands[] =
14351    {
14352      InfoCommand,
14353      PrintCommand,
14354      NextCommand,
14355      QuitCommand
14356    };
14357
14358  static CommandType
14359    *Commands[MagickMenus] =
14360    {
14361      FileCommands,
14362      EditCommands,
14363      ViewCommands,
14364      TransformCommands,
14365      EnhanceCommands,
14366      EffectsCommands,
14367      FXCommands,
14368      ImageEditCommands,
14369      MiscellanyCommands,
14370      HelpCommands
14371    };
14372
14373  char
14374    command[MaxTextExtent],
14375    *directory,
14376    geometry[MaxTextExtent],
14377    resource_name[MaxTextExtent];
14378
14379  CommandType
14380    command_type;
14381
14382  Image
14383    *display_image,
14384    *nexus;
14385
14386  int
14387    entry,
14388    id;
14389
14390  KeySym
14391    key_symbol;
14392
14393  MagickStatusType
14394    context_mask,
14395    status;
14396
14397  RectangleInfo
14398    geometry_info;
14399
14400  register int
14401    i;
14402
14403  static char
14404    working_directory[MaxTextExtent];
14405
14406  static XPoint
14407    vid_info;
14408
14409  static XWindowInfo
14410    *magick_windows[MaxXWindows];
14411
14412  static unsigned int
14413    number_windows;
14414
14415  struct stat
14416    attributes;
14417
14418  time_t
14419    timer,
14420    timestamp,
14421    update_time;
14422
14423  unsigned int
14424    height,
14425    width;
14426
14427  size_t
14428    delay;
14429
14430  WarningHandler
14431    warning_handler;
14432
14433  Window
14434    root_window;
14435
14436  XClassHint
14437    *class_hints;
14438
14439  XEvent
14440    event;
14441
14442  XFontStruct
14443    *font_info;
14444
14445  XGCValues
14446    context_values;
14447
14448  XPixelInfo
14449    *icon_pixel,
14450    *pixel;
14451
14452  XResourceInfo
14453    *icon_resources;
14454
14455  XStandardColormap
14456    *icon_map,
14457    *map_info;
14458
14459  XVisualInfo
14460    *icon_visual,
14461    *visual_info;
14462
14463  XWindowChanges
14464    window_changes;
14465
14466  XWindows
14467    *windows;
14468
14469  XWMHints
14470    *manager_hints;
14471
14472  assert(image != (Image **) NULL);
14473  assert((*image)->signature == MagickSignature);
14474  if ((*image)->debug != MagickFalse)
14475    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14476  display_image=(*image);
14477  warning_handler=(WarningHandler) NULL;
14478  windows=XSetWindows((XWindows *) ~0);
14479  if (windows != (XWindows *) NULL)
14480    {
14481      int
14482        status;
14483
14484      status=chdir(working_directory);
14485      if (status == -1)
14486        (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14487          "UnableToOpenFile","%s",working_directory);
14488      warning_handler=resource_info->display_warnings ?
14489        SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14490      warning_handler=resource_info->display_warnings ?
14491        SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14492    }
14493  else
14494    {
14495      /*
14496        Allocate windows structure.
14497      */
14498      resource_info->colors=display_image->colors;
14499      windows=XSetWindows(XInitializeWindows(display,resource_info));
14500      if (windows == (XWindows *) NULL)
14501        ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14502          (*image)->filename);
14503      /*
14504        Initialize window id's.
14505      */
14506      number_windows=0;
14507      magick_windows[number_windows++]=(&windows->icon);
14508      magick_windows[number_windows++]=(&windows->backdrop);
14509      magick_windows[number_windows++]=(&windows->image);
14510      magick_windows[number_windows++]=(&windows->info);
14511      magick_windows[number_windows++]=(&windows->command);
14512      magick_windows[number_windows++]=(&windows->widget);
14513      magick_windows[number_windows++]=(&windows->popup);
14514      magick_windows[number_windows++]=(&windows->magnify);
14515      magick_windows[number_windows++]=(&windows->pan);
14516      for (i=0; i < (int) number_windows; i++)
14517        magick_windows[i]->id=(Window) NULL;
14518      vid_info.x=0;
14519      vid_info.y=0;
14520    }
14521  /*
14522    Initialize font info.
14523  */
14524  if (windows->font_info != (XFontStruct *) NULL)
14525    (void) XFreeFont(display,windows->font_info);
14526  windows->font_info=XBestFont(display,resource_info,MagickFalse);
14527  if (windows->font_info == (XFontStruct *) NULL)
14528    ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14529      resource_info->font);
14530  /*
14531    Initialize Standard Colormap.
14532  */
14533  map_info=windows->map_info;
14534  icon_map=windows->icon_map;
14535  visual_info=windows->visual_info;
14536  icon_visual=windows->icon_visual;
14537  pixel=windows->pixel_info;
14538  icon_pixel=windows->icon_pixel;
14539  font_info=windows->font_info;
14540  icon_resources=windows->icon_resources;
14541  class_hints=windows->class_hints;
14542  manager_hints=windows->manager_hints;
14543  root_window=XRootWindow(display,visual_info->screen);
14544  nexus=NewImageList();
14545  if (display_image->debug != MagickFalse)
14546    {
14547      (void) LogMagickEvent(X11Event,GetMagickModule(),
14548        "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14549        (double) display_image->scene,(double) display_image->columns,
14550        (double) display_image->rows);
14551      if (display_image->colors != 0)
14552        (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14553          display_image->colors);
14554      (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14555        display_image->magick);
14556    }
14557  XMakeStandardColormap(display,visual_info,resource_info,display_image,
14558    map_info,pixel);
14559  display_image->taint=MagickFalse;
14560  /*
14561    Initialize graphic context.
14562  */
14563  windows->context.id=(Window) NULL;
14564  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14565    resource_info,&windows->context);
14566  (void) CloneString(&class_hints->res_name,resource_info->client_name);
14567  (void) CloneString(&class_hints->res_class,resource_info->client_name);
14568  class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14569  manager_hints->flags=InputHint | StateHint;
14570  manager_hints->input=MagickFalse;
14571  manager_hints->initial_state=WithdrawnState;
14572  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14573    &windows->context);
14574  if (display_image->debug != MagickFalse)
14575    (void) LogMagickEvent(X11Event,GetMagickModule(),
14576      "Window id: 0x%lx (context)",windows->context.id);
14577  context_values.background=pixel->background_color.pixel;
14578  context_values.font=font_info->fid;
14579  context_values.foreground=pixel->foreground_color.pixel;
14580  context_values.graphics_exposures=MagickFalse;
14581  context_mask=(MagickStatusType)
14582    (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14583  if (pixel->annotate_context != (GC) NULL)
14584    (void) XFreeGC(display,pixel->annotate_context);
14585  pixel->annotate_context=XCreateGC(display,windows->context.id,
14586    context_mask,&context_values);
14587  if (pixel->annotate_context == (GC) NULL)
14588    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14589      display_image->filename);
14590  context_values.background=pixel->depth_color.pixel;
14591  if (pixel->widget_context != (GC) NULL)
14592    (void) XFreeGC(display,pixel->widget_context);
14593  pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14594    &context_values);
14595  if (pixel->widget_context == (GC) NULL)
14596    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14597      display_image->filename);
14598  context_values.background=pixel->foreground_color.pixel;
14599  context_values.foreground=pixel->background_color.pixel;
14600  context_values.plane_mask=context_values.background ^
14601    context_values.foreground;
14602  if (pixel->highlight_context != (GC) NULL)
14603    (void) XFreeGC(display,pixel->highlight_context);
14604  pixel->highlight_context=XCreateGC(display,windows->context.id,
14605    (size_t) (context_mask | GCPlaneMask),&context_values);
14606  if (pixel->highlight_context == (GC) NULL)
14607    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14608      display_image->filename);
14609  (void) XDestroyWindow(display,windows->context.id);
14610  /*
14611    Initialize icon window.
14612  */
14613  XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14614    icon_resources,&windows->icon);
14615  windows->icon.geometry=resource_info->icon_geometry;
14616  XBestIconSize(display,&windows->icon,display_image);
14617  windows->icon.attributes.colormap=XDefaultColormap(display,
14618    icon_visual->screen);
14619  windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14620  manager_hints->flags=InputHint | StateHint;
14621  manager_hints->input=MagickFalse;
14622  manager_hints->initial_state=IconicState;
14623  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14624    &windows->icon);
14625  if (display_image->debug != MagickFalse)
14626    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14627      windows->icon.id);
14628  /*
14629    Initialize graphic context for icon window.
14630  */
14631  if (icon_pixel->annotate_context != (GC) NULL)
14632    (void) XFreeGC(display,icon_pixel->annotate_context);
14633  context_values.background=icon_pixel->background_color.pixel;
14634  context_values.foreground=icon_pixel->foreground_color.pixel;
14635  icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14636    (size_t) (GCBackground | GCForeground),&context_values);
14637  if (icon_pixel->annotate_context == (GC) NULL)
14638    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14639      display_image->filename);
14640  windows->icon.annotate_context=icon_pixel->annotate_context;
14641  /*
14642    Initialize Image window.
14643  */
14644  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14645    &windows->image);
14646  windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14647  if (resource_info->use_shared_memory == MagickFalse)
14648    windows->image.shared_memory=MagickFalse;
14649  if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14650    {
14651      char
14652        *title;
14653
14654      title=InterpretImageProperties(resource_info->image_info,display_image,
14655        resource_info->title);
14656      (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
14657      (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
14658      title=DestroyString(title);
14659    }
14660  else
14661    {
14662      char
14663        filename[MaxTextExtent];
14664
14665      /*
14666        Window name is the base of the filename.
14667      */
14668      GetPathComponent(display_image->magick_filename,TailPath,filename);
14669      if (GetImageListLength(display_image) == 1)
14670        (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14671          "%s: %s",MagickPackageName,filename);
14672      else
14673        (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14674          "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14675          (double) display_image->scene,(double) GetImageListLength(
14676          display_image));
14677      (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
14678    }
14679  if (resource_info->immutable)
14680    windows->image.immutable=MagickTrue;
14681  windows->image.use_pixmap=resource_info->use_pixmap;
14682  windows->image.geometry=resource_info->image_geometry;
14683  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14684    XDisplayWidth(display,visual_info->screen),
14685    XDisplayHeight(display,visual_info->screen));
14686  geometry_info.width=display_image->columns;
14687  geometry_info.height=display_image->rows;
14688  geometry_info.x=0;
14689  geometry_info.y=0;
14690  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14691    &geometry_info.width,&geometry_info.height);
14692  windows->image.width=(unsigned int) geometry_info.width;
14693  windows->image.height=(unsigned int) geometry_info.height;
14694  windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14695    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14696    KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14697    PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14698  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14699    resource_info,&windows->backdrop);
14700  if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14701    {
14702      /*
14703        Initialize backdrop window.
14704      */
14705      windows->backdrop.x=0;
14706      windows->backdrop.y=0;
14707      (void) CloneString(&windows->backdrop.name,"Backdrop");
14708      windows->backdrop.flags=(size_t) (USSize | USPosition);
14709      windows->backdrop.width=(unsigned int)
14710        XDisplayWidth(display,visual_info->screen);
14711      windows->backdrop.height=(unsigned int)
14712        XDisplayHeight(display,visual_info->screen);
14713      windows->backdrop.border_width=0;
14714      windows->backdrop.immutable=MagickTrue;
14715      windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14716        ButtonReleaseMask;
14717      windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14718        StructureNotifyMask;
14719      manager_hints->flags=IconWindowHint | InputHint | StateHint;
14720      manager_hints->icon_window=windows->icon.id;
14721      manager_hints->input=MagickTrue;
14722      manager_hints->initial_state=resource_info->iconic ? IconicState :
14723        NormalState;
14724      XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14725        &windows->backdrop);
14726      if (display_image->debug != MagickFalse)
14727        (void) LogMagickEvent(X11Event,GetMagickModule(),
14728          "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14729      (void) XMapWindow(display,windows->backdrop.id);
14730      (void) XClearWindow(display,windows->backdrop.id);
14731      if (windows->image.id != (Window) NULL)
14732        {
14733          (void) XDestroyWindow(display,windows->image.id);
14734          windows->image.id=(Window) NULL;
14735        }
14736      /*
14737        Position image in the center the backdrop.
14738      */
14739      windows->image.flags|=USPosition;
14740      windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14741        (windows->image.width/2);
14742      windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14743        (windows->image.height/2);
14744    }
14745  manager_hints->flags=IconWindowHint | InputHint | StateHint;
14746  manager_hints->icon_window=windows->icon.id;
14747  manager_hints->input=MagickTrue;
14748  manager_hints->initial_state=resource_info->iconic ? IconicState :
14749    NormalState;
14750  if (windows->group_leader.id != (Window) NULL)
14751    {
14752      /*
14753        Follow the leader.
14754      */
14755      manager_hints->flags|=WindowGroupHint;
14756      manager_hints->window_group=windows->group_leader.id;
14757      (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14758      if (display_image->debug != MagickFalse)
14759        (void) LogMagickEvent(X11Event,GetMagickModule(),
14760          "Window id: 0x%lx (group leader)",windows->group_leader.id);
14761    }
14762  XMakeWindow(display,
14763    (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14764    argv,argc,class_hints,manager_hints,&windows->image);
14765  (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14766    XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14767  if (windows->group_leader.id != (Window) NULL)
14768    (void) XSetTransientForHint(display,windows->image.id,
14769      windows->group_leader.id);
14770  if (display_image->debug != MagickFalse)
14771    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14772      windows->image.id);
14773  /*
14774    Initialize Info widget.
14775  */
14776  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14777    &windows->info);
14778  (void) CloneString(&windows->info.name,"Info");
14779  (void) CloneString(&windows->info.icon_name,"Info");
14780  windows->info.border_width=1;
14781  windows->info.x=2;
14782  windows->info.y=2;
14783  windows->info.flags|=PPosition;
14784  windows->info.attributes.win_gravity=UnmapGravity;
14785  windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14786    StructureNotifyMask;
14787  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14788  manager_hints->input=MagickFalse;
14789  manager_hints->initial_state=NormalState;
14790  manager_hints->window_group=windows->image.id;
14791  XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14792    &windows->info);
14793  windows->info.highlight_stipple=XCreateBitmapFromData(display,
14794    windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14795  windows->info.shadow_stipple=XCreateBitmapFromData(display,
14796    windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14797  (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14798  if (windows->image.mapped != MagickFalse)
14799    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14800  if (display_image->debug != MagickFalse)
14801    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14802      windows->info.id);
14803  /*
14804    Initialize Command widget.
14805  */
14806  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14807    resource_info,&windows->command);
14808  windows->command.data=MagickMenus;
14809  (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14810  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
14811    resource_info->client_name);
14812  windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14813    resource_name,"geometry",(char *) NULL);
14814  (void) CloneString(&windows->command.name,MagickTitle);
14815  windows->command.border_width=0;
14816  windows->command.flags|=PPosition;
14817  windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14818    ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14819    OwnerGrabButtonMask | StructureNotifyMask;
14820  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14821  manager_hints->input=MagickTrue;
14822  manager_hints->initial_state=NormalState;
14823  manager_hints->window_group=windows->image.id;
14824  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14825    &windows->command);
14826  windows->command.highlight_stipple=XCreateBitmapFromData(display,
14827    windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14828    HighlightHeight);
14829  windows->command.shadow_stipple=XCreateBitmapFromData(display,
14830    windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14831  (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14832  if (windows->command.mapped != MagickFalse)
14833    (void) XMapRaised(display,windows->command.id);
14834  if (display_image->debug != MagickFalse)
14835    (void) LogMagickEvent(X11Event,GetMagickModule(),
14836      "Window id: 0x%lx (command)",windows->command.id);
14837  /*
14838    Initialize Widget window.
14839  */
14840  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14841    resource_info,&windows->widget);
14842  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
14843    resource_info->client_name);
14844  windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14845    resource_name,"geometry",(char *) NULL);
14846  windows->widget.border_width=0;
14847  windows->widget.flags|=PPosition;
14848  windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14849    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14850    KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14851    StructureNotifyMask;
14852  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14853  manager_hints->input=MagickTrue;
14854  manager_hints->initial_state=NormalState;
14855  manager_hints->window_group=windows->image.id;
14856  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14857    &windows->widget);
14858  windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14859    windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14860  windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14861    windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14862  (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14863  if (display_image->debug != MagickFalse)
14864    (void) LogMagickEvent(X11Event,GetMagickModule(),
14865      "Window id: 0x%lx (widget)",windows->widget.id);
14866  /*
14867    Initialize popup window.
14868  */
14869  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14870    resource_info,&windows->popup);
14871  windows->popup.border_width=0;
14872  windows->popup.flags|=PPosition;
14873  windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14874    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14875    KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14876  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14877  manager_hints->input=MagickTrue;
14878  manager_hints->initial_state=NormalState;
14879  manager_hints->window_group=windows->image.id;
14880  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14881    &windows->popup);
14882  windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14883    windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14884  windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14885    windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14886  (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14887  if (display_image->debug != MagickFalse)
14888    (void) LogMagickEvent(X11Event,GetMagickModule(),
14889      "Window id: 0x%lx (pop up)",windows->popup.id);
14890  /*
14891    Initialize Magnify window and cursor.
14892  */
14893  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14894    resource_info,&windows->magnify);
14895  if (resource_info->use_shared_memory == MagickFalse)
14896    windows->magnify.shared_memory=MagickFalse;
14897  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
14898    resource_info->client_name);
14899  windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14900    resource_name,"geometry",(char *) NULL);
14901  (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14902    resource_info->magnify);
14903  if (windows->magnify.cursor != (Cursor) NULL)
14904    (void) XFreeCursor(display,windows->magnify.cursor);
14905  windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14906    map_info->colormap,resource_info->background_color,
14907    resource_info->foreground_color);
14908  if (windows->magnify.cursor == (Cursor) NULL)
14909    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14910      display_image->filename);
14911  windows->magnify.width=MagnifySize;
14912  windows->magnify.height=MagnifySize;
14913  windows->magnify.flags|=PPosition;
14914  windows->magnify.min_width=MagnifySize;
14915  windows->magnify.min_height=MagnifySize;
14916  windows->magnify.width_inc=MagnifySize;
14917  windows->magnify.height_inc=MagnifySize;
14918  windows->magnify.data=resource_info->magnify;
14919  windows->magnify.attributes.cursor=windows->magnify.cursor;
14920  windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14921    ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14922    StructureNotifyMask;
14923  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14924  manager_hints->input=MagickTrue;
14925  manager_hints->initial_state=NormalState;
14926  manager_hints->window_group=windows->image.id;
14927  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14928    &windows->magnify);
14929  if (display_image->debug != MagickFalse)
14930    (void) LogMagickEvent(X11Event,GetMagickModule(),
14931      "Window id: 0x%lx (magnify)",windows->magnify.id);
14932  (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14933  /*
14934    Initialize panning window.
14935  */
14936  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14937    resource_info,&windows->pan);
14938  (void) CloneString(&windows->pan.name,"Pan Icon");
14939  windows->pan.width=windows->icon.width;
14940  windows->pan.height=windows->icon.height;
14941  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
14942    resource_info->client_name);
14943  windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
14944    resource_name,"geometry",(char *) NULL);
14945  (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
14946    &windows->pan.width,&windows->pan.height);
14947  windows->pan.flags|=PPosition;
14948  windows->pan.immutable=MagickTrue;
14949  windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14950    ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
14951    StructureNotifyMask;
14952  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14953  manager_hints->input=MagickFalse;
14954  manager_hints->initial_state=NormalState;
14955  manager_hints->window_group=windows->image.id;
14956  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14957    &windows->pan);
14958  if (display_image->debug != MagickFalse)
14959    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
14960      windows->pan.id);
14961  (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
14962  if (windows->info.mapped != MagickFalse)
14963    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14964  if ((windows->image.mapped == MagickFalse) ||
14965      (windows->backdrop.id != (Window) NULL))
14966    (void) XMapWindow(display,windows->image.id);
14967  /*
14968    Set our progress monitor and warning handlers.
14969  */
14970  if (warning_handler == (WarningHandler) NULL)
14971    {
14972      warning_handler=resource_info->display_warnings ?
14973        SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14974      warning_handler=resource_info->display_warnings ?
14975        SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14976    }
14977  /*
14978    Initialize Image and Magnify X images.
14979  */
14980  windows->image.x=0;
14981  windows->image.y=0;
14982  windows->magnify.shape=MagickFalse;
14983  width=(unsigned int) display_image->columns;
14984  height=(unsigned int) display_image->rows;
14985  if ((display_image->columns != width) || (display_image->rows != height))
14986    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14987      display_image->filename);
14988  status=XMakeImage(display,resource_info,&windows->image,display_image,
14989    width,height,exception);
14990  if (status == MagickFalse)
14991    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14992      display_image->filename);
14993  status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
14994    windows->magnify.width,windows->magnify.height,exception);
14995  if (status == MagickFalse)
14996    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14997      display_image->filename);
14998  if (windows->magnify.mapped != MagickFalse)
14999    (void) XMapRaised(display,windows->magnify.id);
15000  if (windows->pan.mapped != MagickFalse)
15001    (void) XMapRaised(display,windows->pan.id);
15002  windows->image.window_changes.width=(int) display_image->columns;
15003  windows->image.window_changes.height=(int) display_image->rows;
15004  (void) XConfigureImage(display,resource_info,windows,display_image,exception);
15005  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15006  (void) XSync(display,MagickFalse);
15007  /*
15008    Respond to events.
15009  */
15010  delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
15011  timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15012  update_time=0;
15013  if (resource_info->update != MagickFalse)
15014    {
15015      MagickBooleanType
15016        status;
15017
15018      /*
15019        Determine when file data was last modified.
15020      */
15021      status=GetPathAttributes(display_image->filename,&attributes);
15022      if (status != MagickFalse)
15023        update_time=attributes.st_mtime;
15024    }
15025  *state&=(~FormerImageState);
15026  *state&=(~MontageImageState);
15027  *state&=(~NextImageState);
15028  do
15029  {
15030    /*
15031      Handle a window event.
15032    */
15033    if (windows->image.mapped != MagickFalse)
15034      if ((display_image->delay != 0) || (resource_info->update != 0))
15035        {
15036          if (timer < time((time_t *) NULL))
15037            {
15038              if (resource_info->update == MagickFalse)
15039                *state|=NextImageState | ExitState;
15040              else
15041                {
15042                  MagickBooleanType
15043                    status;
15044
15045                  /*
15046                    Determine if image file was modified.
15047                  */
15048                  status=GetPathAttributes(display_image->filename,&attributes);
15049                  if (status != MagickFalse)
15050                    if (update_time != attributes.st_mtime)
15051                      {
15052                        /*
15053                          Redisplay image.
15054                        */
15055                        (void) FormatLocaleString(
15056                          resource_info->image_info->filename,MaxTextExtent,
15057                          "%s:%s",display_image->magick,
15058                          display_image->filename);
15059                        nexus=ReadImage(resource_info->image_info,
15060                          &display_image->exception);
15061                        if (nexus != (Image *) NULL)
15062                          {
15063                            nexus=DestroyImage(nexus);
15064                            *state|=NextImageState | ExitState;
15065                          }
15066                      }
15067                  delay=display_image->delay/MagickMax(
15068                    display_image->ticks_per_second,1L);
15069                  timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15070                }
15071            }
15072          if (XEventsQueued(display,QueuedAfterFlush) == 0)
15073            {
15074              /*
15075                Do not block if delay > 0.
15076              */
15077              XDelay(display,SuspendTime << 2);
15078              continue;
15079            }
15080        }
15081    timestamp=time((time_t *) NULL);
15082    (void) XNextEvent(display,&event);
15083    if (windows->image.stasis == MagickFalse)
15084      windows->image.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15085        MagickTrue : MagickFalse;
15086    if (windows->magnify.stasis == MagickFalse)
15087      windows->magnify.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15088        MagickTrue : MagickFalse;
15089    if (event.xany.window == windows->command.id)
15090      {
15091        /*
15092          Select a command from the Command widget.
15093        */
15094        id=XCommandWidget(display,windows,CommandMenu,&event);
15095        if (id < 0)
15096          continue;
15097        (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15098        command_type=CommandMenus[id];
15099        if (id < MagickMenus)
15100          {
15101            /*
15102              Select a command from a pop-up menu.
15103            */
15104            entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15105              command);
15106            if (entry < 0)
15107              continue;
15108            (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15109            command_type=Commands[id][entry];
15110          }
15111        if (command_type != NullCommand)
15112          nexus=XMagickCommand(display,resource_info,windows,command_type,
15113            &display_image,exception);
15114        continue;
15115      }
15116    switch (event.type)
15117    {
15118      case ButtonPress:
15119      {
15120        if (display_image->debug != MagickFalse)
15121          (void) LogMagickEvent(X11Event,GetMagickModule(),
15122            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15123            event.xbutton.button,event.xbutton.x,event.xbutton.y);
15124        if ((event.xbutton.button == Button3) &&
15125            (event.xbutton.state & Mod1Mask))
15126          {
15127            /*
15128              Convert Alt-Button3 to Button2.
15129            */
15130            event.xbutton.button=Button2;
15131            event.xbutton.state&=(~Mod1Mask);
15132          }
15133        if (event.xbutton.window == windows->backdrop.id)
15134          {
15135            (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15136              event.xbutton.time);
15137            break;
15138          }
15139        if (event.xbutton.window == windows->image.id)
15140          {
15141            switch (event.xbutton.button)
15142            {
15143              case Button1:
15144              {
15145                if (resource_info->immutable)
15146                  {
15147                    /*
15148                      Select a command from the Virtual menu.
15149                    */
15150                    entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15151                      command);
15152                    if (entry >= 0)
15153                      nexus=XMagickCommand(display,resource_info,windows,
15154                        VirtualCommands[entry],&display_image,exception);
15155                    break;
15156                  }
15157                /*
15158                  Map/unmap Command widget.
15159                */
15160                if (windows->command.mapped != MagickFalse)
15161                  (void) XWithdrawWindow(display,windows->command.id,
15162                    windows->command.screen);
15163                else
15164                  {
15165                    (void) XCommandWidget(display,windows,CommandMenu,
15166                      (XEvent *) NULL);
15167                    (void) XMapRaised(display,windows->command.id);
15168                  }
15169                break;
15170              }
15171              case Button2:
15172              {
15173                /*
15174                  User pressed the image magnify button.
15175                */
15176                (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15177                  &display_image,exception);
15178                XMagnifyImage(display,windows,&event);
15179                break;
15180              }
15181              case Button3:
15182              {
15183                if (resource_info->immutable)
15184                  {
15185                    /*
15186                      Select a command from the Virtual menu.
15187                    */
15188                    entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15189                      command);
15190                    if (entry >= 0)
15191                      nexus=XMagickCommand(display,resource_info,windows,
15192                        VirtualCommands[entry],&display_image,exception);
15193                    break;
15194                  }
15195                if (display_image->montage != (char *) NULL)
15196                  {
15197                    /*
15198                      Open or delete a tile from a visual image directory.
15199                    */
15200                    nexus=XTileImage(display,resource_info,windows,
15201                      display_image,&event,exception);
15202                    if (nexus != (Image *) NULL)
15203                      *state|=MontageImageState | NextImageState | ExitState;
15204                    vid_info.x=(short int) windows->image.x;
15205                    vid_info.y=(short int) windows->image.y;
15206                    break;
15207                  }
15208                /*
15209                  Select a command from the Short Cuts menu.
15210                */
15211                entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15212                  command);
15213                if (entry >= 0)
15214                  nexus=XMagickCommand(display,resource_info,windows,
15215                    ShortCutsCommands[entry],&display_image,exception);
15216                break;
15217              }
15218              case Button4:
15219              {
15220                /*
15221                  Wheel up.
15222                */
15223                XTranslateImage(display,windows,*image,XK_Up);
15224                break;
15225              }
15226              case Button5:
15227              {
15228                /*
15229                  Wheel down.
15230                */
15231                XTranslateImage(display,windows,*image,XK_Down);
15232                break;
15233              }
15234              default:
15235                break;
15236            }
15237            break;
15238          }
15239        if (event.xbutton.window == windows->magnify.id)
15240          {
15241            int
15242              factor;
15243
15244            static const char
15245              *MagnifyMenu[] =
15246              {
15247                "2",
15248                "4",
15249                "5",
15250                "6",
15251                "7",
15252                "8",
15253                "9",
15254                "3",
15255                (char *) NULL,
15256              };
15257
15258            static KeySym
15259              MagnifyCommands[] =
15260              {
15261                XK_2,
15262                XK_4,
15263                XK_5,
15264                XK_6,
15265                XK_7,
15266                XK_8,
15267                XK_9,
15268                XK_3
15269              };
15270
15271            /*
15272              Select a magnify factor from the pop-up menu.
15273            */
15274            factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15275            if (factor >= 0)
15276              XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor]);
15277            break;
15278          }
15279        if (event.xbutton.window == windows->pan.id)
15280          {
15281            switch (event.xbutton.button)
15282            {
15283              case Button4:
15284              {
15285                /*
15286                  Wheel up.
15287                */
15288                XTranslateImage(display,windows,*image,XK_Up);
15289                break;
15290              }
15291              case Button5:
15292              {
15293                /*
15294                  Wheel down.
15295                */
15296                XTranslateImage(display,windows,*image,XK_Down);
15297                break;
15298              }
15299              default:
15300              {
15301                XPanImage(display,windows,&event);
15302                break;
15303              }
15304            }
15305            break;
15306          }
15307        delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15308          1L);
15309        timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15310        break;
15311      }
15312      case ButtonRelease:
15313      {
15314        if (display_image->debug != MagickFalse)
15315          (void) LogMagickEvent(X11Event,GetMagickModule(),
15316            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15317            event.xbutton.button,event.xbutton.x,event.xbutton.y);
15318        break;
15319      }
15320      case ClientMessage:
15321      {
15322        if (display_image->debug != MagickFalse)
15323          (void) LogMagickEvent(X11Event,GetMagickModule(),
15324            "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15325            event.xclient.message_type,event.xclient.format,(unsigned long)
15326            event.xclient.data.l[0]);
15327        if (event.xclient.message_type == windows->im_protocols)
15328          {
15329            if (*event.xclient.data.l == (long) windows->im_update_widget)
15330              {
15331                (void) CloneString(&windows->command.name,MagickTitle);
15332                windows->command.data=MagickMenus;
15333                (void) XCommandWidget(display,windows,CommandMenu,
15334                  (XEvent *) NULL);
15335                break;
15336              }
15337            if (*event.xclient.data.l == (long) windows->im_update_colormap)
15338              {
15339                /*
15340                  Update graphic context and window colormap.
15341                */
15342                for (i=0; i < (int) number_windows; i++)
15343                {
15344                  if (magick_windows[i]->id == windows->icon.id)
15345                    continue;
15346                  context_values.background=pixel->background_color.pixel;
15347                  context_values.foreground=pixel->foreground_color.pixel;
15348                  (void) XChangeGC(display,magick_windows[i]->annotate_context,
15349                    context_mask,&context_values);
15350                  (void) XChangeGC(display,magick_windows[i]->widget_context,
15351                    context_mask,&context_values);
15352                  context_values.background=pixel->foreground_color.pixel;
15353                  context_values.foreground=pixel->background_color.pixel;
15354                  context_values.plane_mask=context_values.background ^
15355                    context_values.foreground;
15356                  (void) XChangeGC(display,magick_windows[i]->highlight_context,
15357                    (size_t) (context_mask | GCPlaneMask),
15358                    &context_values);
15359                  magick_windows[i]->attributes.background_pixel=
15360                    pixel->background_color.pixel;
15361                  magick_windows[i]->attributes.border_pixel=
15362                    pixel->border_color.pixel;
15363                  magick_windows[i]->attributes.colormap=map_info->colormap;
15364                  (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15365                    (unsigned long) magick_windows[i]->mask,
15366                    &magick_windows[i]->attributes);
15367                }
15368                if (windows->pan.mapped != MagickFalse)
15369                  {
15370                    (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15371                      windows->pan.pixmap);
15372                    (void) XClearWindow(display,windows->pan.id);
15373                    XDrawPanRectangle(display,windows);
15374                  }
15375                if (windows->backdrop.id != (Window) NULL)
15376                  (void) XInstallColormap(display,map_info->colormap);
15377                break;
15378              }
15379            if (*event.xclient.data.l == (long) windows->im_former_image)
15380              {
15381                *state|=FormerImageState | ExitState;
15382                break;
15383              }
15384            if (*event.xclient.data.l == (long) windows->im_next_image)
15385              {
15386                *state|=NextImageState | ExitState;
15387                break;
15388              }
15389            if (*event.xclient.data.l == (long) windows->im_retain_colors)
15390              {
15391                *state|=RetainColorsState;
15392                break;
15393              }
15394            if (*event.xclient.data.l == (long) windows->im_exit)
15395              {
15396                *state|=ExitState;
15397                break;
15398              }
15399            break;
15400          }
15401        if (event.xclient.message_type == windows->dnd_protocols)
15402          {
15403            Atom
15404              selection,
15405              type;
15406
15407            int
15408              format,
15409              status;
15410
15411            unsigned char
15412              *data;
15413
15414            unsigned long
15415              after,
15416              length;
15417
15418            /*
15419              Display image named by the Drag-and-Drop selection.
15420            */
15421            if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15422              break;
15423            selection=XInternAtom(display,"DndSelection",MagickFalse);
15424            status=XGetWindowProperty(display,root_window,selection,0L,(long)
15425              MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15426              &length,&after,&data);
15427            if ((status != Success) || (length == 0))
15428              break;
15429            if (*event.xclient.data.l == 2)
15430              {
15431                /*
15432                  Offix DND.
15433                */
15434                (void) CopyMagickString(resource_info->image_info->filename,
15435                  (char *) data,MaxTextExtent);
15436              }
15437            else
15438              {
15439                /*
15440                  XDND.
15441                */
15442                if (strncmp((char *) data, "file:", 5) != 0)
15443                  {
15444                    (void) XFree((void *) data);
15445                    break;
15446                  }
15447                (void) CopyMagickString(resource_info->image_info->filename,
15448                  ((char *) data)+5,MaxTextExtent);
15449              }
15450            nexus=ReadImage(resource_info->image_info,
15451              &display_image->exception);
15452            CatchException(&display_image->exception);
15453            if (nexus != (Image *) NULL)
15454              *state|=NextImageState | ExitState;
15455            (void) XFree((void *) data);
15456            break;
15457          }
15458        /*
15459          If client window delete message, exit.
15460        */
15461        if (event.xclient.message_type != windows->wm_protocols)
15462          break;
15463        if (*event.xclient.data.l != (long) windows->wm_delete_window)
15464          break;
15465        (void) XWithdrawWindow(display,event.xclient.window,
15466          visual_info->screen);
15467        if (event.xclient.window == windows->image.id)
15468          {
15469            *state|=ExitState;
15470            break;
15471          }
15472        if (event.xclient.window == windows->pan.id)
15473          {
15474            /*
15475              Restore original image size when pan window is deleted.
15476            */
15477            windows->image.window_changes.width=windows->image.ximage->width;
15478            windows->image.window_changes.height=windows->image.ximage->height;
15479            (void) XConfigureImage(display,resource_info,windows,
15480              display_image,exception);
15481          }
15482        break;
15483      }
15484      case ConfigureNotify:
15485      {
15486        if (display_image->debug != MagickFalse)
15487          (void) LogMagickEvent(X11Event,GetMagickModule(),
15488            "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15489            event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15490            event.xconfigure.y,event.xconfigure.send_event);
15491        if (event.xconfigure.window == windows->image.id)
15492          {
15493            /*
15494              Image window has a new configuration.
15495            */
15496            if (event.xconfigure.send_event != 0)
15497              {
15498                XWindowChanges
15499                  window_changes;
15500
15501                /*
15502                  Position the transient windows relative of the Image window.
15503                */
15504                if (windows->command.geometry == (char *) NULL)
15505                  if (windows->command.mapped == MagickFalse)
15506                    {
15507                      windows->command.x=event.xconfigure.x-
15508                        windows->command.width-25;
15509                      windows->command.y=event.xconfigure.y;
15510                      XConstrainWindowPosition(display,&windows->command);
15511                      window_changes.x=windows->command.x;
15512                      window_changes.y=windows->command.y;
15513                      (void) XReconfigureWMWindow(display,windows->command.id,
15514                        windows->command.screen,(unsigned int) (CWX | CWY),
15515                        &window_changes);
15516                    }
15517                if (windows->widget.geometry == (char *) NULL)
15518                  if (windows->widget.mapped == MagickFalse)
15519                    {
15520                      windows->widget.x=event.xconfigure.x+
15521                        event.xconfigure.width/10;
15522                      windows->widget.y=event.xconfigure.y+
15523                        event.xconfigure.height/10;
15524                      XConstrainWindowPosition(display,&windows->widget);
15525                      window_changes.x=windows->widget.x;
15526                      window_changes.y=windows->widget.y;
15527                      (void) XReconfigureWMWindow(display,windows->widget.id,
15528                        windows->widget.screen,(unsigned int) (CWX | CWY),
15529                        &window_changes);
15530                    }
15531                if (windows->magnify.geometry == (char *) NULL)
15532                  if (windows->magnify.mapped == MagickFalse)
15533                    {
15534                      windows->magnify.x=event.xconfigure.x+
15535                        event.xconfigure.width+25;
15536                      windows->magnify.y=event.xconfigure.y;
15537                      XConstrainWindowPosition(display,&windows->magnify);
15538                      window_changes.x=windows->magnify.x;
15539                      window_changes.y=windows->magnify.y;
15540                      (void) XReconfigureWMWindow(display,windows->magnify.id,
15541                        windows->magnify.screen,(unsigned int) (CWX | CWY),
15542                        &window_changes);
15543                    }
15544                if (windows->pan.geometry == (char *) NULL)
15545                  if (windows->pan.mapped == MagickFalse)
15546                    {
15547                      windows->pan.x=event.xconfigure.x+
15548                        event.xconfigure.width+25;
15549                      windows->pan.y=event.xconfigure.y+
15550                        windows->magnify.height+50;
15551                      XConstrainWindowPosition(display,&windows->pan);
15552                      window_changes.x=windows->pan.x;
15553                      window_changes.y=windows->pan.y;
15554                      (void) XReconfigureWMWindow(display,windows->pan.id,
15555                        windows->pan.screen,(unsigned int) (CWX | CWY),
15556                        &window_changes);
15557                    }
15558              }
15559            if ((event.xconfigure.width == (int) windows->image.width) &&
15560                (event.xconfigure.height == (int) windows->image.height))
15561              break;
15562            windows->image.width=(unsigned int) event.xconfigure.width;
15563            windows->image.height=(unsigned int) event.xconfigure.height;
15564            windows->image.x=0;
15565            windows->image.y=0;
15566            if (display_image->montage != (char *) NULL)
15567              {
15568                windows->image.x=vid_info.x;
15569                windows->image.y=vid_info.y;
15570              }
15571            if ((windows->image.mapped != MagickFalse) &&
15572                (windows->image.stasis != MagickFalse))
15573              {
15574                /*
15575                  Update image window configuration.
15576                */
15577                windows->image.window_changes.width=event.xconfigure.width;
15578                windows->image.window_changes.height=event.xconfigure.height;
15579                (void) XConfigureImage(display,resource_info,windows,
15580                  display_image,exception);
15581              }
15582            /*
15583              Update pan window configuration.
15584            */
15585            if ((event.xconfigure.width < windows->image.ximage->width) ||
15586                (event.xconfigure.height < windows->image.ximage->height))
15587              {
15588                (void) XMapRaised(display,windows->pan.id);
15589                XDrawPanRectangle(display,windows);
15590              }
15591            else
15592              if (windows->pan.mapped != MagickFalse)
15593                (void) XWithdrawWindow(display,windows->pan.id,
15594                  windows->pan.screen);
15595            break;
15596          }
15597        if (event.xconfigure.window == windows->magnify.id)
15598          {
15599            unsigned int
15600              magnify;
15601
15602            /*
15603              Magnify window has a new configuration.
15604            */
15605            windows->magnify.width=(unsigned int) event.xconfigure.width;
15606            windows->magnify.height=(unsigned int) event.xconfigure.height;
15607            if (windows->magnify.mapped == MagickFalse)
15608              break;
15609            magnify=1;
15610            while ((int) magnify <= event.xconfigure.width)
15611              magnify<<=1;
15612            while ((int) magnify <= event.xconfigure.height)
15613              magnify<<=1;
15614            magnify>>=1;
15615            if (((int) magnify != event.xconfigure.width) ||
15616                ((int) magnify != event.xconfigure.height))
15617              {
15618                window_changes.width=(int) magnify;
15619                window_changes.height=(int) magnify;
15620                (void) XReconfigureWMWindow(display,windows->magnify.id,
15621                  windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15622                  &window_changes);
15623                break;
15624              }
15625            if ((windows->magnify.mapped != MagickFalse) &&
15626                (windows->magnify.stasis != MagickFalse))
15627              {
15628                status=XMakeImage(display,resource_info,&windows->magnify,
15629                  display_image,windows->magnify.width,windows->magnify.height,
15630                  exception);
15631                XMakeMagnifyImage(display,windows);
15632              }
15633            break;
15634          }
15635        if ((windows->magnify.mapped != MagickFalse) &&
15636            (event.xconfigure.window == windows->pan.id))
15637          {
15638            /*
15639              Pan icon window has a new configuration.
15640            */
15641            if (event.xconfigure.send_event != 0)
15642              {
15643                windows->pan.x=event.xconfigure.x;
15644                windows->pan.y=event.xconfigure.y;
15645              }
15646            windows->pan.width=(unsigned int) event.xconfigure.width;
15647            windows->pan.height=(unsigned int) event.xconfigure.height;
15648            break;
15649          }
15650        if (event.xconfigure.window == windows->icon.id)
15651          {
15652            /*
15653              Icon window has a new configuration.
15654            */
15655            windows->icon.width=(unsigned int) event.xconfigure.width;
15656            windows->icon.height=(unsigned int) event.xconfigure.height;
15657            break;
15658          }
15659        break;
15660      }
15661      case DestroyNotify:
15662      {
15663        /*
15664          Group leader has exited.
15665        */
15666        if (display_image->debug != MagickFalse)
15667          (void) LogMagickEvent(X11Event,GetMagickModule(),
15668            "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15669        if (event.xdestroywindow.window == windows->group_leader.id)
15670          {
15671            *state|=ExitState;
15672            break;
15673          }
15674        break;
15675      }
15676      case EnterNotify:
15677      {
15678        /*
15679          Selectively install colormap.
15680        */
15681        if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15682          if (event.xcrossing.mode != NotifyUngrab)
15683            XInstallColormap(display,map_info->colormap);
15684        break;
15685      }
15686      case Expose:
15687      {
15688        if (display_image->debug != MagickFalse)
15689          (void) LogMagickEvent(X11Event,GetMagickModule(),
15690            "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15691            event.xexpose.width,event.xexpose.height,event.xexpose.x,
15692            event.xexpose.y);
15693        /*
15694          Refresh windows that are now exposed.
15695        */
15696        if ((event.xexpose.window == windows->image.id) &&
15697            (windows->image.mapped != MagickFalse))
15698          {
15699            XRefreshWindow(display,&windows->image,&event);
15700            delay=display_image->delay/MagickMax(
15701              display_image->ticks_per_second,1L);
15702            timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15703            break;
15704          }
15705        if ((event.xexpose.window == windows->magnify.id) &&
15706            (windows->magnify.mapped != MagickFalse))
15707          {
15708            XMakeMagnifyImage(display,windows);
15709            break;
15710          }
15711        if (event.xexpose.window == windows->pan.id)
15712          {
15713            XDrawPanRectangle(display,windows);
15714            break;
15715          }
15716        if (event.xexpose.window == windows->icon.id)
15717          {
15718            XRefreshWindow(display,&windows->icon,&event);
15719            break;
15720          }
15721        break;
15722      }
15723      case KeyPress:
15724      {
15725        int
15726          length;
15727
15728        /*
15729          Respond to a user key press.
15730        */
15731        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15732          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15733        *(command+length)='\0';
15734        if (display_image->debug != MagickFalse)
15735          (void) LogMagickEvent(X11Event,GetMagickModule(),
15736            "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15737            key_symbol,command);
15738        if (event.xkey.window == windows->image.id)
15739          {
15740            command_type=XImageWindowCommand(display,resource_info,windows,
15741              event.xkey.state,key_symbol,&display_image,exception);
15742            if (command_type != NullCommand)
15743              nexus=XMagickCommand(display,resource_info,windows,command_type,
15744                &display_image,exception);
15745          }
15746        if (event.xkey.window == windows->magnify.id)
15747          XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol);
15748        if (event.xkey.window == windows->pan.id)
15749          {
15750            if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15751              (void) XWithdrawWindow(display,windows->pan.id,
15752                windows->pan.screen);
15753            else
15754              if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15755                XTextViewWidget(display,resource_info,windows,MagickFalse,
15756                  "Help Viewer - Image Pan",ImagePanHelp);
15757              else
15758                XTranslateImage(display,windows,*image,key_symbol);
15759          }
15760        delay=display_image->delay/MagickMax(
15761          display_image->ticks_per_second,1L);
15762        timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15763        break;
15764      }
15765      case KeyRelease:
15766      {
15767        /*
15768          Respond to a user key release.
15769        */
15770        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15771          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15772        if (display_image->debug != MagickFalse)
15773          (void) LogMagickEvent(X11Event,GetMagickModule(),
15774            "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15775        break;
15776      }
15777      case LeaveNotify:
15778      {
15779        /*
15780          Selectively uninstall colormap.
15781        */
15782        if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15783          if (event.xcrossing.mode != NotifyUngrab)
15784            XUninstallColormap(display,map_info->colormap);
15785        break;
15786      }
15787      case MapNotify:
15788      {
15789        if (display_image->debug != MagickFalse)
15790          (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15791            event.xmap.window);
15792        if (event.xmap.window == windows->backdrop.id)
15793          {
15794            (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15795              CurrentTime);
15796            windows->backdrop.mapped=MagickTrue;
15797            break;
15798          }
15799        if (event.xmap.window == windows->image.id)
15800          {
15801            if (windows->backdrop.id != (Window) NULL)
15802              (void) XInstallColormap(display,map_info->colormap);
15803            if (LocaleCompare(display_image->magick,"LOGO") == 0)
15804              {
15805                if (LocaleCompare(display_image->filename,"LOGO") == 0)
15806                  nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15807              }
15808            if (((int) windows->image.width < windows->image.ximage->width) ||
15809                ((int) windows->image.height < windows->image.ximage->height))
15810              (void) XMapRaised(display,windows->pan.id);
15811            windows->image.mapped=MagickTrue;
15812            break;
15813          }
15814        if (event.xmap.window == windows->magnify.id)
15815          {
15816            XMakeMagnifyImage(display,windows);
15817            windows->magnify.mapped=MagickTrue;
15818            (void) XWithdrawWindow(display,windows->info.id,
15819              windows->info.screen);
15820            break;
15821          }
15822        if (event.xmap.window == windows->pan.id)
15823          {
15824            XMakePanImage(display,resource_info,windows,display_image,
15825              exception);
15826            windows->pan.mapped=MagickTrue;
15827            break;
15828          }
15829        if (event.xmap.window == windows->info.id)
15830          {
15831            windows->info.mapped=MagickTrue;
15832            break;
15833          }
15834        if (event.xmap.window == windows->icon.id)
15835          {
15836            MagickBooleanType
15837              taint;
15838
15839            /*
15840              Create an icon image.
15841            */
15842            taint=display_image->taint;
15843            XMakeStandardColormap(display,icon_visual,icon_resources,
15844              display_image,icon_map,icon_pixel);
15845            (void) XMakeImage(display,icon_resources,&windows->icon,
15846              display_image,windows->icon.width,windows->icon.height,
15847              exception);
15848            display_image->taint=taint;
15849            (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15850              windows->icon.pixmap);
15851            (void) XClearWindow(display,windows->icon.id);
15852            (void) XWithdrawWindow(display,windows->info.id,
15853              windows->info.screen);
15854            windows->icon.mapped=MagickTrue;
15855            break;
15856          }
15857        if (event.xmap.window == windows->command.id)
15858          {
15859            windows->command.mapped=MagickTrue;
15860            break;
15861          }
15862        if (event.xmap.window == windows->popup.id)
15863          {
15864            windows->popup.mapped=MagickTrue;
15865            break;
15866          }
15867        if (event.xmap.window == windows->widget.id)
15868          {
15869            windows->widget.mapped=MagickTrue;
15870            break;
15871          }
15872        break;
15873      }
15874      case MappingNotify:
15875      {
15876        (void) XRefreshKeyboardMapping(&event.xmapping);
15877        break;
15878      }
15879      case NoExpose:
15880        break;
15881      case PropertyNotify:
15882      {
15883        Atom
15884          type;
15885
15886        int
15887          format,
15888          status;
15889
15890        unsigned char
15891          *data;
15892
15893        unsigned long
15894          after,
15895          length;
15896
15897        if (display_image->debug != MagickFalse)
15898          (void) LogMagickEvent(X11Event,GetMagickModule(),
15899            "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15900            event.xproperty.atom,event.xproperty.state);
15901        if (event.xproperty.atom != windows->im_remote_command)
15902          break;
15903        /*
15904          Display image named by the remote command protocol.
15905        */
15906        status=XGetWindowProperty(display,event.xproperty.window,
15907          event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15908          AnyPropertyType,&type,&format,&length,&after,&data);
15909        if ((status != Success) || (length == 0))
15910          break;
15911        if (LocaleCompare((char *) data,"-quit") == 0)
15912          {
15913            XClientMessage(display,windows->image.id,windows->im_protocols,
15914              windows->im_exit,CurrentTime);
15915            (void) XFree((void *) data);
15916            break;
15917          }
15918        (void) CopyMagickString(resource_info->image_info->filename,
15919          (char *) data,MaxTextExtent);
15920        (void) XFree((void *) data);
15921        nexus=ReadImage(resource_info->image_info,&display_image->exception);
15922        CatchException(&display_image->exception);
15923        if (nexus != (Image *) NULL)
15924          *state|=NextImageState | ExitState;
15925        break;
15926      }
15927      case ReparentNotify:
15928      {
15929        if (display_image->debug != MagickFalse)
15930          (void) LogMagickEvent(X11Event,GetMagickModule(),
15931            "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15932            event.xreparent.window);
15933        break;
15934      }
15935      case UnmapNotify:
15936      {
15937        if (display_image->debug != MagickFalse)
15938          (void) LogMagickEvent(X11Event,GetMagickModule(),
15939            "Unmap Notify: 0x%lx",event.xunmap.window);
15940        if (event.xunmap.window == windows->backdrop.id)
15941          {
15942            windows->backdrop.mapped=MagickFalse;
15943            break;
15944          }
15945        if (event.xunmap.window == windows->image.id)
15946          {
15947            windows->image.mapped=MagickFalse;
15948            break;
15949          }
15950        if (event.xunmap.window == windows->magnify.id)
15951          {
15952            windows->magnify.mapped=MagickFalse;
15953            break;
15954          }
15955        if (event.xunmap.window == windows->pan.id)
15956          {
15957            windows->pan.mapped=MagickFalse;
15958            break;
15959          }
15960        if (event.xunmap.window == windows->info.id)
15961          {
15962            windows->info.mapped=MagickFalse;
15963            break;
15964          }
15965        if (event.xunmap.window == windows->icon.id)
15966          {
15967            if (map_info->colormap == icon_map->colormap)
15968              XConfigureImageColormap(display,resource_info,windows,
15969                display_image);
15970            (void) XFreeStandardColormap(display,icon_visual,icon_map,
15971              icon_pixel);
15972            windows->icon.mapped=MagickFalse;
15973            break;
15974          }
15975        if (event.xunmap.window == windows->command.id)
15976          {
15977            windows->command.mapped=MagickFalse;
15978            break;
15979          }
15980        if (event.xunmap.window == windows->popup.id)
15981          {
15982            if (windows->backdrop.id != (Window) NULL)
15983              (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15984                CurrentTime);
15985            windows->popup.mapped=MagickFalse;
15986            break;
15987          }
15988        if (event.xunmap.window == windows->widget.id)
15989          {
15990            if (windows->backdrop.id != (Window) NULL)
15991              (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15992                CurrentTime);
15993            windows->widget.mapped=MagickFalse;
15994            break;
15995          }
15996        break;
15997      }
15998      default:
15999      {
16000        if (display_image->debug != MagickFalse)
16001          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
16002            event.type);
16003        break;
16004      }
16005    }
16006  } while (!(*state & ExitState));
16007  if ((*state & ExitState) == 0)
16008    (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
16009      &display_image,exception);
16010  else
16011    if (resource_info->confirm_edit != MagickFalse)
16012      {
16013        /*
16014          Query user if image has changed.
16015        */
16016        if ((resource_info->immutable == MagickFalse) &&
16017            (display_image->taint != MagickFalse))
16018          {
16019            int
16020              status;
16021
16022            status=XConfirmWidget(display,windows,"Your image changed.",
16023              "Do you want to save it");
16024            if (status == 0)
16025              *state&=(~ExitState);
16026            else
16027              if (status > 0)
16028                (void) XMagickCommand(display,resource_info,windows,SaveCommand,
16029                  &display_image,exception);
16030          }
16031      }
16032  if ((windows->visual_info->klass == GrayScale) ||
16033      (windows->visual_info->klass == PseudoColor) ||
16034      (windows->visual_info->klass == DirectColor))
16035    {
16036      /*
16037        Withdraw pan and Magnify window.
16038      */
16039      if (windows->info.mapped != MagickFalse)
16040        (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16041      if (windows->magnify.mapped != MagickFalse)
16042        (void) XWithdrawWindow(display,windows->magnify.id,
16043          windows->magnify.screen);
16044      if (windows->command.mapped != MagickFalse)
16045        (void) XWithdrawWindow(display,windows->command.id,
16046          windows->command.screen);
16047    }
16048  if (windows->pan.mapped != MagickFalse)
16049    (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16050  if (resource_info->backdrop == MagickFalse)
16051    if (windows->backdrop.mapped)
16052      {
16053        (void) XWithdrawWindow(display,windows->backdrop.id,
16054          windows->backdrop.screen);
16055        (void) XDestroyWindow(display,windows->backdrop.id);
16056        windows->backdrop.id=(Window) NULL;
16057        (void) XWithdrawWindow(display,windows->image.id,
16058          windows->image.screen);
16059        (void) XDestroyWindow(display,windows->image.id);
16060        windows->image.id=(Window) NULL;
16061      }
16062  XSetCursorState(display,windows,MagickTrue);
16063  XCheckRefreshWindows(display,windows);
16064  if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16065    *state&=(~ExitState);
16066  if (*state & ExitState)
16067    {
16068      /*
16069        Free Standard Colormap.
16070      */
16071      (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16072      if (resource_info->map_type == (char *) NULL)
16073        (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16074      /*
16075        Free X resources.
16076      */
16077      if (resource_info->copy_image != (Image *) NULL)
16078        {
16079          resource_info->copy_image=DestroyImage(resource_info->copy_image);
16080          resource_info->copy_image=NewImageList();
16081        }
16082      DestroyXResources();
16083    }
16084  (void) XSync(display,MagickFalse);
16085  /*
16086    Restore our progress monitor and warning handlers.
16087  */
16088  (void) SetErrorHandler(warning_handler);
16089  (void) SetWarningHandler(warning_handler);
16090  /*
16091    Change to home directory.
16092  */
16093  directory=getcwd(working_directory,MaxTextExtent);
16094  (void) directory;
16095  {
16096    int
16097      status;
16098
16099    status=chdir(resource_info->home_directory);
16100    if (status == -1)
16101      (void) ThrowMagickException(&display_image->exception,GetMagickModule(),
16102        FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
16103  }
16104  *image=display_image;
16105  return(nexus);
16106}
16107#else
16108
16109/*
16110%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16111%                                                                             %
16112%                                                                             %
16113%                                                                             %
16114+   D i s p l a y I m a g e s                                                 %
16115%                                                                             %
16116%                                                                             %
16117%                                                                             %
16118%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16119%
16120%  DisplayImages() displays an image sequence to any X window screen.  It
16121%  returns a value other than 0 if successful.  Check the exception member
16122%  of image to determine the reason for any failure.
16123%
16124%  The format of the DisplayImages method is:
16125%
16126%      MagickBooleanType DisplayImages(const ImageInfo *image_info,
16127%        Image *images,ExceptionInfo *exception)
16128%
16129%  A description of each parameter follows:
16130%
16131%    o image_info: the image info.
16132%
16133%    o image: the image.
16134%
16135%    o exception: return any errors or warnings in this structure.
16136%
16137*/
16138MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16139  Image *image,ExceptionInfo *exception)
16140{
16141  assert(image_info != (const ImageInfo *) NULL);
16142  assert(image_info->signature == MagickSignature);
16143  assert(image != (Image *) NULL);
16144  assert(image->signature == MagickSignature);
16145  if (image->debug != MagickFalse)
16146    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16147  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16148    "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image->filename);
16149  return(MagickFalse);
16150}
16151
16152/*
16153%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16154%                                                                             %
16155%                                                                             %
16156%                                                                             %
16157+   R e m o t e D i s p l a y C o m m a n d                                   %
16158%                                                                             %
16159%                                                                             %
16160%                                                                             %
16161%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16162%
16163%  RemoteDisplayCommand() encourages a remote display program to display the
16164%  specified image filename.
16165%
16166%  The format of the RemoteDisplayCommand method is:
16167%
16168%      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16169%        const char *window,const char *filename,ExceptionInfo *exception)
16170%
16171%  A description of each parameter follows:
16172%
16173%    o image_info: the image info.
16174%
16175%    o window: Specifies the name or id of an X window.
16176%
16177%    o filename: the name of the image filename to display.
16178%
16179%    o exception: return any errors or warnings in this structure.
16180%
16181*/
16182MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16183  const char *window,const char *filename,ExceptionInfo *exception)
16184{
16185  assert(image_info != (const ImageInfo *) NULL);
16186  assert(image_info->signature == MagickSignature);
16187  assert(filename != (char *) NULL);
16188  (void) window;
16189  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16190  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16191    "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image_info->filename);
16192  return(MagickFalse);
16193}
16194#endif
16195