display.c revision f05d4947caf1bc27fbec041eb37c474a80c83c0b
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-2012 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/cache-private.h"
47#include "MagickCore/client.h"
48#include "MagickCore/color.h"
49#include "MagickCore/colorspace.h"
50#include "MagickCore/composite.h"
51#include "MagickCore/constitute.h"
52#include "MagickCore/decorate.h"
53#include "MagickCore/delegate.h"
54#include "MagickCore/display.h"
55#include "MagickCore/display-private.h"
56#include "MagickCore/distort.h"
57#include "MagickCore/draw.h"
58#include "MagickCore/effect.h"
59#include "MagickCore/enhance.h"
60#include "MagickCore/exception.h"
61#include "MagickCore/exception-private.h"
62#include "MagickCore/fx.h"
63#include "MagickCore/geometry.h"
64#include "MagickCore/image.h"
65#include "MagickCore/image-private.h"
66#include "MagickCore/list.h"
67#include "MagickCore/log.h"
68#include "MagickCore/magick.h"
69#include "MagickCore/memory_.h"
70#include "MagickCore/monitor.h"
71#include "MagickCore/monitor-private.h"
72#include "MagickCore/montage.h"
73#include "MagickCore/option.h"
74#include "MagickCore/paint.h"
75#include "MagickCore/pixel.h"
76#include "MagickCore/pixel-accessor.h"
77#include "MagickCore/PreRvIcccm.h"
78#include "MagickCore/property.h"
79#include "MagickCore/quantum.h"
80#include "MagickCore/quantum-private.h"
81#include "MagickCore/resize.h"
82#include "MagickCore/resource_.h"
83#include "MagickCore/shear.h"
84#include "MagickCore/segment.h"
85#include "MagickCore/statistic.h"
86#include "MagickCore/string_.h"
87#include "MagickCore/string-private.h"
88#include "MagickCore/transform.h"
89#include "MagickCore/threshold.h"
90#include "MagickCore/utility.h"
91#include "MagickCore/utility-private.h"
92#include "MagickCore/version.h"
93#include "MagickCore/widget.h"
94#include "MagickCore/widget-private.h"
95#include "MagickCore/xwindow.h"
96#include "MagickCore/xwindow-private.h"
97
98#if defined(MAGICKCORE_X11_DELEGATE)
99/*
100  Define declarations.
101*/
102#define MaxColors  MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
103
104/*
105  Constant declarations.
106*/
107static const unsigned char
108  HighlightBitmap[8] =
109  {
110    0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
111  },
112  OpaqueBitmap[8] =
113  {
114    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
115  },
116  ShadowBitmap[8] =
117  {
118    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
119  };
120
121static const char
122  *PageSizes[] =
123  {
124    "Letter",
125    "Tabloid",
126    "Ledger",
127    "Legal",
128    "Statement",
129    "Executive",
130    "A3",
131    "A4",
132    "A5",
133    "B4",
134    "B5",
135    "Folio",
136    "Quarto",
137    "10x14",
138    (char *) NULL
139  };
140
141/*
142  Help widget declarations.
143*/
144static const char
145  *ImageAnnotateHelp[] =
146  {
147    "In annotate mode, the Command widget has these options:",
148    "",
149    "    Font Name",
150    "      fixed",
151    "      variable",
152    "      5x8",
153    "      6x10",
154    "      7x13bold",
155    "      8x13bold",
156    "      9x15bold",
157    "      10x20",
158    "      12x24",
159    "      Browser...",
160    "    Font Color",
161    "      black",
162    "      blue",
163    "      cyan",
164    "      green",
165    "      gray",
166    "      red",
167    "      magenta",
168    "      yellow",
169    "      white",
170    "      transparent",
171    "      Browser...",
172    "    Font Color",
173    "      black",
174    "      blue",
175    "      cyan",
176    "      green",
177    "      gray",
178    "      red",
179    "      magenta",
180    "      yellow",
181    "      white",
182    "      transparent",
183    "      Browser...",
184    "    Rotate Text",
185    "      -90",
186    "      -45",
187    "      -30",
188    "      0",
189    "      30",
190    "      45",
191    "      90",
192    "      180",
193    "      Dialog...",
194    "    Help",
195    "    Dismiss",
196    "",
197    "Choose a font name from the Font Name sub-menu.  Additional",
198    "font names can be specified with the font browser.  You can",
199    "change the menu names by setting the X resources font1",
200    "through font9.",
201    "",
202    "Choose a font color from the Font Color sub-menu.",
203    "Additional font colors can be specified with the color",
204    "browser.  You can change the menu colors by setting the X",
205    "resources pen1 through pen9.",
206    "",
207    "If you select the color browser and press Grab, you can",
208    "choose the font color by moving the pointer to the desired",
209    "color on the screen and press any button.",
210    "",
211    "If you choose to rotate the text, choose Rotate Text from the",
212    "menu and select an angle.  Typically you will only want to",
213    "rotate one line of text at a time.  Depending on the angle you",
214    "choose, subsequent lines may end up overwriting each other.",
215    "",
216    "Choosing a font and its color is optional.  The default font",
217    "is fixed and the default color is black.  However, you must",
218    "choose a location to begin entering text and press button 1.",
219    "An underscore character will appear at the location of the",
220    "pointer.  The cursor changes to a pencil to indicate you are",
221    "in text mode.  To exit immediately, press Dismiss.",
222    "",
223    "In text mode, any key presses will display the character at",
224    "the location of the underscore and advance the underscore",
225    "cursor.  Enter your text and once completed press Apply to",
226    "finish your image annotation.  To correct errors press BACK",
227    "SPACE.  To delete an entire line of text, press DELETE.  Any",
228    "text that exceeds the boundaries of the image window is",
229    "automagically continued onto the next line.",
230    "",
231    "The actual color you request for the font is saved in the",
232    "image.  However, the color that appears in your image window",
233    "may be different.  For example, on a monochrome screen the",
234    "text will appear black or white even if you choose the color",
235    "red as the font color.  However, the image saved to a file",
236    "with -write is written with red lettering.  To assure the",
237    "correct color text in the final image, any PseudoClass image",
238    "is promoted to DirectClass (see miff(5)).  To force a",
239    "PseudoClass image to remain PseudoClass, use -colors.",
240    (char *) NULL,
241  },
242  *ImageChopHelp[] =
243  {
244    "In chop mode, the Command widget has these options:",
245    "",
246    "    Direction",
247    "      horizontal",
248    "      vertical",
249    "    Help",
250    "    Dismiss",
251    "",
252    "If the you choose the horizontal direction (this the",
253    "default), the area of the image between the two horizontal",
254    "endpoints of the chop line is removed.  Otherwise, the area",
255    "of the image between the two vertical endpoints of the chop",
256    "line is removed.",
257    "",
258    "Select a location within the image window to begin your chop,",
259    "press and hold any button.  Next, move the pointer to",
260    "another location in the image.  As you move a line will",
261    "connect the initial location and the pointer.  When you",
262    "release the button, the area within the image to chop is",
263    "determined by which direction you choose from the Command",
264    "widget.",
265    "",
266    "To cancel the image chopping, move the pointer back to the",
267    "starting point of the line and release the button.",
268    (char *) NULL,
269  },
270  *ImageColorEditHelp[] =
271  {
272    "In color edit mode, the Command widget has these options:",
273    "",
274    "    Method",
275    "      point",
276    "      replace",
277    "      floodfill",
278    "      filltoborder",
279    "      reset",
280    "    Pixel Color",
281    "      black",
282    "      blue",
283    "      cyan",
284    "      green",
285    "      gray",
286    "      red",
287    "      magenta",
288    "      yellow",
289    "      white",
290    "      Browser...",
291    "    Border Color",
292    "      black",
293    "      blue",
294    "      cyan",
295    "      green",
296    "      gray",
297    "      red",
298    "      magenta",
299    "      yellow",
300    "      white",
301    "      Browser...",
302    "    Fuzz",
303    "      0%",
304    "      2%",
305    "      5%",
306    "      10%",
307    "      15%",
308    "      Dialog...",
309    "    Undo",
310    "    Help",
311    "    Dismiss",
312    "",
313    "Choose a color editing method from the Method sub-menu",
314    "of the Command widget.  The point method recolors any pixel",
315    "selected with the pointer until the button is released.  The",
316    "replace method recolors any pixel that matches the color of",
317    "the pixel you select with a button press.  Floodfill recolors",
318    "any pixel that matches the color of the pixel you select with",
319    "a button press and is a neighbor.  Whereas filltoborder recolors",
320    "any neighbor pixel that is not the border color.  Finally reset",
321    "changes the entire image to the designated color.",
322    "",
323    "Next, choose a pixel color from the Pixel Color sub-menu.",
324    "Additional pixel colors can be specified with the color",
325    "browser.  You can change the menu colors by setting the X",
326    "resources pen1 through pen9.",
327    "",
328    "Now press button 1 to select a pixel within the image window",
329    "to change its color.  Additional pixels may be recolored as",
330    "prescribed by the method you choose.",
331    "",
332    "If the Magnify widget is mapped, it can be helpful in positioning",
333    "your pointer within the image (refer to button 2).",
334    "",
335    "The actual color you request for the pixels is saved in the",
336    "image.  However, the color that appears in your image window",
337    "may be different.  For example, on a monochrome screen the",
338    "pixel will appear black or white even if you choose the",
339    "color red as the pixel color.  However, the image saved to a",
340    "file with -write is written with red pixels.  To assure the",
341    "correct color text in the final image, any PseudoClass image",
342    "is promoted to DirectClass (see miff(5)).  To force a",
343    "PseudoClass image to remain PseudoClass, use -colors.",
344    (char *) NULL,
345  },
346  *ImageCompositeHelp[] =
347  {
348    "First a widget window is displayed requesting you to enter an",
349    "image name. Press Composite, Grab or type a file name.",
350    "Press Cancel if you choose not to create a composite image.",
351    "When you choose Grab, move the pointer to the desired window",
352    "and press any button.",
353    "",
354    "If the Composite image does not have any matte information,",
355    "you are informed and the file browser is displayed again.",
356    "Enter the name of a mask image.  The image is typically",
357    "grayscale and the same size as the composite image.  If the",
358    "image is not grayscale, it is converted to grayscale and the",
359    "resulting intensities are used as matte information.",
360    "",
361    "A small window appears showing the location of the cursor in",
362    "the image window. You are now in composite mode.  To exit",
363    "immediately, press Dismiss.  In composite mode, the Command",
364    "widget has these options:",
365    "",
366    "    Operators",
367    "      Over",
368    "      In",
369    "      Out",
370    "      Atop",
371    "      Xor",
372    "      Plus",
373    "      Minus",
374    "      Add",
375    "      Subtract",
376    "      Difference",
377    "      Multiply",
378    "      Bumpmap",
379    "      Copy",
380    "      CopyRed",
381    "      CopyGreen",
382    "      CopyBlue",
383    "      CopyOpacity",
384    "      Clear",
385    "    Dissolve",
386    "    Displace",
387    "    Help",
388    "    Dismiss",
389    "",
390    "Choose a composite operation from the Operators sub-menu of",
391    "the Command widget.  How each operator behaves is described",
392    "below.  Image window is the image currently displayed on",
393    "your X server and image is the image obtained with the File",
394    "Browser widget.",
395    "",
396    "Over     The result is the union of the two image shapes,",
397    "         with image obscuring image window in the region of",
398    "         overlap.",
399    "",
400    "In       The result is simply image cut by the shape of",
401    "         image window.  None of the image data of image",
402    "         window is in the result.",
403    "",
404    "Out      The resulting image is image with the shape of",
405    "         image window cut out.",
406    "",
407    "Atop     The result is the same shape as image image window,",
408    "         with image obscuring image window where the image",
409    "         shapes overlap.  Note this differs from over",
410    "         because the portion of image outside image window's",
411    "         shape does not appear in the result.",
412    "",
413    "Xor      The result is the image data from both image and",
414    "         image window that is outside the overlap region.",
415    "         The overlap region is blank.",
416    "",
417    "Plus     The result is just the sum of the image data.",
418    "         Output values are cropped to QuantumRange (no overflow).",
419    "",
420    "Minus    The result of image - image window, with underflow",
421    "         cropped to zero.",
422    "",
423    "Add      The result of image + image window, with overflow",
424    "         wrapping around (mod 256).",
425    "",
426    "Subtract The result of image - image window, with underflow",
427    "         wrapping around (mod 256).  The add and subtract",
428    "         operators can be used to perform reversible",
429    "         transformations.",
430    "",
431    "Difference",
432    "         The result of abs(image - image window).  This",
433    "         useful for comparing two very similar images.",
434    "",
435    "Multiply",
436    "         The result of image * image window.  This",
437    "         useful for the creation of drop-shadows.",
438    "",
439    "Bumpmap  The result of surface normals from image * image",
440    "         window.",
441    "",
442    "Copy     The resulting image is image window replaced with",
443    "         image.  Here the matte information is ignored.",
444    "",
445    "CopyRed  The red layer of the image window is replace with",
446    "         the red layer of the image.  The other layers are",
447    "         untouched.",
448    "",
449    "CopyGreen",
450    "         The green layer of the image window is replace with",
451    "         the green layer of the image.  The other layers are",
452    "         untouched.",
453    "",
454    "CopyBlue The blue layer of the image window is replace with",
455    "         the blue layer of the image.  The other layers are",
456    "         untouched.",
457    "",
458    "CopyOpacity",
459    "         The matte layer of the image window is replace with",
460    "         the matte layer of the image.  The other layers are",
461    "         untouched.",
462    "",
463    "The image compositor requires a matte, or alpha channel in",
464    "the image for some operations.  This extra channel usually",
465    "defines a mask which represents a sort of a cookie-cutter",
466    "for the image.  This the case when matte is opaque (full",
467    "coverage) for pixels inside the shape, zero outside, and",
468    "between 0 and QuantumRange on the boundary.  If image does not",
469    "have a matte channel, it is initialized with 0 for any pixel",
470    "matching in color to pixel location (0,0), otherwise QuantumRange.",
471    "",
472    "If you choose Dissolve, the composite operator becomes Over.  The",
473    "image matte channel percent transparency is initialized to factor.",
474    "The image window is initialized to (100-factor). Where factor is the",
475    "value you specify in the Dialog widget.",
476    "",
477    "Displace shifts the image pixels as defined by a displacement",
478    "map.  With this option, image is used as a displacement map.",
479    "Black, within the displacement map, is a maximum positive",
480    "displacement.  White is a maximum negative displacement and",
481    "middle gray is neutral.  The displacement is scaled to determine",
482    "the pixel shift.  By default, the displacement applies in both the",
483    "horizontal and vertical directions.  However, if you specify a mask,",
484    "image is the horizontal X displacement and mask the vertical Y",
485    "displacement.",
486    "",
487    "Note that matte information for image window is not retained",
488    "for colormapped X server visuals (e.g. StaticColor,",
489    "StaticColor, GrayScale, PseudoColor).  Correct compositing",
490    "behavior may require a TrueColor or DirectColor visual or a",
491    "Standard Colormap.",
492    "",
493    "Choosing a composite operator is optional.  The default",
494    "operator is replace.  However, you must choose a location to",
495    "composite your image and press button 1.  Press and hold the",
496    "button before releasing and an outline of the image will",
497    "appear to help you identify your location.",
498    "",
499    "The actual colors of the composite image is saved.  However,",
500    "the color that appears in image window may be different.",
501    "For example, on a monochrome screen image window will appear",
502    "black or white even though your composited image may have",
503    "many colors.  If the image is saved to a file it is written",
504    "with the correct colors.  To assure the correct colors are",
505    "saved in the final image, any PseudoClass image is promoted",
506    "to DirectClass (see miff(5)).  To force a PseudoClass image",
507    "to remain PseudoClass, use -colors.",
508    (char *) NULL,
509  },
510  *ImageCutHelp[] =
511  {
512    "In cut mode, the Command widget has these options:",
513    "",
514    "    Help",
515    "    Dismiss",
516    "",
517    "To define a cut region, press button 1 and drag.  The",
518    "cut region is defined by a highlighted rectangle that",
519    "expands or contracts as it follows the pointer.  Once you",
520    "are satisfied with the cut region, release the button.",
521    "You are now in rectify mode.  In rectify mode, the Command",
522    "widget has these options:",
523    "",
524    "    Cut",
525    "    Help",
526    "    Dismiss",
527    "",
528    "You can make adjustments by moving the pointer to one of the",
529    "cut rectangle corners, pressing a button, and dragging.",
530    "Finally, press Cut to commit your copy region.  To",
531    "exit without cutting the image, press Dismiss.",
532    (char *) NULL,
533  },
534  *ImageCopyHelp[] =
535  {
536    "In copy mode, the Command widget has these options:",
537    "",
538    "    Help",
539    "    Dismiss",
540    "",
541    "To define a copy region, press button 1 and drag.  The",
542    "copy region is defined by a highlighted rectangle that",
543    "expands or contracts as it follows the pointer.  Once you",
544    "are satisfied with the copy region, release the button.",
545    "You are now in rectify mode.  In rectify mode, the Command",
546    "widget has these options:",
547    "",
548    "    Copy",
549    "    Help",
550    "    Dismiss",
551    "",
552    "You can make adjustments by moving the pointer to one of the",
553    "copy rectangle corners, pressing a button, and dragging.",
554    "Finally, press Copy to commit your copy region.  To",
555    "exit without copying the image, press Dismiss.",
556    (char *) NULL,
557  },
558  *ImageCropHelp[] =
559  {
560    "In crop mode, the Command widget has these options:",
561    "",
562    "    Help",
563    "    Dismiss",
564    "",
565    "To define a cropping region, press button 1 and drag.  The",
566    "cropping region is defined by a highlighted rectangle that",
567    "expands or contracts as it follows the pointer.  Once you",
568    "are satisfied with the cropping region, release the button.",
569    "You are now in rectify mode.  In rectify mode, the Command",
570    "widget has these options:",
571    "",
572    "    Crop",
573    "    Help",
574    "    Dismiss",
575    "",
576    "You can make adjustments by moving the pointer to one of the",
577    "cropping rectangle corners, pressing a button, and dragging.",
578    "Finally, press Crop to commit your cropping region.  To",
579    "exit without cropping the image, press Dismiss.",
580    (char *) NULL,
581  },
582  *ImageDrawHelp[] =
583  {
584    "The cursor changes to a crosshair to indicate you are in",
585    "draw mode.  To exit immediately, press Dismiss.  In draw mode,",
586    "the Command widget has these options:",
587    "",
588    "    Element",
589    "      point",
590    "      line",
591    "      rectangle",
592    "      fill rectangle",
593    "      circle",
594    "      fill circle",
595    "      ellipse",
596    "      fill ellipse",
597    "      polygon",
598    "      fill polygon",
599    "    Color",
600    "      black",
601    "      blue",
602    "      cyan",
603    "      green",
604    "      gray",
605    "      red",
606    "      magenta",
607    "      yellow",
608    "      white",
609    "      transparent",
610    "      Browser...",
611    "    Stipple",
612    "      Brick",
613    "      Diagonal",
614    "      Scales",
615    "      Vertical",
616    "      Wavy",
617    "      Translucent",
618    "      Opaque",
619    "      Open...",
620    "    Width",
621    "      1",
622    "      2",
623    "      4",
624    "      8",
625    "      16",
626    "      Dialog...",
627    "    Undo",
628    "    Help",
629    "    Dismiss",
630    "",
631    "Choose a drawing primitive from the Element sub-menu.",
632    "",
633    "Choose a color from the Color sub-menu.  Additional",
634    "colors can be specified with the color browser.",
635    "",
636    "If you choose the color browser and press Grab, you can",
637    "select the color by moving the pointer to the desired",
638    "color on the screen and press any button.  The transparent",
639    "color updates the image matte channel and is useful for",
640    "image compositing.",
641    "",
642    "Choose a stipple, if appropriate, from the Stipple sub-menu.",
643    "Additional stipples can be specified with the file browser.",
644    "Stipples obtained from the file browser must be on disk in the",
645    "X11 bitmap format.",
646    "",
647    "Choose a width, if appropriate, from the Width sub-menu.  To",
648    "choose a specific width select the Dialog widget.",
649    "",
650    "Choose a point in the Image window and press button 1 and",
651    "hold.  Next, move the pointer to another location in the",
652    "image.  As you move, a line connects the initial location and",
653    "the pointer.  When you release the button, the image is",
654    "updated with the primitive you just drew.  For polygons, the",
655    "image is updated when you press and release the button without",
656    "moving the pointer.",
657    "",
658    "To cancel image drawing, move the pointer back to the",
659    "starting point of the line and release the button.",
660    (char *) NULL,
661  },
662  *DisplayHelp[] =
663  {
664    "BUTTONS",
665    "  The effects of each button press is described below.  Three",
666    "  buttons are required.  If you have a two button mouse,",
667    "  button 1 and 3 are returned.  Press ALT and button 3 to",
668    "  simulate button 2.",
669    "",
670    "  1    Press this button to map or unmap the Command widget.",
671    "",
672    "  2    Press and drag to define a region of the image to",
673    "       magnify.",
674    "",
675    "  3    Press and drag to choose from a select set of commands.",
676    "       This button behaves differently if the image being",
677    "       displayed is a visual image directory.  Here, choose a",
678    "       particular tile of the directory and press this button and",
679    "       drag to select a command from a pop-up menu.  Choose from",
680    "       these menu items:",
681    "",
682    "           Open",
683    "           Next",
684    "           Former",
685    "           Delete",
686    "           Update",
687    "",
688    "       If you choose Open, the image represented by the tile is",
689    "       displayed.  To return to the visual image directory, choose",
690    "       Next from the Command widget.  Next and Former moves to the",
691    "       next or former image respectively.  Choose Delete to delete",
692    "       a particular image tile.  Finally, choose Update to",
693    "       synchronize all the image tiles with their respective",
694    "       images.",
695    "",
696    "COMMAND WIDGET",
697    "  The Command widget lists a number of sub-menus and commands.",
698    "  They are",
699    "",
700    "      File",
701    "        Open...",
702    "        Next",
703    "        Former",
704    "        Select...",
705    "        Save...",
706    "        Print...",
707    "        Delete...",
708    "        New...",
709    "        Visual Directory...",
710    "        Quit",
711    "      Edit",
712    "        Undo",
713    "        Redo",
714    "        Cut",
715    "        Copy",
716    "        Paste",
717    "      View",
718    "        Half Size",
719    "        Original Size",
720    "        Double Size",
721    "        Resize...",
722    "        Apply",
723    "        Refresh",
724    "        Restore",
725    "      Transform",
726    "        Crop",
727    "        Chop",
728    "        Flop",
729    "        Flip",
730    "        Rotate Right",
731    "        Rotate Left",
732    "        Rotate...",
733    "        Shear...",
734    "        Roll...",
735    "        Trim Edges",
736    "      Enhance",
737    "        Brightness...",
738    "        Saturation...",
739    "        Hue...",
740    "        Gamma...",
741    "        Sharpen...",
742    "        Dull",
743    "        Contrast Stretch...",
744    "        Sigmoidal Contrast...",
745    "        Normalize",
746    "        Equalize",
747    "        Negate",
748    "        Grayscale",
749    "        Map...",
750    "        Quantize...",
751    "      Effects",
752    "        Despeckle",
753    "        Emboss",
754    "        Reduce Noise",
755    "        Add Noise",
756    "        Sharpen...",
757    "        Blur...",
758    "        Threshold...",
759    "        Edge Detect...",
760    "        Spread...",
761    "        Shade...",
762    "        Painting...",
763    "        Segment...",
764    "      F/X",
765    "        Solarize...",
766    "        Sepia Tone...",
767    "        Swirl...",
768    "        Implode...",
769    "        Vignette...",
770    "        Wave...",
771    "        Oil Painting...",
772    "        Charcoal Drawing...",
773    "      Image Edit",
774    "        Annotate...",
775    "        Draw...",
776    "        Color...",
777    "        Matte...",
778    "        Composite...",
779    "        Add Border...",
780    "        Add Frame...",
781    "        Comment...",
782    "        Launch...",
783    "        Region of Interest...",
784    "      Miscellany",
785    "        Image Info",
786    "        Zoom Image",
787    "        Show Preview...",
788    "        Show Histogram",
789    "        Show Matte",
790    "        Background...",
791    "        Slide Show",
792    "        Preferences...",
793    "      Help",
794    "        Overview",
795    "        Browse Documentation",
796    "        About Display",
797    "",
798    "  Menu items with a indented triangle have a sub-menu.  They",
799    "  are represented above as the indented items.  To access a",
800    "  sub-menu item, move the pointer to the appropriate menu and",
801    "  press a button and drag.  When you find the desired sub-menu",
802    "  item, release the button and the command is executed.  Move",
803    "  the pointer away from the sub-menu if you decide not to",
804    "  execute a particular command.",
805    "",
806    "KEYBOARD ACCELERATORS",
807    "  Accelerators are one or two key presses that effect a",
808    "  particular command.  The keyboard accelerators that",
809    "  display(1) understands is:",
810    "",
811    "  Ctl+O     Press to open an image from a file.",
812    "",
813    "  space     Press to display the next image.",
814    "",
815    "            If the image is a multi-paged document such as a Postscript",
816    "            document, you can skip ahead several pages by preceding",
817    "            this command with a number.  For example to display the",
818    "            third page beyond the current page, press 3<space>.",
819    "",
820    "  backspace Press to display the former image.",
821    "",
822    "            If the image is a multi-paged document such as a Postscript",
823    "            document, you can skip behind several pages by preceding",
824    "            this command with a number.  For example to display the",
825    "            third page preceding the current page, press 3<backspace>.",
826    "",
827    "  Ctl+S     Press to write the image to a file.",
828    "",
829    "  Ctl+P     Press to print the image to a Postscript printer.",
830    "",
831    "  Ctl+D     Press to delete an image file.",
832    "",
833    "  Ctl+N     Press to create a blank canvas.",
834    "",
835    "  Ctl+Q     Press to discard all images and exit program.",
836    "",
837    "  Ctl+Z     Press to undo last image transformation.",
838    "",
839    "  Ctl+R     Press to redo last image transformation.",
840    "",
841    "  Ctl+X     Press to cut a region of the image.",
842    "",
843    "  Ctl+C     Press to copy a region of the image.",
844    "",
845    "  Ctl+V     Press to paste a region to the image.",
846    "",
847    "  <         Press to half the image size.",
848    "",
849    "  -         Press to return to the original image size.",
850    "",
851    "  >         Press to double the image size.",
852    "",
853    "  %         Press to resize the image to a width and height you",
854    "            specify.",
855    "",
856    "Cmd-A       Press to make any image transformations permanent."
857    "",
858    "            By default, any image size transformations are applied",
859    "            to the original image to create the image displayed on",
860    "            the X server.  However, the transformations are not",
861    "            permanent (i.e. the original image does not change",
862    "            size only the X image does).  For example, if you",
863    "            press > the X image will appear to double in size,",
864    "            but the original image will in fact remain the same size.",
865    "            To force the original image to double in size, press >",
866    "            followed by Cmd-A.",
867    "",
868    "  @         Press to refresh the image window.",
869    "",
870    "  C         Press to cut out a rectangular region of the image.",
871    "",
872    "  [         Press to chop the image.",
873    "",
874    "  H         Press to flop image in the horizontal direction.",
875    "",
876    "  V         Press to flip image in the vertical direction.",
877    "",
878    "  /         Press to rotate the image 90 degrees clockwise.",
879    "",
880    " \\         Press to rotate the image 90 degrees counter-clockwise.",
881    "",
882    "  *         Press to rotate the image the number of degrees you",
883    "            specify.",
884    "",
885    "  S         Press to shear the image the number of degrees you",
886    "            specify.",
887    "",
888    "  R         Press to roll the image.",
889    "",
890    "  T         Press to trim the image edges.",
891    "",
892    "  Shft-H    Press to vary the image hue.",
893    "",
894    "  Shft-S    Press to vary the color saturation.",
895    "",
896    "  Shft-L    Press to vary the color brightness.",
897    "",
898    "  Shft-G    Press to gamma correct the image.",
899    "",
900    "  Shft-C    Press to sharpen the image contrast.",
901    "",
902    "  Shft-Z    Press to dull the image contrast.",
903    "",
904    "  =         Press to perform histogram equalization on the image.",
905    "",
906    "  Shft-N    Press to perform histogram normalization on the image.",
907    "",
908    "  Shft-~    Press to negate the colors of the image.",
909    "",
910    "  .         Press to convert the image colors to gray.",
911    "",
912    "  Shft-#    Press to set the maximum number of unique colors in the",
913    "            image.",
914    "",
915    "  F2        Press to reduce the speckles in an image.",
916    "",
917    "  F3        Press to eliminate peak noise from an image.",
918    "",
919    "  F4        Press to add noise to an image.",
920    "",
921    "  F5        Press to sharpen an image.",
922    "",
923    "  F6        Press to delete an image file.",
924    "",
925    "  F7        Press to threshold the image.",
926    "",
927    "  F8        Press to detect edges within an image.",
928    "",
929    "  F9        Press to emboss an image.",
930    "",
931    "  F10       Press to displace pixels by a random amount.",
932    "",
933    "  F11       Press to negate all pixels above the threshold level.",
934    "",
935    "  F12       Press to shade the image using a distant light source.",
936    "",
937    "  F13       Press to lighten or darken image edges to create a 3-D effect.",
938    "",
939    "  F14       Press to segment the image by color.",
940    "",
941    "  Meta-S    Press to swirl image pixels about the center.",
942    "",
943    "  Meta-I    Press to implode image pixels about the center.",
944    "",
945    "  Meta-W    Press to alter an image along a sine wave.",
946    "",
947    "  Meta-P    Press to simulate an oil painting.",
948    "",
949    "  Meta-C    Press to simulate a charcoal drawing.",
950    "",
951    "  Alt-A     Press to annotate the image with text.",
952    "",
953    "  Alt-D     Press to draw on an image.",
954    "",
955    "  Alt-P     Press to edit an image pixel color.",
956    "",
957    "  Alt-M     Press to edit the image matte information.",
958    "",
959    "  Alt-V     Press to composite the image with another.",
960    "",
961    "  Alt-B     Press to add a border to the image.",
962    "",
963    "  Alt-F     Press to add an ornamental border to the image.",
964    "",
965    "  Alt-Shft-!",
966    "            Press to add an image comment.",
967    "",
968    "  Ctl-A     Press to apply image processing techniques to a region",
969    "            of interest.",
970    "",
971    "  Shft-?    Press to display information about the image.",
972    "",
973    "  Shft-+    Press to map the zoom image window.",
974    "",
975    "  Shft-P    Press to preview an image enhancement, effect, or f/x.",
976    "",
977    "  F1        Press to display helpful information about display(1).",
978    "",
979    "  Find      Press to browse documentation about ImageMagick.",
980    "",
981    "  1-9       Press to change the level of magnification.",
982    "",
983    "  Use the arrow keys to move the image one pixel up, down,",
984    "  left, or right within the magnify window.  Be sure to first",
985    "  map the magnify window by pressing button 2.",
986    "",
987    "  Press ALT and one of the arrow keys to trim off one pixel",
988    "  from any side of the image.",
989    (char *) NULL,
990  },
991  *ImageMatteEditHelp[] =
992  {
993    "Matte information within an image is useful for some",
994    "operations such as image compositing (See IMAGE",
995    "COMPOSITING).  This extra channel usually defines a mask",
996    "which represents a sort of a cookie-cutter for the image.",
997    "This the case when matte is opaque (full coverage) for",
998    "pixels inside the shape, zero outside, and between 0 and",
999    "QuantumRange on the boundary.",
1000    "",
1001    "A small window appears showing the location of the cursor in",
1002    "the image window. You are now in matte edit mode.  To exit",
1003    "immediately, press Dismiss.  In matte edit mode, the Command",
1004    "widget has these options:",
1005    "",
1006    "    Method",
1007    "      point",
1008    "      replace",
1009    "      floodfill",
1010    "      filltoborder",
1011    "      reset",
1012    "    Border Color",
1013    "      black",
1014    "      blue",
1015    "      cyan",
1016    "      green",
1017    "      gray",
1018    "      red",
1019    "      magenta",
1020    "      yellow",
1021    "      white",
1022    "      Browser...",
1023    "    Fuzz",
1024    "      0%",
1025    "      2%",
1026    "      5%",
1027    "      10%",
1028    "      15%",
1029    "      Dialog...",
1030    "    Matte",
1031    "      Opaque",
1032    "      Transparent",
1033    "      Dialog...",
1034    "    Undo",
1035    "    Help",
1036    "    Dismiss",
1037    "",
1038    "Choose a matte editing method from the Method sub-menu of",
1039    "the Command widget.  The point method changes the matte value",
1040    "of any pixel selected with the pointer until the button is",
1041    "is released.  The replace method changes the matte value of",
1042    "any pixel that matches the color of the pixel you select with",
1043    "a button press.  Floodfill changes the matte value of any pixel",
1044    "that matches the color of the pixel you select with a button",
1045    "press and is a neighbor.  Whereas filltoborder changes the matte",
1046    "value any neighbor pixel that is not the border color.  Finally",
1047    "reset changes the entire image to the designated matte value.",
1048    "",
1049    "Choose Matte Value and pick Opaque or Transarent.  For other values",
1050    "select the Dialog entry.  Here a dialog appears requesting a matte",
1051    "value.  The value you select is assigned as the opacity value of the",
1052    "selected pixel or pixels.",
1053    "",
1054    "Now, press any button to select a pixel within the image",
1055    "window to change its matte value.",
1056    "",
1057    "If the Magnify widget is mapped, it can be helpful in positioning",
1058    "your pointer within the image (refer to button 2).",
1059    "",
1060    "Matte information is only valid in a DirectClass image.",
1061    "Therefore, any PseudoClass image is promoted to DirectClass",
1062    "(see miff(5)).  Note that matte information for PseudoClass",
1063    "is not retained for colormapped X server visuals (e.g.",
1064    "StaticColor, StaticColor, GrayScale, PseudoColor) unless you",
1065    "immediately save your image to a file (refer to Write).",
1066    "Correct matte editing behavior may require a TrueColor or",
1067    "DirectColor visual or a Standard Colormap.",
1068    (char *) NULL,
1069  },
1070  *ImagePanHelp[] =
1071  {
1072    "When an image exceeds the width or height of the X server",
1073    "screen, display maps a small panning icon.  The rectangle",
1074    "within the panning icon shows the area that is currently",
1075    "displayed in the image window.  To pan about the image,",
1076    "press any button and drag the pointer within the panning",
1077    "icon.  The pan rectangle moves with the pointer and the",
1078    "image window is updated to reflect the location of the",
1079    "rectangle within the panning icon.  When you have selected",
1080    "the area of the image you wish to view, release the button.",
1081    "",
1082    "Use the arrow keys to pan the image one pixel up, down,",
1083    "left, or right within the image window.",
1084    "",
1085    "The panning icon is withdrawn if the image becomes smaller",
1086    "than the dimensions of the X server screen.",
1087    (char *) NULL,
1088  },
1089  *ImagePasteHelp[] =
1090  {
1091    "A small window appears showing the location of the cursor in",
1092    "the image window. You are now in paste mode.  To exit",
1093    "immediately, press Dismiss.  In paste mode, the Command",
1094    "widget has these options:",
1095    "",
1096    "    Operators",
1097    "      over",
1098    "      in",
1099    "      out",
1100    "      atop",
1101    "      xor",
1102    "      plus",
1103    "      minus",
1104    "      add",
1105    "      subtract",
1106    "      difference",
1107    "      replace",
1108    "    Help",
1109    "    Dismiss",
1110    "",
1111    "Choose a composite operation from the Operators sub-menu of",
1112    "the Command widget.  How each operator behaves is described",
1113    "below.  Image window is the image currently displayed on",
1114    "your X server and image is the image obtained with the File",
1115    "Browser widget.",
1116    "",
1117    "Over     The result is the union of the two image shapes,",
1118    "         with image obscuring image window in the region of",
1119    "         overlap.",
1120    "",
1121    "In       The result is simply image cut by the shape of",
1122    "         image window.  None of the image data of image",
1123    "         window is in the result.",
1124    "",
1125    "Out      The resulting image is image with the shape of",
1126    "         image window cut out.",
1127    "",
1128    "Atop     The result is the same shape as image image window,",
1129    "         with image obscuring image window where the image",
1130    "         shapes overlap.  Note this differs from over",
1131    "         because the portion of image outside image window's",
1132    "         shape does not appear in the result.",
1133    "",
1134    "Xor      The result is the image data from both image and",
1135    "         image window that is outside the overlap region.",
1136    "         The overlap region is blank.",
1137    "",
1138    "Plus     The result is just the sum of the image data.",
1139    "         Output values are cropped to QuantumRange (no overflow).",
1140    "         This operation is independent of the matte",
1141    "         channels.",
1142    "",
1143    "Minus    The result of image - image window, with underflow",
1144    "         cropped to zero.",
1145    "",
1146    "Add      The result of image + image window, with overflow",
1147    "         wrapping around (mod 256).",
1148    "",
1149    "Subtract The result of image - image window, with underflow",
1150    "         wrapping around (mod 256).  The add and subtract",
1151    "         operators can be used to perform reversible",
1152    "         transformations.",
1153    "",
1154    "Difference",
1155    "         The result of abs(image - image window).  This",
1156    "         useful for comparing two very similar images.",
1157    "",
1158    "Copy     The resulting image is image window replaced with",
1159    "         image.  Here the matte information is ignored.",
1160    "",
1161    "CopyRed  The red layer of the image window is replace with",
1162    "         the red layer of the image.  The other layers are",
1163    "         untouched.",
1164    "",
1165    "CopyGreen",
1166    "         The green layer of the image window is replace with",
1167    "         the green layer of the image.  The other layers are",
1168    "         untouched.",
1169    "",
1170    "CopyBlue The blue layer of the image window is replace with",
1171    "         the blue layer of the image.  The other layers are",
1172    "         untouched.",
1173    "",
1174    "CopyOpacity",
1175    "         The matte layer of the image window is replace with",
1176    "         the matte layer of the image.  The other layers are",
1177    "         untouched.",
1178    "",
1179    "The image compositor requires a matte, or alpha channel in",
1180    "the image for some operations.  This extra channel usually",
1181    "defines a mask which represents a sort of a cookie-cutter",
1182    "for the image.  This the case when matte is opaque (full",
1183    "coverage) for pixels inside the shape, zero outside, and",
1184    "between 0 and QuantumRange on the boundary.  If image does not",
1185    "have a matte channel, it is initialized with 0 for any pixel",
1186    "matching in color to pixel location (0,0), otherwise QuantumRange.",
1187    "",
1188    "Note that matte information for image window is not retained",
1189    "for colormapped X server visuals (e.g. StaticColor,",
1190    "StaticColor, GrayScale, PseudoColor).  Correct compositing",
1191    "behavior may require a TrueColor or DirectColor visual or a",
1192    "Standard Colormap.",
1193    "",
1194    "Choosing a composite operator is optional.  The default",
1195    "operator is replace.  However, you must choose a location to",
1196    "paste your image and press button 1.  Press and hold the",
1197    "button before releasing and an outline of the image will",
1198    "appear to help you identify your location.",
1199    "",
1200    "The actual colors of the pasted image is saved.  However,",
1201    "the color that appears in image window may be different.",
1202    "For example, on a monochrome screen image window will appear",
1203    "black or white even though your pasted image may have",
1204    "many colors.  If the image is saved to a file it is written",
1205    "with the correct colors.  To assure the correct colors are",
1206    "saved in the final image, any PseudoClass image is promoted",
1207    "to DirectClass (see miff(5)).  To force a PseudoClass image",
1208    "to remain PseudoClass, use -colors.",
1209    (char *) NULL,
1210  },
1211  *ImageROIHelp[] =
1212  {
1213    "In region of interest mode, the Command widget has these",
1214    "options:",
1215    "",
1216    "    Help",
1217    "    Dismiss",
1218    "",
1219    "To define a region of interest, press button 1 and drag.",
1220    "The region of interest is defined by a highlighted rectangle",
1221    "that expands or contracts as it follows the pointer.  Once",
1222    "you are satisfied with the region of interest, release the",
1223    "button.  You are now in apply mode.  In apply mode the",
1224    "Command widget has these options:",
1225    "",
1226    "      File",
1227    "        Save...",
1228    "        Print...",
1229    "      Edit",
1230    "        Undo",
1231    "        Redo",
1232    "      Transform",
1233    "        Flop",
1234    "        Flip",
1235    "        Rotate Right",
1236    "        Rotate Left",
1237    "      Enhance",
1238    "        Hue...",
1239    "        Saturation...",
1240    "        Brightness...",
1241    "        Gamma...",
1242    "        Spiff",
1243    "        Dull",
1244    "        Contrast Stretch",
1245    "        Sigmoidal Contrast...",
1246    "        Normalize",
1247    "        Equalize",
1248    "        Negate",
1249    "        Grayscale",
1250    "        Map...",
1251    "        Quantize...",
1252    "      Effects",
1253    "        Despeckle",
1254    "        Emboss",
1255    "        Reduce Noise",
1256    "        Sharpen...",
1257    "        Blur...",
1258    "        Threshold...",
1259    "        Edge Detect...",
1260    "        Spread...",
1261    "        Shade...",
1262    "        Raise...",
1263    "        Segment...",
1264    "      F/X",
1265    "        Solarize...",
1266    "        Sepia Tone...",
1267    "        Swirl...",
1268    "        Implode...",
1269    "        Vignette...",
1270    "        Wave...",
1271    "        Oil Painting...",
1272    "        Charcoal Drawing...",
1273    "      Miscellany",
1274    "        Image Info",
1275    "        Zoom Image",
1276    "        Show Preview...",
1277    "        Show Histogram",
1278    "        Show Matte",
1279    "      Help",
1280    "      Dismiss",
1281    "",
1282    "You can make adjustments to the region of interest by moving",
1283    "the pointer to one of the rectangle corners, pressing a",
1284    "button, and dragging.  Finally, choose an image processing",
1285    "technique from the Command widget.  You can choose more than",
1286    "one image processing technique to apply to an area.",
1287    "Alternatively, you can move the region of interest before",
1288    "applying another image processing technique.  To exit, press",
1289    "Dismiss.",
1290    (char *) NULL,
1291  },
1292  *ImageRotateHelp[] =
1293  {
1294    "In rotate mode, the Command widget has these options:",
1295    "",
1296    "    Pixel Color",
1297    "      black",
1298    "      blue",
1299    "      cyan",
1300    "      green",
1301    "      gray",
1302    "      red",
1303    "      magenta",
1304    "      yellow",
1305    "      white",
1306    "      Browser...",
1307    "    Direction",
1308    "      horizontal",
1309    "      vertical",
1310    "    Help",
1311    "    Dismiss",
1312    "",
1313    "Choose a background color from the Pixel Color sub-menu.",
1314    "Additional background colors can be specified with the color",
1315    "browser.  You can change the menu colors by setting the X",
1316    "resources pen1 through pen9.",
1317    "",
1318    "If you choose the color browser and press Grab, you can",
1319    "select the background color by moving the pointer to the",
1320    "desired color on the screen and press any button.",
1321    "",
1322    "Choose a point in the image window and press this button and",
1323    "hold.  Next, move the pointer to another location in the",
1324    "image.  As you move a line connects the initial location and",
1325    "the pointer.  When you release the button, the degree of",
1326    "image rotation is determined by the slope of the line you",
1327    "just drew.  The slope is relative to the direction you",
1328    "choose from the Direction sub-menu of the Command widget.",
1329    "",
1330    "To cancel the image rotation, move the pointer back to the",
1331    "starting point of the line and release the button.",
1332    (char *) NULL,
1333  };
1334
1335/*
1336  Enumeration declarations.
1337*/
1338typedef enum
1339{
1340  CopyMode,
1341  CropMode,
1342  CutMode
1343} ClipboardMode;
1344
1345typedef enum
1346{
1347  OpenCommand,
1348  NextCommand,
1349  FormerCommand,
1350  SelectCommand,
1351  SaveCommand,
1352  PrintCommand,
1353  DeleteCommand,
1354  NewCommand,
1355  VisualDirectoryCommand,
1356  QuitCommand,
1357  UndoCommand,
1358  RedoCommand,
1359  CutCommand,
1360  CopyCommand,
1361  PasteCommand,
1362  HalfSizeCommand,
1363  OriginalSizeCommand,
1364  DoubleSizeCommand,
1365  ResizeCommand,
1366  ApplyCommand,
1367  RefreshCommand,
1368  RestoreCommand,
1369  CropCommand,
1370  ChopCommand,
1371  FlopCommand,
1372  FlipCommand,
1373  RotateRightCommand,
1374  RotateLeftCommand,
1375  RotateCommand,
1376  ShearCommand,
1377  RollCommand,
1378  TrimCommand,
1379  HueCommand,
1380  SaturationCommand,
1381  BrightnessCommand,
1382  GammaCommand,
1383  SpiffCommand,
1384  DullCommand,
1385  ContrastStretchCommand,
1386  SigmoidalContrastCommand,
1387  NormalizeCommand,
1388  EqualizeCommand,
1389  NegateCommand,
1390  GrayscaleCommand,
1391  MapCommand,
1392  QuantizeCommand,
1393  DespeckleCommand,
1394  EmbossCommand,
1395  ReduceNoiseCommand,
1396  AddNoiseCommand,
1397  SharpenCommand,
1398  BlurCommand,
1399  ThresholdCommand,
1400  EdgeDetectCommand,
1401  SpreadCommand,
1402  ShadeCommand,
1403  RaiseCommand,
1404  SegmentCommand,
1405  SolarizeCommand,
1406  SepiaToneCommand,
1407  SwirlCommand,
1408  ImplodeCommand,
1409  VignetteCommand,
1410  WaveCommand,
1411  OilPaintCommand,
1412  CharcoalDrawCommand,
1413  AnnotateCommand,
1414  DrawCommand,
1415  ColorCommand,
1416  MatteCommand,
1417  CompositeCommand,
1418  AddBorderCommand,
1419  AddFrameCommand,
1420  CommentCommand,
1421  LaunchCommand,
1422  RegionofInterestCommand,
1423  ROIHelpCommand,
1424  ROIDismissCommand,
1425  InfoCommand,
1426  ZoomCommand,
1427  ShowPreviewCommand,
1428  ShowHistogramCommand,
1429  ShowMatteCommand,
1430  BackgroundCommand,
1431  SlideShowCommand,
1432  PreferencesCommand,
1433  HelpCommand,
1434  BrowseDocumentationCommand,
1435  VersionCommand,
1436  SaveToUndoBufferCommand,
1437  FreeBuffersCommand,
1438  NullCommand
1439} CommandType;
1440
1441typedef enum
1442{
1443  AnnotateNameCommand,
1444  AnnotateFontColorCommand,
1445  AnnotateBackgroundColorCommand,
1446  AnnotateRotateCommand,
1447  AnnotateHelpCommand,
1448  AnnotateDismissCommand,
1449  TextHelpCommand,
1450  TextApplyCommand,
1451  ChopDirectionCommand,
1452  ChopHelpCommand,
1453  ChopDismissCommand,
1454  HorizontalChopCommand,
1455  VerticalChopCommand,
1456  ColorEditMethodCommand,
1457  ColorEditColorCommand,
1458  ColorEditBorderCommand,
1459  ColorEditFuzzCommand,
1460  ColorEditUndoCommand,
1461  ColorEditHelpCommand,
1462  ColorEditDismissCommand,
1463  CompositeOperatorsCommand,
1464  CompositeDissolveCommand,
1465  CompositeDisplaceCommand,
1466  CompositeHelpCommand,
1467  CompositeDismissCommand,
1468  CropHelpCommand,
1469  CropDismissCommand,
1470  RectifyCopyCommand,
1471  RectifyHelpCommand,
1472  RectifyDismissCommand,
1473  DrawElementCommand,
1474  DrawColorCommand,
1475  DrawStippleCommand,
1476  DrawWidthCommand,
1477  DrawUndoCommand,
1478  DrawHelpCommand,
1479  DrawDismissCommand,
1480  MatteEditMethod,
1481  MatteEditBorderCommand,
1482  MatteEditFuzzCommand,
1483  MatteEditValueCommand,
1484  MatteEditUndoCommand,
1485  MatteEditHelpCommand,
1486  MatteEditDismissCommand,
1487  PasteOperatorsCommand,
1488  PasteHelpCommand,
1489  PasteDismissCommand,
1490  RotateColorCommand,
1491  RotateDirectionCommand,
1492  RotateCropCommand,
1493  RotateSharpenCommand,
1494  RotateHelpCommand,
1495  RotateDismissCommand,
1496  HorizontalRotateCommand,
1497  VerticalRotateCommand,
1498  TileLoadCommand,
1499  TileNextCommand,
1500  TileFormerCommand,
1501  TileDeleteCommand,
1502  TileUpdateCommand
1503} ModeType;
1504
1505/*
1506  Stipples.
1507*/
1508#define BricksWidth  20
1509#define BricksHeight  20
1510#define DiagonalWidth  16
1511#define DiagonalHeight  16
1512#define HighlightWidth  8
1513#define HighlightHeight  8
1514#define OpaqueWidth  8
1515#define OpaqueHeight  8
1516#define ScalesWidth  16
1517#define ScalesHeight  16
1518#define ShadowWidth  8
1519#define ShadowHeight  8
1520#define VerticalWidth  16
1521#define VerticalHeight  16
1522#define WavyWidth  16
1523#define WavyHeight  16
1524
1525/*
1526  Constant declaration.
1527*/
1528static const int
1529  RoiDelta = 8;
1530
1531static const unsigned char
1532  BricksBitmap[] =
1533  {
1534    0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1535    0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1536    0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1537    0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1538    0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1539  },
1540  DiagonalBitmap[] =
1541  {
1542    0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1543    0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1544    0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1545  },
1546  ScalesBitmap[] =
1547  {
1548    0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1549    0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1550    0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1551  },
1552  VerticalBitmap[] =
1553  {
1554    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1555    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1556    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1557  },
1558  WavyBitmap[] =
1559  {
1560    0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1561    0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1562    0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1563  };
1564
1565/*
1566  Function prototypes.
1567*/
1568static CommandType
1569  XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1570    const MagickStatusType,KeySym,Image **,ExceptionInfo *);
1571
1572static Image
1573  *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
1574    Image **,ExceptionInfo *),
1575  *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1576  *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *,
1577    ExceptionInfo *),
1578  *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *,
1579    ExceptionInfo *);
1580
1581static MagickBooleanType
1582  XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *,
1583    ExceptionInfo *),
1584  XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **,
1585    ExceptionInfo *),
1586  XChopImage(Display *,XResourceInfo *,XWindows *,Image **,
1587    ExceptionInfo *),
1588  XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode,
1589    ExceptionInfo *),
1590  XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1591    ExceptionInfo *),
1592  XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *,
1593    ExceptionInfo *),
1594  XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1595  XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1596    ExceptionInfo *),
1597  XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1598    ExceptionInfo *),
1599  XPasteImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1600  XPrintImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1601  XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **,
1602    ExceptionInfo *),
1603  XROIImage(Display *,XResourceInfo *,XWindows *,Image **,ExceptionInfo *),
1604  XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1605  XTrimImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
1606
1607static void
1608  XDrawPanRectangle(Display *,XWindows *),
1609  XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **,
1610    ExceptionInfo *),
1611  XMagnifyImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1612  XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1613  XPanImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1614  XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1615    const KeySym,ExceptionInfo *),
1616  XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1617  XScreenEvent(Display *,XWindows *,XEvent *,ExceptionInfo *),
1618  XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1619
1620/*
1621%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1622%                                                                             %
1623%                                                                             %
1624%                                                                             %
1625%   D i s p l a y I m a g e s                                                 %
1626%                                                                             %
1627%                                                                             %
1628%                                                                             %
1629%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1630%
1631%  DisplayImages() displays an image sequence to any X window screen.  It
1632%  returns a value other than 0 if successful.  Check the exception member
1633%  of image to determine the reason for any failure.
1634%
1635%  The format of the DisplayImages method is:
1636%
1637%      MagickBooleanType DisplayImages(const ImageInfo *image_info,
1638%        Image *images,ExceptionInfo *exception)
1639%
1640%  A description of each parameter follows:
1641%
1642%    o image_info: the image info.
1643%
1644%    o image: the image.
1645%
1646%    o exception: return any errors or warnings in this structure.
1647%
1648*/
1649MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1650  Image *images,ExceptionInfo *exception)
1651{
1652  char
1653    *argv[1];
1654
1655  Display
1656    *display;
1657
1658  Image
1659    *image;
1660
1661  register ssize_t
1662    i;
1663
1664  size_t
1665    state;
1666
1667  XrmDatabase
1668    resource_database;
1669
1670  XResourceInfo
1671    resource_info;
1672
1673  assert(image_info != (const ImageInfo *) NULL);
1674  assert(image_info->signature == MagickSignature);
1675  assert(images != (Image *) NULL);
1676  assert(images->signature == MagickSignature);
1677  if (images->debug != MagickFalse)
1678    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1679  display=XOpenDisplay(image_info->server_name);
1680  if (display == (Display *) NULL)
1681    {
1682      (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1683        "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1684      return(MagickFalse);
1685    }
1686  if (exception->severity != UndefinedException)
1687    CatchException(exception);
1688  (void) XSetErrorHandler(XError);
1689  resource_database=XGetResourceDatabase(display,GetClientName());
1690  (void) ResetMagickMemory(&resource_info,0,sizeof(resource_info));
1691  XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1692  if (image_info->page != (char *) NULL)
1693    resource_info.image_geometry=AcquireString(image_info->page);
1694  resource_info.immutable=MagickTrue;
1695  argv[0]=AcquireString(GetClientName());
1696  state=DefaultState;
1697  for (i=0; (state & ExitState) == 0; i++)
1698  {
1699    if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1700      break;
1701    image=GetImageFromList(images,i % GetImageListLength(images));
1702    (void) XDisplayImage(display,&resource_info,argv,1,&image,&state,exception);
1703  }
1704  (void) SetErrorHandler((ErrorHandler) NULL);
1705  (void) SetWarningHandler((WarningHandler) NULL);
1706  argv[0]=DestroyString(argv[0]);
1707  (void) XCloseDisplay(display);
1708  XDestroyResourceInfo(&resource_info);
1709  if (exception->severity != UndefinedException)
1710    return(MagickFalse);
1711  return(MagickTrue);
1712}
1713
1714/*
1715%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1716%                                                                             %
1717%                                                                             %
1718%                                                                             %
1719%   R e m o t e D i s p l a y C o m m a n d                                   %
1720%                                                                             %
1721%                                                                             %
1722%                                                                             %
1723%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1724%
1725%  RemoteDisplayCommand() encourages a remote display program to display the
1726%  specified image filename.
1727%
1728%  The format of the RemoteDisplayCommand method is:
1729%
1730%      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1731%        const char *window,const char *filename,ExceptionInfo *exception)
1732%
1733%  A description of each parameter follows:
1734%
1735%    o image_info: the image info.
1736%
1737%    o window: Specifies the name or id of an X window.
1738%
1739%    o filename: the name of the image filename to display.
1740%
1741%    o exception: return any errors or warnings in this structure.
1742%
1743*/
1744MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1745  const char *window,const char *filename,ExceptionInfo *exception)
1746{
1747  Display
1748    *display;
1749
1750  MagickStatusType
1751    status;
1752
1753  assert(image_info != (const ImageInfo *) NULL);
1754  assert(image_info->signature == MagickSignature);
1755  assert(filename != (char *) NULL);
1756  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1757  display=XOpenDisplay(image_info->server_name);
1758  if (display == (Display *) NULL)
1759    {
1760      (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1761        "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1762      return(MagickFalse);
1763    }
1764  (void) XSetErrorHandler(XError);
1765  status=XRemoteCommand(display,window,filename);
1766  (void) XCloseDisplay(display);
1767  return(status != 0 ? MagickTrue : MagickFalse);
1768}
1769
1770/*
1771%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1772%                                                                             %
1773%                                                                             %
1774%                                                                             %
1775+   X A n n o t a t e E d i t I m a g e                                       %
1776%                                                                             %
1777%                                                                             %
1778%                                                                             %
1779%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1780%
1781%  XAnnotateEditImage() annotates the image with text.
1782%
1783%  The format of the XAnnotateEditImage method is:
1784%
1785%      MagickBooleanType XAnnotateEditImage(Display *display,
1786%        XResourceInfo *resource_info,XWindows *windows,Image *image,
1787%        ExceptionInfo *exception)
1788%
1789%  A description of each parameter follows:
1790%
1791%    o display: Specifies a connection to an X server;  returned from
1792%      XOpenDisplay.
1793%
1794%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1795%
1796%    o windows: Specifies a pointer to a XWindows structure.
1797%
1798%    o image: the image; returned from ReadImage.
1799%
1800*/
1801
1802static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
1803{
1804  if (x > y)
1805    return(x);
1806  return(y);
1807}
1808
1809static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
1810{
1811  if (x < y)
1812    return(x);
1813  return(y);
1814}
1815
1816static MagickBooleanType XAnnotateEditImage(Display *display,
1817  XResourceInfo *resource_info,XWindows *windows,Image *image,
1818  ExceptionInfo *exception)
1819{
1820  static const char
1821    *AnnotateMenu[] =
1822    {
1823      "Font Name",
1824      "Font Color",
1825      "Box Color",
1826      "Rotate Text",
1827      "Help",
1828      "Dismiss",
1829      (char *) NULL
1830    },
1831    *TextMenu[] =
1832    {
1833      "Help",
1834      "Apply",
1835      (char *) NULL
1836    };
1837
1838  static const ModeType
1839    AnnotateCommands[] =
1840    {
1841      AnnotateNameCommand,
1842      AnnotateFontColorCommand,
1843      AnnotateBackgroundColorCommand,
1844      AnnotateRotateCommand,
1845      AnnotateHelpCommand,
1846      AnnotateDismissCommand
1847    },
1848    TextCommands[] =
1849    {
1850      TextHelpCommand,
1851      TextApplyCommand
1852    };
1853
1854  static MagickBooleanType
1855    transparent_box = MagickTrue,
1856    transparent_pen = MagickFalse;
1857
1858  static MagickRealType
1859    degrees = 0.0;
1860
1861  static unsigned int
1862    box_id = MaxNumberPens-2,
1863    font_id = 0,
1864    pen_id = 0;
1865
1866  char
1867    command[MaxTextExtent],
1868    text[MaxTextExtent];
1869
1870  const char
1871    *ColorMenu[MaxNumberPens+1];
1872
1873  Cursor
1874    cursor;
1875
1876  GC
1877    annotate_context;
1878
1879  int
1880    id,
1881    pen_number,
1882    status,
1883    x,
1884    y;
1885
1886  KeySym
1887    key_symbol;
1888
1889  register char
1890    *p;
1891
1892  register ssize_t
1893    i;
1894
1895  unsigned int
1896    height,
1897    width;
1898
1899  size_t
1900    state;
1901
1902  XAnnotateInfo
1903    *annotate_info,
1904    *previous_info;
1905
1906  XColor
1907    color;
1908
1909  XFontStruct
1910    *font_info;
1911
1912  XEvent
1913    event,
1914    text_event;
1915
1916  /*
1917    Map Command widget.
1918  */
1919  (void) CloneString(&windows->command.name,"Annotate");
1920  windows->command.data=4;
1921  (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1922  (void) XMapRaised(display,windows->command.id);
1923  XClientMessage(display,windows->image.id,windows->im_protocols,
1924    windows->im_update_widget,CurrentTime);
1925  /*
1926    Track pointer until button 1 is pressed.
1927  */
1928  XQueryPosition(display,windows->image.id,&x,&y);
1929  (void) XSelectInput(display,windows->image.id,
1930    windows->image.attributes.event_mask | PointerMotionMask);
1931  cursor=XCreateFontCursor(display,XC_left_side);
1932  (void) XCheckDefineCursor(display,windows->image.id,cursor);
1933  state=DefaultState;
1934  do
1935  {
1936    if (windows->info.mapped != MagickFalse)
1937      {
1938        /*
1939          Display pointer position.
1940        */
1941        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
1942          x+windows->image.x,y+windows->image.y);
1943        XInfoWidget(display,windows,text);
1944      }
1945    /*
1946      Wait for next event.
1947    */
1948    XScreenEvent(display,windows,&event,exception);
1949    if (event.xany.window == windows->command.id)
1950      {
1951        /*
1952          Select a command from the Command widget.
1953        */
1954        id=XCommandWidget(display,windows,AnnotateMenu,&event);
1955        (void) XCheckDefineCursor(display,windows->image.id,cursor);
1956        if (id < 0)
1957          continue;
1958        switch (AnnotateCommands[id])
1959        {
1960          case AnnotateNameCommand:
1961          {
1962            const char
1963              *FontMenu[MaxNumberFonts];
1964
1965            int
1966              font_number;
1967
1968            /*
1969              Initialize menu selections.
1970            */
1971            for (i=0; i < MaxNumberFonts; i++)
1972              FontMenu[i]=resource_info->font_name[i];
1973            FontMenu[MaxNumberFonts-2]="Browser...";
1974            FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1975            /*
1976              Select a font name from the pop-up menu.
1977            */
1978            font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1979              (const char **) FontMenu,command);
1980            if (font_number < 0)
1981              break;
1982            if (font_number == (MaxNumberFonts-2))
1983              {
1984                static char
1985                  font_name[MaxTextExtent] = "fixed";
1986
1987                /*
1988                  Select a font name from a browser.
1989                */
1990                resource_info->font_name[font_number]=font_name;
1991                XFontBrowserWidget(display,windows,"Select",font_name);
1992                if (*font_name == '\0')
1993                  break;
1994              }
1995            /*
1996              Initialize font info.
1997            */
1998            font_info=XLoadQueryFont(display,resource_info->font_name[
1999              font_number]);
2000            if (font_info == (XFontStruct *) NULL)
2001              {
2002                XNoticeWidget(display,windows,"Unable to load font:",
2003                  resource_info->font_name[font_number]);
2004                break;
2005              }
2006            font_id=(unsigned int) font_number;
2007            (void) XFreeFont(display,font_info);
2008            break;
2009          }
2010          case AnnotateFontColorCommand:
2011          {
2012            /*
2013              Initialize menu selections.
2014            */
2015            for (i=0; i < (int) (MaxNumberPens-2); i++)
2016              ColorMenu[i]=resource_info->pen_colors[i];
2017            ColorMenu[MaxNumberPens-2]="transparent";
2018            ColorMenu[MaxNumberPens-1]="Browser...";
2019            ColorMenu[MaxNumberPens]=(const char *) NULL;
2020            /*
2021              Select a pen color from the pop-up menu.
2022            */
2023            pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2024              (const char **) ColorMenu,command);
2025            if (pen_number < 0)
2026              break;
2027            transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
2028              MagickFalse;
2029            if (transparent_pen != MagickFalse)
2030              break;
2031            if (pen_number == (MaxNumberPens-1))
2032              {
2033                static char
2034                  color_name[MaxTextExtent] = "gray";
2035
2036                /*
2037                  Select a pen color from a dialog.
2038                */
2039                resource_info->pen_colors[pen_number]=color_name;
2040                XColorBrowserWidget(display,windows,"Select",color_name);
2041                if (*color_name == '\0')
2042                  break;
2043              }
2044            /*
2045              Set pen color.
2046            */
2047            (void) XParseColor(display,windows->map_info->colormap,
2048              resource_info->pen_colors[pen_number],&color);
2049            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2050              (unsigned int) MaxColors,&color);
2051            windows->pixel_info->pen_colors[pen_number]=color;
2052            pen_id=(unsigned int) pen_number;
2053            break;
2054          }
2055          case AnnotateBackgroundColorCommand:
2056          {
2057            /*
2058              Initialize menu selections.
2059            */
2060            for (i=0; i < (int) (MaxNumberPens-2); i++)
2061              ColorMenu[i]=resource_info->pen_colors[i];
2062            ColorMenu[MaxNumberPens-2]="transparent";
2063            ColorMenu[MaxNumberPens-1]="Browser...";
2064            ColorMenu[MaxNumberPens]=(const char *) NULL;
2065            /*
2066              Select a pen color from the pop-up menu.
2067            */
2068            pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2069              (const char **) ColorMenu,command);
2070            if (pen_number < 0)
2071              break;
2072            transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2073              MagickFalse;
2074            if (transparent_box != MagickFalse)
2075              break;
2076            if (pen_number == (MaxNumberPens-1))
2077              {
2078                static char
2079                  color_name[MaxTextExtent] = "gray";
2080
2081                /*
2082                  Select a pen color from a dialog.
2083                */
2084                resource_info->pen_colors[pen_number]=color_name;
2085                XColorBrowserWidget(display,windows,"Select",color_name);
2086                if (*color_name == '\0')
2087                  break;
2088              }
2089            /*
2090              Set pen color.
2091            */
2092            (void) XParseColor(display,windows->map_info->colormap,
2093              resource_info->pen_colors[pen_number],&color);
2094            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2095              (unsigned int) MaxColors,&color);
2096            windows->pixel_info->pen_colors[pen_number]=color;
2097            box_id=(unsigned int) pen_number;
2098            break;
2099          }
2100          case AnnotateRotateCommand:
2101          {
2102            int
2103              entry;
2104
2105            static char
2106              angle[MaxTextExtent] = "30.0";
2107
2108            static const char
2109              *RotateMenu[] =
2110              {
2111                "-90",
2112                "-45",
2113                "-30",
2114                "0",
2115                "30",
2116                "45",
2117                "90",
2118                "180",
2119                "Dialog...",
2120                (char *) NULL,
2121              };
2122
2123            /*
2124              Select a command from the pop-up menu.
2125            */
2126            entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2127              command);
2128            if (entry < 0)
2129              break;
2130            if (entry != 8)
2131              {
2132                degrees=StringToDouble(RotateMenu[entry],(char **) NULL);
2133                break;
2134              }
2135            (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2136              angle);
2137            if (*angle == '\0')
2138              break;
2139            degrees=StringToDouble(angle,(char **) NULL);
2140            break;
2141          }
2142          case AnnotateHelpCommand:
2143          {
2144            XTextViewWidget(display,resource_info,windows,MagickFalse,
2145              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2146            break;
2147          }
2148          case AnnotateDismissCommand:
2149          {
2150            /*
2151              Prematurely exit.
2152            */
2153            state|=EscapeState;
2154            state|=ExitState;
2155            break;
2156          }
2157          default:
2158            break;
2159        }
2160        continue;
2161      }
2162    switch (event.type)
2163    {
2164      case ButtonPress:
2165      {
2166        if (event.xbutton.button != Button1)
2167          break;
2168        if (event.xbutton.window != windows->image.id)
2169          break;
2170        /*
2171          Change to text entering mode.
2172        */
2173        x=event.xbutton.x;
2174        y=event.xbutton.y;
2175        state|=ExitState;
2176        break;
2177      }
2178      case ButtonRelease:
2179        break;
2180      case Expose:
2181        break;
2182      case KeyPress:
2183      {
2184        if (event.xkey.window != windows->image.id)
2185          break;
2186        /*
2187          Respond to a user key press.
2188        */
2189        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2190          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2191        switch ((int) key_symbol)
2192        {
2193          case XK_Escape:
2194          case XK_F20:
2195          {
2196            /*
2197              Prematurely exit.
2198            */
2199            state|=EscapeState;
2200            state|=ExitState;
2201            break;
2202          }
2203          case XK_F1:
2204          case XK_Help:
2205          {
2206            XTextViewWidget(display,resource_info,windows,MagickFalse,
2207              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2208            break;
2209          }
2210          default:
2211          {
2212            (void) XBell(display,0);
2213            break;
2214          }
2215        }
2216        break;
2217      }
2218      case MotionNotify:
2219      {
2220        /*
2221          Map and unmap Info widget as cursor crosses its boundaries.
2222        */
2223        x=event.xmotion.x;
2224        y=event.xmotion.y;
2225        if (windows->info.mapped != MagickFalse)
2226          {
2227            if ((x < (int) (windows->info.x+windows->info.width)) &&
2228                (y < (int) (windows->info.y+windows->info.height)))
2229              (void) XWithdrawWindow(display,windows->info.id,
2230                windows->info.screen);
2231          }
2232        else
2233          if ((x > (int) (windows->info.x+windows->info.width)) ||
2234              (y > (int) (windows->info.y+windows->info.height)))
2235            (void) XMapWindow(display,windows->info.id);
2236        break;
2237      }
2238      default:
2239        break;
2240    }
2241  } while ((state & ExitState) == 0);
2242  (void) XSelectInput(display,windows->image.id,
2243    windows->image.attributes.event_mask);
2244  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2245  if ((state & EscapeState) != 0)
2246    return(MagickTrue);
2247  /*
2248    Set font info and check boundary conditions.
2249  */
2250  font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2251  if (font_info == (XFontStruct *) NULL)
2252    {
2253      XNoticeWidget(display,windows,"Unable to load font:",
2254        resource_info->font_name[font_id]);
2255      font_info=windows->font_info;
2256    }
2257  if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2258    x=(int) windows->image.width-font_info->max_bounds.width;
2259  if (y < (int) (font_info->ascent+font_info->descent))
2260    y=(int) font_info->ascent+font_info->descent;
2261  if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2262      ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2263    return(MagickFalse);
2264  /*
2265    Initialize annotate structure.
2266  */
2267  annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2268  if (annotate_info == (XAnnotateInfo *) NULL)
2269    return(MagickFalse);
2270  XGetAnnotateInfo(annotate_info);
2271  annotate_info->x=x;
2272  annotate_info->y=y;
2273  if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
2274    annotate_info->stencil=OpaqueStencil;
2275  else
2276    if (transparent_box == MagickFalse)
2277      annotate_info->stencil=BackgroundStencil;
2278    else
2279      annotate_info->stencil=ForegroundStencil;
2280  annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2281  annotate_info->degrees=degrees;
2282  annotate_info->font_info=font_info;
2283  annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2284    windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
2285    sizeof(*annotate_info->text));
2286  if (annotate_info->text == (char *) NULL)
2287    return(MagickFalse);
2288  /*
2289    Create cursor and set graphic context.
2290  */
2291  cursor=XCreateFontCursor(display,XC_pencil);
2292  (void) XCheckDefineCursor(display,windows->image.id,cursor);
2293  annotate_context=windows->image.annotate_context;
2294  (void) XSetFont(display,annotate_context,font_info->fid);
2295  (void) XSetBackground(display,annotate_context,
2296    windows->pixel_info->pen_colors[box_id].pixel);
2297  (void) XSetForeground(display,annotate_context,
2298    windows->pixel_info->pen_colors[pen_id].pixel);
2299  /*
2300    Begin annotating the image with text.
2301  */
2302  (void) CloneString(&windows->command.name,"Text");
2303  windows->command.data=0;
2304  (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2305  state=DefaultState;
2306  (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2307  text_event.xexpose.width=(int) font_info->max_bounds.width;
2308  text_event.xexpose.height=font_info->max_bounds.ascent+
2309    font_info->max_bounds.descent;
2310  p=annotate_info->text;
2311  do
2312  {
2313    /*
2314      Display text cursor.
2315    */
2316    *p='\0';
2317    (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2318    /*
2319      Wait for next event.
2320    */
2321    XScreenEvent(display,windows,&event,exception);
2322    if (event.xany.window == windows->command.id)
2323      {
2324        /*
2325          Select a command from the Command widget.
2326        */
2327        (void) XSetBackground(display,annotate_context,
2328          windows->pixel_info->background_color.pixel);
2329        (void) XSetForeground(display,annotate_context,
2330          windows->pixel_info->foreground_color.pixel);
2331        id=XCommandWidget(display,windows,AnnotateMenu,&event);
2332        (void) XSetBackground(display,annotate_context,
2333          windows->pixel_info->pen_colors[box_id].pixel);
2334        (void) XSetForeground(display,annotate_context,
2335          windows->pixel_info->pen_colors[pen_id].pixel);
2336        if (id < 0)
2337          continue;
2338        switch (TextCommands[id])
2339        {
2340          case TextHelpCommand:
2341          {
2342            XTextViewWidget(display,resource_info,windows,MagickFalse,
2343              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2344            (void) XCheckDefineCursor(display,windows->image.id,cursor);
2345            break;
2346          }
2347          case TextApplyCommand:
2348          {
2349            /*
2350              Finished annotating.
2351            */
2352            annotate_info->width=(unsigned int) XTextWidth(font_info,
2353              annotate_info->text,(int) strlen(annotate_info->text));
2354            XRefreshWindow(display,&windows->image,&text_event);
2355            state|=ExitState;
2356            break;
2357          }
2358          default:
2359            break;
2360        }
2361        continue;
2362      }
2363    /*
2364      Erase text cursor.
2365    */
2366    text_event.xexpose.x=x;
2367    text_event.xexpose.y=y-font_info->max_bounds.ascent;
2368    (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2369      (unsigned int) text_event.xexpose.width,(unsigned int)
2370      text_event.xexpose.height,MagickFalse);
2371    XRefreshWindow(display,&windows->image,&text_event);
2372    switch (event.type)
2373    {
2374      case ButtonPress:
2375      {
2376        if (event.xbutton.window != windows->image.id)
2377          break;
2378        if (event.xbutton.button == Button2)
2379          {
2380            /*
2381              Request primary selection.
2382            */
2383            (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2384              windows->image.id,CurrentTime);
2385            break;
2386          }
2387        break;
2388      }
2389      case Expose:
2390      {
2391        if (event.xexpose.count == 0)
2392          {
2393            XAnnotateInfo
2394              *text_info;
2395
2396            /*
2397              Refresh Image window.
2398            */
2399            XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2400            text_info=annotate_info;
2401            while (text_info != (XAnnotateInfo *) NULL)
2402            {
2403              if (annotate_info->stencil == ForegroundStencil)
2404                (void) XDrawString(display,windows->image.id,annotate_context,
2405                  text_info->x,text_info->y,text_info->text,
2406                  (int) strlen(text_info->text));
2407              else
2408                (void) XDrawImageString(display,windows->image.id,
2409                  annotate_context,text_info->x,text_info->y,text_info->text,
2410                  (int) strlen(text_info->text));
2411              text_info=text_info->previous;
2412            }
2413            (void) XDrawString(display,windows->image.id,annotate_context,
2414              x,y,"_",1);
2415          }
2416        break;
2417      }
2418      case KeyPress:
2419      {
2420        int
2421          length;
2422
2423        if (event.xkey.window != windows->image.id)
2424          break;
2425        /*
2426          Respond to a user key press.
2427        */
2428        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2429          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2430        *(command+length)='\0';
2431        if (((event.xkey.state & ControlMask) != 0) ||
2432            ((event.xkey.state & Mod1Mask) != 0))
2433          state|=ModifierState;
2434        if ((state & ModifierState) != 0)
2435          switch ((int) key_symbol)
2436          {
2437            case XK_u:
2438            case XK_U:
2439            {
2440              key_symbol=DeleteCommand;
2441              break;
2442            }
2443            default:
2444              break;
2445          }
2446        switch ((int) key_symbol)
2447        {
2448          case XK_BackSpace:
2449          {
2450            /*
2451              Erase one character.
2452            */
2453            if (p == annotate_info->text)
2454              {
2455                if (annotate_info->previous == (XAnnotateInfo *) NULL)
2456                  break;
2457                else
2458                  {
2459                    /*
2460                      Go to end of the previous line of text.
2461                    */
2462                    annotate_info=annotate_info->previous;
2463                    p=annotate_info->text;
2464                    x=annotate_info->x+annotate_info->width;
2465                    y=annotate_info->y;
2466                    if (annotate_info->width != 0)
2467                      p+=strlen(annotate_info->text);
2468                    break;
2469                  }
2470              }
2471            p--;
2472            x-=XTextWidth(font_info,p,1);
2473            text_event.xexpose.x=x;
2474            text_event.xexpose.y=y-font_info->max_bounds.ascent;
2475            XRefreshWindow(display,&windows->image,&text_event);
2476            break;
2477          }
2478          case XK_bracketleft:
2479          {
2480            key_symbol=XK_Escape;
2481            break;
2482          }
2483          case DeleteCommand:
2484          {
2485            /*
2486              Erase the entire line of text.
2487            */
2488            while (p != annotate_info->text)
2489            {
2490              p--;
2491              x-=XTextWidth(font_info,p,1);
2492              text_event.xexpose.x=x;
2493              XRefreshWindow(display,&windows->image,&text_event);
2494            }
2495            break;
2496          }
2497          case XK_Escape:
2498          case XK_F20:
2499          {
2500            /*
2501              Finished annotating.
2502            */
2503            annotate_info->width=(unsigned int) XTextWidth(font_info,
2504              annotate_info->text,(int) strlen(annotate_info->text));
2505            XRefreshWindow(display,&windows->image,&text_event);
2506            state|=ExitState;
2507            break;
2508          }
2509          default:
2510          {
2511            /*
2512              Draw a single character on the Image window.
2513            */
2514            if ((state & ModifierState) != 0)
2515              break;
2516            if (*command == '\0')
2517              break;
2518            *p=(*command);
2519            if (annotate_info->stencil == ForegroundStencil)
2520              (void) XDrawString(display,windows->image.id,annotate_context,
2521                x,y,p,1);
2522            else
2523              (void) XDrawImageString(display,windows->image.id,
2524                annotate_context,x,y,p,1);
2525            x+=XTextWidth(font_info,p,1);
2526            p++;
2527            if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2528              break;
2529          }
2530          case XK_Return:
2531          case XK_KP_Enter:
2532          {
2533            /*
2534              Advance to the next line of text.
2535            */
2536            *p='\0';
2537            annotate_info->width=(unsigned int) XTextWidth(font_info,
2538              annotate_info->text,(int) strlen(annotate_info->text));
2539            if (annotate_info->next != (XAnnotateInfo *) NULL)
2540              {
2541                /*
2542                  Line of text already exists.
2543                */
2544                annotate_info=annotate_info->next;
2545                x=annotate_info->x;
2546                y=annotate_info->y;
2547                p=annotate_info->text;
2548                break;
2549              }
2550            annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2551              sizeof(*annotate_info->next));
2552            if (annotate_info->next == (XAnnotateInfo *) NULL)
2553              return(MagickFalse);
2554            *annotate_info->next=(*annotate_info);
2555            annotate_info->next->previous=annotate_info;
2556            annotate_info=annotate_info->next;
2557            annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2558              windows->image.width/MagickMax((ssize_t)
2559              font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2560            if (annotate_info->text == (char *) NULL)
2561              return(MagickFalse);
2562            annotate_info->y+=annotate_info->height;
2563            if (annotate_info->y > (int) windows->image.height)
2564              annotate_info->y=(int) annotate_info->height;
2565            annotate_info->next=(XAnnotateInfo *) NULL;
2566            x=annotate_info->x;
2567            y=annotate_info->y;
2568            p=annotate_info->text;
2569            break;
2570          }
2571        }
2572        break;
2573      }
2574      case KeyRelease:
2575      {
2576        /*
2577          Respond to a user key release.
2578        */
2579        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2580          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2581        state&=(~ModifierState);
2582        break;
2583      }
2584      case SelectionNotify:
2585      {
2586        Atom
2587          type;
2588
2589        int
2590          format;
2591
2592        unsigned char
2593          *data;
2594
2595        unsigned long
2596          after,
2597          length;
2598
2599        /*
2600          Obtain response from primary selection.
2601        */
2602        if (event.xselection.property == (Atom) None)
2603          break;
2604        status=XGetWindowProperty(display,event.xselection.requestor,
2605          event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
2606          &type,&format,&length,&after,&data);
2607        if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2608            (length == 0))
2609          break;
2610        /*
2611          Annotate Image window with primary selection.
2612        */
2613        for (i=0; i < (ssize_t) length; i++)
2614        {
2615          if ((char) data[i] != '\n')
2616            {
2617              /*
2618                Draw a single character on the Image window.
2619              */
2620              *p=(char) data[i];
2621              (void) XDrawString(display,windows->image.id,annotate_context,
2622                x,y,p,1);
2623              x+=XTextWidth(font_info,p,1);
2624              p++;
2625              if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2626                continue;
2627            }
2628          /*
2629            Advance to the next line of text.
2630          */
2631          *p='\0';
2632          annotate_info->width=(unsigned int) XTextWidth(font_info,
2633            annotate_info->text,(int) strlen(annotate_info->text));
2634          if (annotate_info->next != (XAnnotateInfo *) NULL)
2635            {
2636              /*
2637                Line of text already exists.
2638              */
2639              annotate_info=annotate_info->next;
2640              x=annotate_info->x;
2641              y=annotate_info->y;
2642              p=annotate_info->text;
2643              continue;
2644            }
2645          annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2646            sizeof(*annotate_info->next));
2647          if (annotate_info->next == (XAnnotateInfo *) NULL)
2648            return(MagickFalse);
2649          *annotate_info->next=(*annotate_info);
2650          annotate_info->next->previous=annotate_info;
2651          annotate_info=annotate_info->next;
2652          annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2653            windows->image.width/MagickMax((ssize_t)
2654            font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2655          if (annotate_info->text == (char *) NULL)
2656            return(MagickFalse);
2657          annotate_info->y+=annotate_info->height;
2658          if (annotate_info->y > (int) windows->image.height)
2659            annotate_info->y=(int) annotate_info->height;
2660          annotate_info->next=(XAnnotateInfo *) NULL;
2661          x=annotate_info->x;
2662          y=annotate_info->y;
2663          p=annotate_info->text;
2664        }
2665        (void) XFree((void *) data);
2666        break;
2667      }
2668      default:
2669        break;
2670    }
2671  } while ((state & ExitState) == 0);
2672  (void) XFreeCursor(display,cursor);
2673  /*
2674    Annotation is relative to image configuration.
2675  */
2676  width=(unsigned int) image->columns;
2677  height=(unsigned int) image->rows;
2678  x=0;
2679  y=0;
2680  if (windows->image.crop_geometry != (char *) NULL)
2681    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2682  /*
2683    Initialize annotated image.
2684  */
2685  XSetCursorState(display,windows,MagickTrue);
2686  XCheckRefreshWindows(display,windows);
2687  while (annotate_info != (XAnnotateInfo *) NULL)
2688  {
2689    if (annotate_info->width == 0)
2690      {
2691        /*
2692          No text on this line--  go to the next line of text.
2693        */
2694        previous_info=annotate_info->previous;
2695        annotate_info->text=(char *)
2696          RelinquishMagickMemory(annotate_info->text);
2697        annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2698        annotate_info=previous_info;
2699        continue;
2700      }
2701    /*
2702      Determine pixel index for box and pen color.
2703    */
2704    windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2705    if (windows->pixel_info->colors != 0)
2706      for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2707        if (windows->pixel_info->pixels[i] ==
2708            windows->pixel_info->pen_colors[box_id].pixel)
2709          {
2710            windows->pixel_info->box_index=(unsigned short) i;
2711            break;
2712          }
2713    windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2714    if (windows->pixel_info->colors != 0)
2715      for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2716        if (windows->pixel_info->pixels[i] ==
2717            windows->pixel_info->pen_colors[pen_id].pixel)
2718          {
2719            windows->pixel_info->pen_index=(unsigned short) i;
2720            break;
2721          }
2722    /*
2723      Define the annotate geometry string.
2724    */
2725    annotate_info->x=(int)
2726      width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2727    annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2728      windows->image.y)/windows->image.ximage->height;
2729    (void) FormatLocaleString(annotate_info->geometry,MaxTextExtent,
2730      "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2731      height*annotate_info->height/windows->image.ximage->height,
2732      annotate_info->x+x,annotate_info->y+y);
2733    /*
2734      Annotate image with text.
2735    */
2736    status=XAnnotateImage(display,windows->pixel_info,annotate_info,image,
2737      exception);
2738    if (status == 0)
2739      return(MagickFalse);
2740    /*
2741      Free up memory.
2742    */
2743    previous_info=annotate_info->previous;
2744    annotate_info->text=DestroyString(annotate_info->text);
2745    annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2746    annotate_info=previous_info;
2747  }
2748  (void) XSetForeground(display,annotate_context,
2749    windows->pixel_info->foreground_color.pixel);
2750  (void) XSetBackground(display,annotate_context,
2751    windows->pixel_info->background_color.pixel);
2752  (void) XSetFont(display,annotate_context,windows->font_info->fid);
2753  XSetCursorState(display,windows,MagickFalse);
2754  (void) XFreeFont(display,font_info);
2755  /*
2756    Update image configuration.
2757  */
2758  XConfigureImageColormap(display,resource_info,windows,image,exception);
2759  (void) XConfigureImage(display,resource_info,windows,image,exception);
2760  return(MagickTrue);
2761}
2762
2763/*
2764%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2765%                                                                             %
2766%                                                                             %
2767%                                                                             %
2768+   X B a c k g r o u n d I m a g e                                           %
2769%                                                                             %
2770%                                                                             %
2771%                                                                             %
2772%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2773%
2774%  XBackgroundImage() displays the image in the background of a window.
2775%
2776%  The format of the XBackgroundImage method is:
2777%
2778%      MagickBooleanType XBackgroundImage(Display *display,
2779%        XResourceInfo *resource_info,XWindows *windows,Image **image,
2780%        ExceptionInfo *exception)
2781%
2782%  A description of each parameter follows:
2783%
2784%    o display: Specifies a connection to an X server; returned from
2785%      XOpenDisplay.
2786%
2787%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2788%
2789%    o windows: Specifies a pointer to a XWindows structure.
2790%
2791%    o image: the image.
2792%
2793%    o exception: return any errors or warnings in this structure.
2794%
2795*/
2796static MagickBooleanType XBackgroundImage(Display *display,
2797  XResourceInfo *resource_info,XWindows *windows,Image **image,
2798  ExceptionInfo *exception)
2799{
2800#define BackgroundImageTag  "Background/Image"
2801
2802  int
2803    status;
2804
2805  static char
2806    window_id[MaxTextExtent] = "root";
2807
2808  XResourceInfo
2809    background_resources;
2810
2811  /*
2812    Put image in background.
2813  */
2814  status=XDialogWidget(display,windows,"Background",
2815    "Enter window id (id 0x00 selects window with pointer):",window_id);
2816  if (*window_id == '\0')
2817    return(MagickFalse);
2818  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
2819    exception);
2820  XInfoWidget(display,windows,BackgroundImageTag);
2821  XSetCursorState(display,windows,MagickTrue);
2822  XCheckRefreshWindows(display,windows);
2823  background_resources=(*resource_info);
2824  background_resources.window_id=window_id;
2825  background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2826  status=XDisplayBackgroundImage(display,&background_resources,*image,
2827    exception);
2828  if (status != MagickFalse)
2829    XClientMessage(display,windows->image.id,windows->im_protocols,
2830      windows->im_retain_colors,CurrentTime);
2831  XSetCursorState(display,windows,MagickFalse);
2832  (void) XMagickCommand(display,resource_info,windows,UndoCommand,image,
2833    exception);
2834  return(MagickTrue);
2835}
2836
2837/*
2838%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2839%                                                                             %
2840%                                                                             %
2841%                                                                             %
2842+   X C h o p I m a g e                                                       %
2843%                                                                             %
2844%                                                                             %
2845%                                                                             %
2846%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2847%
2848%  XChopImage() chops the X image.
2849%
2850%  The format of the XChopImage method is:
2851%
2852%    MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2853%      XWindows *windows,Image **image,ExceptionInfo *exception)
2854%
2855%  A description of each parameter follows:
2856%
2857%    o display: Specifies a connection to an X server; returned from
2858%      XOpenDisplay.
2859%
2860%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2861%
2862%    o windows: Specifies a pointer to a XWindows structure.
2863%
2864%    o image: the image.
2865%
2866%    o exception: return any errors or warnings in this structure.
2867%
2868*/
2869static MagickBooleanType XChopImage(Display *display,
2870  XResourceInfo *resource_info,XWindows *windows,Image **image,
2871  ExceptionInfo *exception)
2872{
2873  static const char
2874    *ChopMenu[] =
2875    {
2876      "Direction",
2877      "Help",
2878      "Dismiss",
2879      (char *) NULL
2880    };
2881
2882  static ModeType
2883    direction = HorizontalChopCommand;
2884
2885  static const ModeType
2886    ChopCommands[] =
2887    {
2888      ChopDirectionCommand,
2889      ChopHelpCommand,
2890      ChopDismissCommand
2891    },
2892    DirectionCommands[] =
2893    {
2894      HorizontalChopCommand,
2895      VerticalChopCommand
2896    };
2897
2898  char
2899    text[MaxTextExtent];
2900
2901  Image
2902    *chop_image;
2903
2904  int
2905    id,
2906    x,
2907    y;
2908
2909  MagickRealType
2910    scale_factor;
2911
2912  RectangleInfo
2913    chop_info;
2914
2915  unsigned int
2916    distance,
2917    height,
2918    width;
2919
2920  size_t
2921    state;
2922
2923  XEvent
2924    event;
2925
2926  XSegment
2927    segment_info;
2928
2929  /*
2930    Map Command widget.
2931  */
2932  (void) CloneString(&windows->command.name,"Chop");
2933  windows->command.data=1;
2934  (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2935  (void) XMapRaised(display,windows->command.id);
2936  XClientMessage(display,windows->image.id,windows->im_protocols,
2937    windows->im_update_widget,CurrentTime);
2938  /*
2939    Track pointer until button 1 is pressed.
2940  */
2941  XQueryPosition(display,windows->image.id,&x,&y);
2942  (void) XSelectInput(display,windows->image.id,
2943    windows->image.attributes.event_mask | PointerMotionMask);
2944  state=DefaultState;
2945  do
2946  {
2947    if (windows->info.mapped != MagickFalse)
2948      {
2949        /*
2950          Display pointer position.
2951        */
2952        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
2953          x+windows->image.x,y+windows->image.y);
2954        XInfoWidget(display,windows,text);
2955      }
2956    /*
2957      Wait for next event.
2958    */
2959    XScreenEvent(display,windows,&event,exception);
2960    if (event.xany.window == windows->command.id)
2961      {
2962        /*
2963          Select a command from the Command widget.
2964        */
2965        id=XCommandWidget(display,windows,ChopMenu,&event);
2966        if (id < 0)
2967          continue;
2968        switch (ChopCommands[id])
2969        {
2970          case ChopDirectionCommand:
2971          {
2972            char
2973              command[MaxTextExtent];
2974
2975            static const char
2976              *Directions[] =
2977              {
2978                "horizontal",
2979                "vertical",
2980                (char *) NULL,
2981              };
2982
2983            /*
2984              Select a command from the pop-up menu.
2985            */
2986            id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2987            if (id >= 0)
2988              direction=DirectionCommands[id];
2989            break;
2990          }
2991          case ChopHelpCommand:
2992          {
2993            XTextViewWidget(display,resource_info,windows,MagickFalse,
2994              "Help Viewer - Image Chop",ImageChopHelp);
2995            break;
2996          }
2997          case ChopDismissCommand:
2998          {
2999            /*
3000              Prematurely exit.
3001            */
3002            state|=EscapeState;
3003            state|=ExitState;
3004            break;
3005          }
3006          default:
3007            break;
3008        }
3009        continue;
3010      }
3011    switch (event.type)
3012    {
3013      case ButtonPress:
3014      {
3015        if (event.xbutton.button != Button1)
3016          break;
3017        if (event.xbutton.window != windows->image.id)
3018          break;
3019        /*
3020          User has committed to start point of chopping line.
3021        */
3022        segment_info.x1=(short int) event.xbutton.x;
3023        segment_info.x2=(short int) event.xbutton.x;
3024        segment_info.y1=(short int) event.xbutton.y;
3025        segment_info.y2=(short int) event.xbutton.y;
3026        state|=ExitState;
3027        break;
3028      }
3029      case ButtonRelease:
3030        break;
3031      case Expose:
3032        break;
3033      case KeyPress:
3034      {
3035        char
3036          command[MaxTextExtent];
3037
3038        KeySym
3039          key_symbol;
3040
3041        if (event.xkey.window != windows->image.id)
3042          break;
3043        /*
3044          Respond to a user key press.
3045        */
3046        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3047          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3048        switch ((int) key_symbol)
3049        {
3050          case XK_Escape:
3051          case XK_F20:
3052          {
3053            /*
3054              Prematurely exit.
3055            */
3056            state|=EscapeState;
3057            state|=ExitState;
3058            break;
3059          }
3060          case XK_F1:
3061          case XK_Help:
3062          {
3063            (void) XSetFunction(display,windows->image.highlight_context,
3064              GXcopy);
3065            XTextViewWidget(display,resource_info,windows,MagickFalse,
3066              "Help Viewer - Image Chop",ImageChopHelp);
3067            (void) XSetFunction(display,windows->image.highlight_context,
3068              GXinvert);
3069            break;
3070          }
3071          default:
3072          {
3073            (void) XBell(display,0);
3074            break;
3075          }
3076        }
3077        break;
3078      }
3079      case MotionNotify:
3080      {
3081        /*
3082          Map and unmap Info widget as text cursor crosses its boundaries.
3083        */
3084        x=event.xmotion.x;
3085        y=event.xmotion.y;
3086        if (windows->info.mapped != MagickFalse)
3087          {
3088            if ((x < (int) (windows->info.x+windows->info.width)) &&
3089                (y < (int) (windows->info.y+windows->info.height)))
3090              (void) XWithdrawWindow(display,windows->info.id,
3091                windows->info.screen);
3092          }
3093        else
3094          if ((x > (int) (windows->info.x+windows->info.width)) ||
3095              (y > (int) (windows->info.y+windows->info.height)))
3096            (void) XMapWindow(display,windows->info.id);
3097      }
3098    }
3099  } while ((state & ExitState) == 0);
3100  (void) XSelectInput(display,windows->image.id,
3101    windows->image.attributes.event_mask);
3102  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3103  if ((state & EscapeState) != 0)
3104    return(MagickTrue);
3105  /*
3106    Draw line as pointer moves until the mouse button is released.
3107  */
3108  chop_info.width=0;
3109  chop_info.height=0;
3110  chop_info.x=0;
3111  chop_info.y=0;
3112  distance=0;
3113  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3114  state=DefaultState;
3115  do
3116  {
3117    if (distance > 9)
3118      {
3119        /*
3120          Display info and draw chopping line.
3121        */
3122        if (windows->info.mapped == MagickFalse)
3123          (void) XMapWindow(display,windows->info.id);
3124        (void) FormatLocaleString(text,MaxTextExtent,
3125          " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3126          chop_info.height,(double) chop_info.x,(double) chop_info.y);
3127        XInfoWidget(display,windows,text);
3128        XHighlightLine(display,windows->image.id,
3129          windows->image.highlight_context,&segment_info);
3130      }
3131    else
3132      if (windows->info.mapped != MagickFalse)
3133        (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3134    /*
3135      Wait for next event.
3136    */
3137    XScreenEvent(display,windows,&event,exception);
3138    if (distance > 9)
3139      XHighlightLine(display,windows->image.id,
3140        windows->image.highlight_context,&segment_info);
3141    switch (event.type)
3142    {
3143      case ButtonPress:
3144      {
3145        segment_info.x2=(short int) event.xmotion.x;
3146        segment_info.y2=(short int) event.xmotion.y;
3147        break;
3148      }
3149      case ButtonRelease:
3150      {
3151        /*
3152          User has committed to chopping line.
3153        */
3154        segment_info.x2=(short int) event.xbutton.x;
3155        segment_info.y2=(short int) event.xbutton.y;
3156        state|=ExitState;
3157        break;
3158      }
3159      case Expose:
3160        break;
3161      case MotionNotify:
3162      {
3163        segment_info.x2=(short int) event.xmotion.x;
3164        segment_info.y2=(short int) event.xmotion.y;
3165      }
3166      default:
3167        break;
3168    }
3169    /*
3170      Check boundary conditions.
3171    */
3172    if (segment_info.x2 < 0)
3173      segment_info.x2=0;
3174    else
3175      if (segment_info.x2 > windows->image.ximage->width)
3176        segment_info.x2=windows->image.ximage->width;
3177    if (segment_info.y2 < 0)
3178      segment_info.y2=0;
3179    else
3180      if (segment_info.y2 > windows->image.ximage->height)
3181        segment_info.y2=windows->image.ximage->height;
3182    distance=(unsigned int)
3183      (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3184       ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3185    /*
3186      Compute chopping geometry.
3187    */
3188    if (direction == HorizontalChopCommand)
3189      {
3190        chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3191        chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3192        chop_info.height=0;
3193        chop_info.y=0;
3194        if (segment_info.x1 > (int) segment_info.x2)
3195          {
3196            chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3197            chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3198          }
3199      }
3200    else
3201      {
3202        chop_info.width=0;
3203        chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3204        chop_info.x=0;
3205        chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3206        if (segment_info.y1 > segment_info.y2)
3207          {
3208            chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3209            chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3210          }
3211      }
3212  } while ((state & ExitState) == 0);
3213  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3214  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3215  if (distance <= 9)
3216    return(MagickTrue);
3217  /*
3218    Image chopping is relative to image configuration.
3219  */
3220  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
3221    exception);
3222  XSetCursorState(display,windows,MagickTrue);
3223  XCheckRefreshWindows(display,windows);
3224  windows->image.window_changes.width=windows->image.ximage->width-
3225    (unsigned int) chop_info.width;
3226  windows->image.window_changes.height=windows->image.ximage->height-
3227    (unsigned int) chop_info.height;
3228  width=(unsigned int) (*image)->columns;
3229  height=(unsigned int) (*image)->rows;
3230  x=0;
3231  y=0;
3232  if (windows->image.crop_geometry != (char *) NULL)
3233    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3234  scale_factor=(MagickRealType) width/windows->image.ximage->width;
3235  chop_info.x+=x;
3236  chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3237  chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3238  scale_factor=(MagickRealType) height/windows->image.ximage->height;
3239  chop_info.y+=y;
3240  chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3241  chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3242  /*
3243    Chop image.
3244  */
3245  chop_image=ChopImage(*image,&chop_info,exception);
3246  XSetCursorState(display,windows,MagickFalse);
3247  if (chop_image == (Image *) NULL)
3248    return(MagickFalse);
3249  *image=DestroyImage(*image);
3250  *image=chop_image;
3251  /*
3252    Update image configuration.
3253  */
3254  XConfigureImageColormap(display,resource_info,windows,*image,exception);
3255  (void) XConfigureImage(display,resource_info,windows,*image,exception);
3256  return(MagickTrue);
3257}
3258
3259/*
3260%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3261%                                                                             %
3262%                                                                             %
3263%                                                                             %
3264+   X C o l o r E d i t I m a g e                                             %
3265%                                                                             %
3266%                                                                             %
3267%                                                                             %
3268%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3269%
3270%  XColorEditImage() allows the user to interactively change the color of one
3271%  pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3272%
3273%  The format of the XColorEditImage method is:
3274%
3275%      MagickBooleanType XColorEditImage(Display *display,
3276%        XResourceInfo *resource_info,XWindows *windows,Image **image,
3277%          ExceptionInfo *exception)
3278%
3279%  A description of each parameter follows:
3280%
3281%    o display: Specifies a connection to an X server;  returned from
3282%      XOpenDisplay.
3283%
3284%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3285%
3286%    o windows: Specifies a pointer to a XWindows structure.
3287%
3288%    o image: the image; returned from ReadImage.
3289%
3290%    o exception: return any errors or warnings in this structure.
3291%
3292*/
3293static MagickBooleanType XColorEditImage(Display *display,
3294  XResourceInfo *resource_info,XWindows *windows,Image **image,
3295  ExceptionInfo *exception)
3296{
3297  static const char
3298    *ColorEditMenu[] =
3299    {
3300      "Method",
3301      "Pixel Color",
3302      "Border Color",
3303      "Fuzz",
3304      "Undo",
3305      "Help",
3306      "Dismiss",
3307      (char *) NULL
3308    };
3309
3310  static const ModeType
3311    ColorEditCommands[] =
3312    {
3313      ColorEditMethodCommand,
3314      ColorEditColorCommand,
3315      ColorEditBorderCommand,
3316      ColorEditFuzzCommand,
3317      ColorEditUndoCommand,
3318      ColorEditHelpCommand,
3319      ColorEditDismissCommand
3320    };
3321
3322  static PaintMethod
3323    method = PointMethod;
3324
3325  static unsigned int
3326    pen_id = 0;
3327
3328  static XColor
3329    border_color = { 0, 0, 0, 0, 0, 0 };
3330
3331  char
3332    command[MaxTextExtent],
3333    text[MaxTextExtent];
3334
3335  Cursor
3336    cursor;
3337
3338  int
3339    entry,
3340    id,
3341    x,
3342    x_offset,
3343    y,
3344    y_offset;
3345
3346  register Quantum
3347    *q;
3348
3349  register ssize_t
3350    i;
3351
3352  unsigned int
3353    height,
3354    width;
3355
3356  size_t
3357    state;
3358
3359  XColor
3360    color;
3361
3362  XEvent
3363    event;
3364
3365  /*
3366    Map Command widget.
3367  */
3368  (void) CloneString(&windows->command.name,"Color Edit");
3369  windows->command.data=4;
3370  (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3371  (void) XMapRaised(display,windows->command.id);
3372  XClientMessage(display,windows->image.id,windows->im_protocols,
3373    windows->im_update_widget,CurrentTime);
3374  /*
3375    Make cursor.
3376  */
3377  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3378    resource_info->background_color,resource_info->foreground_color);
3379  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3380  /*
3381    Track pointer until button 1 is pressed.
3382  */
3383  XQueryPosition(display,windows->image.id,&x,&y);
3384  (void) XSelectInput(display,windows->image.id,
3385    windows->image.attributes.event_mask | PointerMotionMask);
3386  state=DefaultState;
3387  do
3388  {
3389    if (windows->info.mapped != MagickFalse)
3390      {
3391        /*
3392          Display pointer position.
3393        */
3394        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
3395          x+windows->image.x,y+windows->image.y);
3396        XInfoWidget(display,windows,text);
3397      }
3398    /*
3399      Wait for next event.
3400    */
3401    XScreenEvent(display,windows,&event,exception);
3402    if (event.xany.window == windows->command.id)
3403      {
3404        /*
3405          Select a command from the Command widget.
3406        */
3407        id=XCommandWidget(display,windows,ColorEditMenu,&event);
3408        if (id < 0)
3409          {
3410            (void) XCheckDefineCursor(display,windows->image.id,cursor);
3411            continue;
3412          }
3413        switch (ColorEditCommands[id])
3414        {
3415          case ColorEditMethodCommand:
3416          {
3417            char
3418              **methods;
3419
3420            /*
3421              Select a method from the pop-up menu.
3422            */
3423            methods=(char **) GetCommandOptions(MagickMethodOptions);
3424            if (methods == (char **) NULL)
3425              break;
3426            entry=XMenuWidget(display,windows,ColorEditMenu[id],
3427              (const char **) methods,command);
3428            if (entry >= 0)
3429              method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3430                MagickFalse,methods[entry]);
3431            methods=DestroyStringList(methods);
3432            break;
3433          }
3434          case ColorEditColorCommand:
3435          {
3436            const char
3437              *ColorMenu[MaxNumberPens];
3438
3439            int
3440              pen_number;
3441
3442            /*
3443              Initialize menu selections.
3444            */
3445            for (i=0; i < (int) (MaxNumberPens-2); i++)
3446              ColorMenu[i]=resource_info->pen_colors[i];
3447            ColorMenu[MaxNumberPens-2]="Browser...";
3448            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3449            /*
3450              Select a pen color from the pop-up menu.
3451            */
3452            pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3453              (const char **) ColorMenu,command);
3454            if (pen_number < 0)
3455              break;
3456            if (pen_number == (MaxNumberPens-2))
3457              {
3458                static char
3459                  color_name[MaxTextExtent] = "gray";
3460
3461                /*
3462                  Select a pen color from a dialog.
3463                */
3464                resource_info->pen_colors[pen_number]=color_name;
3465                XColorBrowserWidget(display,windows,"Select",color_name);
3466                if (*color_name == '\0')
3467                  break;
3468              }
3469            /*
3470              Set pen color.
3471            */
3472            (void) XParseColor(display,windows->map_info->colormap,
3473              resource_info->pen_colors[pen_number],&color);
3474            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3475              (unsigned int) MaxColors,&color);
3476            windows->pixel_info->pen_colors[pen_number]=color;
3477            pen_id=(unsigned int) pen_number;
3478            break;
3479          }
3480          case ColorEditBorderCommand:
3481          {
3482            const char
3483              *ColorMenu[MaxNumberPens];
3484
3485            int
3486              pen_number;
3487
3488            /*
3489              Initialize menu selections.
3490            */
3491            for (i=0; i < (int) (MaxNumberPens-2); i++)
3492              ColorMenu[i]=resource_info->pen_colors[i];
3493            ColorMenu[MaxNumberPens-2]="Browser...";
3494            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3495            /*
3496              Select a pen color from the pop-up menu.
3497            */
3498            pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3499              (const char **) ColorMenu,command);
3500            if (pen_number < 0)
3501              break;
3502            if (pen_number == (MaxNumberPens-2))
3503              {
3504                static char
3505                  color_name[MaxTextExtent] = "gray";
3506
3507                /*
3508                  Select a pen color from a dialog.
3509                */
3510                resource_info->pen_colors[pen_number]=color_name;
3511                XColorBrowserWidget(display,windows,"Select",color_name);
3512                if (*color_name == '\0')
3513                  break;
3514              }
3515            /*
3516              Set border color.
3517            */
3518            (void) XParseColor(display,windows->map_info->colormap,
3519              resource_info->pen_colors[pen_number],&border_color);
3520            break;
3521          }
3522          case ColorEditFuzzCommand:
3523          {
3524            static char
3525              fuzz[MaxTextExtent];
3526
3527            static const char
3528              *FuzzMenu[] =
3529              {
3530                "0%",
3531                "2%",
3532                "5%",
3533                "10%",
3534                "15%",
3535                "Dialog...",
3536                (char *) NULL,
3537              };
3538
3539            /*
3540              Select a command from the pop-up menu.
3541            */
3542            entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3543              command);
3544            if (entry < 0)
3545              break;
3546            if (entry != 5)
3547              {
3548                (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
3549                  QuantumRange+1.0);
3550                break;
3551              }
3552            (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
3553            (void) XDialogWidget(display,windows,"Ok",
3554              "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3555            if (*fuzz == '\0')
3556              break;
3557            (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
3558            (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
3559              1.0);
3560            break;
3561          }
3562          case ColorEditUndoCommand:
3563          {
3564            (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3565              image,exception);
3566            break;
3567          }
3568          case ColorEditHelpCommand:
3569          default:
3570          {
3571            XTextViewWidget(display,resource_info,windows,MagickFalse,
3572              "Help Viewer - Image Annotation",ImageColorEditHelp);
3573            break;
3574          }
3575          case ColorEditDismissCommand:
3576          {
3577            /*
3578              Prematurely exit.
3579            */
3580            state|=EscapeState;
3581            state|=ExitState;
3582            break;
3583          }
3584        }
3585        (void) XCheckDefineCursor(display,windows->image.id,cursor);
3586        continue;
3587      }
3588    switch (event.type)
3589    {
3590      case ButtonPress:
3591      {
3592        if (event.xbutton.button != Button1)
3593          break;
3594        if ((event.xbutton.window != windows->image.id) &&
3595            (event.xbutton.window != windows->magnify.id))
3596          break;
3597        /*
3598          exit loop.
3599        */
3600        x=event.xbutton.x;
3601        y=event.xbutton.y;
3602        (void) XMagickCommand(display,resource_info,windows,
3603          SaveToUndoBufferCommand,image,exception);
3604        state|=UpdateConfigurationState;
3605        break;
3606      }
3607      case ButtonRelease:
3608      {
3609        if (event.xbutton.button != Button1)
3610          break;
3611        if ((event.xbutton.window != windows->image.id) &&
3612            (event.xbutton.window != windows->magnify.id))
3613          break;
3614        /*
3615          Update colormap information.
3616        */
3617        x=event.xbutton.x;
3618        y=event.xbutton.y;
3619        XConfigureImageColormap(display,resource_info,windows,*image,exception);
3620        (void) XConfigureImage(display,resource_info,windows,*image,exception);
3621        XInfoWidget(display,windows,text);
3622        (void) XCheckDefineCursor(display,windows->image.id,cursor);
3623        state&=(~UpdateConfigurationState);
3624        break;
3625      }
3626      case Expose:
3627        break;
3628      case KeyPress:
3629      {
3630        KeySym
3631          key_symbol;
3632
3633        if (event.xkey.window == windows->magnify.id)
3634          {
3635            Window
3636              window;
3637
3638            window=windows->magnify.id;
3639            while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3640          }
3641        if (event.xkey.window != windows->image.id)
3642          break;
3643        /*
3644          Respond to a user key press.
3645        */
3646        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3647          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3648        switch ((int) key_symbol)
3649        {
3650          case XK_Escape:
3651          case XK_F20:
3652          {
3653            /*
3654              Prematurely exit.
3655            */
3656            state|=ExitState;
3657            break;
3658          }
3659          case XK_F1:
3660          case XK_Help:
3661          {
3662            XTextViewWidget(display,resource_info,windows,MagickFalse,
3663              "Help Viewer - Image Annotation",ImageColorEditHelp);
3664            break;
3665          }
3666          default:
3667          {
3668            (void) XBell(display,0);
3669            break;
3670          }
3671        }
3672        break;
3673      }
3674      case MotionNotify:
3675      {
3676        /*
3677          Map and unmap Info widget as cursor crosses its boundaries.
3678        */
3679        x=event.xmotion.x;
3680        y=event.xmotion.y;
3681        if (windows->info.mapped != MagickFalse)
3682          {
3683            if ((x < (int) (windows->info.x+windows->info.width)) &&
3684                (y < (int) (windows->info.y+windows->info.height)))
3685              (void) XWithdrawWindow(display,windows->info.id,
3686                windows->info.screen);
3687          }
3688        else
3689          if ((x > (int) (windows->info.x+windows->info.width)) ||
3690              (y > (int) (windows->info.y+windows->info.height)))
3691            (void) XMapWindow(display,windows->info.id);
3692        break;
3693      }
3694      default:
3695        break;
3696    }
3697    if (event.xany.window == windows->magnify.id)
3698      {
3699        x=windows->magnify.x-windows->image.x;
3700        y=windows->magnify.y-windows->image.y;
3701      }
3702    x_offset=x;
3703    y_offset=y;
3704    if ((state & UpdateConfigurationState) != 0)
3705      {
3706        CacheView
3707          *image_view;
3708
3709        int
3710          x,
3711          y;
3712
3713        /*
3714          Pixel edit is relative to image configuration.
3715        */
3716        (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3717          MagickTrue);
3718        color=windows->pixel_info->pen_colors[pen_id];
3719        XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3720        width=(unsigned int) (*image)->columns;
3721        height=(unsigned int) (*image)->rows;
3722        x=0;
3723        y=0;
3724        if (windows->image.crop_geometry != (char *) NULL)
3725          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3726            &width,&height);
3727        x_offset=(int)
3728          (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3729        y_offset=(int)
3730          (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3731        if ((x_offset < 0) || (y_offset < 0))
3732          continue;
3733        if ((x_offset >= (int) (*image)->columns) ||
3734            (y_offset >= (int) (*image)->rows))
3735          continue;
3736        image_view=AcquireCacheView(*image);
3737        switch (method)
3738        {
3739          case PointMethod:
3740          default:
3741          {
3742            /*
3743              Update color information using point algorithm.
3744            */
3745            if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3746              return(MagickFalse);
3747            q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3748              (ssize_t) y_offset,1,1,exception);
3749            if (q == (Quantum *) NULL)
3750              break;
3751            SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3752            SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3753            SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3754            (void) SyncCacheViewAuthenticPixels(image_view,exception);
3755            break;
3756          }
3757          case ReplaceMethod:
3758          {
3759            PixelInfo
3760              pixel,
3761              target;
3762
3763            /*
3764              Update color information using replace algorithm.
3765            */
3766            (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
3767              x_offset,(ssize_t) y_offset,&target,exception);
3768            if ((*image)->storage_class == DirectClass)
3769              {
3770                for (y=0; y < (int) (*image)->rows; y++)
3771                {
3772                  q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3773                    (*image)->columns,1,exception);
3774                  if (q == (Quantum *) NULL)
3775                    break;
3776                  for (x=0; x < (int) (*image)->columns; x++)
3777                  {
3778                    GetPixelInfoPixel(*image,q,&pixel);
3779                    if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
3780                      {
3781                        SetPixelRed(*image,ScaleShortToQuantum(
3782                          color.red),q);
3783                        SetPixelGreen(*image,ScaleShortToQuantum(
3784                          color.green),q);
3785                        SetPixelBlue(*image,ScaleShortToQuantum(
3786                          color.blue),q);
3787                      }
3788                    q+=GetPixelChannels(*image);
3789                  }
3790                  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3791                    break;
3792                }
3793              }
3794            else
3795              {
3796                for (i=0; i < (ssize_t) (*image)->colors; i++)
3797                  if (IsFuzzyEquivalencePixelInfo((*image)->colormap+i,&target))
3798                    {
3799                      (*image)->colormap[i].red=(double) ScaleShortToQuantum(
3800                        color.red);
3801                      (*image)->colormap[i].green=(double) ScaleShortToQuantum(
3802                        color.green);
3803                      (*image)->colormap[i].blue=(double) ScaleShortToQuantum(
3804                        color.blue);
3805                    }
3806                (void) SyncImage(*image,exception);
3807              }
3808            break;
3809          }
3810          case FloodfillMethod:
3811          case FillToBorderMethod:
3812          {
3813            DrawInfo
3814              *draw_info;
3815
3816            PixelInfo
3817              target;
3818
3819            /*
3820              Update color information using floodfill algorithm.
3821            */
3822            (void) GetOneVirtualPixelInfo(*image,
3823              GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
3824              y_offset,&target,exception);
3825            if (method == FillToBorderMethod)
3826              {
3827                target.red=(MagickRealType)
3828                  ScaleShortToQuantum(border_color.red);
3829                target.green=(MagickRealType)
3830                  ScaleShortToQuantum(border_color.green);
3831                target.blue=(MagickRealType)
3832                  ScaleShortToQuantum(border_color.blue);
3833              }
3834            draw_info=CloneDrawInfo(resource_info->image_info,
3835              (DrawInfo *) NULL);
3836            (void) QueryColorCompliance(resource_info->pen_colors[pen_id],
3837              AllCompliance,&draw_info->fill,exception);
3838            (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
3839              x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
3840              MagickFalse : MagickTrue,exception);
3841            draw_info=DestroyDrawInfo(draw_info);
3842            break;
3843          }
3844          case ResetMethod:
3845          {
3846            /*
3847              Update color information using reset algorithm.
3848            */
3849            if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3850              return(MagickFalse);
3851            for (y=0; y < (int) (*image)->rows; y++)
3852            {
3853              q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3854                (*image)->columns,1,exception);
3855              if (q == (Quantum *) NULL)
3856                break;
3857              for (x=0; x < (int) (*image)->columns; x++)
3858              {
3859                SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3860                SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3861                SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3862                q+=GetPixelChannels(*image);
3863              }
3864              if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3865                break;
3866            }
3867            break;
3868          }
3869        }
3870        image_view=DestroyCacheView(image_view);
3871        state&=(~UpdateConfigurationState);
3872      }
3873  } while ((state & ExitState) == 0);
3874  (void) XSelectInput(display,windows->image.id,
3875    windows->image.attributes.event_mask);
3876  XSetCursorState(display,windows,MagickFalse);
3877  (void) XFreeCursor(display,cursor);
3878  return(MagickTrue);
3879}
3880
3881/*
3882%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3883%                                                                             %
3884%                                                                             %
3885%                                                                             %
3886+   X C o m p o s i t e I m a g e                                             %
3887%                                                                             %
3888%                                                                             %
3889%                                                                             %
3890%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3891%
3892%  XCompositeImage() requests an image name from the user, reads the image and
3893%  composites it with the X window image at a location the user chooses with
3894%  the pointer.
3895%
3896%  The format of the XCompositeImage method is:
3897%
3898%      MagickBooleanType XCompositeImage(Display *display,
3899%        XResourceInfo *resource_info,XWindows *windows,Image *image,
3900%        ExceptionInfo *exception)
3901%
3902%  A description of each parameter follows:
3903%
3904%    o display: Specifies a connection to an X server;  returned from
3905%      XOpenDisplay.
3906%
3907%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3908%
3909%    o windows: Specifies a pointer to a XWindows structure.
3910%
3911%    o image: the image; returned from ReadImage.
3912%
3913%    o exception: return any errors or warnings in this structure.
3914%
3915*/
3916static MagickBooleanType XCompositeImage(Display *display,
3917  XResourceInfo *resource_info,XWindows *windows,Image *image,
3918  ExceptionInfo *exception)
3919{
3920  static char
3921    displacement_geometry[MaxTextExtent] = "30x30",
3922    filename[MaxTextExtent] = "\0";
3923
3924  static const char
3925    *CompositeMenu[] =
3926    {
3927      "Operators",
3928      "Dissolve",
3929      "Displace",
3930      "Help",
3931      "Dismiss",
3932      (char *) NULL
3933    };
3934
3935  static CompositeOperator
3936    compose = CopyCompositeOp;
3937
3938  static const ModeType
3939    CompositeCommands[] =
3940    {
3941      CompositeOperatorsCommand,
3942      CompositeDissolveCommand,
3943      CompositeDisplaceCommand,
3944      CompositeHelpCommand,
3945      CompositeDismissCommand
3946    };
3947
3948  char
3949    text[MaxTextExtent];
3950
3951  Cursor
3952    cursor;
3953
3954  Image
3955    *composite_image;
3956
3957  int
3958    entry,
3959    id,
3960    x,
3961    y;
3962
3963  MagickRealType
3964    blend,
3965    scale_factor;
3966
3967  RectangleInfo
3968    highlight_info,
3969    composite_info;
3970
3971  unsigned int
3972    height,
3973    width;
3974
3975  size_t
3976    state;
3977
3978  XEvent
3979    event;
3980
3981  /*
3982    Request image file name from user.
3983  */
3984  XFileBrowserWidget(display,windows,"Composite",filename);
3985  if (*filename == '\0')
3986    return(MagickTrue);
3987  /*
3988    Read image.
3989  */
3990  XSetCursorState(display,windows,MagickTrue);
3991  XCheckRefreshWindows(display,windows);
3992  (void) CopyMagickString(resource_info->image_info->filename,filename,
3993    MaxTextExtent);
3994  composite_image=ReadImage(resource_info->image_info,exception);
3995  CatchException(exception);
3996  XSetCursorState(display,windows,MagickFalse);
3997  if (composite_image == (Image *) NULL)
3998    return(MagickFalse);
3999  /*
4000    Map Command widget.
4001  */
4002  (void) CloneString(&windows->command.name,"Composite");
4003  windows->command.data=1;
4004  (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
4005  (void) XMapRaised(display,windows->command.id);
4006  XClientMessage(display,windows->image.id,windows->im_protocols,
4007    windows->im_update_widget,CurrentTime);
4008  /*
4009    Track pointer until button 1 is pressed.
4010  */
4011  XQueryPosition(display,windows->image.id,&x,&y);
4012  (void) XSelectInput(display,windows->image.id,
4013    windows->image.attributes.event_mask | PointerMotionMask);
4014  composite_info.x=(ssize_t) windows->image.x+x;
4015  composite_info.y=(ssize_t) windows->image.y+y;
4016  composite_info.width=0;
4017  composite_info.height=0;
4018  cursor=XCreateFontCursor(display,XC_ul_angle);
4019  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4020  blend=0.0;
4021  state=DefaultState;
4022  do
4023  {
4024    if (windows->info.mapped != MagickFalse)
4025      {
4026        /*
4027          Display pointer position.
4028        */
4029        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4030          (long) composite_info.x,(long) composite_info.y);
4031        XInfoWidget(display,windows,text);
4032      }
4033    highlight_info=composite_info;
4034    highlight_info.x=composite_info.x-windows->image.x;
4035    highlight_info.y=composite_info.y-windows->image.y;
4036    XHighlightRectangle(display,windows->image.id,
4037      windows->image.highlight_context,&highlight_info);
4038    /*
4039      Wait for next event.
4040    */
4041    XScreenEvent(display,windows,&event,exception);
4042    XHighlightRectangle(display,windows->image.id,
4043      windows->image.highlight_context,&highlight_info);
4044    if (event.xany.window == windows->command.id)
4045      {
4046        /*
4047          Select a command from the Command widget.
4048        */
4049        id=XCommandWidget(display,windows,CompositeMenu,&event);
4050        if (id < 0)
4051          continue;
4052        switch (CompositeCommands[id])
4053        {
4054          case CompositeOperatorsCommand:
4055          {
4056            char
4057              command[MaxTextExtent],
4058              **operators;
4059
4060            /*
4061              Select a command from the pop-up menu.
4062            */
4063            operators=GetCommandOptions(MagickComposeOptions);
4064            if (operators == (char **) NULL)
4065              break;
4066            entry=XMenuWidget(display,windows,CompositeMenu[id],
4067              (const char **) operators,command);
4068            if (entry >= 0)
4069              compose=(CompositeOperator) ParseCommandOption(
4070                MagickComposeOptions,MagickFalse,operators[entry]);
4071            operators=DestroyStringList(operators);
4072            break;
4073          }
4074          case CompositeDissolveCommand:
4075          {
4076            static char
4077              factor[MaxTextExtent] = "20.0";
4078
4079            /*
4080              Dissolve the two images a given percent.
4081            */
4082            (void) XSetFunction(display,windows->image.highlight_context,
4083              GXcopy);
4084            (void) XDialogWidget(display,windows,"Dissolve",
4085              "Enter the blend factor (0.0 - 99.9%):",factor);
4086            (void) XSetFunction(display,windows->image.highlight_context,
4087              GXinvert);
4088            if (*factor == '\0')
4089              break;
4090            blend=StringToDouble(factor,(char **) NULL);
4091            compose=DissolveCompositeOp;
4092            break;
4093          }
4094          case CompositeDisplaceCommand:
4095          {
4096            /*
4097              Get horizontal and vertical scale displacement geometry.
4098            */
4099            (void) XSetFunction(display,windows->image.highlight_context,
4100              GXcopy);
4101            (void) XDialogWidget(display,windows,"Displace",
4102              "Enter the horizontal and vertical scale:",displacement_geometry);
4103            (void) XSetFunction(display,windows->image.highlight_context,
4104              GXinvert);
4105            if (*displacement_geometry == '\0')
4106              break;
4107            compose=DisplaceCompositeOp;
4108            break;
4109          }
4110          case CompositeHelpCommand:
4111          {
4112            (void) XSetFunction(display,windows->image.highlight_context,
4113              GXcopy);
4114            XTextViewWidget(display,resource_info,windows,MagickFalse,
4115              "Help Viewer - Image Composite",ImageCompositeHelp);
4116            (void) XSetFunction(display,windows->image.highlight_context,
4117              GXinvert);
4118            break;
4119          }
4120          case CompositeDismissCommand:
4121          {
4122            /*
4123              Prematurely exit.
4124            */
4125            state|=EscapeState;
4126            state|=ExitState;
4127            break;
4128          }
4129          default:
4130            break;
4131        }
4132        continue;
4133      }
4134    switch (event.type)
4135    {
4136      case ButtonPress:
4137      {
4138        if (image->debug != MagickFalse)
4139          (void) LogMagickEvent(X11Event,GetMagickModule(),
4140            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4141            event.xbutton.button,event.xbutton.x,event.xbutton.y);
4142        if (event.xbutton.button != Button1)
4143          break;
4144        if (event.xbutton.window != windows->image.id)
4145          break;
4146        /*
4147          Change cursor.
4148        */
4149        composite_info.width=composite_image->columns;
4150        composite_info.height=composite_image->rows;
4151        (void) XCheckDefineCursor(display,windows->image.id,cursor);
4152        composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4153        composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4154        break;
4155      }
4156      case ButtonRelease:
4157      {
4158        if (image->debug != MagickFalse)
4159          (void) LogMagickEvent(X11Event,GetMagickModule(),
4160            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4161            event.xbutton.button,event.xbutton.x,event.xbutton.y);
4162        if (event.xbutton.button != Button1)
4163          break;
4164        if (event.xbutton.window != windows->image.id)
4165          break;
4166        if ((composite_info.width != 0) && (composite_info.height != 0))
4167          {
4168            /*
4169              User has selected the location of the composite image.
4170            */
4171            composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4172            composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4173            state|=ExitState;
4174          }
4175        break;
4176      }
4177      case Expose:
4178        break;
4179      case KeyPress:
4180      {
4181        char
4182          command[MaxTextExtent];
4183
4184        KeySym
4185          key_symbol;
4186
4187        int
4188          length;
4189
4190        if (event.xkey.window != windows->image.id)
4191          break;
4192        /*
4193          Respond to a user key press.
4194        */
4195        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4196          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4197        *(command+length)='\0';
4198        if (image->debug != MagickFalse)
4199          (void) LogMagickEvent(X11Event,GetMagickModule(),
4200            "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4201        switch ((int) key_symbol)
4202        {
4203          case XK_Escape:
4204          case XK_F20:
4205          {
4206            /*
4207              Prematurely exit.
4208            */
4209            composite_image=DestroyImage(composite_image);
4210            state|=EscapeState;
4211            state|=ExitState;
4212            break;
4213          }
4214          case XK_F1:
4215          case XK_Help:
4216          {
4217            (void) XSetFunction(display,windows->image.highlight_context,
4218              GXcopy);
4219            XTextViewWidget(display,resource_info,windows,MagickFalse,
4220              "Help Viewer - Image Composite",ImageCompositeHelp);
4221            (void) XSetFunction(display,windows->image.highlight_context,
4222              GXinvert);
4223            break;
4224          }
4225          default:
4226          {
4227            (void) XBell(display,0);
4228            break;
4229          }
4230        }
4231        break;
4232      }
4233      case MotionNotify:
4234      {
4235        /*
4236          Map and unmap Info widget as text cursor crosses its boundaries.
4237        */
4238        x=event.xmotion.x;
4239        y=event.xmotion.y;
4240        if (windows->info.mapped != MagickFalse)
4241          {
4242            if ((x < (int) (windows->info.x+windows->info.width)) &&
4243                (y < (int) (windows->info.y+windows->info.height)))
4244              (void) XWithdrawWindow(display,windows->info.id,
4245                windows->info.screen);
4246          }
4247        else
4248          if ((x > (int) (windows->info.x+windows->info.width)) ||
4249              (y > (int) (windows->info.y+windows->info.height)))
4250            (void) XMapWindow(display,windows->info.id);
4251        composite_info.x=(ssize_t) windows->image.x+x;
4252        composite_info.y=(ssize_t) windows->image.y+y;
4253        break;
4254      }
4255      default:
4256      {
4257        if (image->debug != MagickFalse)
4258          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4259            event.type);
4260        break;
4261      }
4262    }
4263  } while ((state & ExitState) == 0);
4264  (void) XSelectInput(display,windows->image.id,
4265    windows->image.attributes.event_mask);
4266  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4267  XSetCursorState(display,windows,MagickFalse);
4268  (void) XFreeCursor(display,cursor);
4269  if ((state & EscapeState) != 0)
4270    return(MagickTrue);
4271  /*
4272    Image compositing is relative to image configuration.
4273  */
4274  XSetCursorState(display,windows,MagickTrue);
4275  XCheckRefreshWindows(display,windows);
4276  width=(unsigned int) image->columns;
4277  height=(unsigned int) image->rows;
4278  x=0;
4279  y=0;
4280  if (windows->image.crop_geometry != (char *) NULL)
4281    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4282  scale_factor=(MagickRealType) width/windows->image.ximage->width;
4283  composite_info.x+=x;
4284  composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4285  composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4286  scale_factor=(MagickRealType) height/windows->image.ximage->height;
4287  composite_info.y+=y;
4288  composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4289  composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4290  if ((composite_info.width != composite_image->columns) ||
4291      (composite_info.height != composite_image->rows))
4292    {
4293      Image
4294        *resize_image;
4295
4296      /*
4297        Scale composite image.
4298      */
4299      resize_image=ResizeImage(composite_image,composite_info.width,
4300        composite_info.height,composite_image->filter,composite_image->blur,
4301        exception);
4302      composite_image=DestroyImage(composite_image);
4303      if (resize_image == (Image *) NULL)
4304        {
4305          XSetCursorState(display,windows,MagickFalse);
4306          return(MagickFalse);
4307        }
4308      composite_image=resize_image;
4309    }
4310  if (compose == DisplaceCompositeOp)
4311    (void) SetImageArtifact(composite_image,"compose:args",
4312      displacement_geometry);
4313  if (blend != 0.0)
4314    {
4315      CacheView
4316        *image_view;
4317
4318      int
4319        y;
4320
4321      Quantum
4322        opacity;
4323
4324      register int
4325        x;
4326
4327      register Quantum
4328        *q;
4329
4330      /*
4331        Create mattes for blending.
4332      */
4333      (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,exception);
4334      opacity=(Quantum) (ScaleQuantumToChar((Quantum) QuantumRange)-
4335        ((ssize_t) ScaleQuantumToChar((Quantum) QuantumRange)*blend)/100);
4336      if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
4337        return(MagickFalse);
4338      image->matte=MagickTrue;
4339      image_view=AcquireCacheView(image);
4340      for (y=0; y < (int) image->rows; y++)
4341      {
4342        q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4343          exception);
4344        if (q == (Quantum *) NULL)
4345          break;
4346        for (x=0; x < (int) image->columns; x++)
4347        {
4348          SetPixelAlpha(image,opacity,q);
4349          q+=GetPixelChannels(image);
4350        }
4351        if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4352          break;
4353      }
4354      image_view=DestroyCacheView(image_view);
4355    }
4356  /*
4357    Composite image with X Image window.
4358  */
4359  (void) CompositeImage(image,compose,composite_image,composite_info.x,
4360    composite_info.y,exception);
4361  composite_image=DestroyImage(composite_image);
4362  XSetCursorState(display,windows,MagickFalse);
4363  /*
4364    Update image configuration.
4365  */
4366  XConfigureImageColormap(display,resource_info,windows,image,exception);
4367  (void) XConfigureImage(display,resource_info,windows,image,exception);
4368  return(MagickTrue);
4369}
4370
4371/*
4372%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4373%                                                                             %
4374%                                                                             %
4375%                                                                             %
4376+   X C o n f i g u r e I m a g e                                             %
4377%                                                                             %
4378%                                                                             %
4379%                                                                             %
4380%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4381%
4382%  XConfigureImage() creates a new X image.  It also notifies the window
4383%  manager of the new image size and configures the transient widows.
4384%
4385%  The format of the XConfigureImage method is:
4386%
4387%      MagickBooleanType XConfigureImage(Display *display,
4388%        XResourceInfo *resource_info,XWindows *windows,Image *image,
4389%        ExceptionInfo *exception)
4390%
4391%  A description of each parameter follows:
4392%
4393%    o display: Specifies a connection to an X server; returned from
4394%      XOpenDisplay.
4395%
4396%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4397%
4398%    o windows: Specifies a pointer to a XWindows structure.
4399%
4400%    o image: the image.
4401%
4402%    o exception: return any errors or warnings in this structure.
4403%
4404%    o exception: return any errors or warnings in this structure.
4405%
4406*/
4407static MagickBooleanType XConfigureImage(Display *display,
4408  XResourceInfo *resource_info,XWindows *windows,Image *image,
4409  ExceptionInfo *exception)
4410{
4411  char
4412    geometry[MaxTextExtent];
4413
4414  MagickStatusType
4415    status;
4416
4417  size_t
4418    mask,
4419    height,
4420    width;
4421
4422  ssize_t
4423    x,
4424    y;
4425
4426  XSizeHints
4427    *size_hints;
4428
4429  XWindowChanges
4430    window_changes;
4431
4432  /*
4433    Dismiss if window dimensions are zero.
4434  */
4435  width=(unsigned int) windows->image.window_changes.width;
4436  height=(unsigned int) windows->image.window_changes.height;
4437  if (image->debug != MagickFalse)
4438    (void) LogMagickEvent(X11Event,GetMagickModule(),
4439      "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4440      windows->image.ximage->height,(double) width,(double) height);
4441  if ((width*height) == 0)
4442    return(MagickTrue);
4443  x=0;
4444  y=0;
4445  /*
4446    Resize image to fit Image window dimensions.
4447  */
4448  XSetCursorState(display,windows,MagickTrue);
4449  (void) XFlush(display);
4450  if (((int) width != windows->image.ximage->width) ||
4451      ((int) height != windows->image.ximage->height))
4452    image->taint=MagickTrue;
4453  windows->magnify.x=(int)
4454    width*windows->magnify.x/windows->image.ximage->width;
4455  windows->magnify.y=(int)
4456    height*windows->magnify.y/windows->image.ximage->height;
4457  windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4458  windows->image.y=(int)
4459    (height*windows->image.y/windows->image.ximage->height);
4460  status=XMakeImage(display,resource_info,&windows->image,image,
4461    (unsigned int) width,(unsigned int) height,exception);
4462  if (status == MagickFalse)
4463    XNoticeWidget(display,windows,"Unable to configure X image:",
4464      windows->image.name);
4465  /*
4466    Notify window manager of the new configuration.
4467  */
4468  if (resource_info->image_geometry != (char *) NULL)
4469    (void) FormatLocaleString(geometry,MaxTextExtent,"%s>!",
4470      resource_info->image_geometry);
4471  else
4472    (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
4473      XDisplayWidth(display,windows->image.screen),
4474      XDisplayHeight(display,windows->image.screen));
4475  (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4476  window_changes.width=(int) width;
4477  if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4478    window_changes.width=XDisplayWidth(display,windows->image.screen);
4479  window_changes.height=(int) height;
4480  if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4481    window_changes.height=XDisplayHeight(display,windows->image.screen);
4482  mask=(size_t) (CWWidth | CWHeight);
4483  if (resource_info->backdrop)
4484    {
4485      mask|=CWX | CWY;
4486      window_changes.x=(int)
4487        ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4488      window_changes.y=(int)
4489        ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4490    }
4491  (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4492    (unsigned int) mask,&window_changes);
4493  (void) XClearWindow(display,windows->image.id);
4494  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4495  /*
4496    Update Magnify window configuration.
4497  */
4498  if (windows->magnify.mapped != MagickFalse)
4499    XMakeMagnifyImage(display,windows,exception);
4500  windows->pan.crop_geometry=windows->image.crop_geometry;
4501  XBestIconSize(display,&windows->pan,image);
4502  while (((windows->pan.width << 1) < MaxIconSize) &&
4503         ((windows->pan.height << 1) < MaxIconSize))
4504  {
4505    windows->pan.width<<=1;
4506    windows->pan.height<<=1;
4507  }
4508  if (windows->pan.geometry != (char *) NULL)
4509    (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4510      &windows->pan.width,&windows->pan.height);
4511  window_changes.width=(int) windows->pan.width;
4512  window_changes.height=(int) windows->pan.height;
4513  size_hints=XAllocSizeHints();
4514  if (size_hints != (XSizeHints *) NULL)
4515    {
4516      /*
4517        Set new size hints.
4518      */
4519      size_hints->flags=PSize | PMinSize | PMaxSize;
4520      size_hints->width=window_changes.width;
4521      size_hints->height=window_changes.height;
4522      size_hints->min_width=size_hints->width;
4523      size_hints->min_height=size_hints->height;
4524      size_hints->max_width=size_hints->width;
4525      size_hints->max_height=size_hints->height;
4526      (void) XSetNormalHints(display,windows->pan.id,size_hints);
4527      (void) XFree((void *) size_hints);
4528    }
4529  (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4530    (unsigned int) (CWWidth | CWHeight),&window_changes);
4531  /*
4532    Update icon window configuration.
4533  */
4534  windows->icon.crop_geometry=windows->image.crop_geometry;
4535  XBestIconSize(display,&windows->icon,image);
4536  window_changes.width=(int) windows->icon.width;
4537  window_changes.height=(int) windows->icon.height;
4538  (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4539    (unsigned int) (CWWidth | CWHeight),&window_changes);
4540  XSetCursorState(display,windows,MagickFalse);
4541  return(status != 0 ? MagickTrue : MagickFalse);
4542}
4543
4544/*
4545%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4546%                                                                             %
4547%                                                                             %
4548%                                                                             %
4549+   X C r o p I m a g e                                                       %
4550%                                                                             %
4551%                                                                             %
4552%                                                                             %
4553%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4554%
4555%  XCropImage() allows the user to select a region of the image and crop, copy,
4556%  or cut it.  For copy or cut, the image can subsequently be composited onto
4557%  the image with XPasteImage.
4558%
4559%  The format of the XCropImage method is:
4560%
4561%      MagickBooleanType XCropImage(Display *display,
4562%        XResourceInfo *resource_info,XWindows *windows,Image *image,
4563%        const ClipboardMode mode,ExceptionInfo *exception)
4564%
4565%  A description of each parameter follows:
4566%
4567%    o display: Specifies a connection to an X server; returned from
4568%      XOpenDisplay.
4569%
4570%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4571%
4572%    o windows: Specifies a pointer to a XWindows structure.
4573%
4574%    o image: the image; returned from ReadImage.
4575%
4576%    o mode: This unsigned value specified whether the image should be
4577%      cropped, copied, or cut.
4578%
4579%    o exception: return any errors or warnings in this structure.
4580%
4581*/
4582static MagickBooleanType XCropImage(Display *display,
4583  XResourceInfo *resource_info,XWindows *windows,Image *image,
4584  const ClipboardMode mode,ExceptionInfo *exception)
4585{
4586  static const char
4587    *CropModeMenu[] =
4588    {
4589      "Help",
4590      "Dismiss",
4591      (char *) NULL
4592    },
4593    *RectifyModeMenu[] =
4594    {
4595      "Crop",
4596      "Help",
4597      "Dismiss",
4598      (char *) NULL
4599    };
4600
4601  static const ModeType
4602    CropCommands[] =
4603    {
4604      CropHelpCommand,
4605      CropDismissCommand
4606    },
4607    RectifyCommands[] =
4608    {
4609      RectifyCopyCommand,
4610      RectifyHelpCommand,
4611      RectifyDismissCommand
4612    };
4613
4614  CacheView
4615    *image_view;
4616
4617  char
4618    command[MaxTextExtent],
4619    text[MaxTextExtent];
4620
4621  Cursor
4622    cursor;
4623
4624  int
4625    id,
4626    x,
4627    y;
4628
4629  KeySym
4630    key_symbol;
4631
4632  Image
4633    *crop_image;
4634
4635  MagickRealType
4636    scale_factor;
4637
4638  RectangleInfo
4639    crop_info,
4640    highlight_info;
4641
4642  register Quantum
4643    *q;
4644
4645  unsigned int
4646    height,
4647    width;
4648
4649  size_t
4650    state;
4651
4652  XEvent
4653    event;
4654
4655  /*
4656    Map Command widget.
4657  */
4658  switch (mode)
4659  {
4660    case CopyMode:
4661    {
4662      (void) CloneString(&windows->command.name,"Copy");
4663      break;
4664    }
4665    case CropMode:
4666    {
4667      (void) CloneString(&windows->command.name,"Crop");
4668      break;
4669    }
4670    case CutMode:
4671    {
4672      (void) CloneString(&windows->command.name,"Cut");
4673      break;
4674    }
4675  }
4676  RectifyModeMenu[0]=windows->command.name;
4677  windows->command.data=0;
4678  (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4679  (void) XMapRaised(display,windows->command.id);
4680  XClientMessage(display,windows->image.id,windows->im_protocols,
4681    windows->im_update_widget,CurrentTime);
4682  /*
4683    Track pointer until button 1 is pressed.
4684  */
4685  XQueryPosition(display,windows->image.id,&x,&y);
4686  (void) XSelectInput(display,windows->image.id,
4687    windows->image.attributes.event_mask | PointerMotionMask);
4688  crop_info.x=(ssize_t) windows->image.x+x;
4689  crop_info.y=(ssize_t) windows->image.y+y;
4690  crop_info.width=0;
4691  crop_info.height=0;
4692  cursor=XCreateFontCursor(display,XC_fleur);
4693  state=DefaultState;
4694  do
4695  {
4696    if (windows->info.mapped != MagickFalse)
4697      {
4698        /*
4699          Display pointer position.
4700        */
4701        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4702          (long) crop_info.x,(long) crop_info.y);
4703        XInfoWidget(display,windows,text);
4704      }
4705    /*
4706      Wait for next event.
4707    */
4708    XScreenEvent(display,windows,&event,exception);
4709    if (event.xany.window == windows->command.id)
4710      {
4711        /*
4712          Select a command from the Command widget.
4713        */
4714        id=XCommandWidget(display,windows,CropModeMenu,&event);
4715        if (id < 0)
4716          continue;
4717        switch (CropCommands[id])
4718        {
4719          case CropHelpCommand:
4720          {
4721            switch (mode)
4722            {
4723              case CopyMode:
4724              {
4725                XTextViewWidget(display,resource_info,windows,MagickFalse,
4726                  "Help Viewer - Image Copy",ImageCopyHelp);
4727                break;
4728              }
4729              case CropMode:
4730              {
4731                XTextViewWidget(display,resource_info,windows,MagickFalse,
4732                  "Help Viewer - Image Crop",ImageCropHelp);
4733                break;
4734              }
4735              case CutMode:
4736              {
4737                XTextViewWidget(display,resource_info,windows,MagickFalse,
4738                  "Help Viewer - Image Cut",ImageCutHelp);
4739                break;
4740              }
4741            }
4742            break;
4743          }
4744          case CropDismissCommand:
4745          {
4746            /*
4747              Prematurely exit.
4748            */
4749            state|=EscapeState;
4750            state|=ExitState;
4751            break;
4752          }
4753          default:
4754            break;
4755        }
4756        continue;
4757      }
4758    switch (event.type)
4759    {
4760      case ButtonPress:
4761      {
4762        if (event.xbutton.button != Button1)
4763          break;
4764        if (event.xbutton.window != windows->image.id)
4765          break;
4766        /*
4767          Note first corner of cropping rectangle-- exit loop.
4768        */
4769        (void) XCheckDefineCursor(display,windows->image.id,cursor);
4770        crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4771        crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4772        state|=ExitState;
4773        break;
4774      }
4775      case ButtonRelease:
4776        break;
4777      case Expose:
4778        break;
4779      case KeyPress:
4780      {
4781        if (event.xkey.window != windows->image.id)
4782          break;
4783        /*
4784          Respond to a user key press.
4785        */
4786        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4787          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4788        switch ((int) key_symbol)
4789        {
4790          case XK_Escape:
4791          case XK_F20:
4792          {
4793            /*
4794              Prematurely exit.
4795            */
4796            state|=EscapeState;
4797            state|=ExitState;
4798            break;
4799          }
4800          case XK_F1:
4801          case XK_Help:
4802          {
4803            switch (mode)
4804            {
4805              case CopyMode:
4806              {
4807                XTextViewWidget(display,resource_info,windows,MagickFalse,
4808                  "Help Viewer - Image Copy",ImageCopyHelp);
4809                break;
4810              }
4811              case CropMode:
4812              {
4813                XTextViewWidget(display,resource_info,windows,MagickFalse,
4814                  "Help Viewer - Image Crop",ImageCropHelp);
4815                break;
4816              }
4817              case CutMode:
4818              {
4819                XTextViewWidget(display,resource_info,windows,MagickFalse,
4820                  "Help Viewer - Image Cut",ImageCutHelp);
4821                break;
4822              }
4823            }
4824            break;
4825          }
4826          default:
4827          {
4828            (void) XBell(display,0);
4829            break;
4830          }
4831        }
4832        break;
4833      }
4834      case MotionNotify:
4835      {
4836        if (event.xmotion.window != windows->image.id)
4837          break;
4838        /*
4839          Map and unmap Info widget as text cursor crosses its boundaries.
4840        */
4841        x=event.xmotion.x;
4842        y=event.xmotion.y;
4843        if (windows->info.mapped != MagickFalse)
4844          {
4845            if ((x < (int) (windows->info.x+windows->info.width)) &&
4846                (y < (int) (windows->info.y+windows->info.height)))
4847              (void) XWithdrawWindow(display,windows->info.id,
4848                windows->info.screen);
4849          }
4850        else
4851          if ((x > (int) (windows->info.x+windows->info.width)) ||
4852              (y > (int) (windows->info.y+windows->info.height)))
4853            (void) XMapWindow(display,windows->info.id);
4854        crop_info.x=(ssize_t) windows->image.x+x;
4855        crop_info.y=(ssize_t) windows->image.y+y;
4856        break;
4857      }
4858      default:
4859        break;
4860    }
4861  } while ((state & ExitState) == 0);
4862  (void) XSelectInput(display,windows->image.id,
4863    windows->image.attributes.event_mask);
4864  if ((state & EscapeState) != 0)
4865    {
4866      /*
4867        User want to exit without cropping.
4868      */
4869      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4870      (void) XFreeCursor(display,cursor);
4871      return(MagickTrue);
4872    }
4873  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4874  do
4875  {
4876    /*
4877      Size rectangle as pointer moves until the mouse button is released.
4878    */
4879    x=(int) crop_info.x;
4880    y=(int) crop_info.y;
4881    crop_info.width=0;
4882    crop_info.height=0;
4883    state=DefaultState;
4884    do
4885    {
4886      highlight_info=crop_info;
4887      highlight_info.x=crop_info.x-windows->image.x;
4888      highlight_info.y=crop_info.y-windows->image.y;
4889      if ((highlight_info.width > 3) && (highlight_info.height > 3))
4890        {
4891          /*
4892            Display info and draw cropping rectangle.
4893          */
4894          if (windows->info.mapped == MagickFalse)
4895            (void) XMapWindow(display,windows->info.id);
4896          (void) FormatLocaleString(text,MaxTextExtent,
4897            " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4898            crop_info.height,(double) crop_info.x,(double) crop_info.y);
4899          XInfoWidget(display,windows,text);
4900          XHighlightRectangle(display,windows->image.id,
4901            windows->image.highlight_context,&highlight_info);
4902        }
4903      else
4904        if (windows->info.mapped != MagickFalse)
4905          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4906      /*
4907        Wait for next event.
4908      */
4909      XScreenEvent(display,windows,&event,exception);
4910      if ((highlight_info.width > 3) && (highlight_info.height > 3))
4911        XHighlightRectangle(display,windows->image.id,
4912          windows->image.highlight_context,&highlight_info);
4913      switch (event.type)
4914      {
4915        case ButtonPress:
4916        {
4917          crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4918          crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4919          break;
4920        }
4921        case ButtonRelease:
4922        {
4923          /*
4924            User has committed to cropping rectangle.
4925          */
4926          crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4927          crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4928          XSetCursorState(display,windows,MagickFalse);
4929          state|=ExitState;
4930          windows->command.data=0;
4931          (void) XCommandWidget(display,windows,RectifyModeMenu,
4932            (XEvent *) NULL);
4933          break;
4934        }
4935        case Expose:
4936          break;
4937        case MotionNotify:
4938        {
4939          crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4940          crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4941        }
4942        default:
4943          break;
4944      }
4945      if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4946          ((state & ExitState) != 0))
4947        {
4948          /*
4949            Check boundary conditions.
4950          */
4951          if (crop_info.x < 0)
4952            crop_info.x=0;
4953          else
4954            if (crop_info.x > (ssize_t) windows->image.ximage->width)
4955              crop_info.x=(ssize_t) windows->image.ximage->width;
4956          if ((int) crop_info.x < x)
4957            crop_info.width=(unsigned int) (x-crop_info.x);
4958          else
4959            {
4960              crop_info.width=(unsigned int) (crop_info.x-x);
4961              crop_info.x=(ssize_t) x;
4962            }
4963          if (crop_info.y < 0)
4964            crop_info.y=0;
4965          else
4966            if (crop_info.y > (ssize_t) windows->image.ximage->height)
4967              crop_info.y=(ssize_t) windows->image.ximage->height;
4968          if ((int) crop_info.y < y)
4969            crop_info.height=(unsigned int) (y-crop_info.y);
4970          else
4971            {
4972              crop_info.height=(unsigned int) (crop_info.y-y);
4973              crop_info.y=(ssize_t) y;
4974            }
4975        }
4976    } while ((state & ExitState) == 0);
4977    /*
4978      Wait for user to grab a corner of the rectangle or press return.
4979    */
4980    state=DefaultState;
4981    (void) XMapWindow(display,windows->info.id);
4982    do
4983    {
4984      if (windows->info.mapped != MagickFalse)
4985        {
4986          /*
4987            Display pointer position.
4988          */
4989          (void) FormatLocaleString(text,MaxTextExtent,
4990            " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4991            crop_info.height,(double) crop_info.x,(double) crop_info.y);
4992          XInfoWidget(display,windows,text);
4993        }
4994      highlight_info=crop_info;
4995      highlight_info.x=crop_info.x-windows->image.x;
4996      highlight_info.y=crop_info.y-windows->image.y;
4997      if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4998        {
4999          state|=EscapeState;
5000          state|=ExitState;
5001          break;
5002        }
5003      XHighlightRectangle(display,windows->image.id,
5004        windows->image.highlight_context,&highlight_info);
5005      XScreenEvent(display,windows,&event,exception);
5006      if (event.xany.window == windows->command.id)
5007        {
5008          /*
5009            Select a command from the Command widget.
5010          */
5011          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5012          id=XCommandWidget(display,windows,RectifyModeMenu,&event);
5013          (void) XSetFunction(display,windows->image.highlight_context,
5014            GXinvert);
5015          XHighlightRectangle(display,windows->image.id,
5016            windows->image.highlight_context,&highlight_info);
5017          if (id >= 0)
5018            switch (RectifyCommands[id])
5019            {
5020              case RectifyCopyCommand:
5021              {
5022                state|=ExitState;
5023                break;
5024              }
5025              case RectifyHelpCommand:
5026              {
5027                (void) XSetFunction(display,windows->image.highlight_context,
5028                  GXcopy);
5029                switch (mode)
5030                {
5031                  case CopyMode:
5032                  {
5033                    XTextViewWidget(display,resource_info,windows,MagickFalse,
5034                      "Help Viewer - Image Copy",ImageCopyHelp);
5035                    break;
5036                  }
5037                  case CropMode:
5038                  {
5039                    XTextViewWidget(display,resource_info,windows,MagickFalse,
5040                      "Help Viewer - Image Crop",ImageCropHelp);
5041                    break;
5042                  }
5043                  case CutMode:
5044                  {
5045                    XTextViewWidget(display,resource_info,windows,MagickFalse,
5046                      "Help Viewer - Image Cut",ImageCutHelp);
5047                    break;
5048                  }
5049                }
5050                (void) XSetFunction(display,windows->image.highlight_context,
5051                  GXinvert);
5052                break;
5053              }
5054              case RectifyDismissCommand:
5055              {
5056                /*
5057                  Prematurely exit.
5058                */
5059                state|=EscapeState;
5060                state|=ExitState;
5061                break;
5062              }
5063              default:
5064                break;
5065            }
5066          continue;
5067        }
5068      XHighlightRectangle(display,windows->image.id,
5069        windows->image.highlight_context,&highlight_info);
5070      switch (event.type)
5071      {
5072        case ButtonPress:
5073        {
5074          if (event.xbutton.button != Button1)
5075            break;
5076          if (event.xbutton.window != windows->image.id)
5077            break;
5078          x=windows->image.x+event.xbutton.x;
5079          y=windows->image.y+event.xbutton.y;
5080          if ((x < (int) (crop_info.x+RoiDelta)) &&
5081              (x > (int) (crop_info.x-RoiDelta)) &&
5082              (y < (int) (crop_info.y+RoiDelta)) &&
5083              (y > (int) (crop_info.y-RoiDelta)))
5084            {
5085              crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5086              crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5087              state|=UpdateConfigurationState;
5088              break;
5089            }
5090          if ((x < (int) (crop_info.x+RoiDelta)) &&
5091              (x > (int) (crop_info.x-RoiDelta)) &&
5092              (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5093              (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5094            {
5095              crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5096              state|=UpdateConfigurationState;
5097              break;
5098            }
5099          if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5100              (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5101              (y < (int) (crop_info.y+RoiDelta)) &&
5102              (y > (int) (crop_info.y-RoiDelta)))
5103            {
5104              crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5105              state|=UpdateConfigurationState;
5106              break;
5107            }
5108          if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5109              (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5110              (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5111              (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5112            {
5113              state|=UpdateConfigurationState;
5114              break;
5115            }
5116        }
5117        case ButtonRelease:
5118        {
5119          if (event.xbutton.window == windows->pan.id)
5120            if ((highlight_info.x != crop_info.x-windows->image.x) ||
5121                (highlight_info.y != crop_info.y-windows->image.y))
5122              XHighlightRectangle(display,windows->image.id,
5123                windows->image.highlight_context,&highlight_info);
5124          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5125            event.xbutton.time);
5126          break;
5127        }
5128        case Expose:
5129        {
5130          if (event.xexpose.window == windows->image.id)
5131            if (event.xexpose.count == 0)
5132              {
5133                event.xexpose.x=(int) highlight_info.x;
5134                event.xexpose.y=(int) highlight_info.y;
5135                event.xexpose.width=(int) highlight_info.width;
5136                event.xexpose.height=(int) highlight_info.height;
5137                XRefreshWindow(display,&windows->image,&event);
5138              }
5139          if (event.xexpose.window == windows->info.id)
5140            if (event.xexpose.count == 0)
5141              XInfoWidget(display,windows,text);
5142          break;
5143        }
5144        case KeyPress:
5145        {
5146          if (event.xkey.window != windows->image.id)
5147            break;
5148          /*
5149            Respond to a user key press.
5150          */
5151          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5152            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5153          switch ((int) key_symbol)
5154          {
5155            case XK_Escape:
5156            case XK_F20:
5157              state|=EscapeState;
5158            case XK_Return:
5159            {
5160              state|=ExitState;
5161              break;
5162            }
5163            case XK_Home:
5164            case XK_KP_Home:
5165            {
5166              crop_info.x=(ssize_t) (windows->image.width/2L-
5167                crop_info.width/2L);
5168              crop_info.y=(ssize_t) (windows->image.height/2L-
5169                crop_info.height/2L);
5170              break;
5171            }
5172            case XK_Left:
5173            case XK_KP_Left:
5174            {
5175              crop_info.x--;
5176              break;
5177            }
5178            case XK_Up:
5179            case XK_KP_Up:
5180            case XK_Next:
5181            {
5182              crop_info.y--;
5183              break;
5184            }
5185            case XK_Right:
5186            case XK_KP_Right:
5187            {
5188              crop_info.x++;
5189              break;
5190            }
5191            case XK_Prior:
5192            case XK_Down:
5193            case XK_KP_Down:
5194            {
5195              crop_info.y++;
5196              break;
5197            }
5198            case XK_F1:
5199            case XK_Help:
5200            {
5201              (void) XSetFunction(display,windows->image.highlight_context,
5202                GXcopy);
5203              switch (mode)
5204              {
5205                case CopyMode:
5206                {
5207                  XTextViewWidget(display,resource_info,windows,MagickFalse,
5208                    "Help Viewer - Image Copy",ImageCopyHelp);
5209                  break;
5210                }
5211                case CropMode:
5212                {
5213                  XTextViewWidget(display,resource_info,windows,MagickFalse,
5214                    "Help Viewer - Image Cropg",ImageCropHelp);
5215                  break;
5216                }
5217                case CutMode:
5218                {
5219                  XTextViewWidget(display,resource_info,windows,MagickFalse,
5220                    "Help Viewer - Image Cutg",ImageCutHelp);
5221                  break;
5222                }
5223              }
5224              (void) XSetFunction(display,windows->image.highlight_context,
5225                GXinvert);
5226              break;
5227            }
5228            default:
5229            {
5230              (void) XBell(display,0);
5231              break;
5232            }
5233          }
5234          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5235            event.xkey.time);
5236          break;
5237        }
5238        case KeyRelease:
5239          break;
5240        case MotionNotify:
5241        {
5242          if (event.xmotion.window != windows->image.id)
5243            break;
5244          /*
5245            Map and unmap Info widget as text cursor crosses its boundaries.
5246          */
5247          x=event.xmotion.x;
5248          y=event.xmotion.y;
5249          if (windows->info.mapped != MagickFalse)
5250            {
5251              if ((x < (int) (windows->info.x+windows->info.width)) &&
5252                  (y < (int) (windows->info.y+windows->info.height)))
5253                (void) XWithdrawWindow(display,windows->info.id,
5254                  windows->info.screen);
5255            }
5256          else
5257            if ((x > (int) (windows->info.x+windows->info.width)) ||
5258                (y > (int) (windows->info.y+windows->info.height)))
5259              (void) XMapWindow(display,windows->info.id);
5260          crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5261          crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5262          break;
5263        }
5264        case SelectionRequest:
5265        {
5266          XSelectionEvent
5267            notify;
5268
5269          XSelectionRequestEvent
5270            *request;
5271
5272          /*
5273            Set primary selection.
5274          */
5275          (void) FormatLocaleString(text,MaxTextExtent,
5276            "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5277            crop_info.height,(double) crop_info.x,(double) crop_info.y);
5278          request=(&(event.xselectionrequest));
5279          (void) XChangeProperty(request->display,request->requestor,
5280            request->property,request->target,8,PropModeReplace,
5281            (unsigned char *) text,(int) strlen(text));
5282          notify.type=SelectionNotify;
5283          notify.display=request->display;
5284          notify.requestor=request->requestor;
5285          notify.selection=request->selection;
5286          notify.target=request->target;
5287          notify.time=request->time;
5288          if (request->property == None)
5289            notify.property=request->target;
5290          else
5291            notify.property=request->property;
5292          (void) XSendEvent(request->display,request->requestor,False,0,
5293            (XEvent *) &notify);
5294        }
5295        default:
5296          break;
5297      }
5298      if ((state & UpdateConfigurationState) != 0)
5299        {
5300          (void) XPutBackEvent(display,&event);
5301          (void) XCheckDefineCursor(display,windows->image.id,cursor);
5302          break;
5303        }
5304    } while ((state & ExitState) == 0);
5305  } while ((state & ExitState) == 0);
5306  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5307  XSetCursorState(display,windows,MagickFalse);
5308  if ((state & EscapeState) != 0)
5309    return(MagickTrue);
5310  if (mode == CropMode)
5311    if (((int) crop_info.width != windows->image.ximage->width) ||
5312        ((int) crop_info.height != windows->image.ximage->height))
5313      {
5314        /*
5315          Reconfigure Image window as defined by cropping rectangle.
5316        */
5317        XSetCropGeometry(display,windows,&crop_info,image);
5318        windows->image.window_changes.width=(int) crop_info.width;
5319        windows->image.window_changes.height=(int) crop_info.height;
5320        (void) XConfigureImage(display,resource_info,windows,image,exception);
5321        return(MagickTrue);
5322      }
5323  /*
5324    Copy image before applying image transforms.
5325  */
5326  XSetCursorState(display,windows,MagickTrue);
5327  XCheckRefreshWindows(display,windows);
5328  width=(unsigned int) image->columns;
5329  height=(unsigned int) image->rows;
5330  x=0;
5331  y=0;
5332  if (windows->image.crop_geometry != (char *) NULL)
5333    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5334  scale_factor=(MagickRealType) width/windows->image.ximage->width;
5335  crop_info.x+=x;
5336  crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5337  crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5338  scale_factor=(MagickRealType) height/windows->image.ximage->height;
5339  crop_info.y+=y;
5340  crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5341  crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5342  crop_image=CropImage(image,&crop_info,exception);
5343  XSetCursorState(display,windows,MagickFalse);
5344  if (crop_image == (Image *) NULL)
5345    return(MagickFalse);
5346  if (resource_info->copy_image != (Image *) NULL)
5347    resource_info->copy_image=DestroyImage(resource_info->copy_image);
5348  resource_info->copy_image=crop_image;
5349  if (mode == CopyMode)
5350    {
5351      (void) XConfigureImage(display,resource_info,windows,image,exception);
5352      return(MagickTrue);
5353    }
5354  /*
5355    Cut image.
5356  */
5357  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
5358    return(MagickFalse);
5359  image->matte=MagickTrue;
5360  image_view=AcquireCacheView(image);
5361  for (y=0; y < (int) crop_info.height; y++)
5362  {
5363    q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5364      crop_info.width,1,exception);
5365    if (q == (Quantum *) NULL)
5366      break;
5367    for (x=0; x < (int) crop_info.width; x++)
5368    {
5369      SetPixelAlpha(image,TransparentAlpha,q);
5370      q+=GetPixelChannels(image);
5371    }
5372    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5373      break;
5374  }
5375  image_view=DestroyCacheView(image_view);
5376  /*
5377    Update image configuration.
5378  */
5379  XConfigureImageColormap(display,resource_info,windows,image,exception);
5380  (void) XConfigureImage(display,resource_info,windows,image,exception);
5381  return(MagickTrue);
5382}
5383
5384/*
5385%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5386%                                                                             %
5387%                                                                             %
5388%                                                                             %
5389+   X D r a w I m a g e                                                       %
5390%                                                                             %
5391%                                                                             %
5392%                                                                             %
5393%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5394%
5395%  XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5396%  the image.
5397%
5398%  The format of the XDrawEditImage method is:
5399%
5400%      MagickBooleanType XDrawEditImage(Display *display,
5401%        XResourceInfo *resource_info,XWindows *windows,Image **image,
5402%        ExceptionInfo *exception)
5403%
5404%  A description of each parameter follows:
5405%
5406%    o display: Specifies a connection to an X server; returned from
5407%      XOpenDisplay.
5408%
5409%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5410%
5411%    o windows: Specifies a pointer to a XWindows structure.
5412%
5413%    o image: the image.
5414%
5415%    o exception: return any errors or warnings in this structure.
5416%
5417*/
5418static MagickBooleanType XDrawEditImage(Display *display,
5419  XResourceInfo *resource_info,XWindows *windows,Image **image,
5420  ExceptionInfo *exception)
5421{
5422  static const char
5423    *DrawMenu[] =
5424    {
5425      "Element",
5426      "Color",
5427      "Stipple",
5428      "Width",
5429      "Undo",
5430      "Help",
5431      "Dismiss",
5432      (char *) NULL
5433    };
5434
5435  static ElementType
5436    element = PointElement;
5437
5438  static const ModeType
5439    DrawCommands[] =
5440    {
5441      DrawElementCommand,
5442      DrawColorCommand,
5443      DrawStippleCommand,
5444      DrawWidthCommand,
5445      DrawUndoCommand,
5446      DrawHelpCommand,
5447      DrawDismissCommand
5448    };
5449
5450  static Pixmap
5451    stipple = (Pixmap) NULL;
5452
5453  static unsigned int
5454    pen_id = 0,
5455    line_width = 1;
5456
5457  char
5458    command[MaxTextExtent],
5459    text[MaxTextExtent];
5460
5461  Cursor
5462    cursor;
5463
5464  int
5465    entry,
5466    id,
5467    number_coordinates,
5468    x,
5469    y;
5470
5471  MagickRealType
5472    degrees;
5473
5474  MagickStatusType
5475    status;
5476
5477  RectangleInfo
5478    rectangle_info;
5479
5480  register int
5481    i;
5482
5483  unsigned int
5484    distance,
5485    height,
5486    max_coordinates,
5487    width;
5488
5489  size_t
5490    state;
5491
5492  Window
5493    root_window;
5494
5495  XDrawInfo
5496    draw_info;
5497
5498  XEvent
5499    event;
5500
5501  XPoint
5502    *coordinate_info;
5503
5504  XSegment
5505    line_info;
5506
5507  /*
5508    Allocate polygon info.
5509  */
5510  max_coordinates=2048;
5511  coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5512    sizeof(*coordinate_info));
5513  if (coordinate_info == (XPoint *) NULL)
5514    {
5515      (void) ThrowMagickException(exception,GetMagickModule(),
5516        ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5517      return(MagickFalse);
5518    }
5519  /*
5520    Map Command widget.
5521  */
5522  (void) CloneString(&windows->command.name,"Draw");
5523  windows->command.data=4;
5524  (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5525  (void) XMapRaised(display,windows->command.id);
5526  XClientMessage(display,windows->image.id,windows->im_protocols,
5527    windows->im_update_widget,CurrentTime);
5528  /*
5529    Wait for first button press.
5530  */
5531  root_window=XRootWindow(display,XDefaultScreen(display));
5532  draw_info.stencil=OpaqueStencil;
5533  status=MagickTrue;
5534  cursor=XCreateFontCursor(display,XC_tcross);
5535  for ( ; ; )
5536  {
5537    XQueryPosition(display,windows->image.id,&x,&y);
5538    (void) XSelectInput(display,windows->image.id,
5539      windows->image.attributes.event_mask | PointerMotionMask);
5540    (void) XCheckDefineCursor(display,windows->image.id,cursor);
5541    state=DefaultState;
5542    do
5543    {
5544      if (windows->info.mapped != MagickFalse)
5545        {
5546          /*
5547            Display pointer position.
5548          */
5549          (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
5550            x+windows->image.x,y+windows->image.y);
5551          XInfoWidget(display,windows,text);
5552        }
5553      /*
5554        Wait for next event.
5555      */
5556      XScreenEvent(display,windows,&event,exception);
5557      if (event.xany.window == windows->command.id)
5558        {
5559          /*
5560            Select a command from the Command widget.
5561          */
5562          id=XCommandWidget(display,windows,DrawMenu,&event);
5563          if (id < 0)
5564            continue;
5565          switch (DrawCommands[id])
5566          {
5567            case DrawElementCommand:
5568            {
5569              static const char
5570                *Elements[] =
5571                {
5572                  "point",
5573                  "line",
5574                  "rectangle",
5575                  "fill rectangle",
5576                  "circle",
5577                  "fill circle",
5578                  "ellipse",
5579                  "fill ellipse",
5580                  "polygon",
5581                  "fill polygon",
5582                  (char *) NULL,
5583                };
5584
5585              /*
5586                Select a command from the pop-up menu.
5587              */
5588              element=(ElementType) (XMenuWidget(display,windows,
5589                DrawMenu[id],Elements,command)+1);
5590              break;
5591            }
5592            case DrawColorCommand:
5593            {
5594              const char
5595                *ColorMenu[MaxNumberPens+1];
5596
5597              int
5598                pen_number;
5599
5600              MagickBooleanType
5601                transparent;
5602
5603              XColor
5604                color;
5605
5606              /*
5607                Initialize menu selections.
5608              */
5609              for (i=0; i < (int) (MaxNumberPens-2); i++)
5610                ColorMenu[i]=resource_info->pen_colors[i];
5611              ColorMenu[MaxNumberPens-2]="transparent";
5612              ColorMenu[MaxNumberPens-1]="Browser...";
5613              ColorMenu[MaxNumberPens]=(char *) NULL;
5614              /*
5615                Select a pen color from the pop-up menu.
5616              */
5617              pen_number=XMenuWidget(display,windows,DrawMenu[id],
5618                (const char **) ColorMenu,command);
5619              if (pen_number < 0)
5620                break;
5621              transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5622                MagickFalse;
5623              if (transparent != MagickFalse)
5624                {
5625                  draw_info.stencil=TransparentStencil;
5626                  break;
5627                }
5628              if (pen_number == (MaxNumberPens-1))
5629                {
5630                  static char
5631                    color_name[MaxTextExtent] = "gray";
5632
5633                  /*
5634                    Select a pen color from a dialog.
5635                  */
5636                  resource_info->pen_colors[pen_number]=color_name;
5637                  XColorBrowserWidget(display,windows,"Select",color_name);
5638                  if (*color_name == '\0')
5639                    break;
5640                }
5641              /*
5642                Set pen color.
5643              */
5644              (void) XParseColor(display,windows->map_info->colormap,
5645                resource_info->pen_colors[pen_number],&color);
5646              XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5647                (unsigned int) MaxColors,&color);
5648              windows->pixel_info->pen_colors[pen_number]=color;
5649              pen_id=(unsigned int) pen_number;
5650              draw_info.stencil=OpaqueStencil;
5651              break;
5652            }
5653            case DrawStippleCommand:
5654            {
5655              Image
5656                *stipple_image;
5657
5658              ImageInfo
5659                *image_info;
5660
5661              int
5662                status;
5663
5664              static char
5665                filename[MaxTextExtent] = "\0";
5666
5667              static const char
5668                *StipplesMenu[] =
5669                {
5670                  "Brick",
5671                  "Diagonal",
5672                  "Scales",
5673                  "Vertical",
5674                  "Wavy",
5675                  "Translucent",
5676                  "Opaque",
5677                  (char *) NULL,
5678                  (char *) NULL,
5679                };
5680
5681              /*
5682                Select a command from the pop-up menu.
5683              */
5684              StipplesMenu[7]="Open...";
5685              entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5686                command);
5687              if (entry < 0)
5688                break;
5689              if (stipple != (Pixmap) NULL)
5690                (void) XFreePixmap(display,stipple);
5691              stipple=(Pixmap) NULL;
5692              if (entry != 7)
5693                {
5694                  switch (entry)
5695                  {
5696                    case 0:
5697                    {
5698                      stipple=XCreateBitmapFromData(display,root_window,
5699                        (char *) BricksBitmap,BricksWidth,BricksHeight);
5700                      break;
5701                    }
5702                    case 1:
5703                    {
5704                      stipple=XCreateBitmapFromData(display,root_window,
5705                        (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5706                      break;
5707                    }
5708                    case 2:
5709                    {
5710                      stipple=XCreateBitmapFromData(display,root_window,
5711                        (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5712                      break;
5713                    }
5714                    case 3:
5715                    {
5716                      stipple=XCreateBitmapFromData(display,root_window,
5717                        (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5718                      break;
5719                    }
5720                    case 4:
5721                    {
5722                      stipple=XCreateBitmapFromData(display,root_window,
5723                        (char *) WavyBitmap,WavyWidth,WavyHeight);
5724                      break;
5725                    }
5726                    case 5:
5727                    {
5728                      stipple=XCreateBitmapFromData(display,root_window,
5729                        (char *) HighlightBitmap,HighlightWidth,
5730                        HighlightHeight);
5731                      break;
5732                    }
5733                    case 6:
5734                    default:
5735                    {
5736                      stipple=XCreateBitmapFromData(display,root_window,
5737                        (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5738                      break;
5739                    }
5740                  }
5741                  break;
5742                }
5743              XFileBrowserWidget(display,windows,"Stipple",filename);
5744              if (*filename == '\0')
5745                break;
5746              /*
5747                Read image.
5748              */
5749              XSetCursorState(display,windows,MagickTrue);
5750              XCheckRefreshWindows(display,windows);
5751              image_info=AcquireImageInfo();
5752              (void) CopyMagickString(image_info->filename,filename,
5753                MaxTextExtent);
5754              stipple_image=ReadImage(image_info,exception);
5755              CatchException(exception);
5756              XSetCursorState(display,windows,MagickFalse);
5757              if (stipple_image == (Image *) NULL)
5758                break;
5759              (void) AcquireUniqueFileResource(filename);
5760              (void) FormatLocaleString(stipple_image->filename,MaxTextExtent,
5761                "xbm:%s",filename);
5762              (void) WriteImage(image_info,stipple_image,exception);
5763              stipple_image=DestroyImage(stipple_image);
5764              image_info=DestroyImageInfo(image_info);
5765              status=XReadBitmapFile(display,root_window,filename,&width,
5766                &height,&stipple,&x,&y);
5767              (void) RelinquishUniqueFileResource(filename);
5768              if ((status != BitmapSuccess) != 0)
5769                XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5770                  filename);
5771              break;
5772            }
5773            case DrawWidthCommand:
5774            {
5775              static char
5776                width[MaxTextExtent] = "0";
5777
5778              static const char
5779                *WidthsMenu[] =
5780                {
5781                  "1",
5782                  "2",
5783                  "4",
5784                  "8",
5785                  "16",
5786                  "Dialog...",
5787                  (char *) NULL,
5788                };
5789
5790              /*
5791                Select a command from the pop-up menu.
5792              */
5793              entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5794                command);
5795              if (entry < 0)
5796                break;
5797              if (entry != 5)
5798                {
5799                  line_width=(unsigned int) StringToUnsignedLong(
5800                    WidthsMenu[entry]);
5801                  break;
5802                }
5803              (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5804                width);
5805              if (*width == '\0')
5806                break;
5807              line_width=(unsigned int) StringToUnsignedLong(width);
5808              break;
5809            }
5810            case DrawUndoCommand:
5811            {
5812              (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5813                image,exception);
5814              break;
5815            }
5816            case DrawHelpCommand:
5817            {
5818              XTextViewWidget(display,resource_info,windows,MagickFalse,
5819                "Help Viewer - Image Rotation",ImageDrawHelp);
5820              (void) XCheckDefineCursor(display,windows->image.id,cursor);
5821              break;
5822            }
5823            case DrawDismissCommand:
5824            {
5825              /*
5826                Prematurely exit.
5827              */
5828              state|=EscapeState;
5829              state|=ExitState;
5830              break;
5831            }
5832            default:
5833              break;
5834          }
5835          (void) XCheckDefineCursor(display,windows->image.id,cursor);
5836          continue;
5837        }
5838      switch (event.type)
5839      {
5840        case ButtonPress:
5841        {
5842          if (event.xbutton.button != Button1)
5843            break;
5844          if (event.xbutton.window != windows->image.id)
5845            break;
5846          /*
5847            exit loop.
5848          */
5849          x=event.xbutton.x;
5850          y=event.xbutton.y;
5851          state|=ExitState;
5852          break;
5853        }
5854        case ButtonRelease:
5855          break;
5856        case Expose:
5857          break;
5858        case KeyPress:
5859        {
5860          KeySym
5861            key_symbol;
5862
5863          if (event.xkey.window != windows->image.id)
5864            break;
5865          /*
5866            Respond to a user key press.
5867          */
5868          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5869            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5870          switch ((int) key_symbol)
5871          {
5872            case XK_Escape:
5873            case XK_F20:
5874            {
5875              /*
5876                Prematurely exit.
5877              */
5878              state|=EscapeState;
5879              state|=ExitState;
5880              break;
5881            }
5882            case XK_F1:
5883            case XK_Help:
5884            {
5885              XTextViewWidget(display,resource_info,windows,MagickFalse,
5886                "Help Viewer - Image Rotation",ImageDrawHelp);
5887              break;
5888            }
5889            default:
5890            {
5891              (void) XBell(display,0);
5892              break;
5893            }
5894          }
5895          break;
5896        }
5897        case MotionNotify:
5898        {
5899          /*
5900            Map and unmap Info widget as text cursor crosses its boundaries.
5901          */
5902          x=event.xmotion.x;
5903          y=event.xmotion.y;
5904          if (windows->info.mapped != MagickFalse)
5905            {
5906              if ((x < (int) (windows->info.x+windows->info.width)) &&
5907                  (y < (int) (windows->info.y+windows->info.height)))
5908                (void) XWithdrawWindow(display,windows->info.id,
5909                  windows->info.screen);
5910            }
5911          else
5912            if ((x > (int) (windows->info.x+windows->info.width)) ||
5913                (y > (int) (windows->info.y+windows->info.height)))
5914              (void) XMapWindow(display,windows->info.id);
5915          break;
5916        }
5917      }
5918    } while ((state & ExitState) == 0);
5919    (void) XSelectInput(display,windows->image.id,
5920      windows->image.attributes.event_mask);
5921    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5922    if ((state & EscapeState) != 0)
5923      break;
5924    /*
5925      Draw element as pointer moves until the button is released.
5926    */
5927    distance=0;
5928    degrees=0.0;
5929    line_info.x1=x;
5930    line_info.y1=y;
5931    line_info.x2=x;
5932    line_info.y2=y;
5933    rectangle_info.x=(ssize_t) x;
5934    rectangle_info.y=(ssize_t) y;
5935    rectangle_info.width=0;
5936    rectangle_info.height=0;
5937    number_coordinates=1;
5938    coordinate_info->x=x;
5939    coordinate_info->y=y;
5940    (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5941    state=DefaultState;
5942    do
5943    {
5944      switch (element)
5945      {
5946        case PointElement:
5947        default:
5948        {
5949          if (number_coordinates > 1)
5950            {
5951              (void) XDrawLines(display,windows->image.id,
5952                windows->image.highlight_context,coordinate_info,
5953                number_coordinates,CoordModeOrigin);
5954              (void) FormatLocaleString(text,MaxTextExtent," %+d%+d",
5955                coordinate_info[number_coordinates-1].x,
5956                coordinate_info[number_coordinates-1].y);
5957              XInfoWidget(display,windows,text);
5958            }
5959          break;
5960        }
5961        case LineElement:
5962        {
5963          if (distance > 9)
5964            {
5965              /*
5966                Display angle of the line.
5967              */
5968              degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5969                line_info.y1),(double) (line_info.x2-line_info.x1)));
5970              (void) FormatLocaleString(text,MaxTextExtent," %g",
5971                (double) degrees);
5972              XInfoWidget(display,windows,text);
5973              XHighlightLine(display,windows->image.id,
5974                windows->image.highlight_context,&line_info);
5975            }
5976          else
5977            if (windows->info.mapped != MagickFalse)
5978              (void) XWithdrawWindow(display,windows->info.id,
5979                windows->info.screen);
5980          break;
5981        }
5982        case RectangleElement:
5983        case FillRectangleElement:
5984        {
5985          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5986            {
5987              /*
5988                Display info and draw drawing rectangle.
5989              */
5990              (void) FormatLocaleString(text,MaxTextExtent,
5991                " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5992                (double) rectangle_info.height,(double) rectangle_info.x,
5993                (double) rectangle_info.y);
5994              XInfoWidget(display,windows,text);
5995              XHighlightRectangle(display,windows->image.id,
5996                windows->image.highlight_context,&rectangle_info);
5997            }
5998          else
5999            if (windows->info.mapped != MagickFalse)
6000              (void) XWithdrawWindow(display,windows->info.id,
6001                windows->info.screen);
6002          break;
6003        }
6004        case CircleElement:
6005        case FillCircleElement:
6006        case EllipseElement:
6007        case FillEllipseElement:
6008        {
6009          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6010            {
6011              /*
6012                Display info and draw drawing rectangle.
6013              */
6014              (void) FormatLocaleString(text,MaxTextExtent,
6015                " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
6016                (double) rectangle_info.height,(double) rectangle_info.x,
6017                (double) rectangle_info.y);
6018              XInfoWidget(display,windows,text);
6019              XHighlightEllipse(display,windows->image.id,
6020                windows->image.highlight_context,&rectangle_info);
6021            }
6022          else
6023            if (windows->info.mapped != MagickFalse)
6024              (void) XWithdrawWindow(display,windows->info.id,
6025                windows->info.screen);
6026          break;
6027        }
6028        case PolygonElement:
6029        case FillPolygonElement:
6030        {
6031          if (number_coordinates > 1)
6032            (void) XDrawLines(display,windows->image.id,
6033              windows->image.highlight_context,coordinate_info,
6034              number_coordinates,CoordModeOrigin);
6035          if (distance > 9)
6036            {
6037              /*
6038                Display angle of the line.
6039              */
6040              degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
6041                line_info.y1),(double) (line_info.x2-line_info.x1)));
6042              (void) FormatLocaleString(text,MaxTextExtent," %g",
6043                (double) degrees);
6044              XInfoWidget(display,windows,text);
6045              XHighlightLine(display,windows->image.id,
6046                windows->image.highlight_context,&line_info);
6047            }
6048          else
6049            if (windows->info.mapped != MagickFalse)
6050              (void) XWithdrawWindow(display,windows->info.id,
6051                windows->info.screen);
6052          break;
6053        }
6054      }
6055      /*
6056        Wait for next event.
6057      */
6058      XScreenEvent(display,windows,&event,exception);
6059      switch (element)
6060      {
6061        case PointElement:
6062        default:
6063        {
6064          if (number_coordinates > 1)
6065            (void) XDrawLines(display,windows->image.id,
6066              windows->image.highlight_context,coordinate_info,
6067              number_coordinates,CoordModeOrigin);
6068          break;
6069        }
6070        case LineElement:
6071        {
6072          if (distance > 9)
6073            XHighlightLine(display,windows->image.id,
6074              windows->image.highlight_context,&line_info);
6075          break;
6076        }
6077        case RectangleElement:
6078        case FillRectangleElement:
6079        {
6080          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6081            XHighlightRectangle(display,windows->image.id,
6082              windows->image.highlight_context,&rectangle_info);
6083          break;
6084        }
6085        case CircleElement:
6086        case FillCircleElement:
6087        case EllipseElement:
6088        case FillEllipseElement:
6089        {
6090          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6091            XHighlightEllipse(display,windows->image.id,
6092              windows->image.highlight_context,&rectangle_info);
6093          break;
6094        }
6095        case PolygonElement:
6096        case FillPolygonElement:
6097        {
6098          if (number_coordinates > 1)
6099            (void) XDrawLines(display,windows->image.id,
6100              windows->image.highlight_context,coordinate_info,
6101              number_coordinates,CoordModeOrigin);
6102          if (distance > 9)
6103            XHighlightLine(display,windows->image.id,
6104              windows->image.highlight_context,&line_info);
6105          break;
6106        }
6107      }
6108      switch (event.type)
6109      {
6110        case ButtonPress:
6111          break;
6112        case ButtonRelease:
6113        {
6114          /*
6115            User has committed to element.
6116          */
6117          line_info.x2=event.xbutton.x;
6118          line_info.y2=event.xbutton.y;
6119          rectangle_info.x=(ssize_t) event.xbutton.x;
6120          rectangle_info.y=(ssize_t) event.xbutton.y;
6121          coordinate_info[number_coordinates].x=event.xbutton.x;
6122          coordinate_info[number_coordinates].y=event.xbutton.y;
6123          if (((element != PolygonElement) &&
6124               (element != FillPolygonElement)) || (distance <= 9))
6125            {
6126              state|=ExitState;
6127              break;
6128            }
6129          number_coordinates++;
6130          if (number_coordinates < (int) max_coordinates)
6131            {
6132              line_info.x1=event.xbutton.x;
6133              line_info.y1=event.xbutton.y;
6134              break;
6135            }
6136          max_coordinates<<=1;
6137          coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6138            max_coordinates,sizeof(*coordinate_info));
6139          if (coordinate_info == (XPoint *) NULL)
6140            (void) ThrowMagickException(exception,GetMagickModule(),
6141              ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6142          break;
6143        }
6144        case Expose:
6145          break;
6146        case MotionNotify:
6147        {
6148          if (event.xmotion.window != windows->image.id)
6149            break;
6150          if (element != PointElement)
6151            {
6152              line_info.x2=event.xmotion.x;
6153              line_info.y2=event.xmotion.y;
6154              rectangle_info.x=(ssize_t) event.xmotion.x;
6155              rectangle_info.y=(ssize_t) event.xmotion.y;
6156              break;
6157            }
6158          coordinate_info[number_coordinates].x=event.xbutton.x;
6159          coordinate_info[number_coordinates].y=event.xbutton.y;
6160          number_coordinates++;
6161          if (number_coordinates < (int) max_coordinates)
6162            break;
6163          max_coordinates<<=1;
6164          coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6165            max_coordinates,sizeof(*coordinate_info));
6166          if (coordinate_info == (XPoint *) NULL)
6167            (void) ThrowMagickException(exception,GetMagickModule(),
6168              ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6169          break;
6170        }
6171        default:
6172          break;
6173      }
6174      /*
6175        Check boundary conditions.
6176      */
6177      if (line_info.x2 < 0)
6178        line_info.x2=0;
6179      else
6180        if (line_info.x2 > (int) windows->image.width)
6181          line_info.x2=(short) windows->image.width;
6182      if (line_info.y2 < 0)
6183        line_info.y2=0;
6184      else
6185        if (line_info.y2 > (int) windows->image.height)
6186          line_info.y2=(short) windows->image.height;
6187      distance=(unsigned int)
6188        (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6189         ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6190      if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6191          ((state & ExitState) != 0))
6192        {
6193          if (rectangle_info.x < 0)
6194            rectangle_info.x=0;
6195          else
6196            if (rectangle_info.x > (ssize_t) windows->image.width)
6197              rectangle_info.x=(ssize_t) windows->image.width;
6198          if ((int) rectangle_info.x < x)
6199            rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6200          else
6201            {
6202              rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6203              rectangle_info.x=(ssize_t) x;
6204            }
6205          if (rectangle_info.y < 0)
6206            rectangle_info.y=0;
6207          else
6208            if (rectangle_info.y > (ssize_t) windows->image.height)
6209              rectangle_info.y=(ssize_t) windows->image.height;
6210          if ((int) rectangle_info.y < y)
6211            rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6212          else
6213            {
6214              rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6215              rectangle_info.y=(ssize_t) y;
6216            }
6217        }
6218    } while ((state & ExitState) == 0);
6219    (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6220    if ((element == PointElement) || (element == PolygonElement) ||
6221        (element == FillPolygonElement))
6222      {
6223        /*
6224          Determine polygon bounding box.
6225        */
6226        rectangle_info.x=(ssize_t) coordinate_info->x;
6227        rectangle_info.y=(ssize_t) coordinate_info->y;
6228        x=coordinate_info->x;
6229        y=coordinate_info->y;
6230        for (i=1; i < number_coordinates; i++)
6231        {
6232          if (coordinate_info[i].x > x)
6233            x=coordinate_info[i].x;
6234          if (coordinate_info[i].y > y)
6235            y=coordinate_info[i].y;
6236          if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6237            rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6238          if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6239            rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6240        }
6241        rectangle_info.width=(size_t) (x-rectangle_info.x);
6242        rectangle_info.height=(size_t) (y-rectangle_info.y);
6243        for (i=0; i < number_coordinates; i++)
6244        {
6245          coordinate_info[i].x-=rectangle_info.x;
6246          coordinate_info[i].y-=rectangle_info.y;
6247        }
6248      }
6249    else
6250      if (distance <= 9)
6251        continue;
6252      else
6253        if ((element == RectangleElement) ||
6254            (element == CircleElement) || (element == EllipseElement))
6255          {
6256            rectangle_info.width--;
6257            rectangle_info.height--;
6258          }
6259    /*
6260      Drawing is relative to image configuration.
6261    */
6262    draw_info.x=(int) rectangle_info.x;
6263    draw_info.y=(int) rectangle_info.y;
6264    (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6265      image,exception);
6266    width=(unsigned int) (*image)->columns;
6267    height=(unsigned int) (*image)->rows;
6268    x=0;
6269    y=0;
6270    if (windows->image.crop_geometry != (char *) NULL)
6271      (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6272    draw_info.x+=windows->image.x-(line_width/2);
6273    if (draw_info.x < 0)
6274      draw_info.x=0;
6275    draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6276    draw_info.y+=windows->image.y-(line_width/2);
6277    if (draw_info.y < 0)
6278      draw_info.y=0;
6279    draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6280    draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6281    if (draw_info.width > (unsigned int) (*image)->columns)
6282      draw_info.width=(unsigned int) (*image)->columns;
6283    draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6284    if (draw_info.height > (unsigned int) (*image)->rows)
6285      draw_info.height=(unsigned int) (*image)->rows;
6286    (void) FormatLocaleString(draw_info.geometry,MaxTextExtent,"%ux%u%+d%+d",
6287      width*draw_info.width/windows->image.ximage->width,
6288      height*draw_info.height/windows->image.ximage->height,
6289      draw_info.x+x,draw_info.y+y);
6290    /*
6291      Initialize drawing attributes.
6292    */
6293    draw_info.degrees=0.0;
6294    draw_info.element=element;
6295    draw_info.stipple=stipple;
6296    draw_info.line_width=line_width;
6297    draw_info.line_info=line_info;
6298    if (line_info.x1 > (int) (line_width/2))
6299      draw_info.line_info.x1=(short) line_width/2;
6300    if (line_info.y1 > (int) (line_width/2))
6301      draw_info.line_info.y1=(short) line_width/2;
6302    draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6303    draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6304    if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6305      {
6306        draw_info.line_info.x2=(-draw_info.line_info.x2);
6307        draw_info.line_info.y2=(-draw_info.line_info.y2);
6308      }
6309    if (draw_info.line_info.x2 < 0)
6310      {
6311        draw_info.line_info.x2=(-draw_info.line_info.x2);
6312        Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6313      }
6314    if (draw_info.line_info.y2 < 0)
6315      {
6316        draw_info.line_info.y2=(-draw_info.line_info.y2);
6317        Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6318      }
6319    draw_info.rectangle_info=rectangle_info;
6320    if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6321      draw_info.rectangle_info.x=(ssize_t) line_width/2;
6322    if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6323      draw_info.rectangle_info.y=(ssize_t) line_width/2;
6324    draw_info.number_coordinates=(unsigned int) number_coordinates;
6325    draw_info.coordinate_info=coordinate_info;
6326    windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6327    /*
6328      Draw element on image.
6329    */
6330    XSetCursorState(display,windows,MagickTrue);
6331    XCheckRefreshWindows(display,windows);
6332    status=XDrawImage(display,windows->pixel_info,&draw_info,*image,exception);
6333    XSetCursorState(display,windows,MagickFalse);
6334    /*
6335      Update image colormap and return to image drawing.
6336    */
6337    XConfigureImageColormap(display,resource_info,windows,*image,exception);
6338    (void) XConfigureImage(display,resource_info,windows,*image,exception);
6339  }
6340  XSetCursorState(display,windows,MagickFalse);
6341  coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6342  return(status != 0 ? MagickTrue : MagickFalse);
6343}
6344
6345/*
6346%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6347%                                                                             %
6348%                                                                             %
6349%                                                                             %
6350+   X D r a w P a n R e c t a n g l e                                         %
6351%                                                                             %
6352%                                                                             %
6353%                                                                             %
6354%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6355%
6356%  XDrawPanRectangle() draws a rectangle in the pan window.  The pan window
6357%  displays a zoom image and the rectangle shows which portion of the image is
6358%  displayed in the Image window.
6359%
6360%  The format of the XDrawPanRectangle method is:
6361%
6362%      XDrawPanRectangle(Display *display,XWindows *windows)
6363%
6364%  A description of each parameter follows:
6365%
6366%    o display: Specifies a connection to an X server;  returned from
6367%      XOpenDisplay.
6368%
6369%    o windows: Specifies a pointer to a XWindows structure.
6370%
6371*/
6372static void XDrawPanRectangle(Display *display,XWindows *windows)
6373{
6374  MagickRealType
6375    scale_factor;
6376
6377  RectangleInfo
6378    highlight_info;
6379
6380  /*
6381    Determine dimensions of the panning rectangle.
6382  */
6383  scale_factor=(MagickRealType) windows->pan.width/windows->image.ximage->width;
6384  highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6385  highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6386  scale_factor=(MagickRealType)
6387    windows->pan.height/windows->image.ximage->height;
6388  highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6389  highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6390  /*
6391    Display the panning rectangle.
6392  */
6393  (void) XClearWindow(display,windows->pan.id);
6394  XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6395    &highlight_info);
6396}
6397
6398/*
6399%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6400%                                                                             %
6401%                                                                             %
6402%                                                                             %
6403+   X I m a g e C a c h e                                                     %
6404%                                                                             %
6405%                                                                             %
6406%                                                                             %
6407%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6408%
6409%  XImageCache() handles the creation, manipulation, and destruction of the
6410%  image cache (undo and redo buffers).
6411%
6412%  The format of the XImageCache method is:
6413%
6414%      void XImageCache(Display *display,XResourceInfo *resource_info,
6415%        XWindows *windows,const CommandType command,Image **image,
6416%        ExceptionInfo *exception)
6417%
6418%  A description of each parameter follows:
6419%
6420%    o display: Specifies a connection to an X server; returned from
6421%      XOpenDisplay.
6422%
6423%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6424%
6425%    o windows: Specifies a pointer to a XWindows structure.
6426%
6427%    o command: Specifies a command to perform.
6428%
6429%    o image: the image;  XImageCache may transform the image and return a new
6430%      image pointer.
6431%
6432%    o exception: return any errors or warnings in this structure.
6433%
6434*/
6435static void XImageCache(Display *display,XResourceInfo *resource_info,
6436  XWindows *windows,const CommandType command,Image **image,
6437  ExceptionInfo *exception)
6438{
6439  Image
6440    *cache_image;
6441
6442  static Image
6443    *redo_image = (Image *) NULL,
6444    *undo_image = (Image *) NULL;
6445
6446  switch (command)
6447  {
6448    case FreeBuffersCommand:
6449    {
6450      /*
6451        Free memory from the undo and redo cache.
6452      */
6453      while (undo_image != (Image *) NULL)
6454      {
6455        cache_image=undo_image;
6456        undo_image=GetPreviousImageInList(undo_image);
6457        cache_image->list=DestroyImage(cache_image->list);
6458        cache_image=DestroyImage(cache_image);
6459      }
6460      undo_image=NewImageList();
6461      if (redo_image != (Image *) NULL)
6462        redo_image=DestroyImage(redo_image);
6463      redo_image=NewImageList();
6464      return;
6465    }
6466    case UndoCommand:
6467    {
6468      char
6469        image_geometry[MaxTextExtent];
6470
6471      /*
6472        Undo the last image transformation.
6473      */
6474      if (undo_image == (Image *) NULL)
6475        {
6476          (void) XBell(display,0);
6477          return;
6478        }
6479      cache_image=undo_image;
6480      undo_image=GetPreviousImageInList(undo_image);
6481      windows->image.window_changes.width=(int) cache_image->columns;
6482      windows->image.window_changes.height=(int) cache_image->rows;
6483      (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
6484        windows->image.ximage->width,windows->image.ximage->height);
6485      (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
6486        exception);
6487      if (windows->image.crop_geometry != (char *) NULL)
6488        windows->image.crop_geometry=(char *) RelinquishMagickMemory(
6489          windows->image.crop_geometry);
6490      windows->image.crop_geometry=cache_image->geometry;
6491      if (redo_image != (Image *) NULL)
6492        redo_image=DestroyImage(redo_image);
6493      redo_image=(*image);
6494      *image=cache_image->list;
6495      cache_image=DestroyImage(cache_image);
6496      if (windows->image.orphan != MagickFalse)
6497        return;
6498      XConfigureImageColormap(display,resource_info,windows,*image,exception);
6499      (void) XConfigureImage(display,resource_info,windows,*image,exception);
6500      return;
6501    }
6502    case CutCommand:
6503    case PasteCommand:
6504    case ApplyCommand:
6505    case HalfSizeCommand:
6506    case OriginalSizeCommand:
6507    case DoubleSizeCommand:
6508    case ResizeCommand:
6509    case TrimCommand:
6510    case CropCommand:
6511    case ChopCommand:
6512    case FlipCommand:
6513    case FlopCommand:
6514    case RotateRightCommand:
6515    case RotateLeftCommand:
6516    case RotateCommand:
6517    case ShearCommand:
6518    case RollCommand:
6519    case NegateCommand:
6520    case ContrastStretchCommand:
6521    case SigmoidalContrastCommand:
6522    case NormalizeCommand:
6523    case EqualizeCommand:
6524    case HueCommand:
6525    case SaturationCommand:
6526    case BrightnessCommand:
6527    case GammaCommand:
6528    case SpiffCommand:
6529    case DullCommand:
6530    case GrayscaleCommand:
6531    case MapCommand:
6532    case QuantizeCommand:
6533    case DespeckleCommand:
6534    case EmbossCommand:
6535    case ReduceNoiseCommand:
6536    case AddNoiseCommand:
6537    case SharpenCommand:
6538    case BlurCommand:
6539    case ThresholdCommand:
6540    case EdgeDetectCommand:
6541    case SpreadCommand:
6542    case ShadeCommand:
6543    case RaiseCommand:
6544    case SegmentCommand:
6545    case SolarizeCommand:
6546    case SepiaToneCommand:
6547    case SwirlCommand:
6548    case ImplodeCommand:
6549    case VignetteCommand:
6550    case WaveCommand:
6551    case OilPaintCommand:
6552    case CharcoalDrawCommand:
6553    case AnnotateCommand:
6554    case AddBorderCommand:
6555    case AddFrameCommand:
6556    case CompositeCommand:
6557    case CommentCommand:
6558    case LaunchCommand:
6559    case RegionofInterestCommand:
6560    case SaveToUndoBufferCommand:
6561    case RedoCommand:
6562    {
6563      Image
6564        *previous_image;
6565
6566      ssize_t
6567        bytes;
6568
6569      bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelInfo));
6570      if (undo_image != (Image *) NULL)
6571        {
6572          /*
6573            Ensure the undo cache has enough memory available.
6574          */
6575          previous_image=undo_image;
6576          while (previous_image != (Image *) NULL)
6577          {
6578            bytes+=previous_image->list->columns*previous_image->list->rows*
6579              sizeof(PixelInfo);
6580            if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6581              {
6582                previous_image=GetPreviousImageInList(previous_image);
6583                continue;
6584              }
6585            bytes-=previous_image->list->columns*previous_image->list->rows*
6586              sizeof(PixelInfo);
6587            if (previous_image == undo_image)
6588              undo_image=NewImageList();
6589            else
6590              previous_image->next->previous=NewImageList();
6591            break;
6592          }
6593          while (previous_image != (Image *) NULL)
6594          {
6595            /*
6596              Delete any excess memory from undo cache.
6597            */
6598            cache_image=previous_image;
6599            previous_image=GetPreviousImageInList(previous_image);
6600            cache_image->list=DestroyImage(cache_image->list);
6601            cache_image=DestroyImage(cache_image);
6602          }
6603        }
6604      if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6605        break;
6606      /*
6607        Save image before transformations are applied.
6608      */
6609      cache_image=AcquireImage((ImageInfo *) NULL,exception);
6610      if (cache_image == (Image *) NULL)
6611        break;
6612      XSetCursorState(display,windows,MagickTrue);
6613      XCheckRefreshWindows(display,windows);
6614      cache_image->list=CloneImage(*image,0,0,MagickTrue,exception);
6615      XSetCursorState(display,windows,MagickFalse);
6616      if (cache_image->list == (Image *) NULL)
6617        {
6618          cache_image=DestroyImage(cache_image);
6619          break;
6620        }
6621      cache_image->columns=(size_t) windows->image.ximage->width;
6622      cache_image->rows=(size_t) windows->image.ximage->height;
6623      cache_image->geometry=windows->image.crop_geometry;
6624      if (windows->image.crop_geometry != (char *) NULL)
6625        {
6626          cache_image->geometry=AcquireString((char *) NULL);
6627          (void) CopyMagickString(cache_image->geometry,
6628            windows->image.crop_geometry,MaxTextExtent);
6629        }
6630      if (undo_image == (Image *) NULL)
6631        {
6632          undo_image=cache_image;
6633          break;
6634        }
6635      undo_image->next=cache_image;
6636      undo_image->next->previous=undo_image;
6637      undo_image=undo_image->next;
6638      break;
6639    }
6640    default:
6641      break;
6642  }
6643  if (command == RedoCommand)
6644    {
6645      /*
6646        Redo the last image transformation.
6647      */
6648      if (redo_image == (Image *) NULL)
6649        {
6650          (void) XBell(display,0);
6651          return;
6652        }
6653      windows->image.window_changes.width=(int) redo_image->columns;
6654      windows->image.window_changes.height=(int) redo_image->rows;
6655      if (windows->image.crop_geometry != (char *) NULL)
6656        windows->image.crop_geometry=(char *)
6657          RelinquishMagickMemory(windows->image.crop_geometry);
6658      windows->image.crop_geometry=redo_image->geometry;
6659      *image=DestroyImage(*image);
6660      *image=redo_image;
6661      redo_image=NewImageList();
6662      if (windows->image.orphan != MagickFalse)
6663        return;
6664      XConfigureImageColormap(display,resource_info,windows,*image,exception);
6665      (void) XConfigureImage(display,resource_info,windows,*image,exception);
6666      return;
6667    }
6668  if (command != InfoCommand)
6669    return;
6670  /*
6671    Display image info.
6672  */
6673  XSetCursorState(display,windows,MagickTrue);
6674  XCheckRefreshWindows(display,windows);
6675  XDisplayImageInfo(display,resource_info,windows,undo_image,*image,exception);
6676  XSetCursorState(display,windows,MagickFalse);
6677}
6678
6679/*
6680%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6681%                                                                             %
6682%                                                                             %
6683%                                                                             %
6684+   X I m a g e W i n d o w C o m m a n d                                     %
6685%                                                                             %
6686%                                                                             %
6687%                                                                             %
6688%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6689%
6690%  XImageWindowCommand() makes a transform to the image or Image window as
6691%  specified by a user menu button or keyboard command.
6692%
6693%  The format of the XImageWindowCommand method is:
6694%
6695%      CommandType XImageWindowCommand(Display *display,
6696%        XResourceInfo *resource_info,XWindows *windows,
6697%        const MagickStatusType state,KeySym key_symbol,Image **image,
6698%        ExceptionInfo *exception)
6699%
6700%  A description of each parameter follows:
6701%
6702%    o nexus:  Method XImageWindowCommand returns an image when the
6703%      user chooses 'Open Image' from the command menu.  Otherwise a null
6704%      image is returned.
6705%
6706%    o display: Specifies a connection to an X server; returned from
6707%      XOpenDisplay.
6708%
6709%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6710%
6711%    o windows: Specifies a pointer to a XWindows structure.
6712%
6713%    o state: key mask.
6714%
6715%    o key_symbol: Specifies a command to perform.
6716%
6717%    o image: the image;  XImageWIndowCommand may transform the image and
6718%      return a new image pointer.
6719%
6720%    o exception: return any errors or warnings in this structure.
6721%
6722*/
6723static CommandType XImageWindowCommand(Display *display,
6724  XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6725  KeySym key_symbol,Image **image,ExceptionInfo *exception)
6726{
6727  static char
6728    delta[MaxTextExtent] = "";
6729
6730  static const char
6731    Digits[] = "01234567890";
6732
6733  static KeySym
6734    last_symbol = XK_0;
6735
6736  if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6737    {
6738      if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6739        {
6740          *delta='\0';
6741          resource_info->quantum=1;
6742        }
6743      last_symbol=key_symbol;
6744      delta[strlen(delta)+1]='\0';
6745      delta[strlen(delta)]=Digits[key_symbol-XK_0];
6746      resource_info->quantum=StringToLong(delta);
6747      return(NullCommand);
6748    }
6749  last_symbol=key_symbol;
6750  if (resource_info->immutable)
6751    {
6752      /*
6753        Virtual image window has a restricted command set.
6754      */
6755      switch (key_symbol)
6756      {
6757        case XK_question:
6758          return(InfoCommand);
6759        case XK_p:
6760        case XK_Print:
6761          return(PrintCommand);
6762        case XK_space:
6763          return(NextCommand);
6764        case XK_q:
6765        case XK_Escape:
6766          return(QuitCommand);
6767        default:
6768          break;
6769      }
6770      return(NullCommand);
6771    }
6772  switch ((int) key_symbol)
6773  {
6774    case XK_o:
6775    {
6776      if ((state & ControlMask) == 0)
6777        break;
6778      return(OpenCommand);
6779    }
6780    case XK_space:
6781      return(NextCommand);
6782    case XK_BackSpace:
6783      return(FormerCommand);
6784    case XK_s:
6785    {
6786      if ((state & Mod1Mask) != 0)
6787        return(SwirlCommand);
6788      if ((state & ControlMask) == 0)
6789        return(ShearCommand);
6790      return(SaveCommand);
6791    }
6792    case XK_p:
6793    case XK_Print:
6794    {
6795      if ((state & Mod1Mask) != 0)
6796        return(OilPaintCommand);
6797      if ((state & Mod4Mask) != 0)
6798        return(ColorCommand);
6799      if ((state & ControlMask) == 0)
6800        return(NullCommand);
6801      return(PrintCommand);
6802    }
6803    case XK_d:
6804    {
6805      if ((state & Mod4Mask) != 0)
6806        return(DrawCommand);
6807      if ((state & ControlMask) == 0)
6808        return(NullCommand);
6809      return(DeleteCommand);
6810    }
6811    case XK_Select:
6812    {
6813      if ((state & ControlMask) == 0)
6814        return(NullCommand);
6815      return(SelectCommand);
6816    }
6817    case XK_n:
6818    {
6819      if ((state & ControlMask) == 0)
6820        return(NullCommand);
6821      return(NewCommand);
6822    }
6823    case XK_q:
6824    case XK_Escape:
6825      return(QuitCommand);
6826    case XK_z:
6827    case XK_Undo:
6828    {
6829      if ((state & ControlMask) == 0)
6830        return(NullCommand);
6831      return(UndoCommand);
6832    }
6833    case XK_r:
6834    case XK_Redo:
6835    {
6836      if ((state & ControlMask) == 0)
6837        return(RollCommand);
6838      return(RedoCommand);
6839    }
6840    case XK_x:
6841    {
6842      if ((state & ControlMask) == 0)
6843        return(NullCommand);
6844      return(CutCommand);
6845    }
6846    case XK_c:
6847    {
6848      if ((state & Mod1Mask) != 0)
6849        return(CharcoalDrawCommand);
6850      if ((state & ControlMask) == 0)
6851        return(CropCommand);
6852      return(CopyCommand);
6853    }
6854    case XK_v:
6855    case XK_Insert:
6856    {
6857      if ((state & Mod4Mask) != 0)
6858        return(CompositeCommand);
6859      if ((state & ControlMask) == 0)
6860        return(FlipCommand);
6861      return(PasteCommand);
6862    }
6863    case XK_less:
6864      return(HalfSizeCommand);
6865    case XK_minus:
6866      return(OriginalSizeCommand);
6867    case XK_greater:
6868      return(DoubleSizeCommand);
6869    case XK_percent:
6870      return(ResizeCommand);
6871    case XK_at:
6872      return(RefreshCommand);
6873    case XK_bracketleft:
6874      return(ChopCommand);
6875    case XK_h:
6876      return(FlopCommand);
6877    case XK_slash:
6878      return(RotateRightCommand);
6879    case XK_backslash:
6880      return(RotateLeftCommand);
6881    case XK_asterisk:
6882      return(RotateCommand);
6883    case XK_t:
6884      return(TrimCommand);
6885    case XK_H:
6886      return(HueCommand);
6887    case XK_S:
6888      return(SaturationCommand);
6889    case XK_L:
6890      return(BrightnessCommand);
6891    case XK_G:
6892      return(GammaCommand);
6893    case XK_C:
6894      return(SpiffCommand);
6895    case XK_Z:
6896      return(DullCommand);
6897    case XK_N:
6898      return(NormalizeCommand);
6899    case XK_equal:
6900      return(EqualizeCommand);
6901    case XK_asciitilde:
6902      return(NegateCommand);
6903    case XK_period:
6904      return(GrayscaleCommand);
6905    case XK_numbersign:
6906      return(QuantizeCommand);
6907    case XK_F2:
6908      return(DespeckleCommand);
6909    case XK_F3:
6910      return(EmbossCommand);
6911    case XK_F4:
6912      return(ReduceNoiseCommand);
6913    case XK_F5:
6914      return(AddNoiseCommand);
6915    case XK_F6:
6916      return(SharpenCommand);
6917    case XK_F7:
6918      return(BlurCommand);
6919    case XK_F8:
6920      return(ThresholdCommand);
6921    case XK_F9:
6922      return(EdgeDetectCommand);
6923    case XK_F10:
6924      return(SpreadCommand);
6925    case XK_F11:
6926      return(ShadeCommand);
6927    case XK_F12:
6928      return(RaiseCommand);
6929    case XK_F13:
6930      return(SegmentCommand);
6931    case XK_i:
6932    {
6933      if ((state & Mod1Mask) == 0)
6934        return(NullCommand);
6935      return(ImplodeCommand);
6936    }
6937    case XK_w:
6938    {
6939      if ((state & Mod1Mask) == 0)
6940        return(NullCommand);
6941      return(WaveCommand);
6942    }
6943    case XK_m:
6944    {
6945      if ((state & Mod4Mask) == 0)
6946        return(NullCommand);
6947      return(MatteCommand);
6948    }
6949    case XK_b:
6950    {
6951      if ((state & Mod4Mask) == 0)
6952        return(NullCommand);
6953      return(AddBorderCommand);
6954    }
6955    case XK_f:
6956    {
6957      if ((state & Mod4Mask) == 0)
6958        return(NullCommand);
6959      return(AddFrameCommand);
6960    }
6961    case XK_exclam:
6962    {
6963      if ((state & Mod4Mask) == 0)
6964        return(NullCommand);
6965      return(CommentCommand);
6966    }
6967    case XK_a:
6968    {
6969      if ((state & Mod1Mask) != 0)
6970        return(ApplyCommand);
6971      if ((state & Mod4Mask) != 0)
6972        return(AnnotateCommand);
6973      if ((state & ControlMask) == 0)
6974        return(NullCommand);
6975      return(RegionofInterestCommand);
6976    }
6977    case XK_question:
6978      return(InfoCommand);
6979    case XK_plus:
6980      return(ZoomCommand);
6981    case XK_P:
6982    {
6983      if ((state & ShiftMask) == 0)
6984        return(NullCommand);
6985      return(ShowPreviewCommand);
6986    }
6987    case XK_Execute:
6988      return(LaunchCommand);
6989    case XK_F1:
6990      return(HelpCommand);
6991    case XK_Find:
6992      return(BrowseDocumentationCommand);
6993    case XK_Menu:
6994    {
6995      (void) XMapRaised(display,windows->command.id);
6996      return(NullCommand);
6997    }
6998    case XK_Next:
6999    case XK_Prior:
7000    case XK_Home:
7001    case XK_KP_Home:
7002    {
7003      XTranslateImage(display,windows,*image,key_symbol);
7004      return(NullCommand);
7005    }
7006    case XK_Up:
7007    case XK_KP_Up:
7008    case XK_Down:
7009    case XK_KP_Down:
7010    case XK_Left:
7011    case XK_KP_Left:
7012    case XK_Right:
7013    case XK_KP_Right:
7014    {
7015      if ((state & Mod1Mask) != 0)
7016        {
7017          RectangleInfo
7018            crop_info;
7019
7020          /*
7021            Trim one pixel from edge of image.
7022          */
7023          crop_info.x=0;
7024          crop_info.y=0;
7025          crop_info.width=(size_t) windows->image.ximage->width;
7026          crop_info.height=(size_t) windows->image.ximage->height;
7027          if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
7028            {
7029              if (resource_info->quantum >= (int) crop_info.height)
7030                resource_info->quantum=(int) crop_info.height-1;
7031              crop_info.height-=resource_info->quantum;
7032            }
7033          if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
7034            {
7035              if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
7036                resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
7037              crop_info.y+=resource_info->quantum;
7038              crop_info.height-=resource_info->quantum;
7039            }
7040          if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
7041            {
7042              if (resource_info->quantum >= (int) crop_info.width)
7043                resource_info->quantum=(int) crop_info.width-1;
7044              crop_info.width-=resource_info->quantum;
7045            }
7046          if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
7047            {
7048              if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
7049                resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
7050              crop_info.x+=resource_info->quantum;
7051              crop_info.width-=resource_info->quantum;
7052            }
7053          if ((int) (windows->image.x+windows->image.width) >
7054              (int) crop_info.width)
7055            windows->image.x=(int) (crop_info.width-windows->image.width);
7056          if ((int) (windows->image.y+windows->image.height) >
7057              (int) crop_info.height)
7058            windows->image.y=(int) (crop_info.height-windows->image.height);
7059          XSetCropGeometry(display,windows,&crop_info,*image);
7060          windows->image.window_changes.width=(int) crop_info.width;
7061          windows->image.window_changes.height=(int) crop_info.height;
7062          (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
7063          (void) XConfigureImage(display,resource_info,windows,*image,
7064            exception);
7065          return(NullCommand);
7066        }
7067      XTranslateImage(display,windows,*image,key_symbol);
7068      return(NullCommand);
7069    }
7070    default:
7071      return(NullCommand);
7072  }
7073  return(NullCommand);
7074}
7075
7076/*
7077%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7078%                                                                             %
7079%                                                                             %
7080%                                                                             %
7081+   X M a g i c k C o m m a n d                                               %
7082%                                                                             %
7083%                                                                             %
7084%                                                                             %
7085%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7086%
7087%  XMagickCommand() makes a transform to the image or Image window as
7088%  specified by a user menu button or keyboard command.
7089%
7090%  The format of the XMagickCommand method is:
7091%
7092%      Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7093%        XWindows *windows,const CommandType command,Image **image,
7094%        ExceptionInfo *exception)
7095%
7096%  A description of each parameter follows:
7097%
7098%    o display: Specifies a connection to an X server; returned from
7099%      XOpenDisplay.
7100%
7101%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7102%
7103%    o windows: Specifies a pointer to a XWindows structure.
7104%
7105%    o command: Specifies a command to perform.
7106%
7107%    o image: the image;  XMagickCommand may transform the image and return a
7108%      new image pointer.
7109%
7110%    o exception: return any errors or warnings in this structure.
7111%
7112*/
7113static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7114  XWindows *windows,const CommandType command,Image **image,
7115  ExceptionInfo *exception)
7116{
7117  char
7118    filename[MaxTextExtent],
7119    geometry[MaxTextExtent],
7120    modulate_factors[MaxTextExtent];
7121
7122  GeometryInfo
7123    geometry_info;
7124
7125  Image
7126    *nexus;
7127
7128  ImageInfo
7129    *image_info;
7130
7131  int
7132    x,
7133    y;
7134
7135  MagickStatusType
7136    flags,
7137    status;
7138
7139  QuantizeInfo
7140    quantize_info;
7141
7142  RectangleInfo
7143    page_geometry;
7144
7145  register int
7146    i;
7147
7148  static char
7149    color[MaxTextExtent] = "gray";
7150
7151  unsigned int
7152    height,
7153    width;
7154
7155  /*
7156    Process user command.
7157  */
7158  XCheckRefreshWindows(display,windows);
7159  XImageCache(display,resource_info,windows,command,image,exception);
7160  nexus=NewImageList();
7161  windows->image.window_changes.width=windows->image.ximage->width;
7162  windows->image.window_changes.height=windows->image.ximage->height;
7163  image_info=CloneImageInfo(resource_info->image_info);
7164  SetGeometryInfo(&geometry_info);
7165  GetQuantizeInfo(&quantize_info);
7166  switch (command)
7167  {
7168    case OpenCommand:
7169    {
7170      /*
7171        Load image.
7172      */
7173      nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7174      break;
7175    }
7176    case NextCommand:
7177    {
7178      /*
7179        Display next image.
7180      */
7181      for (i=0; i < resource_info->quantum; i++)
7182        XClientMessage(display,windows->image.id,windows->im_protocols,
7183          windows->im_next_image,CurrentTime);
7184      break;
7185    }
7186    case FormerCommand:
7187    {
7188      /*
7189        Display former image.
7190      */
7191      for (i=0; i < resource_info->quantum; i++)
7192        XClientMessage(display,windows->image.id,windows->im_protocols,
7193          windows->im_former_image,CurrentTime);
7194      break;
7195    }
7196    case SelectCommand:
7197    {
7198      int
7199        status;
7200
7201      /*
7202        Select image.
7203      */
7204      status=chdir(resource_info->home_directory);
7205      if (status == -1)
7206        (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
7207          "UnableToOpenFile","%s",resource_info->home_directory);
7208      nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7209      break;
7210    }
7211    case SaveCommand:
7212    {
7213      /*
7214        Save image.
7215      */
7216      status=XSaveImage(display,resource_info,windows,*image,exception);
7217      if (status == MagickFalse)
7218        {
7219          char
7220            message[MaxTextExtent];
7221
7222          (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7223            exception->reason != (char *) NULL ? exception->reason : "",
7224            exception->description != (char *) NULL ? exception->description :
7225            "");
7226          XNoticeWidget(display,windows,"Unable to save file:",message);
7227          break;
7228        }
7229      break;
7230    }
7231    case PrintCommand:
7232    {
7233      /*
7234        Print image.
7235      */
7236      status=XPrintImage(display,resource_info,windows,*image,exception);
7237      if (status == MagickFalse)
7238        {
7239          char
7240            message[MaxTextExtent];
7241
7242          (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7243            exception->reason != (char *) NULL ? exception->reason : "",
7244            exception->description != (char *) NULL ? exception->description :
7245            "");
7246          XNoticeWidget(display,windows,"Unable to print file:",message);
7247          break;
7248        }
7249      break;
7250    }
7251    case DeleteCommand:
7252    {
7253      static char
7254        filename[MaxTextExtent] = "\0";
7255
7256      /*
7257        Delete image file.
7258      */
7259      XFileBrowserWidget(display,windows,"Delete",filename);
7260      if (*filename == '\0')
7261        break;
7262      status=remove_utf8(filename) != 0 ? MagickTrue : MagickFalse;
7263      if (status != MagickFalse)
7264        XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7265      break;
7266    }
7267    case NewCommand:
7268    {
7269      int
7270        status;
7271
7272      static char
7273        color[MaxTextExtent] = "gray",
7274        geometry[MaxTextExtent] = "640x480";
7275
7276      static const char
7277        *format = "gradient";
7278
7279      /*
7280        Query user for canvas geometry.
7281      */
7282      status=XDialogWidget(display,windows,"New","Enter image geometry:",
7283        geometry);
7284      if (*geometry == '\0')
7285        break;
7286      if (status == 0)
7287        format="xc";
7288      XColorBrowserWidget(display,windows,"Select",color);
7289      if (*color == '\0')
7290        break;
7291      /*
7292        Create canvas.
7293      */
7294      (void) FormatLocaleString(image_info->filename,MaxTextExtent,
7295        "%s:%s",format,color);
7296      (void) CloneString(&image_info->size,geometry);
7297      nexus=ReadImage(image_info,exception);
7298      CatchException(exception);
7299      XClientMessage(display,windows->image.id,windows->im_protocols,
7300        windows->im_next_image,CurrentTime);
7301      break;
7302    }
7303    case VisualDirectoryCommand:
7304    {
7305      /*
7306        Visual Image directory.
7307      */
7308      nexus=XVisualDirectoryImage(display,resource_info,windows,exception);
7309      break;
7310    }
7311    case QuitCommand:
7312    {
7313      /*
7314        exit program.
7315      */
7316      if (resource_info->confirm_exit == MagickFalse)
7317        XClientMessage(display,windows->image.id,windows->im_protocols,
7318          windows->im_exit,CurrentTime);
7319      else
7320        {
7321          int
7322            status;
7323
7324          /*
7325            Confirm program exit.
7326          */
7327          status=XConfirmWidget(display,windows,"Do you really want to exit",
7328            resource_info->client_name);
7329          if (status > 0)
7330            XClientMessage(display,windows->image.id,windows->im_protocols,
7331              windows->im_exit,CurrentTime);
7332        }
7333      break;
7334    }
7335    case CutCommand:
7336    {
7337      /*
7338        Cut image.
7339      */
7340      (void) XCropImage(display,resource_info,windows,*image,CutMode,exception);
7341      break;
7342    }
7343    case CopyCommand:
7344    {
7345      /*
7346        Copy image.
7347      */
7348      (void) XCropImage(display,resource_info,windows,*image,CopyMode,
7349        exception);
7350      break;
7351    }
7352    case PasteCommand:
7353    {
7354      /*
7355        Paste image.
7356      */
7357      status=XPasteImage(display,resource_info,windows,*image,exception);
7358      if (status == MagickFalse)
7359        {
7360          XNoticeWidget(display,windows,"Unable to paste X image",
7361            (*image)->filename);
7362          break;
7363        }
7364      break;
7365    }
7366    case HalfSizeCommand:
7367    {
7368      /*
7369        Half image size.
7370      */
7371      windows->image.window_changes.width=windows->image.ximage->width/2;
7372      windows->image.window_changes.height=windows->image.ximage->height/2;
7373      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7374      break;
7375    }
7376    case OriginalSizeCommand:
7377    {
7378      /*
7379        Original image size.
7380      */
7381      windows->image.window_changes.width=(int) (*image)->columns;
7382      windows->image.window_changes.height=(int) (*image)->rows;
7383      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7384      break;
7385    }
7386    case DoubleSizeCommand:
7387    {
7388      /*
7389        Double the image size.
7390      */
7391      windows->image.window_changes.width=windows->image.ximage->width << 1;
7392      windows->image.window_changes.height=windows->image.ximage->height << 1;
7393      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7394      break;
7395    }
7396    case ResizeCommand:
7397    {
7398      int
7399        status;
7400
7401      size_t
7402        height,
7403        width;
7404
7405      ssize_t
7406        x,
7407        y;
7408
7409      /*
7410        Resize image.
7411      */
7412      width=(size_t) windows->image.ximage->width;
7413      height=(size_t) windows->image.ximage->height;
7414      x=0;
7415      y=0;
7416      (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g+0+0",
7417        (double) width,(double) height);
7418      status=XDialogWidget(display,windows,"Resize",
7419        "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7420      if (*geometry == '\0')
7421        break;
7422      if (status == 0)
7423        (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7424      (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7425      windows->image.window_changes.width=(int) width;
7426      windows->image.window_changes.height=(int) height;
7427      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7428      break;
7429    }
7430    case ApplyCommand:
7431    {
7432      char
7433        image_geometry[MaxTextExtent];
7434
7435      if ((windows->image.crop_geometry == (char *) NULL) &&
7436          ((int) (*image)->columns == windows->image.ximage->width) &&
7437          ((int) (*image)->rows == windows->image.ximage->height))
7438        break;
7439      /*
7440        Apply size transforms to image.
7441      */
7442      XSetCursorState(display,windows,MagickTrue);
7443      XCheckRefreshWindows(display,windows);
7444      /*
7445        Crop and/or scale displayed image.
7446      */
7447      (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
7448        windows->image.ximage->width,windows->image.ximage->height);
7449      (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
7450        exception);
7451      if (windows->image.crop_geometry != (char *) NULL)
7452        windows->image.crop_geometry=(char *) RelinquishMagickMemory(
7453          windows->image.crop_geometry);
7454      windows->image.x=0;
7455      windows->image.y=0;
7456      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7457      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7458      break;
7459    }
7460    case RefreshCommand:
7461    {
7462      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7463      break;
7464    }
7465    case RestoreCommand:
7466    {
7467      /*
7468        Restore Image window to its original size.
7469      */
7470      if ((windows->image.width == (unsigned int) (*image)->columns) &&
7471          (windows->image.height == (unsigned int) (*image)->rows) &&
7472          (windows->image.crop_geometry == (char *) NULL))
7473        {
7474          (void) XBell(display,0);
7475          break;
7476        }
7477      windows->image.window_changes.width=(int) (*image)->columns;
7478      windows->image.window_changes.height=(int) (*image)->rows;
7479      if (windows->image.crop_geometry != (char *) NULL)
7480        {
7481          windows->image.crop_geometry=(char *)
7482            RelinquishMagickMemory(windows->image.crop_geometry);
7483          windows->image.crop_geometry=(char *) NULL;
7484          windows->image.x=0;
7485          windows->image.y=0;
7486        }
7487      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7488      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7489      break;
7490    }
7491    case CropCommand:
7492    {
7493      /*
7494        Crop image.
7495      */
7496      (void) XCropImage(display,resource_info,windows,*image,CropMode,
7497        exception);
7498      break;
7499    }
7500    case ChopCommand:
7501    {
7502      /*
7503        Chop image.
7504      */
7505      status=XChopImage(display,resource_info,windows,image,exception);
7506      if (status == MagickFalse)
7507        {
7508          XNoticeWidget(display,windows,"Unable to cut X image",
7509            (*image)->filename);
7510          break;
7511        }
7512      break;
7513    }
7514    case FlopCommand:
7515    {
7516      Image
7517        *flop_image;
7518
7519      /*
7520        Flop image scanlines.
7521      */
7522      XSetCursorState(display,windows,MagickTrue);
7523      XCheckRefreshWindows(display,windows);
7524      flop_image=FlopImage(*image,exception);
7525      if (flop_image != (Image *) NULL)
7526        {
7527          *image=DestroyImage(*image);
7528          *image=flop_image;
7529        }
7530      CatchException(exception);
7531      XSetCursorState(display,windows,MagickFalse);
7532      if (windows->image.crop_geometry != (char *) NULL)
7533        {
7534          /*
7535            Flop crop geometry.
7536          */
7537          width=(unsigned int) (*image)->columns;
7538          height=(unsigned int) (*image)->rows;
7539          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7540            &width,&height);
7541          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7542            "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7543        }
7544      if (windows->image.orphan != MagickFalse)
7545        break;
7546      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7547      break;
7548    }
7549    case FlipCommand:
7550    {
7551      Image
7552        *flip_image;
7553
7554      /*
7555        Flip image scanlines.
7556      */
7557      XSetCursorState(display,windows,MagickTrue);
7558      XCheckRefreshWindows(display,windows);
7559      flip_image=FlipImage(*image,exception);
7560      if (flip_image != (Image *) NULL)
7561        {
7562          *image=DestroyImage(*image);
7563          *image=flip_image;
7564        }
7565      CatchException(exception);
7566      XSetCursorState(display,windows,MagickFalse);
7567      if (windows->image.crop_geometry != (char *) NULL)
7568        {
7569          /*
7570            Flip crop geometry.
7571          */
7572          width=(unsigned int) (*image)->columns;
7573          height=(unsigned int) (*image)->rows;
7574          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7575            &width,&height);
7576          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7577            "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7578        }
7579      if (windows->image.orphan != MagickFalse)
7580        break;
7581      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7582      break;
7583    }
7584    case RotateRightCommand:
7585    {
7586      /*
7587        Rotate image 90 degrees clockwise.
7588      */
7589      status=XRotateImage(display,resource_info,windows,90.0,image,exception);
7590      if (status == MagickFalse)
7591        {
7592          XNoticeWidget(display,windows,"Unable to rotate X image",
7593            (*image)->filename);
7594          break;
7595        }
7596      break;
7597    }
7598    case RotateLeftCommand:
7599    {
7600      /*
7601        Rotate image 90 degrees counter-clockwise.
7602      */
7603      status=XRotateImage(display,resource_info,windows,-90.0,image,exception);
7604      if (status == MagickFalse)
7605        {
7606          XNoticeWidget(display,windows,"Unable to rotate X image",
7607            (*image)->filename);
7608          break;
7609        }
7610      break;
7611    }
7612    case RotateCommand:
7613    {
7614      /*
7615        Rotate image.
7616      */
7617      status=XRotateImage(display,resource_info,windows,0.0,image,exception);
7618      if (status == MagickFalse)
7619        {
7620          XNoticeWidget(display,windows,"Unable to rotate X image",
7621            (*image)->filename);
7622          break;
7623        }
7624      break;
7625    }
7626    case ShearCommand:
7627    {
7628      Image
7629        *shear_image;
7630
7631      static char
7632        geometry[MaxTextExtent] = "45.0x45.0";
7633
7634      /*
7635        Query user for shear color and geometry.
7636      */
7637      XColorBrowserWidget(display,windows,"Select",color);
7638      if (*color == '\0')
7639        break;
7640      (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7641        geometry);
7642      if (*geometry == '\0')
7643        break;
7644      /*
7645        Shear image.
7646      */
7647      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7648        exception);
7649      XSetCursorState(display,windows,MagickTrue);
7650      XCheckRefreshWindows(display,windows);
7651      (void) QueryColorCompliance(color,AllCompliance,
7652        &(*image)->background_color,exception);
7653      flags=ParseGeometry(geometry,&geometry_info);
7654      if ((flags & SigmaValue) == 0)
7655        geometry_info.sigma=geometry_info.rho;
7656      shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7657        exception);
7658      if (shear_image != (Image *) NULL)
7659        {
7660          *image=DestroyImage(*image);
7661          *image=shear_image;
7662        }
7663      CatchException(exception);
7664      XSetCursorState(display,windows,MagickFalse);
7665      if (windows->image.orphan != MagickFalse)
7666        break;
7667      windows->image.window_changes.width=(int) (*image)->columns;
7668      windows->image.window_changes.height=(int) (*image)->rows;
7669      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7670      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7671      break;
7672    }
7673    case RollCommand:
7674    {
7675      Image
7676        *roll_image;
7677
7678      static char
7679        geometry[MaxTextExtent] = "+2+2";
7680
7681      /*
7682        Query user for the roll geometry.
7683      */
7684      (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7685        geometry);
7686      if (*geometry == '\0')
7687        break;
7688      /*
7689        Roll image.
7690      */
7691      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7692        exception);
7693      XSetCursorState(display,windows,MagickTrue);
7694      XCheckRefreshWindows(display,windows);
7695      (void) ParsePageGeometry(*image,geometry,&page_geometry,
7696        exception);
7697      roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7698        exception);
7699      if (roll_image != (Image *) NULL)
7700        {
7701          *image=DestroyImage(*image);
7702          *image=roll_image;
7703        }
7704      CatchException(exception);
7705      XSetCursorState(display,windows,MagickFalse);
7706      if (windows->image.orphan != MagickFalse)
7707        break;
7708      windows->image.window_changes.width=(int) (*image)->columns;
7709      windows->image.window_changes.height=(int) (*image)->rows;
7710      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7711      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7712      break;
7713    }
7714    case TrimCommand:
7715    {
7716      static char
7717        fuzz[MaxTextExtent];
7718
7719      /*
7720        Query user for the fuzz factor.
7721      */
7722      (void) FormatLocaleString(fuzz,MaxTextExtent,"%g%%",100.0*
7723        (*image)->fuzz/(QuantumRange+1.0));
7724      (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7725      if (*fuzz == '\0')
7726        break;
7727      (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+1.0);
7728      /*
7729        Trim image.
7730      */
7731      status=XTrimImage(display,resource_info,windows,*image,exception);
7732      if (status == MagickFalse)
7733        {
7734          XNoticeWidget(display,windows,"Unable to trim X image",
7735            (*image)->filename);
7736          break;
7737        }
7738      break;
7739    }
7740    case HueCommand:
7741    {
7742      static char
7743        hue_percent[MaxTextExtent] = "110";
7744
7745      /*
7746        Query user for percent hue change.
7747      */
7748      (void) XDialogWidget(display,windows,"Apply",
7749        "Enter percent change in image hue (0-200):",hue_percent);
7750      if (*hue_percent == '\0')
7751        break;
7752      /*
7753        Vary the image hue.
7754      */
7755      XSetCursorState(display,windows,MagickTrue);
7756      XCheckRefreshWindows(display,windows);
7757      (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7758      (void) ConcatenateMagickString(modulate_factors,hue_percent,
7759        MaxTextExtent);
7760      (void) ModulateImage(*image,modulate_factors,exception);
7761      XSetCursorState(display,windows,MagickFalse);
7762      if (windows->image.orphan != MagickFalse)
7763        break;
7764      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7765      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7766      break;
7767    }
7768    case SaturationCommand:
7769    {
7770      static char
7771        saturation_percent[MaxTextExtent] = "110";
7772
7773      /*
7774        Query user for percent saturation change.
7775      */
7776      (void) XDialogWidget(display,windows,"Apply",
7777        "Enter percent change in color saturation (0-200):",saturation_percent);
7778      if (*saturation_percent == '\0')
7779        break;
7780      /*
7781        Vary color saturation.
7782      */
7783      XSetCursorState(display,windows,MagickTrue);
7784      XCheckRefreshWindows(display,windows);
7785      (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7786      (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7787        MaxTextExtent);
7788      (void) ModulateImage(*image,modulate_factors,exception);
7789      XSetCursorState(display,windows,MagickFalse);
7790      if (windows->image.orphan != MagickFalse)
7791        break;
7792      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7793      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7794      break;
7795    }
7796    case BrightnessCommand:
7797    {
7798      static char
7799        brightness_percent[MaxTextExtent] = "110";
7800
7801      /*
7802        Query user for percent brightness change.
7803      */
7804      (void) XDialogWidget(display,windows,"Apply",
7805        "Enter percent change in color brightness (0-200):",brightness_percent);
7806      if (*brightness_percent == '\0')
7807        break;
7808      /*
7809        Vary the color brightness.
7810      */
7811      XSetCursorState(display,windows,MagickTrue);
7812      XCheckRefreshWindows(display,windows);
7813      (void) CopyMagickString(modulate_factors,brightness_percent,
7814        MaxTextExtent);
7815      (void) ModulateImage(*image,modulate_factors,exception);
7816      XSetCursorState(display,windows,MagickFalse);
7817      if (windows->image.orphan != MagickFalse)
7818        break;
7819      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7820      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7821      break;
7822    }
7823    case GammaCommand:
7824    {
7825      static char
7826        factor[MaxTextExtent] = "1.6";
7827
7828      /*
7829        Query user for gamma value.
7830      */
7831      (void) XDialogWidget(display,windows,"Gamma",
7832        "Enter gamma value (e.g. 1.2):",factor);
7833      if (*factor == '\0')
7834        break;
7835      /*
7836        Gamma correct image.
7837      */
7838      XSetCursorState(display,windows,MagickTrue);
7839      XCheckRefreshWindows(display,windows);
7840      (void) GammaImage(*image,atof(factor),exception);
7841      XSetCursorState(display,windows,MagickFalse);
7842      if (windows->image.orphan != MagickFalse)
7843        break;
7844      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7845      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7846      break;
7847    }
7848    case SpiffCommand:
7849    {
7850      /*
7851        Sharpen the image contrast.
7852      */
7853      XSetCursorState(display,windows,MagickTrue);
7854      XCheckRefreshWindows(display,windows);
7855      (void) ContrastImage(*image,MagickTrue,exception);
7856      XSetCursorState(display,windows,MagickFalse);
7857      if (windows->image.orphan != MagickFalse)
7858        break;
7859      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7860      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7861      break;
7862    }
7863    case DullCommand:
7864    {
7865      /*
7866        Dull the image contrast.
7867      */
7868      XSetCursorState(display,windows,MagickTrue);
7869      XCheckRefreshWindows(display,windows);
7870      (void) ContrastImage(*image,MagickFalse,exception);
7871      XSetCursorState(display,windows,MagickFalse);
7872      if (windows->image.orphan != MagickFalse)
7873        break;
7874      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7875      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7876      break;
7877    }
7878    case ContrastStretchCommand:
7879    {
7880      double
7881        black_point,
7882        white_point;
7883
7884      static char
7885        levels[MaxTextExtent] = "1%";
7886
7887      /*
7888        Query user for gamma value.
7889      */
7890      (void) XDialogWidget(display,windows,"Contrast Stretch",
7891        "Enter black and white points:",levels);
7892      if (*levels == '\0')
7893        break;
7894      /*
7895        Contrast stretch image.
7896      */
7897      XSetCursorState(display,windows,MagickTrue);
7898      XCheckRefreshWindows(display,windows);
7899      flags=ParseGeometry(levels,&geometry_info);
7900      black_point=geometry_info.rho;
7901      white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7902      if ((flags & PercentValue) != 0)
7903        {
7904          black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7905          white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7906        }
7907      white_point=(MagickRealType) (*image)->columns*(*image)->rows-white_point;
7908      (void) ContrastStretchImage(*image,black_point,white_point,
7909        exception);
7910      XSetCursorState(display,windows,MagickFalse);
7911      if (windows->image.orphan != MagickFalse)
7912        break;
7913      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7914      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7915      break;
7916    }
7917    case SigmoidalContrastCommand:
7918    {
7919      GeometryInfo
7920        geometry_info;
7921
7922      MagickStatusType
7923        flags;
7924
7925      static char
7926        levels[MaxTextExtent] = "3x50%";
7927
7928      /*
7929        Query user for gamma value.
7930      */
7931      (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7932        "Enter contrast and midpoint:",levels);
7933      if (*levels == '\0')
7934        break;
7935      /*
7936        Contrast stretch image.
7937      */
7938      XSetCursorState(display,windows,MagickTrue);
7939      XCheckRefreshWindows(display,windows);
7940      flags=ParseGeometry(levels,&geometry_info);
7941      if ((flags & SigmaValue) == 0)
7942        geometry_info.sigma=1.0*QuantumRange/2.0;
7943      if ((flags & PercentValue) != 0)
7944        geometry_info.sigma=1.0*QuantumRange*geometry_info.sigma/100.0;
7945      (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
7946        geometry_info.sigma,exception);
7947      XSetCursorState(display,windows,MagickFalse);
7948      if (windows->image.orphan != MagickFalse)
7949        break;
7950      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7951      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7952      break;
7953    }
7954    case NormalizeCommand:
7955    {
7956      /*
7957        Perform histogram normalization on the image.
7958      */
7959      XSetCursorState(display,windows,MagickTrue);
7960      XCheckRefreshWindows(display,windows);
7961      (void) NormalizeImage(*image,exception);
7962      XSetCursorState(display,windows,MagickFalse);
7963      if (windows->image.orphan != MagickFalse)
7964        break;
7965      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7966      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7967      break;
7968    }
7969    case EqualizeCommand:
7970    {
7971      /*
7972        Perform histogram equalization on the image.
7973      */
7974      XSetCursorState(display,windows,MagickTrue);
7975      XCheckRefreshWindows(display,windows);
7976      (void) EqualizeImage(*image,exception);
7977      XSetCursorState(display,windows,MagickFalse);
7978      if (windows->image.orphan != MagickFalse)
7979        break;
7980      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7981      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7982      break;
7983    }
7984    case NegateCommand:
7985    {
7986      /*
7987        Negate colors in image.
7988      */
7989      XSetCursorState(display,windows,MagickTrue);
7990      XCheckRefreshWindows(display,windows);
7991      (void) NegateImage(*image,MagickFalse,exception);
7992      XSetCursorState(display,windows,MagickFalse);
7993      if (windows->image.orphan != MagickFalse)
7994        break;
7995      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7996      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7997      break;
7998    }
7999    case GrayscaleCommand:
8000    {
8001      /*
8002        Convert image to grayscale.
8003      */
8004      XSetCursorState(display,windows,MagickTrue);
8005      XCheckRefreshWindows(display,windows);
8006      (void) SetImageType(*image,(*image)->matte == MagickFalse ?
8007        GrayscaleType : GrayscaleMatteType,exception);
8008      XSetCursorState(display,windows,MagickFalse);
8009      if (windows->image.orphan != MagickFalse)
8010        break;
8011      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8012      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8013      break;
8014    }
8015    case MapCommand:
8016    {
8017      Image
8018        *affinity_image;
8019
8020      static char
8021        filename[MaxTextExtent] = "\0";
8022
8023      /*
8024        Request image file name from user.
8025      */
8026      XFileBrowserWidget(display,windows,"Map",filename);
8027      if (*filename == '\0')
8028        break;
8029      /*
8030        Map image.
8031      */
8032      XSetCursorState(display,windows,MagickTrue);
8033      XCheckRefreshWindows(display,windows);
8034      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
8035      affinity_image=ReadImage(image_info,exception);
8036      if (affinity_image != (Image *) NULL)
8037        {
8038          (void) RemapImage(&quantize_info,*image,affinity_image,exception);
8039          affinity_image=DestroyImage(affinity_image);
8040        }
8041      CatchException(exception);
8042      XSetCursorState(display,windows,MagickFalse);
8043      if (windows->image.orphan != MagickFalse)
8044        break;
8045      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8046      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8047      break;
8048    }
8049    case QuantizeCommand:
8050    {
8051      int
8052        status;
8053
8054      static char
8055        colors[MaxTextExtent] = "256";
8056
8057      /*
8058        Query user for maximum number of colors.
8059      */
8060      status=XDialogWidget(display,windows,"Quantize",
8061        "Maximum number of colors:",colors);
8062      if (*colors == '\0')
8063        break;
8064      /*
8065        Color reduce the image.
8066      */
8067      XSetCursorState(display,windows,MagickTrue);
8068      XCheckRefreshWindows(display,windows);
8069      quantize_info.number_colors=StringToUnsignedLong(colors);
8070      quantize_info.dither=status != 0 ? MagickTrue : MagickFalse;
8071      (void) QuantizeImage(&quantize_info,*image,exception);
8072      XSetCursorState(display,windows,MagickFalse);
8073      if (windows->image.orphan != MagickFalse)
8074        break;
8075      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8076      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8077      break;
8078    }
8079    case DespeckleCommand:
8080    {
8081      Image
8082        *despeckle_image;
8083
8084      /*
8085        Despeckle image.
8086      */
8087      XSetCursorState(display,windows,MagickTrue);
8088      XCheckRefreshWindows(display,windows);
8089      despeckle_image=DespeckleImage(*image,exception);
8090      if (despeckle_image != (Image *) NULL)
8091        {
8092          *image=DestroyImage(*image);
8093          *image=despeckle_image;
8094        }
8095      CatchException(exception);
8096      XSetCursorState(display,windows,MagickFalse);
8097      if (windows->image.orphan != MagickFalse)
8098        break;
8099      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8100      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8101      break;
8102    }
8103    case EmbossCommand:
8104    {
8105      Image
8106        *emboss_image;
8107
8108      static char
8109        radius[MaxTextExtent] = "0.0x1.0";
8110
8111      /*
8112        Query user for emboss radius.
8113      */
8114      (void) XDialogWidget(display,windows,"Emboss",
8115        "Enter the emboss radius and standard deviation:",radius);
8116      if (*radius == '\0')
8117        break;
8118      /*
8119        Reduce noise in the image.
8120      */
8121      XSetCursorState(display,windows,MagickTrue);
8122      XCheckRefreshWindows(display,windows);
8123      flags=ParseGeometry(radius,&geometry_info);
8124      if ((flags & SigmaValue) == 0)
8125        geometry_info.sigma=1.0;
8126      emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8127        exception);
8128      if (emboss_image != (Image *) NULL)
8129        {
8130          *image=DestroyImage(*image);
8131          *image=emboss_image;
8132        }
8133      CatchException(exception);
8134      XSetCursorState(display,windows,MagickFalse);
8135      if (windows->image.orphan != MagickFalse)
8136        break;
8137      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8138      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8139      break;
8140    }
8141    case ReduceNoiseCommand:
8142    {
8143      Image
8144        *noise_image;
8145
8146      static char
8147        radius[MaxTextExtent] = "0";
8148
8149      /*
8150        Query user for noise radius.
8151      */
8152      (void) XDialogWidget(display,windows,"Reduce Noise",
8153        "Enter the noise radius:",radius);
8154      if (*radius == '\0')
8155        break;
8156      /*
8157        Reduce noise in the image.
8158      */
8159      XSetCursorState(display,windows,MagickTrue);
8160      XCheckRefreshWindows(display,windows);
8161      flags=ParseGeometry(radius,&geometry_info);
8162      noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8163        geometry_info.rho,(size_t) geometry_info.rho,exception);
8164      if (noise_image != (Image *) NULL)
8165        {
8166          *image=DestroyImage(*image);
8167          *image=noise_image;
8168        }
8169      CatchException(exception);
8170      XSetCursorState(display,windows,MagickFalse);
8171      if (windows->image.orphan != MagickFalse)
8172        break;
8173      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8174      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8175      break;
8176    }
8177    case AddNoiseCommand:
8178    {
8179      char
8180        **noises;
8181
8182      Image
8183        *noise_image;
8184
8185      static char
8186        noise_type[MaxTextExtent] = "Gaussian";
8187
8188      /*
8189        Add noise to the image.
8190      */
8191      noises=GetCommandOptions(MagickNoiseOptions);
8192      if (noises == (char **) NULL)
8193        break;
8194      XListBrowserWidget(display,windows,&windows->widget,
8195        (const char **) noises,"Add Noise",
8196        "Select a type of noise to add to your image:",noise_type);
8197      noises=DestroyStringList(noises);
8198      if (*noise_type == '\0')
8199        break;
8200      XSetCursorState(display,windows,MagickTrue);
8201      XCheckRefreshWindows(display,windows);
8202      noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8203        MagickNoiseOptions,MagickFalse,noise_type),1.0,exception);
8204      if (noise_image != (Image *) NULL)
8205        {
8206          *image=DestroyImage(*image);
8207          *image=noise_image;
8208        }
8209      CatchException(exception);
8210      XSetCursorState(display,windows,MagickFalse);
8211      if (windows->image.orphan != MagickFalse)
8212        break;
8213      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8214      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8215      break;
8216    }
8217    case SharpenCommand:
8218    {
8219      Image
8220        *sharp_image;
8221
8222      static char
8223        radius[MaxTextExtent] = "0.0x1.0";
8224
8225      /*
8226        Query user for sharpen radius.
8227      */
8228      (void) XDialogWidget(display,windows,"Sharpen",
8229        "Enter the sharpen radius and standard deviation:",radius);
8230      if (*radius == '\0')
8231        break;
8232      /*
8233        Sharpen image scanlines.
8234      */
8235      XSetCursorState(display,windows,MagickTrue);
8236      XCheckRefreshWindows(display,windows);
8237      flags=ParseGeometry(radius,&geometry_info);
8238      sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8239        geometry_info.xi,exception);
8240      if (sharp_image != (Image *) NULL)
8241        {
8242          *image=DestroyImage(*image);
8243          *image=sharp_image;
8244        }
8245      CatchException(exception);
8246      XSetCursorState(display,windows,MagickFalse);
8247      if (windows->image.orphan != MagickFalse)
8248        break;
8249      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8250      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8251      break;
8252    }
8253    case BlurCommand:
8254    {
8255      Image
8256        *blur_image;
8257
8258      static char
8259        radius[MaxTextExtent] = "0.0x1.0";
8260
8261      /*
8262        Query user for blur radius.
8263      */
8264      (void) XDialogWidget(display,windows,"Blur",
8265        "Enter the blur radius and standard deviation:",radius);
8266      if (*radius == '\0')
8267        break;
8268      /*
8269        Blur an image.
8270      */
8271      XSetCursorState(display,windows,MagickTrue);
8272      XCheckRefreshWindows(display,windows);
8273      flags=ParseGeometry(radius,&geometry_info);
8274      blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8275        geometry_info.xi,exception);
8276      if (blur_image != (Image *) NULL)
8277        {
8278          *image=DestroyImage(*image);
8279          *image=blur_image;
8280        }
8281      CatchException(exception);
8282      XSetCursorState(display,windows,MagickFalse);
8283      if (windows->image.orphan != MagickFalse)
8284        break;
8285      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8286      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8287      break;
8288    }
8289    case ThresholdCommand:
8290    {
8291      double
8292        threshold;
8293
8294      static char
8295        factor[MaxTextExtent] = "128";
8296
8297      /*
8298        Query user for threshold value.
8299      */
8300      (void) XDialogWidget(display,windows,"Threshold",
8301        "Enter threshold value:",factor);
8302      if (*factor == '\0')
8303        break;
8304      /*
8305        Gamma correct image.
8306      */
8307      XSetCursorState(display,windows,MagickTrue);
8308      XCheckRefreshWindows(display,windows);
8309      threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8310      (void) BilevelImage(*image,threshold,exception);
8311      XSetCursorState(display,windows,MagickFalse);
8312      if (windows->image.orphan != MagickFalse)
8313        break;
8314      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8315      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8316      break;
8317    }
8318    case EdgeDetectCommand:
8319    {
8320      Image
8321        *edge_image;
8322
8323      static char
8324        radius[MaxTextExtent] = "0";
8325
8326      /*
8327        Query user for edge factor.
8328      */
8329      (void) XDialogWidget(display,windows,"Detect Edges",
8330        "Enter the edge detect radius:",radius);
8331      if (*radius == '\0')
8332        break;
8333      /*
8334        Detect edge in image.
8335      */
8336      XSetCursorState(display,windows,MagickTrue);
8337      XCheckRefreshWindows(display,windows);
8338      flags=ParseGeometry(radius,&geometry_info);
8339      edge_image=EdgeImage(*image,geometry_info.rho,geometry_info.sigma,
8340        exception);
8341      if (edge_image != (Image *) NULL)
8342        {
8343          *image=DestroyImage(*image);
8344          *image=edge_image;
8345        }
8346      CatchException(exception);
8347      XSetCursorState(display,windows,MagickFalse);
8348      if (windows->image.orphan != MagickFalse)
8349        break;
8350      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8351      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8352      break;
8353    }
8354    case SpreadCommand:
8355    {
8356      Image
8357        *spread_image;
8358
8359      static char
8360        amount[MaxTextExtent] = "2";
8361
8362      /*
8363        Query user for spread amount.
8364      */
8365      (void) XDialogWidget(display,windows,"Spread",
8366        "Enter the displacement amount:",amount);
8367      if (*amount == '\0')
8368        break;
8369      /*
8370        Displace image pixels by a random amount.
8371      */
8372      XSetCursorState(display,windows,MagickTrue);
8373      XCheckRefreshWindows(display,windows);
8374      flags=ParseGeometry(amount,&geometry_info);
8375      spread_image=EdgeImage(*image,geometry_info.rho,geometry_info.sigma,
8376        exception);
8377      if (spread_image != (Image *) NULL)
8378        {
8379          *image=DestroyImage(*image);
8380          *image=spread_image;
8381        }
8382      CatchException(exception);
8383      XSetCursorState(display,windows,MagickFalse);
8384      if (windows->image.orphan != MagickFalse)
8385        break;
8386      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8387      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8388      break;
8389    }
8390    case ShadeCommand:
8391    {
8392      Image
8393        *shade_image;
8394
8395      int
8396        status;
8397
8398      static char
8399        geometry[MaxTextExtent] = "30x30";
8400
8401      /*
8402        Query user for the shade geometry.
8403      */
8404      status=XDialogWidget(display,windows,"Shade",
8405        "Enter the azimuth and elevation of the light source:",geometry);
8406      if (*geometry == '\0')
8407        break;
8408      /*
8409        Shade image pixels.
8410      */
8411      XSetCursorState(display,windows,MagickTrue);
8412      XCheckRefreshWindows(display,windows);
8413      flags=ParseGeometry(geometry,&geometry_info);
8414      if ((flags & SigmaValue) == 0)
8415        geometry_info.sigma=1.0;
8416      shade_image=ShadeImage(*image,status != 0 ? MagickFalse : MagickTrue,
8417        geometry_info.rho,geometry_info.sigma,exception);
8418      if (shade_image != (Image *) NULL)
8419        {
8420          *image=DestroyImage(*image);
8421          *image=shade_image;
8422        }
8423      CatchException(exception);
8424      XSetCursorState(display,windows,MagickFalse);
8425      if (windows->image.orphan != MagickFalse)
8426        break;
8427      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8428      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8429      break;
8430    }
8431    case RaiseCommand:
8432    {
8433      static char
8434        bevel_width[MaxTextExtent] = "10";
8435
8436      /*
8437        Query user for bevel width.
8438      */
8439      (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8440      if (*bevel_width == '\0')
8441        break;
8442      /*
8443        Raise an image.
8444      */
8445      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8446        exception);
8447      XSetCursorState(display,windows,MagickTrue);
8448      XCheckRefreshWindows(display,windows);
8449      (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8450        exception);
8451      (void) RaiseImage(*image,&page_geometry,MagickTrue,exception);
8452      XSetCursorState(display,windows,MagickFalse);
8453      if (windows->image.orphan != MagickFalse)
8454        break;
8455      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8456      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8457      break;
8458    }
8459    case SegmentCommand:
8460    {
8461      static char
8462        threshold[MaxTextExtent] = "1.0x1.5";
8463
8464      /*
8465        Query user for smoothing threshold.
8466      */
8467      (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8468        threshold);
8469      if (*threshold == '\0')
8470        break;
8471      /*
8472        Segment an image.
8473      */
8474      XSetCursorState(display,windows,MagickTrue);
8475      XCheckRefreshWindows(display,windows);
8476      flags=ParseGeometry(threshold,&geometry_info);
8477      if ((flags & SigmaValue) == 0)
8478        geometry_info.sigma=1.0;
8479      (void) SegmentImage(*image,sRGBColorspace,MagickFalse,geometry_info.rho,
8480        geometry_info.sigma,exception);
8481      XSetCursorState(display,windows,MagickFalse);
8482      if (windows->image.orphan != MagickFalse)
8483        break;
8484      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8485      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8486      break;
8487    }
8488    case SepiaToneCommand:
8489    {
8490      double
8491        threshold;
8492
8493      Image
8494        *sepia_image;
8495
8496      static char
8497        factor[MaxTextExtent] = "80%";
8498
8499      /*
8500        Query user for sepia-tone factor.
8501      */
8502      (void) XDialogWidget(display,windows,"Sepia Tone",
8503        "Enter the sepia tone factor (0 - 99.9%):",factor);
8504      if (*factor == '\0')
8505        break;
8506      /*
8507        Sepia tone image pixels.
8508      */
8509      XSetCursorState(display,windows,MagickTrue);
8510      XCheckRefreshWindows(display,windows);
8511      threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8512      sepia_image=SepiaToneImage(*image,threshold,exception);
8513      if (sepia_image != (Image *) NULL)
8514        {
8515          *image=DestroyImage(*image);
8516          *image=sepia_image;
8517        }
8518      CatchException(exception);
8519      XSetCursorState(display,windows,MagickFalse);
8520      if (windows->image.orphan != MagickFalse)
8521        break;
8522      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8523      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8524      break;
8525    }
8526    case SolarizeCommand:
8527    {
8528      double
8529        threshold;
8530
8531      static char
8532        factor[MaxTextExtent] = "60%";
8533
8534      /*
8535        Query user for solarize factor.
8536      */
8537      (void) XDialogWidget(display,windows,"Solarize",
8538        "Enter the solarize factor (0 - 99.9%):",factor);
8539      if (*factor == '\0')
8540        break;
8541      /*
8542        Solarize image pixels.
8543      */
8544      XSetCursorState(display,windows,MagickTrue);
8545      XCheckRefreshWindows(display,windows);
8546      threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8547      (void) SolarizeImage(*image,threshold,exception);
8548      XSetCursorState(display,windows,MagickFalse);
8549      if (windows->image.orphan != MagickFalse)
8550        break;
8551      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8552      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8553      break;
8554    }
8555    case SwirlCommand:
8556    {
8557      Image
8558        *swirl_image;
8559
8560      static char
8561        degrees[MaxTextExtent] = "60";
8562
8563      /*
8564        Query user for swirl angle.
8565      */
8566      (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8567        degrees);
8568      if (*degrees == '\0')
8569        break;
8570      /*
8571        Swirl image pixels about the center.
8572      */
8573      XSetCursorState(display,windows,MagickTrue);
8574      XCheckRefreshWindows(display,windows);
8575      flags=ParseGeometry(degrees,&geometry_info);
8576      swirl_image=SwirlImage(*image,geometry_info.rho,(*image)->interpolate,
8577        exception);
8578      if (swirl_image != (Image *) NULL)
8579        {
8580          *image=DestroyImage(*image);
8581          *image=swirl_image;
8582        }
8583      CatchException(exception);
8584      XSetCursorState(display,windows,MagickFalse);
8585      if (windows->image.orphan != MagickFalse)
8586        break;
8587      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8588      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8589      break;
8590    }
8591    case ImplodeCommand:
8592    {
8593      Image
8594        *implode_image;
8595
8596      static char
8597        factor[MaxTextExtent] = "0.3";
8598
8599      /*
8600        Query user for implode factor.
8601      */
8602      (void) XDialogWidget(display,windows,"Implode",
8603        "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8604      if (*factor == '\0')
8605        break;
8606      /*
8607        Implode image pixels about the center.
8608      */
8609      XSetCursorState(display,windows,MagickTrue);
8610      XCheckRefreshWindows(display,windows);
8611      flags=ParseGeometry(factor,&geometry_info);
8612      implode_image=ImplodeImage(*image,geometry_info.rho,(*image)->interpolate,
8613        exception);
8614      if (implode_image != (Image *) NULL)
8615        {
8616          *image=DestroyImage(*image);
8617          *image=implode_image;
8618        }
8619      CatchException(exception);
8620      XSetCursorState(display,windows,MagickFalse);
8621      if (windows->image.orphan != MagickFalse)
8622        break;
8623      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8624      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8625      break;
8626    }
8627    case VignetteCommand:
8628    {
8629      Image
8630        *vignette_image;
8631
8632      static char
8633        geometry[MaxTextExtent] = "0x20";
8634
8635      /*
8636        Query user for the vignette geometry.
8637      */
8638      (void) XDialogWidget(display,windows,"Vignette",
8639        "Enter the radius, sigma, and x and y offsets:",geometry);
8640      if (*geometry == '\0')
8641        break;
8642      /*
8643        Soften the edges of the image in vignette style
8644      */
8645      XSetCursorState(display,windows,MagickTrue);
8646      XCheckRefreshWindows(display,windows);
8647      flags=ParseGeometry(geometry,&geometry_info);
8648      if ((flags & SigmaValue) == 0)
8649        geometry_info.sigma=1.0;
8650      if ((flags & XiValue) == 0)
8651        geometry_info.xi=0.1*(*image)->columns;
8652      if ((flags & PsiValue) == 0)
8653        geometry_info.psi=0.1*(*image)->rows;
8654      vignette_image=VignetteImage(*image,geometry_info.rho,geometry_info.sigma,
8655        0.0,(ssize_t) ceil(geometry_info.xi-0.5),(ssize_t)
8656        ceil(geometry_info.psi-0.5),exception);
8657      if (vignette_image != (Image *) NULL)
8658        {
8659          *image=DestroyImage(*image);
8660          *image=vignette_image;
8661        }
8662      CatchException(exception);
8663      XSetCursorState(display,windows,MagickFalse);
8664      if (windows->image.orphan != MagickFalse)
8665        break;
8666      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8667      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8668      break;
8669    }
8670    case WaveCommand:
8671    {
8672      Image
8673        *wave_image;
8674
8675      static char
8676        geometry[MaxTextExtent] = "25x150";
8677
8678      /*
8679        Query user for the wave geometry.
8680      */
8681      (void) XDialogWidget(display,windows,"Wave",
8682        "Enter the amplitude and length of the wave:",geometry);
8683      if (*geometry == '\0')
8684        break;
8685      /*
8686        Alter an image along a sine wave.
8687      */
8688      XSetCursorState(display,windows,MagickTrue);
8689      XCheckRefreshWindows(display,windows);
8690      flags=ParseGeometry(geometry,&geometry_info);
8691      if ((flags & SigmaValue) == 0)
8692        geometry_info.sigma=1.0;
8693      wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8694        (*image)->interpolate,exception);
8695      if (wave_image != (Image *) NULL)
8696        {
8697          *image=DestroyImage(*image);
8698          *image=wave_image;
8699        }
8700      CatchException(exception);
8701      XSetCursorState(display,windows,MagickFalse);
8702      if (windows->image.orphan != MagickFalse)
8703        break;
8704      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8705      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8706      break;
8707    }
8708    case OilPaintCommand:
8709    {
8710      Image
8711        *paint_image;
8712
8713      static char
8714        radius[MaxTextExtent] = "0";
8715
8716      /*
8717        Query user for circular neighborhood radius.
8718      */
8719      (void) XDialogWidget(display,windows,"Oil Paint",
8720        "Enter the mask radius:",radius);
8721      if (*radius == '\0')
8722        break;
8723      /*
8724        OilPaint image scanlines.
8725      */
8726      XSetCursorState(display,windows,MagickTrue);
8727      XCheckRefreshWindows(display,windows);
8728      flags=ParseGeometry(radius,&geometry_info);
8729      paint_image=OilPaintImage(*image,geometry_info.rho,geometry_info.sigma,
8730        exception);
8731      if (paint_image != (Image *) NULL)
8732        {
8733          *image=DestroyImage(*image);
8734          *image=paint_image;
8735        }
8736      CatchException(exception);
8737      XSetCursorState(display,windows,MagickFalse);
8738      if (windows->image.orphan != MagickFalse)
8739        break;
8740      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8741      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8742      break;
8743    }
8744    case CharcoalDrawCommand:
8745    {
8746      Image
8747        *charcoal_image;
8748
8749      static char
8750        radius[MaxTextExtent] = "0x1";
8751
8752      /*
8753        Query user for charcoal radius.
8754      */
8755      (void) XDialogWidget(display,windows,"Charcoal Draw",
8756        "Enter the charcoal radius and sigma:",radius);
8757      if (*radius == '\0')
8758        break;
8759      /*
8760        Charcoal the image.
8761      */
8762      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8763        exception);
8764      XSetCursorState(display,windows,MagickTrue);
8765      XCheckRefreshWindows(display,windows);
8766      flags=ParseGeometry(radius,&geometry_info);
8767      if ((flags & SigmaValue) == 0)
8768        geometry_info.sigma=geometry_info.rho;
8769      charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8770        geometry_info.xi,exception);
8771      if (charcoal_image != (Image *) NULL)
8772        {
8773          *image=DestroyImage(*image);
8774          *image=charcoal_image;
8775        }
8776      CatchException(exception);
8777      XSetCursorState(display,windows,MagickFalse);
8778      if (windows->image.orphan != MagickFalse)
8779        break;
8780      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8781      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8782      break;
8783    }
8784    case AnnotateCommand:
8785    {
8786      /*
8787        Annotate the image with text.
8788      */
8789      status=XAnnotateEditImage(display,resource_info,windows,*image,exception);
8790      if (status == MagickFalse)
8791        {
8792          XNoticeWidget(display,windows,"Unable to annotate X image",
8793            (*image)->filename);
8794          break;
8795        }
8796      break;
8797    }
8798    case DrawCommand:
8799    {
8800      /*
8801        Draw image.
8802      */
8803      status=XDrawEditImage(display,resource_info,windows,image,exception);
8804      if (status == MagickFalse)
8805        {
8806          XNoticeWidget(display,windows,"Unable to draw on the X image",
8807            (*image)->filename);
8808          break;
8809        }
8810      break;
8811    }
8812    case ColorCommand:
8813    {
8814      /*
8815        Color edit.
8816      */
8817      status=XColorEditImage(display,resource_info,windows,image,exception);
8818      if (status == MagickFalse)
8819        {
8820          XNoticeWidget(display,windows,"Unable to pixel edit X image",
8821            (*image)->filename);
8822          break;
8823        }
8824      break;
8825    }
8826    case MatteCommand:
8827    {
8828      /*
8829        Matte edit.
8830      */
8831      status=XMatteEditImage(display,resource_info,windows,image,exception);
8832      if (status == MagickFalse)
8833        {
8834          XNoticeWidget(display,windows,"Unable to matte edit X image",
8835            (*image)->filename);
8836          break;
8837        }
8838      break;
8839    }
8840    case CompositeCommand:
8841    {
8842      /*
8843        Composite image.
8844      */
8845      status=XCompositeImage(display,resource_info,windows,*image,
8846        exception);
8847      if (status == MagickFalse)
8848        {
8849          XNoticeWidget(display,windows,"Unable to composite X image",
8850            (*image)->filename);
8851          break;
8852        }
8853      break;
8854    }
8855    case AddBorderCommand:
8856    {
8857      Image
8858        *border_image;
8859
8860      static char
8861        geometry[MaxTextExtent] = "6x6";
8862
8863      /*
8864        Query user for border color and geometry.
8865      */
8866      XColorBrowserWidget(display,windows,"Select",color);
8867      if (*color == '\0')
8868        break;
8869      (void) XDialogWidget(display,windows,"Add Border",
8870        "Enter border geometry:",geometry);
8871      if (*geometry == '\0')
8872        break;
8873      /*
8874        Add a border to the image.
8875      */
8876      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8877        exception);
8878      XSetCursorState(display,windows,MagickTrue);
8879      XCheckRefreshWindows(display,windows);
8880      (void) QueryColorCompliance(color,AllCompliance,&(*image)->border_color,
8881        exception);
8882      (void) ParsePageGeometry(*image,geometry,&page_geometry,
8883        exception);
8884      border_image=BorderImage(*image,&page_geometry,(*image)->compose,
8885        exception);
8886      if (border_image != (Image *) NULL)
8887        {
8888          *image=DestroyImage(*image);
8889          *image=border_image;
8890        }
8891      CatchException(exception);
8892      XSetCursorState(display,windows,MagickFalse);
8893      if (windows->image.orphan != MagickFalse)
8894        break;
8895      windows->image.window_changes.width=(int) (*image)->columns;
8896      windows->image.window_changes.height=(int) (*image)->rows;
8897      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8898      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8899      break;
8900    }
8901    case AddFrameCommand:
8902    {
8903      FrameInfo
8904        frame_info;
8905
8906      Image
8907        *frame_image;
8908
8909      static char
8910        geometry[MaxTextExtent] = "6x6";
8911
8912      /*
8913        Query user for frame color and geometry.
8914      */
8915      XColorBrowserWidget(display,windows,"Select",color);
8916      if (*color == '\0')
8917        break;
8918      (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8919        geometry);
8920      if (*geometry == '\0')
8921        break;
8922      /*
8923        Surround image with an ornamental border.
8924      */
8925      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8926        exception);
8927      XSetCursorState(display,windows,MagickTrue);
8928      XCheckRefreshWindows(display,windows);
8929      (void) QueryColorCompliance(color,AllCompliance,&(*image)->matte_color,
8930        exception);
8931      (void) ParsePageGeometry(*image,geometry,&page_geometry,
8932        exception);
8933      frame_info.width=page_geometry.width;
8934      frame_info.height=page_geometry.height;
8935      frame_info.outer_bevel=page_geometry.x;
8936      frame_info.inner_bevel=page_geometry.y;
8937      frame_info.x=(ssize_t) frame_info.width;
8938      frame_info.y=(ssize_t) frame_info.height;
8939      frame_info.width=(*image)->columns+2*frame_info.width;
8940      frame_info.height=(*image)->rows+2*frame_info.height;
8941      frame_image=FrameImage(*image,&frame_info,(*image)->compose,exception);
8942      if (frame_image != (Image *) NULL)
8943        {
8944          *image=DestroyImage(*image);
8945          *image=frame_image;
8946        }
8947      CatchException(exception);
8948      XSetCursorState(display,windows,MagickFalse);
8949      if (windows->image.orphan != MagickFalse)
8950        break;
8951      windows->image.window_changes.width=(int) (*image)->columns;
8952      windows->image.window_changes.height=(int) (*image)->rows;
8953      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8954      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8955      break;
8956    }
8957    case CommentCommand:
8958    {
8959      const char
8960        *value;
8961
8962      FILE
8963        *file;
8964
8965      int
8966        unique_file;
8967
8968      /*
8969        Edit image comment.
8970      */
8971      unique_file=AcquireUniqueFileResource(image_info->filename);
8972      if (unique_file == -1)
8973        XNoticeWidget(display,windows,"Unable to edit image comment",
8974          image_info->filename);
8975      value=GetImageProperty(*image,"comment",exception);
8976      if (value == (char *) NULL)
8977        unique_file=close(unique_file)-1;
8978      else
8979        {
8980          register const char
8981            *p;
8982
8983          file=fdopen(unique_file,"w");
8984          if (file == (FILE *) NULL)
8985            {
8986              XNoticeWidget(display,windows,"Unable to edit image comment",
8987                image_info->filename);
8988              break;
8989            }
8990          for (p=value; *p != '\0'; p++)
8991            (void) fputc((int) *p,file);
8992          (void) fputc('\n',file);
8993          (void) fclose(file);
8994        }
8995      XSetCursorState(display,windows,MagickTrue);
8996      XCheckRefreshWindows(display,windows);
8997      status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
8998        exception);
8999      if (status == MagickFalse)
9000        XNoticeWidget(display,windows,"Unable to edit image comment",
9001          (char *) NULL);
9002      else
9003        {
9004          char
9005            *comment;
9006
9007          comment=FileToString(image_info->filename,~0UL,exception);
9008          if (comment != (char *) NULL)
9009            {
9010              (void) SetImageProperty(*image,"comment",comment,exception);
9011              (*image)->taint=MagickTrue;
9012            }
9013        }
9014      (void) RelinquishUniqueFileResource(image_info->filename);
9015      XSetCursorState(display,windows,MagickFalse);
9016      break;
9017    }
9018    case LaunchCommand:
9019    {
9020      /*
9021        Launch program.
9022      */
9023      XSetCursorState(display,windows,MagickTrue);
9024      XCheckRefreshWindows(display,windows);
9025      (void) AcquireUniqueFilename(filename);
9026      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"launch:%s",
9027        filename);
9028      status=WriteImage(image_info,*image,exception);
9029      if (status == MagickFalse)
9030        XNoticeWidget(display,windows,"Unable to launch image editor",
9031          (char *) NULL);
9032      else
9033        {
9034          nexus=ReadImage(resource_info->image_info,exception);
9035          CatchException(exception);
9036          XClientMessage(display,windows->image.id,windows->im_protocols,
9037            windows->im_next_image,CurrentTime);
9038        }
9039      (void) RelinquishUniqueFileResource(filename);
9040      XSetCursorState(display,windows,MagickFalse);
9041      break;
9042    }
9043    case RegionofInterestCommand:
9044    {
9045      /*
9046        Apply an image processing technique to a region of interest.
9047      */
9048      (void) XROIImage(display,resource_info,windows,image,exception);
9049      break;
9050    }
9051    case InfoCommand:
9052      break;
9053    case ZoomCommand:
9054    {
9055      /*
9056        Zoom image.
9057      */
9058      if (windows->magnify.mapped != MagickFalse)
9059        (void) XRaiseWindow(display,windows->magnify.id);
9060      else
9061        {
9062          /*
9063            Make magnify image.
9064          */
9065          XSetCursorState(display,windows,MagickTrue);
9066          (void) XMapRaised(display,windows->magnify.id);
9067          XSetCursorState(display,windows,MagickFalse);
9068        }
9069      break;
9070    }
9071    case ShowPreviewCommand:
9072    {
9073      char
9074        **previews;
9075
9076      Image
9077        *preview_image;
9078
9079      static char
9080        preview_type[MaxTextExtent] = "Gamma";
9081
9082      /*
9083        Select preview type from menu.
9084      */
9085      previews=GetCommandOptions(MagickPreviewOptions);
9086      if (previews == (char **) NULL)
9087        break;
9088      XListBrowserWidget(display,windows,&windows->widget,
9089        (const char **) previews,"Preview",
9090        "Select an enhancement, effect, or F/X:",preview_type);
9091      previews=DestroyStringList(previews);
9092      if (*preview_type == '\0')
9093        break;
9094      /*
9095        Show image preview.
9096      */
9097      XSetCursorState(display,windows,MagickTrue);
9098      XCheckRefreshWindows(display,windows);
9099      image_info->preview_type=(PreviewType)
9100        ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
9101      image_info->group=(ssize_t) windows->image.id;
9102      (void) DeleteImageProperty(*image,"label");
9103      (void) SetImageProperty(*image,"label","Preview",exception);
9104      (void) AcquireUniqueFilename(filename);
9105      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"preview:%s",
9106        filename);
9107      status=WriteImage(image_info,*image,exception);
9108      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9109      preview_image=ReadImage(image_info,exception);
9110      (void) RelinquishUniqueFileResource(filename);
9111      if (preview_image == (Image *) NULL)
9112        break;
9113      (void) FormatLocaleString(preview_image->filename,MaxTextExtent,"show:%s",
9114        filename);
9115      status=WriteImage(image_info,preview_image,exception);
9116      preview_image=DestroyImage(preview_image);
9117      if (status == MagickFalse)
9118        XNoticeWidget(display,windows,"Unable to show image preview",
9119          (*image)->filename);
9120      XDelay(display,1500);
9121      XSetCursorState(display,windows,MagickFalse);
9122      break;
9123    }
9124    case ShowHistogramCommand:
9125    {
9126      Image
9127        *histogram_image;
9128
9129      /*
9130        Show image histogram.
9131      */
9132      XSetCursorState(display,windows,MagickTrue);
9133      XCheckRefreshWindows(display,windows);
9134      image_info->group=(ssize_t) windows->image.id;
9135      (void) DeleteImageProperty(*image,"label");
9136      (void) SetImageProperty(*image,"label","Histogram",exception);
9137      (void) AcquireUniqueFilename(filename);
9138      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"histogram:%s",
9139        filename);
9140      status=WriteImage(image_info,*image,exception);
9141      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9142      histogram_image=ReadImage(image_info,exception);
9143      (void) RelinquishUniqueFileResource(filename);
9144      if (histogram_image == (Image *) NULL)
9145        break;
9146      (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
9147        "show:%s",filename);
9148      status=WriteImage(image_info,histogram_image,exception);
9149      histogram_image=DestroyImage(histogram_image);
9150      if (status == MagickFalse)
9151        XNoticeWidget(display,windows,"Unable to show histogram",
9152          (*image)->filename);
9153      XDelay(display,1500);
9154      XSetCursorState(display,windows,MagickFalse);
9155      break;
9156    }
9157    case ShowMatteCommand:
9158    {
9159      Image
9160        *matte_image;
9161
9162      if ((*image)->matte == MagickFalse)
9163        {
9164          XNoticeWidget(display,windows,
9165            "Image does not have any matte information",(*image)->filename);
9166          break;
9167        }
9168      /*
9169        Show image matte.
9170      */
9171      XSetCursorState(display,windows,MagickTrue);
9172      XCheckRefreshWindows(display,windows);
9173      image_info->group=(ssize_t) windows->image.id;
9174      (void) DeleteImageProperty(*image,"label");
9175      (void) SetImageProperty(*image,"label","Matte",exception);
9176      (void) AcquireUniqueFilename(filename);
9177      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"matte:%s",
9178        filename);
9179      status=WriteImage(image_info,*image,exception);
9180      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9181      matte_image=ReadImage(image_info,exception);
9182      (void) RelinquishUniqueFileResource(filename);
9183      if (matte_image == (Image *) NULL)
9184        break;
9185      (void) FormatLocaleString(matte_image->filename,MaxTextExtent,"show:%s",
9186        filename);
9187      status=WriteImage(image_info,matte_image,exception);
9188      matte_image=DestroyImage(matte_image);
9189      if (status == MagickFalse)
9190        XNoticeWidget(display,windows,"Unable to show matte",
9191          (*image)->filename);
9192      XDelay(display,1500);
9193      XSetCursorState(display,windows,MagickFalse);
9194      break;
9195    }
9196    case BackgroundCommand:
9197    {
9198      /*
9199        Background image.
9200      */
9201      status=XBackgroundImage(display,resource_info,windows,image,exception);
9202      if (status == MagickFalse)
9203        break;
9204      nexus=CloneImage(*image,0,0,MagickTrue,exception);
9205      if (nexus != (Image *) NULL)
9206        XClientMessage(display,windows->image.id,windows->im_protocols,
9207          windows->im_next_image,CurrentTime);
9208      break;
9209    }
9210    case SlideShowCommand:
9211    {
9212      static char
9213        delay[MaxTextExtent] = "5";
9214
9215      /*
9216        Display next image after pausing.
9217      */
9218      (void) XDialogWidget(display,windows,"Slide Show",
9219        "Pause how many 1/100ths of a second between images:",delay);
9220      if (*delay == '\0')
9221        break;
9222      resource_info->delay=StringToUnsignedLong(delay);
9223      XClientMessage(display,windows->image.id,windows->im_protocols,
9224        windows->im_next_image,CurrentTime);
9225      break;
9226    }
9227    case PreferencesCommand:
9228    {
9229      /*
9230        Set user preferences.
9231      */
9232      status=XPreferencesWidget(display,resource_info,windows);
9233      if (status == MagickFalse)
9234        break;
9235      nexus=CloneImage(*image,0,0,MagickTrue,exception);
9236      if (nexus != (Image *) NULL)
9237        XClientMessage(display,windows->image.id,windows->im_protocols,
9238          windows->im_next_image,CurrentTime);
9239      break;
9240    }
9241    case HelpCommand:
9242    {
9243      /*
9244        User requested help.
9245      */
9246      XTextViewWidget(display,resource_info,windows,MagickFalse,
9247        "Help Viewer - Display",DisplayHelp);
9248      break;
9249    }
9250    case BrowseDocumentationCommand:
9251    {
9252      Atom
9253        mozilla_atom;
9254
9255      Window
9256        mozilla_window,
9257        root_window;
9258
9259      /*
9260        Browse the ImageMagick documentation.
9261      */
9262      root_window=XRootWindow(display,XDefaultScreen(display));
9263      mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9264      mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9265      if (mozilla_window != (Window) NULL)
9266        {
9267          char
9268            command[MaxTextExtent],
9269            *url;
9270
9271          /*
9272            Display documentation using Netscape remote control.
9273          */
9274          url=GetMagickHomeURL();
9275          (void) FormatLocaleString(command,MaxTextExtent,
9276            "openurl(%s,new-tab)",url);
9277          url=DestroyString(url);
9278          mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9279          (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9280            8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9281          XSetCursorState(display,windows,MagickFalse);
9282          break;
9283        }
9284      XSetCursorState(display,windows,MagickTrue);
9285      XCheckRefreshWindows(display,windows);
9286      status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9287        exception);
9288      if (status == MagickFalse)
9289        XNoticeWidget(display,windows,"Unable to browse documentation",
9290          (char *) NULL);
9291      XDelay(display,1500);
9292      XSetCursorState(display,windows,MagickFalse);
9293      break;
9294    }
9295    case VersionCommand:
9296    {
9297      XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9298        GetMagickCopyright());
9299      break;
9300    }
9301    case SaveToUndoBufferCommand:
9302      break;
9303    default:
9304    {
9305      (void) XBell(display,0);
9306      break;
9307    }
9308  }
9309  image_info=DestroyImageInfo(image_info);
9310  return(nexus);
9311}
9312
9313/*
9314%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9315%                                                                             %
9316%                                                                             %
9317%                                                                             %
9318+   X M a g n i f y I m a g e                                                 %
9319%                                                                             %
9320%                                                                             %
9321%                                                                             %
9322%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9323%
9324%  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9325%  The magnified portion is displayed in a separate window.
9326%
9327%  The format of the XMagnifyImage method is:
9328%
9329%      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9330%        ExceptionInfo *exception)
9331%
9332%  A description of each parameter follows:
9333%
9334%    o display: Specifies a connection to an X server;  returned from
9335%      XOpenDisplay.
9336%
9337%    o windows: Specifies a pointer to a XWindows structure.
9338%
9339%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
9340%      the entire image is refreshed.
9341%
9342%    o exception: return any errors or warnings in this structure.
9343%
9344*/
9345static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9346  ExceptionInfo *exception)
9347{
9348  char
9349    text[MaxTextExtent];
9350
9351  register int
9352    x,
9353    y;
9354
9355  size_t
9356    state;
9357
9358  /*
9359    Update magnified image until the mouse button is released.
9360  */
9361  (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9362  state=DefaultState;
9363  x=event->xbutton.x;
9364  y=event->xbutton.y;
9365  windows->magnify.x=(int) windows->image.x+x;
9366  windows->magnify.y=(int) windows->image.y+y;
9367  do
9368  {
9369    /*
9370      Map and unmap Info widget as text cursor crosses its boundaries.
9371    */
9372    if (windows->info.mapped != MagickFalse)
9373      {
9374        if ((x < (int) (windows->info.x+windows->info.width)) &&
9375            (y < (int) (windows->info.y+windows->info.height)))
9376          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9377      }
9378    else
9379      if ((x > (int) (windows->info.x+windows->info.width)) ||
9380          (y > (int) (windows->info.y+windows->info.height)))
9381        (void) XMapWindow(display,windows->info.id);
9382    if (windows->info.mapped != MagickFalse)
9383      {
9384        /*
9385          Display pointer position.
9386        */
9387        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9388          windows->magnify.x,windows->magnify.y);
9389        XInfoWidget(display,windows,text);
9390      }
9391    /*
9392      Wait for next event.
9393    */
9394    XScreenEvent(display,windows,event,exception);
9395    switch (event->type)
9396    {
9397      case ButtonPress:
9398        break;
9399      case ButtonRelease:
9400      {
9401        /*
9402          User has finished magnifying image.
9403        */
9404        x=event->xbutton.x;
9405        y=event->xbutton.y;
9406        state|=ExitState;
9407        break;
9408      }
9409      case Expose:
9410        break;
9411      case MotionNotify:
9412      {
9413        x=event->xmotion.x;
9414        y=event->xmotion.y;
9415        break;
9416      }
9417      default:
9418        break;
9419    }
9420    /*
9421      Check boundary conditions.
9422    */
9423    if (x < 0)
9424      x=0;
9425    else
9426      if (x >= (int) windows->image.width)
9427        x=(int) windows->image.width-1;
9428    if (y < 0)
9429      y=0;
9430    else
9431     if (y >= (int) windows->image.height)
9432       y=(int) windows->image.height-1;
9433  } while ((state & ExitState) == 0);
9434  /*
9435    Display magnified image.
9436  */
9437  XSetCursorState(display,windows,MagickFalse);
9438}
9439
9440/*
9441%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9442%                                                                             %
9443%                                                                             %
9444%                                                                             %
9445+   X M a g n i f y W i n d o w C o m m a n d                                 %
9446%                                                                             %
9447%                                                                             %
9448%                                                                             %
9449%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9450%
9451%  XMagnifyWindowCommand() moves the image within an Magnify window by one
9452%  pixel as specified by the key symbol.
9453%
9454%  The format of the XMagnifyWindowCommand method is:
9455%
9456%      void XMagnifyWindowCommand(Display *display,XWindows *windows,
9457%        const MagickStatusType state,const KeySym key_symbol,
9458%        ExceptionInfo *exception)
9459%
9460%  A description of each parameter follows:
9461%
9462%    o display: Specifies a connection to an X server; returned from
9463%      XOpenDisplay.
9464%
9465%    o windows: Specifies a pointer to a XWindows structure.
9466%
9467%    o state: key mask.
9468%
9469%    o key_symbol: Specifies a KeySym which indicates which side of the image
9470%      to trim.
9471%
9472%    o exception: return any errors or warnings in this structure.
9473%
9474*/
9475static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9476  const MagickStatusType state,const KeySym key_symbol,ExceptionInfo *exception)
9477{
9478  unsigned int
9479    quantum;
9480
9481  /*
9482    User specified a magnify factor or position.
9483  */
9484  quantum=1;
9485  if ((state & Mod1Mask) != 0)
9486    quantum=10;
9487  switch ((int) key_symbol)
9488  {
9489    case QuitCommand:
9490    {
9491      (void) XWithdrawWindow(display,windows->magnify.id,
9492        windows->magnify.screen);
9493      break;
9494    }
9495    case XK_Home:
9496    case XK_KP_Home:
9497    {
9498      windows->magnify.x=(int) windows->image.width/2;
9499      windows->magnify.y=(int) windows->image.height/2;
9500      break;
9501    }
9502    case XK_Left:
9503    case XK_KP_Left:
9504    {
9505      if (windows->magnify.x > 0)
9506        windows->magnify.x-=quantum;
9507      break;
9508    }
9509    case XK_Up:
9510    case XK_KP_Up:
9511    {
9512      if (windows->magnify.y > 0)
9513        windows->magnify.y-=quantum;
9514      break;
9515    }
9516    case XK_Right:
9517    case XK_KP_Right:
9518    {
9519      if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9520        windows->magnify.x+=quantum;
9521      break;
9522    }
9523    case XK_Down:
9524    case XK_KP_Down:
9525    {
9526      if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9527        windows->magnify.y+=quantum;
9528      break;
9529    }
9530    case XK_0:
9531    case XK_1:
9532    case XK_2:
9533    case XK_3:
9534    case XK_4:
9535    case XK_5:
9536    case XK_6:
9537    case XK_7:
9538    case XK_8:
9539    case XK_9:
9540    {
9541      windows->magnify.data=(key_symbol-XK_0);
9542      break;
9543    }
9544    case XK_KP_0:
9545    case XK_KP_1:
9546    case XK_KP_2:
9547    case XK_KP_3:
9548    case XK_KP_4:
9549    case XK_KP_5:
9550    case XK_KP_6:
9551    case XK_KP_7:
9552    case XK_KP_8:
9553    case XK_KP_9:
9554    {
9555      windows->magnify.data=(key_symbol-XK_KP_0);
9556      break;
9557    }
9558    default:
9559      break;
9560  }
9561  XMakeMagnifyImage(display,windows,exception);
9562}
9563
9564/*
9565%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9566%                                                                             %
9567%                                                                             %
9568%                                                                             %
9569+   X M a k e P a n I m a g e                                                 %
9570%                                                                             %
9571%                                                                             %
9572%                                                                             %
9573%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9574%
9575%  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9576%  icon window.
9577%
9578%  The format of the XMakePanImage method is:
9579%
9580%        void XMakePanImage(Display *display,XResourceInfo *resource_info,
9581%          XWindows *windows,Image *image,ExceptionInfo *exception)
9582%
9583%  A description of each parameter follows:
9584%
9585%    o display: Specifies a connection to an X server;  returned from
9586%      XOpenDisplay.
9587%
9588%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9589%
9590%    o windows: Specifies a pointer to a XWindows structure.
9591%
9592%    o image: the image.
9593%
9594%    o exception: return any errors or warnings in this structure.
9595%
9596*/
9597static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9598  XWindows *windows,Image *image,ExceptionInfo *exception)
9599{
9600  MagickStatusType
9601    status;
9602
9603  /*
9604    Create and display image for panning icon.
9605  */
9606  XSetCursorState(display,windows,MagickTrue);
9607  XCheckRefreshWindows(display,windows);
9608  windows->pan.x=(int) windows->image.x;
9609  windows->pan.y=(int) windows->image.y;
9610  status=XMakeImage(display,resource_info,&windows->pan,image,
9611    windows->pan.width,windows->pan.height,exception);
9612  if (status == MagickFalse)
9613    ThrowXWindowFatalException(ResourceLimitError,
9614     "MemoryAllocationFailed",image->filename);
9615  (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9616    windows->pan.pixmap);
9617  (void) XClearWindow(display,windows->pan.id);
9618  XDrawPanRectangle(display,windows);
9619  XSetCursorState(display,windows,MagickFalse);
9620}
9621
9622/*
9623%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9624%                                                                             %
9625%                                                                             %
9626%                                                                             %
9627+   X M a t t a E d i t I m a g e                                             %
9628%                                                                             %
9629%                                                                             %
9630%                                                                             %
9631%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9632%
9633%  XMatteEditImage() allows the user to interactively change the Matte channel
9634%  of an image.  If the image is PseudoClass it is promoted to DirectClass
9635%  before the matte information is stored.
9636%
9637%  The format of the XMatteEditImage method is:
9638%
9639%      MagickBooleanType XMatteEditImage(Display *display,
9640%        XResourceInfo *resource_info,XWindows *windows,Image **image,
9641%        ExceptionInfo *exception)
9642%
9643%  A description of each parameter follows:
9644%
9645%    o display: Specifies a connection to an X server;  returned from
9646%      XOpenDisplay.
9647%
9648%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9649%
9650%    o windows: Specifies a pointer to a XWindows structure.
9651%
9652%    o image: the image; returned from ReadImage.
9653%
9654%    o exception: return any errors or warnings in this structure.
9655%
9656*/
9657static MagickBooleanType XMatteEditImage(Display *display,
9658  XResourceInfo *resource_info,XWindows *windows,Image **image,
9659  ExceptionInfo *exception)
9660{
9661  static char
9662    matte[MaxTextExtent] = "0";
9663
9664  static const char
9665    *MatteEditMenu[] =
9666    {
9667      "Method",
9668      "Border Color",
9669      "Fuzz",
9670      "Matte Value",
9671      "Undo",
9672      "Help",
9673      "Dismiss",
9674      (char *) NULL
9675    };
9676
9677  static const ModeType
9678    MatteEditCommands[] =
9679    {
9680      MatteEditMethod,
9681      MatteEditBorderCommand,
9682      MatteEditFuzzCommand,
9683      MatteEditValueCommand,
9684      MatteEditUndoCommand,
9685      MatteEditHelpCommand,
9686      MatteEditDismissCommand
9687    };
9688
9689  static PaintMethod
9690    method = PointMethod;
9691
9692  static XColor
9693    border_color = { 0, 0, 0, 0, 0, 0 };
9694
9695  char
9696    command[MaxTextExtent],
9697    text[MaxTextExtent];
9698
9699  Cursor
9700    cursor;
9701
9702  int
9703    entry,
9704    id,
9705    x,
9706    x_offset,
9707    y,
9708    y_offset;
9709
9710  register int
9711    i;
9712
9713  register Quantum
9714    *q;
9715
9716  unsigned int
9717    height,
9718    width;
9719
9720  size_t
9721    state;
9722
9723  XEvent
9724    event;
9725
9726  /*
9727    Map Command widget.
9728  */
9729  (void) CloneString(&windows->command.name,"Matte Edit");
9730  windows->command.data=4;
9731  (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9732  (void) XMapRaised(display,windows->command.id);
9733  XClientMessage(display,windows->image.id,windows->im_protocols,
9734    windows->im_update_widget,CurrentTime);
9735  /*
9736    Make cursor.
9737  */
9738  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9739    resource_info->background_color,resource_info->foreground_color);
9740  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9741  /*
9742    Track pointer until button 1 is pressed.
9743  */
9744  XQueryPosition(display,windows->image.id,&x,&y);
9745  (void) XSelectInput(display,windows->image.id,
9746    windows->image.attributes.event_mask | PointerMotionMask);
9747  state=DefaultState;
9748  do
9749  {
9750    if (windows->info.mapped != MagickFalse)
9751      {
9752        /*
9753          Display pointer position.
9754        */
9755        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9756          x+windows->image.x,y+windows->image.y);
9757        XInfoWidget(display,windows,text);
9758      }
9759    /*
9760      Wait for next event.
9761    */
9762    XScreenEvent(display,windows,&event,exception);
9763    if (event.xany.window == windows->command.id)
9764      {
9765        /*
9766          Select a command from the Command widget.
9767        */
9768        id=XCommandWidget(display,windows,MatteEditMenu,&event);
9769        if (id < 0)
9770          {
9771            (void) XCheckDefineCursor(display,windows->image.id,cursor);
9772            continue;
9773          }
9774        switch (MatteEditCommands[id])
9775        {
9776          case MatteEditMethod:
9777          {
9778            char
9779              **methods;
9780
9781            /*
9782              Select a method from the pop-up menu.
9783            */
9784            methods=GetCommandOptions(MagickMethodOptions);
9785            if (methods == (char **) NULL)
9786              break;
9787            entry=XMenuWidget(display,windows,MatteEditMenu[id],
9788              (const char **) methods,command);
9789            if (entry >= 0)
9790              method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9791                MagickFalse,methods[entry]);
9792            methods=DestroyStringList(methods);
9793            break;
9794          }
9795          case MatteEditBorderCommand:
9796          {
9797            const char
9798              *ColorMenu[MaxNumberPens];
9799
9800            int
9801              pen_number;
9802
9803            /*
9804              Initialize menu selections.
9805            */
9806            for (i=0; i < (int) (MaxNumberPens-2); i++)
9807              ColorMenu[i]=resource_info->pen_colors[i];
9808            ColorMenu[MaxNumberPens-2]="Browser...";
9809            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9810            /*
9811              Select a pen color from the pop-up menu.
9812            */
9813            pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9814              (const char **) ColorMenu,command);
9815            if (pen_number < 0)
9816              break;
9817            if (pen_number == (MaxNumberPens-2))
9818              {
9819                static char
9820                  color_name[MaxTextExtent] = "gray";
9821
9822                /*
9823                  Select a pen color from a dialog.
9824                */
9825                resource_info->pen_colors[pen_number]=color_name;
9826                XColorBrowserWidget(display,windows,"Select",color_name);
9827                if (*color_name == '\0')
9828                  break;
9829              }
9830            /*
9831              Set border color.
9832            */
9833            (void) XParseColor(display,windows->map_info->colormap,
9834              resource_info->pen_colors[pen_number],&border_color);
9835            break;
9836          }
9837          case MatteEditFuzzCommand:
9838          {
9839            static char
9840              fuzz[MaxTextExtent];
9841
9842            static const char
9843              *FuzzMenu[] =
9844              {
9845                "0%",
9846                "2%",
9847                "5%",
9848                "10%",
9849                "15%",
9850                "Dialog...",
9851                (char *) NULL,
9852              };
9853
9854            /*
9855              Select a command from the pop-up menu.
9856            */
9857            entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9858              command);
9859            if (entry < 0)
9860              break;
9861            if (entry != 5)
9862              {
9863                (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
9864                  QuantumRange+1.0);
9865                break;
9866              }
9867            (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9868            (void) XDialogWidget(display,windows,"Ok",
9869              "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9870            if (*fuzz == '\0')
9871              break;
9872            (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
9873            (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
9874              1.0);
9875            break;
9876          }
9877          case MatteEditValueCommand:
9878          {
9879            static char
9880              message[MaxTextExtent];
9881
9882            static const char
9883              *MatteMenu[] =
9884              {
9885                "Opaque",
9886                "Transparent",
9887                "Dialog...",
9888                (char *) NULL,
9889              };
9890
9891            /*
9892              Select a command from the pop-up menu.
9893            */
9894            entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9895              command);
9896            if (entry < 0)
9897              break;
9898            if (entry != 2)
9899              {
9900                (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9901                  OpaqueAlpha);
9902                if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9903                  (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9904                    (Quantum) TransparentAlpha);
9905                break;
9906              }
9907            (void) FormatLocaleString(message,MaxTextExtent,
9908              "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9909              QuantumRange);
9910            (void) XDialogWidget(display,windows,"Matte",message,matte);
9911            if (*matte == '\0')
9912              break;
9913            break;
9914          }
9915          case MatteEditUndoCommand:
9916          {
9917            (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9918              image,exception);
9919            break;
9920          }
9921          case MatteEditHelpCommand:
9922          {
9923            XTextViewWidget(display,resource_info,windows,MagickFalse,
9924              "Help Viewer - Matte Edit",ImageMatteEditHelp);
9925            break;
9926          }
9927          case MatteEditDismissCommand:
9928          {
9929            /*
9930              Prematurely exit.
9931            */
9932            state|=EscapeState;
9933            state|=ExitState;
9934            break;
9935          }
9936          default:
9937            break;
9938        }
9939        (void) XCheckDefineCursor(display,windows->image.id,cursor);
9940        continue;
9941      }
9942    switch (event.type)
9943    {
9944      case ButtonPress:
9945      {
9946        if (event.xbutton.button != Button1)
9947          break;
9948        if ((event.xbutton.window != windows->image.id) &&
9949            (event.xbutton.window != windows->magnify.id))
9950          break;
9951        /*
9952          Update matte data.
9953        */
9954        x=event.xbutton.x;
9955        y=event.xbutton.y;
9956        (void) XMagickCommand(display,resource_info,windows,
9957          SaveToUndoBufferCommand,image,exception);
9958        state|=UpdateConfigurationState;
9959        break;
9960      }
9961      case ButtonRelease:
9962      {
9963        if (event.xbutton.button != Button1)
9964          break;
9965        if ((event.xbutton.window != windows->image.id) &&
9966            (event.xbutton.window != windows->magnify.id))
9967          break;
9968        /*
9969          Update colormap information.
9970        */
9971        x=event.xbutton.x;
9972        y=event.xbutton.y;
9973        XConfigureImageColormap(display,resource_info,windows,*image,exception);
9974        (void) XConfigureImage(display,resource_info,windows,*image,exception);
9975        XInfoWidget(display,windows,text);
9976        (void) XCheckDefineCursor(display,windows->image.id,cursor);
9977        state&=(~UpdateConfigurationState);
9978        break;
9979      }
9980      case Expose:
9981        break;
9982      case KeyPress:
9983      {
9984        char
9985          command[MaxTextExtent];
9986
9987        KeySym
9988          key_symbol;
9989
9990        if (event.xkey.window == windows->magnify.id)
9991          {
9992            Window
9993              window;
9994
9995            window=windows->magnify.id;
9996            while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9997          }
9998        if (event.xkey.window != windows->image.id)
9999          break;
10000        /*
10001          Respond to a user key press.
10002        */
10003        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
10004          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10005        switch ((int) key_symbol)
10006        {
10007          case XK_Escape:
10008          case XK_F20:
10009          {
10010            /*
10011              Prematurely exit.
10012            */
10013            state|=ExitState;
10014            break;
10015          }
10016          case XK_F1:
10017          case XK_Help:
10018          {
10019            XTextViewWidget(display,resource_info,windows,MagickFalse,
10020              "Help Viewer - Matte Edit",ImageMatteEditHelp);
10021            break;
10022          }
10023          default:
10024          {
10025            (void) XBell(display,0);
10026            break;
10027          }
10028        }
10029        break;
10030      }
10031      case MotionNotify:
10032      {
10033        /*
10034          Map and unmap Info widget as cursor crosses its boundaries.
10035        */
10036        x=event.xmotion.x;
10037        y=event.xmotion.y;
10038        if (windows->info.mapped != MagickFalse)
10039          {
10040            if ((x < (int) (windows->info.x+windows->info.width)) &&
10041                (y < (int) (windows->info.y+windows->info.height)))
10042              (void) XWithdrawWindow(display,windows->info.id,
10043                windows->info.screen);
10044          }
10045        else
10046          if ((x > (int) (windows->info.x+windows->info.width)) ||
10047              (y > (int) (windows->info.y+windows->info.height)))
10048            (void) XMapWindow(display,windows->info.id);
10049        break;
10050      }
10051      default:
10052        break;
10053    }
10054    if (event.xany.window == windows->magnify.id)
10055      {
10056        x=windows->magnify.x-windows->image.x;
10057        y=windows->magnify.y-windows->image.y;
10058      }
10059    x_offset=x;
10060    y_offset=y;
10061    if ((state & UpdateConfigurationState) != 0)
10062      {
10063        CacheView
10064          *image_view;
10065
10066        int
10067          x,
10068          y;
10069
10070        /*
10071          Matte edit is relative to image configuration.
10072        */
10073        (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
10074          MagickTrue);
10075        XPutPixel(windows->image.ximage,x_offset,y_offset,
10076          windows->pixel_info->background_color.pixel);
10077        width=(unsigned int) (*image)->columns;
10078        height=(unsigned int) (*image)->rows;
10079        x=0;
10080        y=0;
10081        if (windows->image.crop_geometry != (char *) NULL)
10082          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
10083            &height);
10084        x_offset=(int) (width*(windows->image.x+x_offset)/
10085          windows->image.ximage->width+x);
10086        y_offset=(int) (height*(windows->image.y+y_offset)/
10087          windows->image.ximage->height+y);
10088        if ((x_offset < 0) || (y_offset < 0))
10089          continue;
10090        if ((x_offset >= (int) (*image)->columns) ||
10091            (y_offset >= (int) (*image)->rows))
10092          continue;
10093        if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10094          return(MagickFalse);
10095        if ((*image)->matte == MagickFalse)
10096          (void) SetImageAlphaChannel(*image,OpaqueAlphaChannel,exception);
10097        image_view=AcquireCacheView(*image);
10098        switch (method)
10099        {
10100          case PointMethod:
10101          default:
10102          {
10103            /*
10104              Update matte information using point algorithm.
10105            */
10106            q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10107              (ssize_t) y_offset,1,1,exception);
10108            if (q == (Quantum *) NULL)
10109              break;
10110            SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10111            (void) SyncCacheViewAuthenticPixels(image_view,exception);
10112            break;
10113          }
10114          case ReplaceMethod:
10115          {
10116            PixelInfo
10117              pixel,
10118              target;
10119
10120            /*
10121              Update matte information using replace algorithm.
10122            */
10123            (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
10124              x_offset,(ssize_t) y_offset,&target,exception);
10125            for (y=0; y < (int) (*image)->rows; y++)
10126            {
10127              q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10128                (*image)->columns,1,exception);
10129              if (q == (Quantum *) NULL)
10130                break;
10131              for (x=0; x < (int) (*image)->columns; x++)
10132              {
10133                GetPixelInfoPixel(*image,q,&pixel);
10134                if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
10135                  SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10136                q+=GetPixelChannels(*image);
10137              }
10138              if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10139                break;
10140            }
10141            break;
10142          }
10143          case FloodfillMethod:
10144          case FillToBorderMethod:
10145          {
10146            ChannelType
10147              channel_mask;
10148
10149            DrawInfo
10150              *draw_info;
10151
10152            PixelInfo
10153              target;
10154
10155            /*
10156              Update matte information using floodfill algorithm.
10157            */
10158            (void) GetOneVirtualPixelInfo(*image,
10159              GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
10160              y_offset,&target,exception);
10161            if (method == FillToBorderMethod)
10162              {
10163                target.red=(MagickRealType) ScaleShortToQuantum(
10164                  border_color.red);
10165                target.green=(MagickRealType) ScaleShortToQuantum(
10166                  border_color.green);
10167                target.blue=(MagickRealType) ScaleShortToQuantum(
10168                  border_color.blue);
10169              }
10170            draw_info=CloneDrawInfo(resource_info->image_info,
10171              (DrawInfo *) NULL);
10172            draw_info->fill.alpha=(MagickRealType) ClampToQuantum(
10173              StringToDouble(matte,(char **) NULL));
10174            channel_mask=SetPixelChannelMask(*image,AlphaChannel);
10175            (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10176              x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
10177              MagickFalse : MagickTrue,exception);
10178            (void) SetPixelChannelMapMask(*image,channel_mask);
10179            draw_info=DestroyDrawInfo(draw_info);
10180            break;
10181          }
10182          case ResetMethod:
10183          {
10184            /*
10185              Update matte information using reset algorithm.
10186            */
10187            if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10188              return(MagickFalse);
10189            for (y=0; y < (int) (*image)->rows; y++)
10190            {
10191              q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10192                (*image)->columns,1,exception);
10193              if (q == (Quantum *) NULL)
10194                break;
10195              for (x=0; x < (int) (*image)->columns; x++)
10196              {
10197                SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10198                q+=GetPixelChannels(*image);
10199              }
10200              if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10201                break;
10202            }
10203            if (StringToLong(matte) == (long) OpaqueAlpha)
10204              (*image)->matte=MagickFalse;
10205            break;
10206          }
10207        }
10208        image_view=DestroyCacheView(image_view);
10209        state&=(~UpdateConfigurationState);
10210      }
10211  } while ((state & ExitState) == 0);
10212  (void) XSelectInput(display,windows->image.id,
10213    windows->image.attributes.event_mask);
10214  XSetCursorState(display,windows,MagickFalse);
10215  (void) XFreeCursor(display,cursor);
10216  return(MagickTrue);
10217}
10218
10219/*
10220%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10221%                                                                             %
10222%                                                                             %
10223%                                                                             %
10224+   X O p e n I m a g e                                                       %
10225%                                                                             %
10226%                                                                             %
10227%                                                                             %
10228%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10229%
10230%  XOpenImage() loads an image from a file.
10231%
10232%  The format of the XOpenImage method is:
10233%
10234%     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10235%       XWindows *windows,const unsigned int command)
10236%
10237%  A description of each parameter follows:
10238%
10239%    o display: Specifies a connection to an X server; returned from
10240%      XOpenDisplay.
10241%
10242%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10243%
10244%    o windows: Specifies a pointer to a XWindows structure.
10245%
10246%    o command: A value other than zero indicates that the file is selected
10247%      from the command line argument list.
10248%
10249*/
10250static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10251  XWindows *windows,const MagickBooleanType command)
10252{
10253  const MagickInfo
10254    *magick_info;
10255
10256  ExceptionInfo
10257    *exception;
10258
10259  Image
10260    *nexus;
10261
10262  ImageInfo
10263    *image_info;
10264
10265  static char
10266    filename[MaxTextExtent] = "\0";
10267
10268  /*
10269    Request file name from user.
10270  */
10271  if (command == MagickFalse)
10272    XFileBrowserWidget(display,windows,"Open",filename);
10273  else
10274    {
10275      char
10276        **filelist,
10277        **files;
10278
10279      int
10280        count,
10281        status;
10282
10283      register int
10284        i,
10285        j;
10286
10287      /*
10288        Select next image from the command line.
10289      */
10290      status=XGetCommand(display,windows->image.id,&files,&count);
10291      if (status == 0)
10292        {
10293          ThrowXWindowFatalException(XServerError,"UnableToGetProperty","...");
10294          return((Image *) NULL);
10295        }
10296      filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10297      if (filelist == (char **) NULL)
10298        {
10299          ThrowXWindowFatalException(ResourceLimitError,
10300            "MemoryAllocationFailed","...");
10301          (void) XFreeStringList(files);
10302          return((Image *) NULL);
10303        }
10304      j=0;
10305      for (i=1; i < count; i++)
10306        if (*files[i] != '-')
10307          filelist[j++]=files[i];
10308      filelist[j]=(char *) NULL;
10309      XListBrowserWidget(display,windows,&windows->widget,
10310        (const char **) filelist,"Load","Select Image to Load:",filename);
10311      filelist=(char **) RelinquishMagickMemory(filelist);
10312      (void) XFreeStringList(files);
10313    }
10314  if (*filename == '\0')
10315    return((Image *) NULL);
10316  image_info=CloneImageInfo(resource_info->image_info);
10317  (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10318    (void *) NULL);
10319  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10320  exception=AcquireExceptionInfo();
10321  (void) SetImageInfo(image_info,0,exception);
10322  if (LocaleCompare(image_info->magick,"X") == 0)
10323    {
10324      char
10325        seconds[MaxTextExtent];
10326
10327      /*
10328        User may want to delay the X server screen grab.
10329      */
10330      (void) CopyMagickString(seconds,"0",MaxTextExtent);
10331      (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10332        seconds);
10333      if (*seconds == '\0')
10334        return((Image *) NULL);
10335      XDelay(display,(size_t) (1000*StringToLong(seconds)));
10336    }
10337  magick_info=GetMagickInfo(image_info->magick,exception);
10338  if ((magick_info != (const MagickInfo *) NULL) &&
10339      (magick_info->raw != MagickFalse))
10340    {
10341      char
10342        geometry[MaxTextExtent];
10343
10344      /*
10345        Request image size from the user.
10346      */
10347      (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10348      if (image_info->size != (char *) NULL)
10349        (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10350      (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10351        geometry);
10352      (void) CloneString(&image_info->size,geometry);
10353    }
10354  /*
10355    Load the image.
10356  */
10357  XSetCursorState(display,windows,MagickTrue);
10358  XCheckRefreshWindows(display,windows);
10359  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10360  nexus=ReadImage(image_info,exception);
10361  CatchException(exception);
10362  XSetCursorState(display,windows,MagickFalse);
10363  if (nexus != (Image *) NULL)
10364    XClientMessage(display,windows->image.id,windows->im_protocols,
10365      windows->im_next_image,CurrentTime);
10366  else
10367    {
10368      char
10369        *text,
10370        **textlist;
10371
10372      /*
10373        Unknown image format.
10374      */
10375      text=FileToString(filename,~0,exception);
10376      if (text == (char *) NULL)
10377        return((Image *) NULL);
10378      textlist=StringToList(text);
10379      if (textlist != (char **) NULL)
10380        {
10381          char
10382            title[MaxTextExtent];
10383
10384          register int
10385            i;
10386
10387          (void) FormatLocaleString(title,MaxTextExtent,
10388            "Unknown format: %s",filename);
10389          XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10390            (const char **) textlist);
10391          for (i=0; textlist[i] != (char *) NULL; i++)
10392            textlist[i]=DestroyString(textlist[i]);
10393          textlist=(char **) RelinquishMagickMemory(textlist);
10394        }
10395      text=DestroyString(text);
10396    }
10397  exception=DestroyExceptionInfo(exception);
10398  image_info=DestroyImageInfo(image_info);
10399  return(nexus);
10400}
10401
10402/*
10403%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10404%                                                                             %
10405%                                                                             %
10406%                                                                             %
10407+   X P a n I m a g e                                                         %
10408%                                                                             %
10409%                                                                             %
10410%                                                                             %
10411%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10412%
10413%  XPanImage() pans the image until the mouse button is released.
10414%
10415%  The format of the XPanImage method is:
10416%
10417%      void XPanImage(Display *display,XWindows *windows,XEvent *event,
10418%        ExceptionInfo *exception)
10419%
10420%  A description of each parameter follows:
10421%
10422%    o display: Specifies a connection to an X server;  returned from
10423%      XOpenDisplay.
10424%
10425%    o windows: Specifies a pointer to a XWindows structure.
10426%
10427%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10428%      the entire image is refreshed.
10429%
10430%    o exception: return any errors or warnings in this structure.
10431%
10432*/
10433static void XPanImage(Display *display,XWindows *windows,XEvent *event,
10434  ExceptionInfo *exception)
10435{
10436  char
10437    text[MaxTextExtent];
10438
10439  Cursor
10440    cursor;
10441
10442  MagickRealType
10443    x_factor,
10444    y_factor;
10445
10446  RectangleInfo
10447    pan_info;
10448
10449  size_t
10450    state;
10451
10452  /*
10453    Define cursor.
10454  */
10455  if ((windows->image.ximage->width > (int) windows->image.width) &&
10456      (windows->image.ximage->height > (int) windows->image.height))
10457    cursor=XCreateFontCursor(display,XC_fleur);
10458  else
10459    if (windows->image.ximage->width > (int) windows->image.width)
10460      cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10461    else
10462      if (windows->image.ximage->height > (int) windows->image.height)
10463        cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10464      else
10465        cursor=XCreateFontCursor(display,XC_arrow);
10466  (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10467  /*
10468    Pan image as pointer moves until the mouse button is released.
10469  */
10470  x_factor=(MagickRealType) windows->image.ximage->width/windows->pan.width;
10471  y_factor=(MagickRealType) windows->image.ximage->height/windows->pan.height;
10472  pan_info.width=windows->pan.width*windows->image.width/
10473    windows->image.ximage->width;
10474  pan_info.height=windows->pan.height*windows->image.height/
10475    windows->image.ximage->height;
10476  pan_info.x=0;
10477  pan_info.y=0;
10478  state=UpdateConfigurationState;
10479  do
10480  {
10481    switch (event->type)
10482    {
10483      case ButtonPress:
10484      {
10485        /*
10486          User choose an initial pan location.
10487        */
10488        pan_info.x=(ssize_t) event->xbutton.x;
10489        pan_info.y=(ssize_t) event->xbutton.y;
10490        state|=UpdateConfigurationState;
10491        break;
10492      }
10493      case ButtonRelease:
10494      {
10495        /*
10496          User has finished panning the image.
10497        */
10498        pan_info.x=(ssize_t) event->xbutton.x;
10499        pan_info.y=(ssize_t) event->xbutton.y;
10500        state|=UpdateConfigurationState | ExitState;
10501        break;
10502      }
10503      case MotionNotify:
10504      {
10505        pan_info.x=(ssize_t) event->xmotion.x;
10506        pan_info.y=(ssize_t) event->xmotion.y;
10507        state|=UpdateConfigurationState;
10508      }
10509      default:
10510        break;
10511    }
10512    if ((state & UpdateConfigurationState) != 0)
10513      {
10514        /*
10515          Check boundary conditions.
10516        */
10517        if (pan_info.x < (ssize_t) (pan_info.width/2))
10518          pan_info.x=0;
10519        else
10520          pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10521        if (pan_info.x < 0)
10522          pan_info.x=0;
10523        else
10524          if ((int) (pan_info.x+windows->image.width) >
10525              windows->image.ximage->width)
10526            pan_info.x=(ssize_t)
10527              (windows->image.ximage->width-windows->image.width);
10528        if (pan_info.y < (ssize_t) (pan_info.height/2))
10529          pan_info.y=0;
10530        else
10531          pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10532        if (pan_info.y < 0)
10533          pan_info.y=0;
10534        else
10535          if ((int) (pan_info.y+windows->image.height) >
10536              windows->image.ximage->height)
10537            pan_info.y=(ssize_t)
10538              (windows->image.ximage->height-windows->image.height);
10539        if ((windows->image.x != (int) pan_info.x) ||
10540            (windows->image.y != (int) pan_info.y))
10541          {
10542            /*
10543              Display image pan offset.
10544            */
10545            windows->image.x=(int) pan_info.x;
10546            windows->image.y=(int) pan_info.y;
10547            (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
10548              windows->image.width,windows->image.height,windows->image.x,
10549              windows->image.y);
10550            XInfoWidget(display,windows,text);
10551            /*
10552              Refresh Image window.
10553            */
10554            XDrawPanRectangle(display,windows);
10555            XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10556          }
10557        state&=(~UpdateConfigurationState);
10558      }
10559    /*
10560      Wait for next event.
10561    */
10562    if ((state & ExitState) == 0)
10563      XScreenEvent(display,windows,event,exception);
10564  } while ((state & ExitState) == 0);
10565  /*
10566    Restore cursor.
10567  */
10568  (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10569  (void) XFreeCursor(display,cursor);
10570  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10571}
10572
10573/*
10574%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10575%                                                                             %
10576%                                                                             %
10577%                                                                             %
10578+   X P a s t e I m a g e                                                     %
10579%                                                                             %
10580%                                                                             %
10581%                                                                             %
10582%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10583%
10584%  XPasteImage() pastes an image previously saved with XCropImage in the X
10585%  window image at a location the user chooses with the pointer.
10586%
10587%  The format of the XPasteImage method is:
10588%
10589%      MagickBooleanType XPasteImage(Display *display,
10590%        XResourceInfo *resource_info,XWindows *windows,Image *image,
10591%        ExceptionInfo *exception)
10592%
10593%  A description of each parameter follows:
10594%
10595%    o display: Specifies a connection to an X server;  returned from
10596%      XOpenDisplay.
10597%
10598%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10599%
10600%    o windows: Specifies a pointer to a XWindows structure.
10601%
10602%    o image: the image; returned from ReadImage.
10603%
10604%    o exception: return any errors or warnings in this structure.
10605%
10606*/
10607static MagickBooleanType XPasteImage(Display *display,
10608  XResourceInfo *resource_info,XWindows *windows,Image *image,
10609  ExceptionInfo *exception)
10610{
10611  static const char
10612    *PasteMenu[] =
10613    {
10614      "Operator",
10615      "Help",
10616      "Dismiss",
10617      (char *) NULL
10618    };
10619
10620  static const ModeType
10621    PasteCommands[] =
10622    {
10623      PasteOperatorsCommand,
10624      PasteHelpCommand,
10625      PasteDismissCommand
10626    };
10627
10628  static CompositeOperator
10629    compose = CopyCompositeOp;
10630
10631  char
10632    text[MaxTextExtent];
10633
10634  Cursor
10635    cursor;
10636
10637  Image
10638    *paste_image;
10639
10640  int
10641    entry,
10642    id,
10643    x,
10644    y;
10645
10646  MagickRealType
10647    scale_factor;
10648
10649  RectangleInfo
10650    highlight_info,
10651    paste_info;
10652
10653  unsigned int
10654    height,
10655    width;
10656
10657  size_t
10658    state;
10659
10660  XEvent
10661    event;
10662
10663  /*
10664    Copy image.
10665  */
10666  if (resource_info->copy_image == (Image *) NULL)
10667    return(MagickFalse);
10668  paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,exception);
10669  /*
10670    Map Command widget.
10671  */
10672  (void) CloneString(&windows->command.name,"Paste");
10673  windows->command.data=1;
10674  (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10675  (void) XMapRaised(display,windows->command.id);
10676  XClientMessage(display,windows->image.id,windows->im_protocols,
10677    windows->im_update_widget,CurrentTime);
10678  /*
10679    Track pointer until button 1 is pressed.
10680  */
10681  XSetCursorState(display,windows,MagickFalse);
10682  XQueryPosition(display,windows->image.id,&x,&y);
10683  (void) XSelectInput(display,windows->image.id,
10684    windows->image.attributes.event_mask | PointerMotionMask);
10685  paste_info.x=(ssize_t) windows->image.x+x;
10686  paste_info.y=(ssize_t) windows->image.y+y;
10687  paste_info.width=0;
10688  paste_info.height=0;
10689  cursor=XCreateFontCursor(display,XC_ul_angle);
10690  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10691  state=DefaultState;
10692  do
10693  {
10694    if (windows->info.mapped != MagickFalse)
10695      {
10696        /*
10697          Display pointer position.
10698        */
10699        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
10700          (long) paste_info.x,(long) paste_info.y);
10701        XInfoWidget(display,windows,text);
10702      }
10703    highlight_info=paste_info;
10704    highlight_info.x=paste_info.x-windows->image.x;
10705    highlight_info.y=paste_info.y-windows->image.y;
10706    XHighlightRectangle(display,windows->image.id,
10707      windows->image.highlight_context,&highlight_info);
10708    /*
10709      Wait for next event.
10710    */
10711    XScreenEvent(display,windows,&event,exception);
10712    XHighlightRectangle(display,windows->image.id,
10713      windows->image.highlight_context,&highlight_info);
10714    if (event.xany.window == windows->command.id)
10715      {
10716        /*
10717          Select a command from the Command widget.
10718        */
10719        id=XCommandWidget(display,windows,PasteMenu,&event);
10720        if (id < 0)
10721          continue;
10722        switch (PasteCommands[id])
10723        {
10724          case PasteOperatorsCommand:
10725          {
10726            char
10727              command[MaxTextExtent],
10728              **operators;
10729
10730            /*
10731              Select a command from the pop-up menu.
10732            */
10733            operators=GetCommandOptions(MagickComposeOptions);
10734            if (operators == (char **) NULL)
10735              break;
10736            entry=XMenuWidget(display,windows,PasteMenu[id],
10737              (const char **) operators,command);
10738            if (entry >= 0)
10739              compose=(CompositeOperator) ParseCommandOption(
10740                MagickComposeOptions,MagickFalse,operators[entry]);
10741            operators=DestroyStringList(operators);
10742            break;
10743          }
10744          case PasteHelpCommand:
10745          {
10746            XTextViewWidget(display,resource_info,windows,MagickFalse,
10747              "Help Viewer - Image Composite",ImagePasteHelp);
10748            break;
10749          }
10750          case PasteDismissCommand:
10751          {
10752            /*
10753              Prematurely exit.
10754            */
10755            state|=EscapeState;
10756            state|=ExitState;
10757            break;
10758          }
10759          default:
10760            break;
10761        }
10762        continue;
10763      }
10764    switch (event.type)
10765    {
10766      case ButtonPress:
10767      {
10768        if (image->debug != MagickFalse)
10769          (void) LogMagickEvent(X11Event,GetMagickModule(),
10770            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10771            event.xbutton.button,event.xbutton.x,event.xbutton.y);
10772        if (event.xbutton.button != Button1)
10773          break;
10774        if (event.xbutton.window != windows->image.id)
10775          break;
10776        /*
10777          Paste rectangle is relative to image configuration.
10778        */
10779        width=(unsigned int) image->columns;
10780        height=(unsigned int) image->rows;
10781        x=0;
10782        y=0;
10783        if (windows->image.crop_geometry != (char *) NULL)
10784          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10785            &width,&height);
10786        scale_factor=(MagickRealType) windows->image.ximage->width/width;
10787        paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10788        scale_factor=(MagickRealType) windows->image.ximage->height/height;
10789        paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10790        (void) XCheckDefineCursor(display,windows->image.id,cursor);
10791        paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10792        paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10793        break;
10794      }
10795      case ButtonRelease:
10796      {
10797        if (image->debug != MagickFalse)
10798          (void) LogMagickEvent(X11Event,GetMagickModule(),
10799            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10800            event.xbutton.button,event.xbutton.x,event.xbutton.y);
10801        if (event.xbutton.button != Button1)
10802          break;
10803        if (event.xbutton.window != windows->image.id)
10804          break;
10805        if ((paste_info.width != 0) && (paste_info.height != 0))
10806          {
10807            /*
10808              User has selected the location of the paste image.
10809            */
10810            paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10811            paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10812            state|=ExitState;
10813          }
10814        break;
10815      }
10816      case Expose:
10817        break;
10818      case KeyPress:
10819      {
10820        char
10821          command[MaxTextExtent];
10822
10823        KeySym
10824          key_symbol;
10825
10826        int
10827          length;
10828
10829        if (event.xkey.window != windows->image.id)
10830          break;
10831        /*
10832          Respond to a user key press.
10833        */
10834        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10835          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10836        *(command+length)='\0';
10837        if (image->debug != MagickFalse)
10838          (void) LogMagickEvent(X11Event,GetMagickModule(),
10839            "Key press: 0x%lx (%s)",(long) key_symbol,command);
10840        switch ((int) key_symbol)
10841        {
10842          case XK_Escape:
10843          case XK_F20:
10844          {
10845            /*
10846              Prematurely exit.
10847            */
10848            paste_image=DestroyImage(paste_image);
10849            state|=EscapeState;
10850            state|=ExitState;
10851            break;
10852          }
10853          case XK_F1:
10854          case XK_Help:
10855          {
10856            (void) XSetFunction(display,windows->image.highlight_context,
10857              GXcopy);
10858            XTextViewWidget(display,resource_info,windows,MagickFalse,
10859              "Help Viewer - Image Composite",ImagePasteHelp);
10860            (void) XSetFunction(display,windows->image.highlight_context,
10861              GXinvert);
10862            break;
10863          }
10864          default:
10865          {
10866            (void) XBell(display,0);
10867            break;
10868          }
10869        }
10870        break;
10871      }
10872      case MotionNotify:
10873      {
10874        /*
10875          Map and unmap Info widget as text cursor crosses its boundaries.
10876        */
10877        x=event.xmotion.x;
10878        y=event.xmotion.y;
10879        if (windows->info.mapped != MagickFalse)
10880          {
10881            if ((x < (int) (windows->info.x+windows->info.width)) &&
10882                (y < (int) (windows->info.y+windows->info.height)))
10883              (void) XWithdrawWindow(display,windows->info.id,
10884                windows->info.screen);
10885          }
10886        else
10887          if ((x > (int) (windows->info.x+windows->info.width)) ||
10888              (y > (int) (windows->info.y+windows->info.height)))
10889            (void) XMapWindow(display,windows->info.id);
10890        paste_info.x=(ssize_t) windows->image.x+x;
10891        paste_info.y=(ssize_t) windows->image.y+y;
10892        break;
10893      }
10894      default:
10895      {
10896        if (image->debug != MagickFalse)
10897          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10898            event.type);
10899        break;
10900      }
10901    }
10902  } while ((state & ExitState) == 0);
10903  (void) XSelectInput(display,windows->image.id,
10904    windows->image.attributes.event_mask);
10905  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10906  XSetCursorState(display,windows,MagickFalse);
10907  (void) XFreeCursor(display,cursor);
10908  if ((state & EscapeState) != 0)
10909    return(MagickTrue);
10910  /*
10911    Image pasting is relative to image configuration.
10912  */
10913  XSetCursorState(display,windows,MagickTrue);
10914  XCheckRefreshWindows(display,windows);
10915  width=(unsigned int) image->columns;
10916  height=(unsigned int) image->rows;
10917  x=0;
10918  y=0;
10919  if (windows->image.crop_geometry != (char *) NULL)
10920    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10921  scale_factor=(MagickRealType) width/windows->image.ximage->width;
10922  paste_info.x+=x;
10923  paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10924  paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10925  scale_factor=(MagickRealType) height/windows->image.ximage->height;
10926  paste_info.y+=y;
10927  paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10928  paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10929  /*
10930    Paste image with X Image window.
10931  */
10932  (void) CompositeImage(image,compose,paste_image,paste_info.x,paste_info.y,
10933    exception);
10934  paste_image=DestroyImage(paste_image);
10935  XSetCursorState(display,windows,MagickFalse);
10936  /*
10937    Update image colormap.
10938  */
10939  XConfigureImageColormap(display,resource_info,windows,image,exception);
10940  (void) XConfigureImage(display,resource_info,windows,image,exception);
10941  return(MagickTrue);
10942}
10943
10944/*
10945%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10946%                                                                             %
10947%                                                                             %
10948%                                                                             %
10949+   X P r i n t I m a g e                                                     %
10950%                                                                             %
10951%                                                                             %
10952%                                                                             %
10953%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10954%
10955%  XPrintImage() prints an image to a Postscript printer.
10956%
10957%  The format of the XPrintImage method is:
10958%
10959%      MagickBooleanType XPrintImage(Display *display,
10960%        XResourceInfo *resource_info,XWindows *windows,Image *image,
10961%        ExceptionInfo *exception)
10962%
10963%  A description of each parameter follows:
10964%
10965%    o display: Specifies a connection to an X server; returned from
10966%      XOpenDisplay.
10967%
10968%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10969%
10970%    o windows: Specifies a pointer to a XWindows structure.
10971%
10972%    o image: the image.
10973%
10974%    o exception: return any errors or warnings in this structure.
10975%
10976*/
10977static MagickBooleanType XPrintImage(Display *display,
10978  XResourceInfo *resource_info,XWindows *windows,Image *image,
10979  ExceptionInfo *exception)
10980{
10981  char
10982    filename[MaxTextExtent],
10983    geometry[MaxTextExtent];
10984
10985  Image
10986    *print_image;
10987
10988  ImageInfo
10989    *image_info;
10990
10991  MagickStatusType
10992    status;
10993
10994  /*
10995    Request Postscript page geometry from user.
10996  */
10997  image_info=CloneImageInfo(resource_info->image_info);
10998  (void) FormatLocaleString(geometry,MaxTextExtent,"Letter");
10999  if (image_info->page != (char *) NULL)
11000    (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
11001  XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
11002    "Select Postscript Page Geometry:",geometry);
11003  if (*geometry == '\0')
11004    return(MagickTrue);
11005  image_info->page=GetPageGeometry(geometry);
11006  /*
11007    Apply image transforms.
11008  */
11009  XSetCursorState(display,windows,MagickTrue);
11010  XCheckRefreshWindows(display,windows);
11011  print_image=CloneImage(image,0,0,MagickTrue,exception);
11012  if (print_image == (Image *) NULL)
11013    return(MagickFalse);
11014  (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
11015    windows->image.ximage->width,windows->image.ximage->height);
11016  (void) TransformImage(&print_image,windows->image.crop_geometry,geometry,
11017    exception);
11018  /*
11019    Print image.
11020  */
11021  (void) AcquireUniqueFilename(filename);
11022  (void) FormatLocaleString(print_image->filename,MaxTextExtent,"print:%s",
11023    filename);
11024  status=WriteImage(image_info,print_image,exception);
11025  (void) RelinquishUniqueFileResource(filename);
11026  print_image=DestroyImage(print_image);
11027  image_info=DestroyImageInfo(image_info);
11028  XSetCursorState(display,windows,MagickFalse);
11029  return(status != 0 ? MagickTrue : MagickFalse);
11030}
11031
11032/*
11033%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11034%                                                                             %
11035%                                                                             %
11036%                                                                             %
11037+   X R O I I m a g e                                                         %
11038%                                                                             %
11039%                                                                             %
11040%                                                                             %
11041%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11042%
11043%  XROIImage() applies an image processing technique to a region of interest.
11044%
11045%  The format of the XROIImage method is:
11046%
11047%      MagickBooleanType XROIImage(Display *display,
11048%        XResourceInfo *resource_info,XWindows *windows,Image **image,
11049%        ExceptionInfo *exception)
11050%
11051%  A description of each parameter follows:
11052%
11053%    o display: Specifies a connection to an X server; returned from
11054%      XOpenDisplay.
11055%
11056%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11057%
11058%    o windows: Specifies a pointer to a XWindows structure.
11059%
11060%    o image: the image; returned from ReadImage.
11061%
11062%    o exception: return any errors or warnings in this structure.
11063%
11064*/
11065static MagickBooleanType XROIImage(Display *display,
11066  XResourceInfo *resource_info,XWindows *windows,Image **image,
11067  ExceptionInfo *exception)
11068{
11069#define ApplyMenus  7
11070
11071  static const char
11072    *ROIMenu[] =
11073    {
11074      "Help",
11075      "Dismiss",
11076      (char *) NULL
11077    },
11078    *ApplyMenu[] =
11079    {
11080      "File",
11081      "Edit",
11082      "Transform",
11083      "Enhance",
11084      "Effects",
11085      "F/X",
11086      "Miscellany",
11087      "Help",
11088      "Dismiss",
11089      (char *) NULL
11090    },
11091    *FileMenu[] =
11092    {
11093      "Save...",
11094      "Print...",
11095      (char *) NULL
11096    },
11097    *EditMenu[] =
11098    {
11099      "Undo",
11100      "Redo",
11101      (char *) NULL
11102    },
11103    *TransformMenu[] =
11104    {
11105      "Flop",
11106      "Flip",
11107      "Rotate Right",
11108      "Rotate Left",
11109      (char *) NULL
11110    },
11111    *EnhanceMenu[] =
11112    {
11113      "Hue...",
11114      "Saturation...",
11115      "Brightness...",
11116      "Gamma...",
11117      "Spiff",
11118      "Dull",
11119      "Contrast Stretch...",
11120      "Sigmoidal Contrast...",
11121      "Normalize",
11122      "Equalize",
11123      "Negate",
11124      "Grayscale",
11125      "Map...",
11126      "Quantize...",
11127      (char *) NULL
11128    },
11129    *EffectsMenu[] =
11130    {
11131      "Despeckle",
11132      "Emboss",
11133      "Reduce Noise",
11134      "Add Noise",
11135      "Sharpen...",
11136      "Blur...",
11137      "Threshold...",
11138      "Edge Detect...",
11139      "Spread...",
11140      "Shade...",
11141      "Raise...",
11142      "Segment...",
11143      (char *) NULL
11144    },
11145    *FXMenu[] =
11146    {
11147      "Solarize...",
11148      "Sepia Tone...",
11149      "Swirl...",
11150      "Implode...",
11151      "Vignette...",
11152      "Wave...",
11153      "Oil Paint...",
11154      "Charcoal Draw...",
11155      (char *) NULL
11156    },
11157    *MiscellanyMenu[] =
11158    {
11159      "Image Info",
11160      "Zoom Image",
11161      "Show Preview...",
11162      "Show Histogram",
11163      "Show Matte",
11164      (char *) NULL
11165    };
11166
11167  static const char
11168    **Menus[ApplyMenus] =
11169    {
11170      FileMenu,
11171      EditMenu,
11172      TransformMenu,
11173      EnhanceMenu,
11174      EffectsMenu,
11175      FXMenu,
11176      MiscellanyMenu
11177    };
11178
11179  static const CommandType
11180    ApplyCommands[] =
11181    {
11182      NullCommand,
11183      NullCommand,
11184      NullCommand,
11185      NullCommand,
11186      NullCommand,
11187      NullCommand,
11188      NullCommand,
11189      HelpCommand,
11190      QuitCommand
11191    },
11192    FileCommands[] =
11193    {
11194      SaveCommand,
11195      PrintCommand
11196    },
11197    EditCommands[] =
11198    {
11199      UndoCommand,
11200      RedoCommand
11201    },
11202    TransformCommands[] =
11203    {
11204      FlopCommand,
11205      FlipCommand,
11206      RotateRightCommand,
11207      RotateLeftCommand
11208    },
11209    EnhanceCommands[] =
11210    {
11211      HueCommand,
11212      SaturationCommand,
11213      BrightnessCommand,
11214      GammaCommand,
11215      SpiffCommand,
11216      DullCommand,
11217      ContrastStretchCommand,
11218      SigmoidalContrastCommand,
11219      NormalizeCommand,
11220      EqualizeCommand,
11221      NegateCommand,
11222      GrayscaleCommand,
11223      MapCommand,
11224      QuantizeCommand
11225    },
11226    EffectsCommands[] =
11227    {
11228      DespeckleCommand,
11229      EmbossCommand,
11230      ReduceNoiseCommand,
11231      AddNoiseCommand,
11232      SharpenCommand,
11233      BlurCommand,
11234      EdgeDetectCommand,
11235      SpreadCommand,
11236      ShadeCommand,
11237      RaiseCommand,
11238      SegmentCommand
11239    },
11240    FXCommands[] =
11241    {
11242      SolarizeCommand,
11243      SepiaToneCommand,
11244      SwirlCommand,
11245      ImplodeCommand,
11246      VignetteCommand,
11247      WaveCommand,
11248      OilPaintCommand,
11249      CharcoalDrawCommand
11250    },
11251    MiscellanyCommands[] =
11252    {
11253      InfoCommand,
11254      ZoomCommand,
11255      ShowPreviewCommand,
11256      ShowHistogramCommand,
11257      ShowMatteCommand
11258    },
11259    ROICommands[] =
11260    {
11261      ROIHelpCommand,
11262      ROIDismissCommand
11263    };
11264
11265  static const CommandType
11266    *Commands[ApplyMenus] =
11267    {
11268      FileCommands,
11269      EditCommands,
11270      TransformCommands,
11271      EnhanceCommands,
11272      EffectsCommands,
11273      FXCommands,
11274      MiscellanyCommands
11275    };
11276
11277  char
11278    command[MaxTextExtent],
11279    text[MaxTextExtent];
11280
11281  CommandType
11282    command_type;
11283
11284  Cursor
11285    cursor;
11286
11287  Image
11288    *roi_image;
11289
11290  int
11291    entry,
11292    id,
11293    x,
11294    y;
11295
11296  MagickRealType
11297    scale_factor;
11298
11299  MagickProgressMonitor
11300    progress_monitor;
11301
11302  RectangleInfo
11303    crop_info,
11304    highlight_info,
11305    roi_info;
11306
11307  unsigned int
11308    height,
11309    width;
11310
11311  size_t
11312    state;
11313
11314  XEvent
11315    event;
11316
11317  /*
11318    Map Command widget.
11319  */
11320  (void) CloneString(&windows->command.name,"ROI");
11321  windows->command.data=0;
11322  (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11323  (void) XMapRaised(display,windows->command.id);
11324  XClientMessage(display,windows->image.id,windows->im_protocols,
11325    windows->im_update_widget,CurrentTime);
11326  /*
11327    Track pointer until button 1 is pressed.
11328  */
11329  XQueryPosition(display,windows->image.id,&x,&y);
11330  (void) XSelectInput(display,windows->image.id,
11331    windows->image.attributes.event_mask | PointerMotionMask);
11332  roi_info.x=(ssize_t) windows->image.x+x;
11333  roi_info.y=(ssize_t) windows->image.y+y;
11334  roi_info.width=0;
11335  roi_info.height=0;
11336  cursor=XCreateFontCursor(display,XC_fleur);
11337  state=DefaultState;
11338  do
11339  {
11340    if (windows->info.mapped != MagickFalse)
11341      {
11342        /*
11343          Display pointer position.
11344        */
11345        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
11346          (long) roi_info.x,(long) roi_info.y);
11347        XInfoWidget(display,windows,text);
11348      }
11349    /*
11350      Wait for next event.
11351    */
11352    XScreenEvent(display,windows,&event,exception);
11353    if (event.xany.window == windows->command.id)
11354      {
11355        /*
11356          Select a command from the Command widget.
11357        */
11358        id=XCommandWidget(display,windows,ROIMenu,&event);
11359        if (id < 0)
11360          continue;
11361        switch (ROICommands[id])
11362        {
11363          case ROIHelpCommand:
11364          {
11365            XTextViewWidget(display,resource_info,windows,MagickFalse,
11366              "Help Viewer - Region of Interest",ImageROIHelp);
11367            break;
11368          }
11369          case ROIDismissCommand:
11370          {
11371            /*
11372              Prematurely exit.
11373            */
11374            state|=EscapeState;
11375            state|=ExitState;
11376            break;
11377          }
11378          default:
11379            break;
11380        }
11381        continue;
11382      }
11383    switch (event.type)
11384    {
11385      case ButtonPress:
11386      {
11387        if (event.xbutton.button != Button1)
11388          break;
11389        if (event.xbutton.window != windows->image.id)
11390          break;
11391        /*
11392          Note first corner of region of interest rectangle-- exit loop.
11393        */
11394        (void) XCheckDefineCursor(display,windows->image.id,cursor);
11395        roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11396        roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11397        state|=ExitState;
11398        break;
11399      }
11400      case ButtonRelease:
11401        break;
11402      case Expose:
11403        break;
11404      case KeyPress:
11405      {
11406        KeySym
11407          key_symbol;
11408
11409        if (event.xkey.window != windows->image.id)
11410          break;
11411        /*
11412          Respond to a user key press.
11413        */
11414        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11415          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11416        switch ((int) key_symbol)
11417        {
11418          case XK_Escape:
11419          case XK_F20:
11420          {
11421            /*
11422              Prematurely exit.
11423            */
11424            state|=EscapeState;
11425            state|=ExitState;
11426            break;
11427          }
11428          case XK_F1:
11429          case XK_Help:
11430          {
11431            XTextViewWidget(display,resource_info,windows,MagickFalse,
11432              "Help Viewer - Region of Interest",ImageROIHelp);
11433            break;
11434          }
11435          default:
11436          {
11437            (void) XBell(display,0);
11438            break;
11439          }
11440        }
11441        break;
11442      }
11443      case MotionNotify:
11444      {
11445        /*
11446          Map and unmap Info widget as text cursor crosses its boundaries.
11447        */
11448        x=event.xmotion.x;
11449        y=event.xmotion.y;
11450        if (windows->info.mapped != MagickFalse)
11451          {
11452            if ((x < (int) (windows->info.x+windows->info.width)) &&
11453                (y < (int) (windows->info.y+windows->info.height)))
11454              (void) XWithdrawWindow(display,windows->info.id,
11455                windows->info.screen);
11456          }
11457        else
11458          if ((x > (int) (windows->info.x+windows->info.width)) ||
11459              (y > (int) (windows->info.y+windows->info.height)))
11460            (void) XMapWindow(display,windows->info.id);
11461        roi_info.x=(ssize_t) windows->image.x+x;
11462        roi_info.y=(ssize_t) windows->image.y+y;
11463        break;
11464      }
11465      default:
11466        break;
11467    }
11468  } while ((state & ExitState) == 0);
11469  (void) XSelectInput(display,windows->image.id,
11470    windows->image.attributes.event_mask);
11471  if ((state & EscapeState) != 0)
11472    {
11473      /*
11474        User want to exit without region of interest.
11475      */
11476      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11477      (void) XFreeCursor(display,cursor);
11478      return(MagickTrue);
11479    }
11480  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11481  do
11482  {
11483    /*
11484      Size rectangle as pointer moves until the mouse button is released.
11485    */
11486    x=(int) roi_info.x;
11487    y=(int) roi_info.y;
11488    roi_info.width=0;
11489    roi_info.height=0;
11490    state=DefaultState;
11491    do
11492    {
11493      highlight_info=roi_info;
11494      highlight_info.x=roi_info.x-windows->image.x;
11495      highlight_info.y=roi_info.y-windows->image.y;
11496      if ((highlight_info.width > 3) && (highlight_info.height > 3))
11497        {
11498          /*
11499            Display info and draw region of interest rectangle.
11500          */
11501          if (windows->info.mapped == MagickFalse)
11502            (void) XMapWindow(display,windows->info.id);
11503          (void) FormatLocaleString(text,MaxTextExtent,
11504            " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11505            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11506          XInfoWidget(display,windows,text);
11507          XHighlightRectangle(display,windows->image.id,
11508            windows->image.highlight_context,&highlight_info);
11509        }
11510      else
11511        if (windows->info.mapped != MagickFalse)
11512          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11513      /*
11514        Wait for next event.
11515      */
11516      XScreenEvent(display,windows,&event,exception);
11517      if ((highlight_info.width > 3) && (highlight_info.height > 3))
11518        XHighlightRectangle(display,windows->image.id,
11519          windows->image.highlight_context,&highlight_info);
11520      switch (event.type)
11521      {
11522        case ButtonPress:
11523        {
11524          roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11525          roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11526          break;
11527        }
11528        case ButtonRelease:
11529        {
11530          /*
11531            User has committed to region of interest rectangle.
11532          */
11533          roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11534          roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11535          XSetCursorState(display,windows,MagickFalse);
11536          state|=ExitState;
11537          if (LocaleCompare(windows->command.name,"Apply") == 0)
11538            break;
11539          (void) CloneString(&windows->command.name,"Apply");
11540          windows->command.data=ApplyMenus;
11541          (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11542          break;
11543        }
11544        case Expose:
11545          break;
11546        case MotionNotify:
11547        {
11548          roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11549          roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11550        }
11551        default:
11552          break;
11553      }
11554      if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11555          ((state & ExitState) != 0))
11556        {
11557          /*
11558            Check boundary conditions.
11559          */
11560          if (roi_info.x < 0)
11561            roi_info.x=0;
11562          else
11563            if (roi_info.x > (ssize_t) windows->image.ximage->width)
11564              roi_info.x=(ssize_t) windows->image.ximage->width;
11565          if ((int) roi_info.x < x)
11566            roi_info.width=(unsigned int) (x-roi_info.x);
11567          else
11568            {
11569              roi_info.width=(unsigned int) (roi_info.x-x);
11570              roi_info.x=(ssize_t) x;
11571            }
11572          if (roi_info.y < 0)
11573            roi_info.y=0;
11574          else
11575            if (roi_info.y > (ssize_t) windows->image.ximage->height)
11576              roi_info.y=(ssize_t) windows->image.ximage->height;
11577          if ((int) roi_info.y < y)
11578            roi_info.height=(unsigned int) (y-roi_info.y);
11579          else
11580            {
11581              roi_info.height=(unsigned int) (roi_info.y-y);
11582              roi_info.y=(ssize_t) y;
11583            }
11584        }
11585    } while ((state & ExitState) == 0);
11586    /*
11587      Wait for user to grab a corner of the rectangle or press return.
11588    */
11589    state=DefaultState;
11590    command_type=NullCommand;
11591    (void) XMapWindow(display,windows->info.id);
11592    do
11593    {
11594      if (windows->info.mapped != MagickFalse)
11595        {
11596          /*
11597            Display pointer position.
11598          */
11599          (void) FormatLocaleString(text,MaxTextExtent,
11600            " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11601            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11602          XInfoWidget(display,windows,text);
11603        }
11604      highlight_info=roi_info;
11605      highlight_info.x=roi_info.x-windows->image.x;
11606      highlight_info.y=roi_info.y-windows->image.y;
11607      if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11608        {
11609          state|=EscapeState;
11610          state|=ExitState;
11611          break;
11612        }
11613      if ((state & UpdateRegionState) != 0)
11614        {
11615          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11616          switch (command_type)
11617          {
11618            case UndoCommand:
11619            case RedoCommand:
11620            {
11621              (void) XMagickCommand(display,resource_info,windows,command_type,
11622                image,exception);
11623              break;
11624            }
11625            default:
11626            {
11627              /*
11628                Region of interest is relative to image configuration.
11629              */
11630              progress_monitor=SetImageProgressMonitor(*image,
11631                (MagickProgressMonitor) NULL,(*image)->client_data);
11632              crop_info=roi_info;
11633              width=(unsigned int) (*image)->columns;
11634              height=(unsigned int) (*image)->rows;
11635              x=0;
11636              y=0;
11637              if (windows->image.crop_geometry != (char *) NULL)
11638                (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11639                  &width,&height);
11640              scale_factor=(MagickRealType) width/windows->image.ximage->width;
11641              crop_info.x+=x;
11642              crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11643              crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11644              scale_factor=(MagickRealType)
11645                height/windows->image.ximage->height;
11646              crop_info.y+=y;
11647              crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11648              crop_info.height=(unsigned int)
11649                (scale_factor*crop_info.height+0.5);
11650              roi_image=CropImage(*image,&crop_info,exception);
11651              (void) SetImageProgressMonitor(*image,progress_monitor,
11652                (*image)->client_data);
11653              if (roi_image == (Image *) NULL)
11654                continue;
11655              /*
11656                Apply image processing technique to the region of interest.
11657              */
11658              windows->image.orphan=MagickTrue;
11659              (void) XMagickCommand(display,resource_info,windows,command_type,
11660                &roi_image,exception);
11661              progress_monitor=SetImageProgressMonitor(*image,
11662                (MagickProgressMonitor) NULL,(*image)->client_data);
11663              (void) XMagickCommand(display,resource_info,windows,
11664                SaveToUndoBufferCommand,image,exception);
11665              windows->image.orphan=MagickFalse;
11666              (void) CompositeImage(*image,CopyCompositeOp,roi_image,
11667                crop_info.x,crop_info.y,exception);
11668              roi_image=DestroyImage(roi_image);
11669              (void) SetImageProgressMonitor(*image,progress_monitor,
11670                (*image)->client_data);
11671              break;
11672            }
11673          }
11674          if (command_type != InfoCommand)
11675            {
11676              XConfigureImageColormap(display,resource_info,windows,*image,
11677                exception);
11678              (void) XConfigureImage(display,resource_info,windows,*image,
11679                exception);
11680            }
11681          XCheckRefreshWindows(display,windows);
11682          XInfoWidget(display,windows,text);
11683          (void) XSetFunction(display,windows->image.highlight_context,
11684            GXinvert);
11685          state&=(~UpdateRegionState);
11686        }
11687      XHighlightRectangle(display,windows->image.id,
11688        windows->image.highlight_context,&highlight_info);
11689      XScreenEvent(display,windows,&event,exception);
11690      if (event.xany.window == windows->command.id)
11691        {
11692          /*
11693            Select a command from the Command widget.
11694          */
11695          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11696          command_type=NullCommand;
11697          id=XCommandWidget(display,windows,ApplyMenu,&event);
11698          if (id >= 0)
11699            {
11700              (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11701              command_type=ApplyCommands[id];
11702              if (id < ApplyMenus)
11703                {
11704                  /*
11705                    Select a command from a pop-up menu.
11706                  */
11707                  entry=XMenuWidget(display,windows,ApplyMenu[id],
11708                    (const char **) Menus[id],command);
11709                  if (entry >= 0)
11710                    {
11711                      (void) CopyMagickString(command,Menus[id][entry],
11712                        MaxTextExtent);
11713                      command_type=Commands[id][entry];
11714                    }
11715                }
11716            }
11717          (void) XSetFunction(display,windows->image.highlight_context,
11718            GXinvert);
11719          XHighlightRectangle(display,windows->image.id,
11720            windows->image.highlight_context,&highlight_info);
11721          if (command_type == HelpCommand)
11722            {
11723              (void) XSetFunction(display,windows->image.highlight_context,
11724                GXcopy);
11725              XTextViewWidget(display,resource_info,windows,MagickFalse,
11726                "Help Viewer - Region of Interest",ImageROIHelp);
11727              (void) XSetFunction(display,windows->image.highlight_context,
11728                GXinvert);
11729              continue;
11730            }
11731          if (command_type == QuitCommand)
11732            {
11733              /*
11734                exit.
11735              */
11736              state|=EscapeState;
11737              state|=ExitState;
11738              continue;
11739            }
11740          if (command_type != NullCommand)
11741            state|=UpdateRegionState;
11742          continue;
11743        }
11744      XHighlightRectangle(display,windows->image.id,
11745        windows->image.highlight_context,&highlight_info);
11746      switch (event.type)
11747      {
11748        case ButtonPress:
11749        {
11750          x=windows->image.x;
11751          y=windows->image.y;
11752          if (event.xbutton.button != Button1)
11753            break;
11754          if (event.xbutton.window != windows->image.id)
11755            break;
11756          x=windows->image.x+event.xbutton.x;
11757          y=windows->image.y+event.xbutton.y;
11758          if ((x < (int) (roi_info.x+RoiDelta)) &&
11759              (x > (int) (roi_info.x-RoiDelta)) &&
11760              (y < (int) (roi_info.y+RoiDelta)) &&
11761              (y > (int) (roi_info.y-RoiDelta)))
11762            {
11763              roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11764              roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11765              state|=UpdateConfigurationState;
11766              break;
11767            }
11768          if ((x < (int) (roi_info.x+RoiDelta)) &&
11769              (x > (int) (roi_info.x-RoiDelta)) &&
11770              (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11771              (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11772            {
11773              roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11774              state|=UpdateConfigurationState;
11775              break;
11776            }
11777          if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11778              (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11779              (y < (int) (roi_info.y+RoiDelta)) &&
11780              (y > (int) (roi_info.y-RoiDelta)))
11781            {
11782              roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11783              state|=UpdateConfigurationState;
11784              break;
11785            }
11786          if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11787              (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11788              (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11789              (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11790            {
11791              state|=UpdateConfigurationState;
11792              break;
11793            }
11794        }
11795        case ButtonRelease:
11796        {
11797          if (event.xbutton.window == windows->pan.id)
11798            if ((highlight_info.x != crop_info.x-windows->image.x) ||
11799                (highlight_info.y != crop_info.y-windows->image.y))
11800              XHighlightRectangle(display,windows->image.id,
11801                windows->image.highlight_context,&highlight_info);
11802          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11803            event.xbutton.time);
11804          break;
11805        }
11806        case Expose:
11807        {
11808          if (event.xexpose.window == windows->image.id)
11809            if (event.xexpose.count == 0)
11810              {
11811                event.xexpose.x=(int) highlight_info.x;
11812                event.xexpose.y=(int) highlight_info.y;
11813                event.xexpose.width=(int) highlight_info.width;
11814                event.xexpose.height=(int) highlight_info.height;
11815                XRefreshWindow(display,&windows->image,&event);
11816              }
11817          if (event.xexpose.window == windows->info.id)
11818            if (event.xexpose.count == 0)
11819              XInfoWidget(display,windows,text);
11820          break;
11821        }
11822        case KeyPress:
11823        {
11824          KeySym
11825            key_symbol;
11826
11827          if (event.xkey.window != windows->image.id)
11828            break;
11829          /*
11830            Respond to a user key press.
11831          */
11832          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11833            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11834          switch ((int) key_symbol)
11835          {
11836            case XK_Shift_L:
11837            case XK_Shift_R:
11838              break;
11839            case XK_Escape:
11840            case XK_F20:
11841              state|=EscapeState;
11842            case XK_Return:
11843            {
11844              state|=ExitState;
11845              break;
11846            }
11847            case XK_Home:
11848            case XK_KP_Home:
11849            {
11850              roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11851              roi_info.y=(ssize_t) (windows->image.height/2L-
11852                roi_info.height/2L);
11853              break;
11854            }
11855            case XK_Left:
11856            case XK_KP_Left:
11857            {
11858              roi_info.x--;
11859              break;
11860            }
11861            case XK_Up:
11862            case XK_KP_Up:
11863            case XK_Next:
11864            {
11865              roi_info.y--;
11866              break;
11867            }
11868            case XK_Right:
11869            case XK_KP_Right:
11870            {
11871              roi_info.x++;
11872              break;
11873            }
11874            case XK_Prior:
11875            case XK_Down:
11876            case XK_KP_Down:
11877            {
11878              roi_info.y++;
11879              break;
11880            }
11881            case XK_F1:
11882            case XK_Help:
11883            {
11884              (void) XSetFunction(display,windows->image.highlight_context,
11885                GXcopy);
11886              XTextViewWidget(display,resource_info,windows,MagickFalse,
11887                "Help Viewer - Region of Interest",ImageROIHelp);
11888              (void) XSetFunction(display,windows->image.highlight_context,
11889                GXinvert);
11890              break;
11891            }
11892            default:
11893            {
11894              command_type=XImageWindowCommand(display,resource_info,windows,
11895                event.xkey.state,key_symbol,image,exception);
11896              if (command_type != NullCommand)
11897                state|=UpdateRegionState;
11898              break;
11899            }
11900          }
11901          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11902            event.xkey.time);
11903          break;
11904        }
11905        case KeyRelease:
11906          break;
11907        case MotionNotify:
11908        {
11909          if (event.xbutton.window != windows->image.id)
11910            break;
11911          /*
11912            Map and unmap Info widget as text cursor crosses its boundaries.
11913          */
11914          x=event.xmotion.x;
11915          y=event.xmotion.y;
11916          if (windows->info.mapped != MagickFalse)
11917            {
11918              if ((x < (int) (windows->info.x+windows->info.width)) &&
11919                  (y < (int) (windows->info.y+windows->info.height)))
11920                (void) XWithdrawWindow(display,windows->info.id,
11921                  windows->info.screen);
11922            }
11923          else
11924            if ((x > (int) (windows->info.x+windows->info.width)) ||
11925                (y > (int) (windows->info.y+windows->info.height)))
11926              (void) XMapWindow(display,windows->info.id);
11927          roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11928          roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11929          break;
11930        }
11931        case SelectionRequest:
11932        {
11933          XSelectionEvent
11934            notify;
11935
11936          XSelectionRequestEvent
11937            *request;
11938
11939          /*
11940            Set primary selection.
11941          */
11942          (void) FormatLocaleString(text,MaxTextExtent,
11943            "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11944            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11945          request=(&(event.xselectionrequest));
11946          (void) XChangeProperty(request->display,request->requestor,
11947            request->property,request->target,8,PropModeReplace,
11948            (unsigned char *) text,(int) strlen(text));
11949          notify.type=SelectionNotify;
11950          notify.display=request->display;
11951          notify.requestor=request->requestor;
11952          notify.selection=request->selection;
11953          notify.target=request->target;
11954          notify.time=request->time;
11955          if (request->property == None)
11956            notify.property=request->target;
11957          else
11958            notify.property=request->property;
11959          (void) XSendEvent(request->display,request->requestor,False,0,
11960            (XEvent *) &notify);
11961        }
11962        default:
11963          break;
11964      }
11965      if ((state & UpdateConfigurationState) != 0)
11966        {
11967          (void) XPutBackEvent(display,&event);
11968          (void) XCheckDefineCursor(display,windows->image.id,cursor);
11969          break;
11970        }
11971    } while ((state & ExitState) == 0);
11972  } while ((state & ExitState) == 0);
11973  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11974  XSetCursorState(display,windows,MagickFalse);
11975  if ((state & EscapeState) != 0)
11976    return(MagickTrue);
11977  return(MagickTrue);
11978}
11979
11980/*
11981%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11982%                                                                             %
11983%                                                                             %
11984%                                                                             %
11985+   X R o t a t e I m a g e                                                   %
11986%                                                                             %
11987%                                                                             %
11988%                                                                             %
11989%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11990%
11991%  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
11992%  rotation angle is computed from the slope of a line drawn by the user.
11993%
11994%  The format of the XRotateImage method is:
11995%
11996%      MagickBooleanType XRotateImage(Display *display,
11997%        XResourceInfo *resource_info,XWindows *windows,double degrees,
11998%        Image **image,ExceptionInfo *exception)
11999%
12000%  A description of each parameter follows:
12001%
12002%    o display: Specifies a connection to an X server; returned from
12003%      XOpenDisplay.
12004%
12005%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12006%
12007%    o windows: Specifies a pointer to a XWindows structure.
12008%
12009%    o degrees: Specifies the number of degrees to rotate the image.
12010%
12011%    o image: the image.
12012%
12013%    o exception: return any errors or warnings in this structure.
12014%
12015*/
12016static MagickBooleanType XRotateImage(Display *display,
12017  XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
12018  ExceptionInfo *exception)
12019{
12020  static const char
12021    *RotateMenu[] =
12022    {
12023      "Pixel Color",
12024      "Direction",
12025      "Help",
12026      "Dismiss",
12027      (char *) NULL
12028    };
12029
12030  static ModeType
12031    direction = HorizontalRotateCommand;
12032
12033  static const ModeType
12034    DirectionCommands[] =
12035    {
12036      HorizontalRotateCommand,
12037      VerticalRotateCommand
12038    },
12039    RotateCommands[] =
12040    {
12041      RotateColorCommand,
12042      RotateDirectionCommand,
12043      RotateHelpCommand,
12044      RotateDismissCommand
12045    };
12046
12047  static unsigned int
12048    pen_id = 0;
12049
12050  char
12051    command[MaxTextExtent],
12052    text[MaxTextExtent];
12053
12054  Image
12055    *rotate_image;
12056
12057  int
12058    id,
12059    x,
12060    y;
12061
12062  MagickRealType
12063    normalized_degrees;
12064
12065  register int
12066    i;
12067
12068  unsigned int
12069    height,
12070    rotations,
12071    width;
12072
12073  if (degrees == 0.0)
12074    {
12075      unsigned int
12076        distance;
12077
12078      size_t
12079        state;
12080
12081      XEvent
12082        event;
12083
12084      XSegment
12085        rotate_info;
12086
12087      /*
12088        Map Command widget.
12089      */
12090      (void) CloneString(&windows->command.name,"Rotate");
12091      windows->command.data=2;
12092      (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12093      (void) XMapRaised(display,windows->command.id);
12094      XClientMessage(display,windows->image.id,windows->im_protocols,
12095        windows->im_update_widget,CurrentTime);
12096      /*
12097        Wait for first button press.
12098      */
12099      (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12100      XQueryPosition(display,windows->image.id,&x,&y);
12101      rotate_info.x1=x;
12102      rotate_info.y1=y;
12103      rotate_info.x2=x;
12104      rotate_info.y2=y;
12105      state=DefaultState;
12106      do
12107      {
12108        XHighlightLine(display,windows->image.id,
12109          windows->image.highlight_context,&rotate_info);
12110        /*
12111          Wait for next event.
12112        */
12113        XScreenEvent(display,windows,&event,exception);
12114        XHighlightLine(display,windows->image.id,
12115          windows->image.highlight_context,&rotate_info);
12116        if (event.xany.window == windows->command.id)
12117          {
12118            /*
12119              Select a command from the Command widget.
12120            */
12121            id=XCommandWidget(display,windows,RotateMenu,&event);
12122            if (id < 0)
12123              continue;
12124            (void) XSetFunction(display,windows->image.highlight_context,
12125              GXcopy);
12126            switch (RotateCommands[id])
12127            {
12128              case RotateColorCommand:
12129              {
12130                const char
12131                  *ColorMenu[MaxNumberPens];
12132
12133                int
12134                  pen_number;
12135
12136                XColor
12137                  color;
12138
12139                /*
12140                  Initialize menu selections.
12141                */
12142                for (i=0; i < (int) (MaxNumberPens-2); i++)
12143                  ColorMenu[i]=resource_info->pen_colors[i];
12144                ColorMenu[MaxNumberPens-2]="Browser...";
12145                ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12146                /*
12147                  Select a pen color from the pop-up menu.
12148                */
12149                pen_number=XMenuWidget(display,windows,RotateMenu[id],
12150                  (const char **) ColorMenu,command);
12151                if (pen_number < 0)
12152                  break;
12153                if (pen_number == (MaxNumberPens-2))
12154                  {
12155                    static char
12156                      color_name[MaxTextExtent] = "gray";
12157
12158                    /*
12159                      Select a pen color from a dialog.
12160                    */
12161                    resource_info->pen_colors[pen_number]=color_name;
12162                    XColorBrowserWidget(display,windows,"Select",color_name);
12163                    if (*color_name == '\0')
12164                      break;
12165                  }
12166                /*
12167                  Set pen color.
12168                */
12169                (void) XParseColor(display,windows->map_info->colormap,
12170                  resource_info->pen_colors[pen_number],&color);
12171                XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12172                  (unsigned int) MaxColors,&color);
12173                windows->pixel_info->pen_colors[pen_number]=color;
12174                pen_id=(unsigned int) pen_number;
12175                break;
12176              }
12177              case RotateDirectionCommand:
12178              {
12179                static const char
12180                  *Directions[] =
12181                  {
12182                    "horizontal",
12183                    "vertical",
12184                    (char *) NULL,
12185                  };
12186
12187                /*
12188                  Select a command from the pop-up menu.
12189                */
12190                id=XMenuWidget(display,windows,RotateMenu[id],
12191                  Directions,command);
12192                if (id >= 0)
12193                  direction=DirectionCommands[id];
12194                break;
12195              }
12196              case RotateHelpCommand:
12197              {
12198                XTextViewWidget(display,resource_info,windows,MagickFalse,
12199                  "Help Viewer - Image Rotation",ImageRotateHelp);
12200                break;
12201              }
12202              case RotateDismissCommand:
12203              {
12204                /*
12205                  Prematurely exit.
12206                */
12207                state|=EscapeState;
12208                state|=ExitState;
12209                break;
12210              }
12211              default:
12212                break;
12213            }
12214            (void) XSetFunction(display,windows->image.highlight_context,
12215              GXinvert);
12216            continue;
12217          }
12218        switch (event.type)
12219        {
12220          case ButtonPress:
12221          {
12222            if (event.xbutton.button != Button1)
12223              break;
12224            if (event.xbutton.window != windows->image.id)
12225              break;
12226            /*
12227              exit loop.
12228            */
12229            (void) XSetFunction(display,windows->image.highlight_context,
12230              GXcopy);
12231            rotate_info.x1=event.xbutton.x;
12232            rotate_info.y1=event.xbutton.y;
12233            state|=ExitState;
12234            break;
12235          }
12236          case ButtonRelease:
12237            break;
12238          case Expose:
12239            break;
12240          case KeyPress:
12241          {
12242            char
12243              command[MaxTextExtent];
12244
12245            KeySym
12246              key_symbol;
12247
12248            if (event.xkey.window != windows->image.id)
12249              break;
12250            /*
12251              Respond to a user key press.
12252            */
12253            (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12254              sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12255            switch ((int) key_symbol)
12256            {
12257              case XK_Escape:
12258              case XK_F20:
12259              {
12260                /*
12261                  Prematurely exit.
12262                */
12263                state|=EscapeState;
12264                state|=ExitState;
12265                break;
12266              }
12267              case XK_F1:
12268              case XK_Help:
12269              {
12270                (void) XSetFunction(display,windows->image.highlight_context,
12271                  GXcopy);
12272                XTextViewWidget(display,resource_info,windows,MagickFalse,
12273                  "Help Viewer - Image Rotation",ImageRotateHelp);
12274                (void) XSetFunction(display,windows->image.highlight_context,
12275                  GXinvert);
12276                break;
12277              }
12278              default:
12279              {
12280                (void) XBell(display,0);
12281                break;
12282              }
12283            }
12284            break;
12285          }
12286          case MotionNotify:
12287          {
12288            rotate_info.x1=event.xmotion.x;
12289            rotate_info.y1=event.xmotion.y;
12290          }
12291        }
12292        rotate_info.x2=rotate_info.x1;
12293        rotate_info.y2=rotate_info.y1;
12294        if (direction == HorizontalRotateCommand)
12295          rotate_info.x2+=32;
12296        else
12297          rotate_info.y2-=32;
12298      } while ((state & ExitState) == 0);
12299      (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12300      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12301      if ((state & EscapeState) != 0)
12302        return(MagickTrue);
12303      /*
12304        Draw line as pointer moves until the mouse button is released.
12305      */
12306      distance=0;
12307      (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12308      state=DefaultState;
12309      do
12310      {
12311        if (distance > 9)
12312          {
12313            /*
12314              Display info and draw rotation line.
12315            */
12316            if (windows->info.mapped == MagickFalse)
12317              (void) XMapWindow(display,windows->info.id);
12318            (void) FormatLocaleString(text,MaxTextExtent," %g",
12319              direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12320            XInfoWidget(display,windows,text);
12321            XHighlightLine(display,windows->image.id,
12322              windows->image.highlight_context,&rotate_info);
12323          }
12324        else
12325          if (windows->info.mapped != MagickFalse)
12326            (void) XWithdrawWindow(display,windows->info.id,
12327              windows->info.screen);
12328        /*
12329          Wait for next event.
12330        */
12331        XScreenEvent(display,windows,&event,exception);
12332        if (distance > 9)
12333          XHighlightLine(display,windows->image.id,
12334            windows->image.highlight_context,&rotate_info);
12335        switch (event.type)
12336        {
12337          case ButtonPress:
12338            break;
12339          case ButtonRelease:
12340          {
12341            /*
12342              User has committed to rotation line.
12343            */
12344            rotate_info.x2=event.xbutton.x;
12345            rotate_info.y2=event.xbutton.y;
12346            state|=ExitState;
12347            break;
12348          }
12349          case Expose:
12350            break;
12351          case MotionNotify:
12352          {
12353            rotate_info.x2=event.xmotion.x;
12354            rotate_info.y2=event.xmotion.y;
12355          }
12356          default:
12357            break;
12358        }
12359        /*
12360          Check boundary conditions.
12361        */
12362        if (rotate_info.x2 < 0)
12363          rotate_info.x2=0;
12364        else
12365          if (rotate_info.x2 > (int) windows->image.width)
12366            rotate_info.x2=(short) windows->image.width;
12367        if (rotate_info.y2 < 0)
12368          rotate_info.y2=0;
12369        else
12370          if (rotate_info.y2 > (int) windows->image.height)
12371            rotate_info.y2=(short) windows->image.height;
12372        /*
12373          Compute rotation angle from the slope of the line.
12374        */
12375        degrees=0.0;
12376        distance=(unsigned int)
12377          ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12378          ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12379        if (distance > 9)
12380          degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12381            rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12382      } while ((state & ExitState) == 0);
12383      (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12384      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12385      if (distance <= 9)
12386        return(MagickTrue);
12387    }
12388  if (direction == VerticalRotateCommand)
12389    degrees-=90.0;
12390  if (degrees == 0.0)
12391    return(MagickTrue);
12392  /*
12393    Rotate image.
12394  */
12395  normalized_degrees=degrees;
12396  while (normalized_degrees < -45.0)
12397    normalized_degrees+=360.0;
12398  for (rotations=0; normalized_degrees > 45.0; rotations++)
12399    normalized_degrees-=90.0;
12400  if (normalized_degrees != 0.0)
12401    (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12402      exception);
12403  XSetCursorState(display,windows,MagickTrue);
12404  XCheckRefreshWindows(display,windows);
12405  (*image)->background_color.red=(double) ScaleShortToQuantum(
12406    windows->pixel_info->pen_colors[pen_id].red);
12407  (*image)->background_color.green=(double) ScaleShortToQuantum(
12408    windows->pixel_info->pen_colors[pen_id].green);
12409  (*image)->background_color.blue=(double) ScaleShortToQuantum(
12410    windows->pixel_info->pen_colors[pen_id].blue);
12411  rotate_image=RotateImage(*image,degrees,exception);
12412  XSetCursorState(display,windows,MagickFalse);
12413  if (rotate_image == (Image *) NULL)
12414    return(MagickFalse);
12415  *image=DestroyImage(*image);
12416  *image=rotate_image;
12417  if (windows->image.crop_geometry != (char *) NULL)
12418    {
12419      /*
12420        Rotate crop geometry.
12421      */
12422      width=(unsigned int) (*image)->columns;
12423      height=(unsigned int) (*image)->rows;
12424      (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12425      switch (rotations % 4)
12426      {
12427        default:
12428        case 0:
12429          break;
12430        case 1:
12431        {
12432          /*
12433            Rotate 90 degrees.
12434          */
12435          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12436            "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12437            (int) height-y,x);
12438          break;
12439        }
12440        case 2:
12441        {
12442          /*
12443            Rotate 180 degrees.
12444          */
12445          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12446            "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12447          break;
12448        }
12449        case 3:
12450        {
12451          /*
12452            Rotate 270 degrees.
12453          */
12454          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12455            "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12456          break;
12457        }
12458      }
12459    }
12460  if (windows->image.orphan != MagickFalse)
12461    return(MagickTrue);
12462  if (normalized_degrees != 0.0)
12463    {
12464      /*
12465        Update image colormap.
12466      */
12467      windows->image.window_changes.width=(int) (*image)->columns;
12468      windows->image.window_changes.height=(int) (*image)->rows;
12469      if (windows->image.crop_geometry != (char *) NULL)
12470        {
12471          /*
12472            Obtain dimensions of image from crop geometry.
12473          */
12474          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12475            &width,&height);
12476          windows->image.window_changes.width=(int) width;
12477          windows->image.window_changes.height=(int) height;
12478        }
12479      XConfigureImageColormap(display,resource_info,windows,*image,exception);
12480    }
12481  else
12482    if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12483      {
12484        windows->image.window_changes.width=windows->image.ximage->height;
12485        windows->image.window_changes.height=windows->image.ximage->width;
12486      }
12487  /*
12488    Update image configuration.
12489  */
12490  (void) XConfigureImage(display,resource_info,windows,*image,exception);
12491  return(MagickTrue);
12492}
12493
12494/*
12495%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12496%                                                                             %
12497%                                                                             %
12498%                                                                             %
12499+   X S a v e I m a g e                                                       %
12500%                                                                             %
12501%                                                                             %
12502%                                                                             %
12503%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12504%
12505%  XSaveImage() saves an image to a file.
12506%
12507%  The format of the XSaveImage method is:
12508%
12509%      MagickBooleanType XSaveImage(Display *display,
12510%        XResourceInfo *resource_info,XWindows *windows,Image *image,
12511%        ExceptionInfo *exception)
12512%
12513%  A description of each parameter follows:
12514%
12515%    o display: Specifies a connection to an X server; returned from
12516%      XOpenDisplay.
12517%
12518%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12519%
12520%    o windows: Specifies a pointer to a XWindows structure.
12521%
12522%    o image: the image.
12523%
12524%    o exception: return any errors or warnings in this structure.
12525%
12526*/
12527static MagickBooleanType XSaveImage(Display *display,
12528  XResourceInfo *resource_info,XWindows *windows,Image *image,
12529  ExceptionInfo *exception)
12530{
12531  char
12532    filename[MaxTextExtent],
12533    geometry[MaxTextExtent];
12534
12535  Image
12536    *save_image;
12537
12538  ImageInfo
12539    *image_info;
12540
12541  MagickStatusType
12542    status;
12543
12544  /*
12545    Request file name from user.
12546  */
12547  if (resource_info->write_filename != (char *) NULL)
12548    (void) CopyMagickString(filename,resource_info->write_filename,
12549      MaxTextExtent);
12550  else
12551    {
12552      char
12553        path[MaxTextExtent];
12554
12555      int
12556        status;
12557
12558      GetPathComponent(image->filename,HeadPath,path);
12559      GetPathComponent(image->filename,TailPath,filename);
12560      if (*path != '\0')
12561        {
12562          status=chdir(path);
12563          if (status == -1)
12564            (void) ThrowMagickException(exception,GetMagickModule(),
12565              FileOpenError,"UnableToOpenFile","%s",path);
12566        }
12567    }
12568  XFileBrowserWidget(display,windows,"Save",filename);
12569  if (*filename == '\0')
12570    return(MagickTrue);
12571  if (IsPathAccessible(filename) != MagickFalse)
12572    {
12573      int
12574        status;
12575
12576      /*
12577        File exists-- seek user's permission before overwriting.
12578      */
12579      status=XConfirmWidget(display,windows,"Overwrite",filename);
12580      if (status <= 0)
12581        return(MagickTrue);
12582    }
12583  image_info=CloneImageInfo(resource_info->image_info);
12584  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12585  (void) SetImageInfo(image_info,1,exception);
12586  if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12587      (LocaleCompare(image_info->magick,"JPG") == 0))
12588    {
12589      char
12590        quality[MaxTextExtent];
12591
12592      int
12593        status;
12594
12595      /*
12596        Request JPEG quality from user.
12597      */
12598      (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
12599        image->quality);
12600      status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12601        quality);
12602      if (*quality == '\0')
12603        return(MagickTrue);
12604      image->quality=StringToUnsignedLong(quality);
12605      image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12606    }
12607  if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12608      (LocaleCompare(image_info->magick,"PDF") == 0) ||
12609      (LocaleCompare(image_info->magick,"PS") == 0) ||
12610      (LocaleCompare(image_info->magick,"PS2") == 0))
12611    {
12612      char
12613        geometry[MaxTextExtent];
12614
12615      /*
12616        Request page geometry from user.
12617      */
12618      (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12619      if (LocaleCompare(image_info->magick,"PDF") == 0)
12620        (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12621      if (image_info->page != (char *) NULL)
12622        (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12623      XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12624        "Select page geometry:",geometry);
12625      if (*geometry != '\0')
12626        image_info->page=GetPageGeometry(geometry);
12627    }
12628  /*
12629    Apply image transforms.
12630  */
12631  XSetCursorState(display,windows,MagickTrue);
12632  XCheckRefreshWindows(display,windows);
12633  save_image=CloneImage(image,0,0,MagickTrue,exception);
12634  if (save_image == (Image *) NULL)
12635    return(MagickFalse);
12636  (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
12637    windows->image.ximage->width,windows->image.ximage->height);
12638  (void) TransformImage(&save_image,windows->image.crop_geometry,geometry,
12639    exception);
12640  /*
12641    Write image.
12642  */
12643  (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12644  status=WriteImage(image_info,save_image,exception);
12645  if (status != MagickFalse)
12646    image->taint=MagickFalse;
12647  save_image=DestroyImage(save_image);
12648  image_info=DestroyImageInfo(image_info);
12649  XSetCursorState(display,windows,MagickFalse);
12650  return(status != 0 ? MagickTrue : MagickFalse);
12651}
12652
12653/*
12654%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12655%                                                                             %
12656%                                                                             %
12657%                                                                             %
12658+   X S c r e e n E v e n t                                                   %
12659%                                                                             %
12660%                                                                             %
12661%                                                                             %
12662%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12663%
12664%  XScreenEvent() handles global events associated with the Pan and Magnify
12665%  windows.
12666%
12667%  The format of the XScreenEvent function is:
12668%
12669%      void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12670%        ExceptionInfo *exception)
12671%
12672%  A description of each parameter follows:
12673%
12674%    o display: Specifies a pointer to the Display structure;  returned from
12675%      XOpenDisplay.
12676%
12677%    o windows: Specifies a pointer to a XWindows structure.
12678%
12679%    o event: Specifies a pointer to a X11 XEvent structure.
12680%
12681%    o exception: return any errors or warnings in this structure.
12682%
12683*/
12684
12685#if defined(__cplusplus) || defined(c_plusplus)
12686extern "C" {
12687#endif
12688
12689static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12690{
12691  register XWindows
12692    *windows;
12693
12694  windows=(XWindows *) data;
12695  if ((event->type == ClientMessage) &&
12696      (event->xclient.window == windows->image.id))
12697    return(MagickFalse);
12698  return(MagickTrue);
12699}
12700
12701#if defined(__cplusplus) || defined(c_plusplus)
12702}
12703#endif
12704
12705static void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12706  ExceptionInfo *exception)
12707{
12708  register int
12709    x,
12710    y;
12711
12712  (void) XIfEvent(display,event,XPredicate,(char *) windows);
12713  if (event->xany.window == windows->command.id)
12714    return;
12715  switch (event->type)
12716  {
12717    case ButtonPress:
12718    case ButtonRelease:
12719    {
12720      if ((event->xbutton.button == Button3) &&
12721          (event->xbutton.state & Mod1Mask))
12722        {
12723          /*
12724            Convert Alt-Button3 to Button2.
12725          */
12726          event->xbutton.button=Button2;
12727          event->xbutton.state&=(~Mod1Mask);
12728        }
12729      if (event->xbutton.window == windows->backdrop.id)
12730        {
12731          (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12732            event->xbutton.time);
12733          break;
12734        }
12735      if (event->xbutton.window == windows->pan.id)
12736        {
12737          XPanImage(display,windows,event,exception);
12738          break;
12739        }
12740      if (event->xbutton.window == windows->image.id)
12741        if (event->xbutton.button == Button2)
12742          {
12743            /*
12744              Update magnified image.
12745            */
12746            x=event->xbutton.x;
12747            y=event->xbutton.y;
12748            if (x < 0)
12749              x=0;
12750            else
12751              if (x >= (int) windows->image.width)
12752                x=(int) (windows->image.width-1);
12753            windows->magnify.x=(int) windows->image.x+x;
12754            if (y < 0)
12755              y=0;
12756            else
12757             if (y >= (int) windows->image.height)
12758               y=(int) (windows->image.height-1);
12759            windows->magnify.y=windows->image.y+y;
12760            if (windows->magnify.mapped == MagickFalse)
12761              (void) XMapRaised(display,windows->magnify.id);
12762            XMakeMagnifyImage(display,windows,exception);
12763            if (event->type == ButtonRelease)
12764              (void) XWithdrawWindow(display,windows->info.id,
12765                windows->info.screen);
12766            break;
12767          }
12768      break;
12769    }
12770    case ClientMessage:
12771    {
12772      /*
12773        If client window delete message, exit.
12774      */
12775      if (event->xclient.message_type != windows->wm_protocols)
12776        break;
12777      if (*event->xclient.data.l != (long) windows->wm_delete_window)
12778        break;
12779      if (event->xclient.window == windows->magnify.id)
12780        {
12781          (void) XWithdrawWindow(display,windows->magnify.id,
12782            windows->magnify.screen);
12783          break;
12784        }
12785      break;
12786    }
12787    case ConfigureNotify:
12788    {
12789      if (event->xconfigure.window == windows->magnify.id)
12790        {
12791          unsigned int
12792            magnify;
12793
12794          /*
12795            Magnify window has a new configuration.
12796          */
12797          windows->magnify.width=(unsigned int) event->xconfigure.width;
12798          windows->magnify.height=(unsigned int) event->xconfigure.height;
12799          if (windows->magnify.mapped == MagickFalse)
12800            break;
12801          magnify=1;
12802          while ((int) magnify <= event->xconfigure.width)
12803            magnify<<=1;
12804          while ((int) magnify <= event->xconfigure.height)
12805            magnify<<=1;
12806          magnify>>=1;
12807          if (((int) magnify != event->xconfigure.width) ||
12808              ((int) magnify != event->xconfigure.height))
12809            {
12810              XWindowChanges
12811                window_changes;
12812
12813              window_changes.width=(int) magnify;
12814              window_changes.height=(int) magnify;
12815              (void) XReconfigureWMWindow(display,windows->magnify.id,
12816                windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12817                &window_changes);
12818              break;
12819            }
12820          XMakeMagnifyImage(display,windows,exception);
12821          break;
12822        }
12823      break;
12824    }
12825    case Expose:
12826    {
12827      if (event->xexpose.window == windows->image.id)
12828        {
12829          XRefreshWindow(display,&windows->image,event);
12830          break;
12831        }
12832      if (event->xexpose.window == windows->pan.id)
12833        if (event->xexpose.count == 0)
12834          {
12835            XDrawPanRectangle(display,windows);
12836            break;
12837          }
12838      if (event->xexpose.window == windows->magnify.id)
12839        if (event->xexpose.count == 0)
12840          {
12841            XMakeMagnifyImage(display,windows,exception);
12842            break;
12843          }
12844      break;
12845    }
12846    case KeyPress:
12847    {
12848      char
12849        command[MaxTextExtent];
12850
12851      KeySym
12852        key_symbol;
12853
12854      if (event->xkey.window != windows->magnify.id)
12855        break;
12856      /*
12857        Respond to a user key press.
12858      */
12859      (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12860        sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12861      XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol,
12862        exception);
12863      break;
12864    }
12865    case MapNotify:
12866    {
12867      if (event->xmap.window == windows->magnify.id)
12868        {
12869          windows->magnify.mapped=MagickTrue;
12870          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12871          break;
12872        }
12873      if (event->xmap.window == windows->info.id)
12874        {
12875          windows->info.mapped=MagickTrue;
12876          break;
12877        }
12878      break;
12879    }
12880    case MotionNotify:
12881    {
12882      while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12883      if (event->xmotion.window == windows->image.id)
12884        if (windows->magnify.mapped != MagickFalse)
12885          {
12886            /*
12887              Update magnified image.
12888            */
12889            x=event->xmotion.x;
12890            y=event->xmotion.y;
12891            if (x < 0)
12892              x=0;
12893            else
12894              if (x >= (int) windows->image.width)
12895                x=(int) (windows->image.width-1);
12896            windows->magnify.x=(int) windows->image.x+x;
12897            if (y < 0)
12898              y=0;
12899            else
12900             if (y >= (int) windows->image.height)
12901               y=(int) (windows->image.height-1);
12902            windows->magnify.y=windows->image.y+y;
12903            XMakeMagnifyImage(display,windows,exception);
12904          }
12905      break;
12906    }
12907    case UnmapNotify:
12908    {
12909      if (event->xunmap.window == windows->magnify.id)
12910        {
12911          windows->magnify.mapped=MagickFalse;
12912          break;
12913        }
12914      if (event->xunmap.window == windows->info.id)
12915        {
12916          windows->info.mapped=MagickFalse;
12917          break;
12918        }
12919      break;
12920    }
12921    default:
12922      break;
12923  }
12924}
12925
12926/*
12927%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12928%                                                                             %
12929%                                                                             %
12930%                                                                             %
12931+   X S e t C r o p G e o m e t r y                                           %
12932%                                                                             %
12933%                                                                             %
12934%                                                                             %
12935%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12936%
12937%  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12938%  and translates it to a cropping geometry relative to the image.
12939%
12940%  The format of the XSetCropGeometry method is:
12941%
12942%      void XSetCropGeometry(Display *display,XWindows *windows,
12943%        RectangleInfo *crop_info,Image *image)
12944%
12945%  A description of each parameter follows:
12946%
12947%    o display: Specifies a connection to an X server; returned from
12948%      XOpenDisplay.
12949%
12950%    o windows: Specifies a pointer to a XWindows structure.
12951%
12952%    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12953%      Image window to crop.
12954%
12955%    o image: the image.
12956%
12957*/
12958static void XSetCropGeometry(Display *display,XWindows *windows,
12959  RectangleInfo *crop_info,Image *image)
12960{
12961  char
12962    text[MaxTextExtent];
12963
12964  int
12965    x,
12966    y;
12967
12968  MagickRealType
12969    scale_factor;
12970
12971  unsigned int
12972    height,
12973    width;
12974
12975  if (windows->info.mapped != MagickFalse)
12976    {
12977      /*
12978        Display info on cropping rectangle.
12979      */
12980      (void) FormatLocaleString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
12981        (double) crop_info->width,(double) crop_info->height,(double)
12982        crop_info->x,(double) crop_info->y);
12983      XInfoWidget(display,windows,text);
12984    }
12985  /*
12986    Cropping geometry is relative to any previous crop geometry.
12987  */
12988  x=0;
12989  y=0;
12990  width=(unsigned int) image->columns;
12991  height=(unsigned int) image->rows;
12992  if (windows->image.crop_geometry != (char *) NULL)
12993    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12994  else
12995    windows->image.crop_geometry=AcquireString((char *) NULL);
12996  /*
12997    Define the crop geometry string from the cropping rectangle.
12998  */
12999  scale_factor=(MagickRealType) width/windows->image.ximage->width;
13000  if (crop_info->x > 0)
13001    x+=(int) (scale_factor*crop_info->x+0.5);
13002  width=(unsigned int) (scale_factor*crop_info->width+0.5);
13003  if (width == 0)
13004    width=1;
13005  scale_factor=(MagickRealType) height/windows->image.ximage->height;
13006  if (crop_info->y > 0)
13007    y+=(int) (scale_factor*crop_info->y+0.5);
13008  height=(unsigned int) (scale_factor*crop_info->height+0.5);
13009  if (height == 0)
13010    height=1;
13011  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
13012    "%ux%u%+d%+d",width,height,x,y);
13013}
13014
13015/*
13016%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13017%                                                                             %
13018%                                                                             %
13019%                                                                             %
13020+   X T i l e I m a g e                                                       %
13021%                                                                             %
13022%                                                                             %
13023%                                                                             %
13024%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13025%
13026%  XTileImage() loads or deletes a selected tile from a visual image directory.
13027%  The load or delete command is chosen from a menu.
13028%
13029%  The format of the XTileImage method is:
13030%
13031%      Image *XTileImage(Display *display,XResourceInfo *resource_info,
13032%        XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13033%
13034%  A description of each parameter follows:
13035%
13036%    o tile_image:  XTileImage reads or deletes the tile image
13037%      and returns it.  A null image is returned if an error occurs.
13038%
13039%    o display: Specifies a connection to an X server;  returned from
13040%      XOpenDisplay.
13041%
13042%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13043%
13044%    o windows: Specifies a pointer to a XWindows structure.
13045%
13046%    o image: the image; returned from ReadImage.
13047%
13048%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
13049%      the entire image is refreshed.
13050%
13051%    o exception: return any errors or warnings in this structure.
13052%
13053*/
13054static Image *XTileImage(Display *display,XResourceInfo *resource_info,
13055  XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13056{
13057  static const char
13058    *VerbMenu[] =
13059    {
13060      "Load",
13061      "Next",
13062      "Former",
13063      "Delete",
13064      "Update",
13065      (char *) NULL,
13066    };
13067
13068  static const ModeType
13069    TileCommands[] =
13070    {
13071      TileLoadCommand,
13072      TileNextCommand,
13073      TileFormerCommand,
13074      TileDeleteCommand,
13075      TileUpdateCommand
13076    };
13077
13078  char
13079    command[MaxTextExtent],
13080    filename[MaxTextExtent];
13081
13082  Image
13083    *tile_image;
13084
13085  int
13086    id,
13087    status,
13088    tile,
13089    x,
13090    y;
13091
13092  MagickRealType
13093    scale_factor;
13094
13095  register char
13096    *p,
13097    *q;
13098
13099  register int
13100    i;
13101
13102  unsigned int
13103    height,
13104    width;
13105
13106  /*
13107    Tile image is relative to montage image configuration.
13108  */
13109  x=0;
13110  y=0;
13111  width=(unsigned int) image->columns;
13112  height=(unsigned int) image->rows;
13113  if (windows->image.crop_geometry != (char *) NULL)
13114    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13115  scale_factor=(MagickRealType) width/windows->image.ximage->width;
13116  event->xbutton.x+=windows->image.x;
13117  event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13118  scale_factor=(MagickRealType) height/windows->image.ximage->height;
13119  event->xbutton.y+=windows->image.y;
13120  event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13121  /*
13122    Determine size and location of each tile in the visual image directory.
13123  */
13124  width=(unsigned int) image->columns;
13125  height=(unsigned int) image->rows;
13126  x=0;
13127  y=0;
13128  (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13129  tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13130    (event->xbutton.x-x)/width;
13131  if (tile < 0)
13132    {
13133      /*
13134        Button press is outside any tile.
13135      */
13136      (void) XBell(display,0);
13137      return((Image *) NULL);
13138    }
13139  /*
13140    Determine file name from the tile directory.
13141  */
13142  p=image->directory;
13143  for (i=tile; (i != 0) && (*p != '\0'); )
13144  {
13145    if (*p == '\n')
13146      i--;
13147    p++;
13148  }
13149  if (*p == '\0')
13150    {
13151      /*
13152        Button press is outside any tile.
13153      */
13154      (void) XBell(display,0);
13155      return((Image *) NULL);
13156    }
13157  /*
13158    Select a command from the pop-up menu.
13159  */
13160  id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13161  if (id < 0)
13162    return((Image *) NULL);
13163  q=p;
13164  while ((*q != '\n') && (*q != '\0'))
13165    q++;
13166  (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13167  /*
13168    Perform command for the selected tile.
13169  */
13170  XSetCursorState(display,windows,MagickTrue);
13171  XCheckRefreshWindows(display,windows);
13172  tile_image=NewImageList();
13173  switch (TileCommands[id])
13174  {
13175    case TileLoadCommand:
13176    {
13177      /*
13178        Load tile image.
13179      */
13180      XCheckRefreshWindows(display,windows);
13181      (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13182        MaxTextExtent);
13183      (void) CopyMagickString(resource_info->image_info->filename,filename,
13184        MaxTextExtent);
13185      tile_image=ReadImage(resource_info->image_info,exception);
13186      CatchException(exception);
13187      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13188      break;
13189    }
13190    case TileNextCommand:
13191    {
13192      /*
13193        Display next image.
13194      */
13195      XClientMessage(display,windows->image.id,windows->im_protocols,
13196        windows->im_next_image,CurrentTime);
13197      break;
13198    }
13199    case TileFormerCommand:
13200    {
13201      /*
13202        Display former image.
13203      */
13204      XClientMessage(display,windows->image.id,windows->im_protocols,
13205        windows->im_former_image,CurrentTime);
13206      break;
13207    }
13208    case TileDeleteCommand:
13209    {
13210      /*
13211        Delete tile image.
13212      */
13213      if (IsPathAccessible(filename) == MagickFalse)
13214        {
13215          XNoticeWidget(display,windows,"Image file does not exist:",filename);
13216          break;
13217        }
13218      status=XConfirmWidget(display,windows,"Really delete tile",filename);
13219      if (status <= 0)
13220        break;
13221      status=remove_utf8(filename) != 0 ? MagickTrue : MagickFalse;
13222      if (status != MagickFalse)
13223        {
13224          XNoticeWidget(display,windows,"Unable to delete image file:",
13225            filename);
13226          break;
13227        }
13228    }
13229    case TileUpdateCommand:
13230    {
13231      int
13232        x_offset,
13233        y_offset;
13234
13235      PixelInfo
13236        pixel;
13237
13238      register int
13239        j;
13240
13241      register Quantum
13242        *s;
13243
13244      /*
13245        Ensure all the images exist.
13246      */
13247      tile=0;
13248      GetPixelInfo(image,&pixel);
13249      for (p=image->directory; *p != '\0'; p++)
13250      {
13251        CacheView
13252          *image_view;
13253
13254        q=p;
13255        while ((*q != '\n') && (*q != '\0'))
13256          q++;
13257        (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13258        p=q;
13259        if (IsPathAccessible(filename) != MagickFalse)
13260          {
13261            tile++;
13262            continue;
13263          }
13264        /*
13265          Overwrite tile with background color.
13266        */
13267        x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13268        y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13269        image_view=AcquireCacheView(image);
13270        (void) GetOneCacheViewVirtualPixelInfo(image_view,0,0,&pixel,exception);
13271        for (i=0; i < (int) height; i++)
13272        {
13273          s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13274            y_offset+i,width,1,exception);
13275          if (s == (Quantum *) NULL)
13276            break;
13277          for (j=0; j < (int) width; j++)
13278          {
13279            SetPixelInfoPixel(image,&pixel,s);
13280            s+=GetPixelChannels(image);
13281          }
13282          if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13283            break;
13284        }
13285        image_view=DestroyCacheView(image_view);
13286        tile++;
13287      }
13288      windows->image.window_changes.width=(int) image->columns;
13289      windows->image.window_changes.height=(int) image->rows;
13290      XConfigureImageColormap(display,resource_info,windows,image,exception);
13291      (void) XConfigureImage(display,resource_info,windows,image,exception);
13292      break;
13293    }
13294    default:
13295      break;
13296  }
13297  XSetCursorState(display,windows,MagickFalse);
13298  return(tile_image);
13299}
13300
13301/*
13302%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13303%                                                                             %
13304%                                                                             %
13305%                                                                             %
13306+   X T r a n s l a t e I m a g e                                             %
13307%                                                                             %
13308%                                                                             %
13309%                                                                             %
13310%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13311%
13312%  XTranslateImage() translates the image within an Image window by one pixel
13313%  as specified by the key symbol.  If the image has a `montage string the
13314%  translation is respect to the width and height contained within the string.
13315%
13316%  The format of the XTranslateImage method is:
13317%
13318%      void XTranslateImage(Display *display,XWindows *windows,
13319%        Image *image,const KeySym key_symbol)
13320%
13321%  A description of each parameter follows:
13322%
13323%    o display: Specifies a connection to an X server; returned from
13324%      XOpenDisplay.
13325%
13326%    o windows: Specifies a pointer to a XWindows structure.
13327%
13328%    o image: the image.
13329%
13330%    o key_symbol: Specifies a KeySym which indicates which side of the image
13331%      to trim.
13332%
13333*/
13334static void XTranslateImage(Display *display,XWindows *windows,
13335  Image *image,const KeySym key_symbol)
13336{
13337  char
13338    text[MaxTextExtent];
13339
13340  int
13341    x,
13342    y;
13343
13344  unsigned int
13345    x_offset,
13346    y_offset;
13347
13348  /*
13349    User specified a pan position offset.
13350  */
13351  x_offset=windows->image.width;
13352  y_offset=windows->image.height;
13353  if (image->montage != (char *) NULL)
13354    (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13355  switch ((int) key_symbol)
13356  {
13357    case XK_Home:
13358    case XK_KP_Home:
13359    {
13360      windows->image.x=(int) windows->image.width/2;
13361      windows->image.y=(int) windows->image.height/2;
13362      break;
13363    }
13364    case XK_Left:
13365    case XK_KP_Left:
13366    {
13367      windows->image.x-=x_offset;
13368      break;
13369    }
13370    case XK_Next:
13371    case XK_Up:
13372    case XK_KP_Up:
13373    {
13374      windows->image.y-=y_offset;
13375      break;
13376    }
13377    case XK_Right:
13378    case XK_KP_Right:
13379    {
13380      windows->image.x+=x_offset;
13381      break;
13382    }
13383    case XK_Prior:
13384    case XK_Down:
13385    case XK_KP_Down:
13386    {
13387      windows->image.y+=y_offset;
13388      break;
13389    }
13390    default:
13391      return;
13392  }
13393  /*
13394    Check boundary conditions.
13395  */
13396  if (windows->image.x < 0)
13397    windows->image.x=0;
13398  else
13399    if ((int) (windows->image.x+windows->image.width) >
13400        windows->image.ximage->width)
13401      windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13402  if (windows->image.y < 0)
13403    windows->image.y=0;
13404  else
13405    if ((int) (windows->image.y+windows->image.height) >
13406        windows->image.ximage->height)
13407      windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13408  /*
13409    Refresh Image window.
13410  */
13411  (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
13412    windows->image.width,windows->image.height,windows->image.x,
13413    windows->image.y);
13414  XInfoWidget(display,windows,text);
13415  XCheckRefreshWindows(display,windows);
13416  XDrawPanRectangle(display,windows);
13417  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13418  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13419}
13420
13421/*
13422%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13423%                                                                             %
13424%                                                                             %
13425%                                                                             %
13426+   X T r i m I m a g e                                                       %
13427%                                                                             %
13428%                                                                             %
13429%                                                                             %
13430%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13431%
13432%  XTrimImage() trims the edges from the Image window.
13433%
13434%  The format of the XTrimImage method is:
13435%
13436%      MagickBooleanType XTrimImage(Display *display,
13437%        XResourceInfo *resource_info,XWindows *windows,Image *image,
13438%        ExceptionInfo *exception)
13439%
13440%  A description of each parameter follows:
13441%
13442%    o display: Specifies a connection to an X server; returned from
13443%      XOpenDisplay.
13444%
13445%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13446%
13447%    o windows: Specifies a pointer to a XWindows structure.
13448%
13449%    o image: the image.
13450%
13451%    o exception: return any errors or warnings in this structure.
13452%
13453*/
13454static MagickBooleanType XTrimImage(Display *display,
13455  XResourceInfo *resource_info,XWindows *windows,Image *image,
13456  ExceptionInfo *exception)
13457{
13458  RectangleInfo
13459    trim_info;
13460
13461  register int
13462    x,
13463    y;
13464
13465  size_t
13466    background,
13467    pixel;
13468
13469  /*
13470    Trim edges from image.
13471  */
13472  XSetCursorState(display,windows,MagickTrue);
13473  XCheckRefreshWindows(display,windows);
13474  /*
13475    Crop the left edge.
13476  */
13477  background=XGetPixel(windows->image.ximage,0,0);
13478  trim_info.width=(size_t) windows->image.ximage->width;
13479  for (x=0; x < windows->image.ximage->width; x++)
13480  {
13481    for (y=0; y < windows->image.ximage->height; y++)
13482    {
13483      pixel=XGetPixel(windows->image.ximage,x,y);
13484      if (pixel != background)
13485        break;
13486    }
13487    if (y < windows->image.ximage->height)
13488      break;
13489  }
13490  trim_info.x=(ssize_t) x;
13491  if (trim_info.x == (ssize_t) windows->image.ximage->width)
13492    {
13493      XSetCursorState(display,windows,MagickFalse);
13494      return(MagickFalse);
13495    }
13496  /*
13497    Crop the right edge.
13498  */
13499  background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13500  for (x=windows->image.ximage->width-1; x != 0; x--)
13501  {
13502    for (y=0; y < windows->image.ximage->height; y++)
13503    {
13504      pixel=XGetPixel(windows->image.ximage,x,y);
13505      if (pixel != background)
13506        break;
13507    }
13508    if (y < windows->image.ximage->height)
13509      break;
13510  }
13511  trim_info.width=(size_t) (x-trim_info.x+1);
13512  /*
13513    Crop the top edge.
13514  */
13515  background=XGetPixel(windows->image.ximage,0,0);
13516  trim_info.height=(size_t) windows->image.ximage->height;
13517  for (y=0; y < windows->image.ximage->height; y++)
13518  {
13519    for (x=0; x < windows->image.ximage->width; x++)
13520    {
13521      pixel=XGetPixel(windows->image.ximage,x,y);
13522      if (pixel != background)
13523        break;
13524    }
13525    if (x < windows->image.ximage->width)
13526      break;
13527  }
13528  trim_info.y=(ssize_t) y;
13529  /*
13530    Crop the bottom edge.
13531  */
13532  background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13533  for (y=windows->image.ximage->height-1; y != 0; y--)
13534  {
13535    for (x=0; x < windows->image.ximage->width; x++)
13536    {
13537      pixel=XGetPixel(windows->image.ximage,x,y);
13538      if (pixel != background)
13539        break;
13540    }
13541    if (x < windows->image.ximage->width)
13542      break;
13543  }
13544  trim_info.height=(size_t) y-trim_info.y+1;
13545  if (((unsigned int) trim_info.width != windows->image.width) ||
13546      ((unsigned int) trim_info.height != windows->image.height))
13547    {
13548      /*
13549        Reconfigure Image window as defined by the trimming rectangle.
13550      */
13551      XSetCropGeometry(display,windows,&trim_info,image);
13552      windows->image.window_changes.width=(int) trim_info.width;
13553      windows->image.window_changes.height=(int) trim_info.height;
13554      (void) XConfigureImage(display,resource_info,windows,image,exception);
13555    }
13556  XSetCursorState(display,windows,MagickFalse);
13557  return(MagickTrue);
13558}
13559
13560/*
13561%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13562%                                                                             %
13563%                                                                             %
13564%                                                                             %
13565+   X V i s u a l D i r e c t o r y I m a g e                                 %
13566%                                                                             %
13567%                                                                             %
13568%                                                                             %
13569%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13570%
13571%  XVisualDirectoryImage() creates a Visual Image Directory.
13572%
13573%  The format of the XVisualDirectoryImage method is:
13574%
13575%      Image *XVisualDirectoryImage(Display *display,
13576%        XResourceInfo *resource_info,XWindows *windows,
13577%        ExceptionInfo *exception)
13578%
13579%  A description of each parameter follows:
13580%
13581%    o display: Specifies a connection to an X server; returned from
13582%      XOpenDisplay.
13583%
13584%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13585%
13586%    o windows: Specifies a pointer to a XWindows structure.
13587%
13588%    o exception: return any errors or warnings in this structure.
13589%
13590*/
13591static Image *XVisualDirectoryImage(Display *display,
13592  XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
13593{
13594#define TileImageTag  "Scale/Image"
13595#define XClientName  "montage"
13596
13597  char
13598    **filelist;
13599
13600  Image
13601    *images,
13602    *montage_image,
13603    *next_image,
13604    *thumbnail_image;
13605
13606  ImageInfo
13607    *read_info;
13608
13609  int
13610    number_files;
13611
13612  MagickBooleanType
13613    backdrop;
13614
13615  MagickStatusType
13616    status;
13617
13618  MontageInfo
13619    *montage_info;
13620
13621  RectangleInfo
13622    geometry;
13623
13624  register int
13625    i;
13626
13627  static char
13628    filename[MaxTextExtent] = "\0",
13629    filenames[MaxTextExtent] = "*";
13630
13631  XResourceInfo
13632    background_resources;
13633
13634  /*
13635    Request file name from user.
13636  */
13637  XFileBrowserWidget(display,windows,"Directory",filenames);
13638  if (*filenames == '\0')
13639    return((Image *) NULL);
13640  /*
13641    Expand the filenames.
13642  */
13643  filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13644  if (filelist == (char **) NULL)
13645    {
13646      ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13647        filenames);
13648      return((Image *) NULL);
13649    }
13650  number_files=1;
13651  filelist[0]=filenames;
13652  status=ExpandFilenames(&number_files,&filelist);
13653  if ((status == MagickFalse) || (number_files == 0))
13654    {
13655      if (number_files == 0)
13656        ThrowXWindowFatalException(ImageError,"NoImagesWereFound",filenames)
13657      else
13658        ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13659          filenames);
13660      return((Image *) NULL);
13661    }
13662  /*
13663    Set image background resources.
13664  */
13665  background_resources=(*resource_info);
13666  background_resources.window_id=AcquireString("");
13667  (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
13668    "0x%lx",windows->image.id);
13669  background_resources.backdrop=MagickTrue;
13670  /*
13671    Read each image and convert them to a tile.
13672  */
13673  backdrop=(windows->visual_info->klass == TrueColor) ||
13674    (windows->visual_info->klass == DirectColor) ? MagickTrue : MagickFalse;
13675  read_info=CloneImageInfo(resource_info->image_info);
13676  (void) SetImageOption(read_info,"jpeg:size","120x120");
13677  (void) CloneString(&read_info->size,DefaultTileGeometry);
13678  (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13679    (void *) NULL);
13680  images=NewImageList();
13681  XSetCursorState(display,windows,MagickTrue);
13682  XCheckRefreshWindows(display,windows);
13683  for (i=0; i < (int) number_files; i++)
13684  {
13685    (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13686    filelist[i]=DestroyString(filelist[i]);
13687    *read_info->magick='\0';
13688    next_image=ReadImage(read_info,exception);
13689    CatchException(exception);
13690    if (next_image != (Image *) NULL)
13691      {
13692        (void) DeleteImageProperty(next_image,"label");
13693        (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13694          read_info,next_image,DefaultTileLabel,exception),exception);
13695        (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13696          exception);
13697        thumbnail_image=ThumbnailImage(next_image,geometry.width,
13698          geometry.height,exception);
13699        if (thumbnail_image != (Image *) NULL)
13700          {
13701            next_image=DestroyImage(next_image);
13702            next_image=thumbnail_image;
13703          }
13704        if (backdrop)
13705          {
13706            (void) XDisplayBackgroundImage(display,&background_resources,
13707              next_image,exception);
13708            XSetCursorState(display,windows,MagickTrue);
13709          }
13710        AppendImageToList(&images,next_image);
13711        if (images->progress_monitor != (MagickProgressMonitor) NULL)
13712          {
13713            MagickBooleanType
13714              proceed;
13715
13716            proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13717              (MagickSizeType) number_files);
13718            if (proceed == MagickFalse)
13719              break;
13720          }
13721      }
13722  }
13723  filelist=(char **) RelinquishMagickMemory(filelist);
13724  if (images == (Image *) NULL)
13725    {
13726      read_info=DestroyImageInfo(read_info);
13727      XSetCursorState(display,windows,MagickFalse);
13728      ThrowXWindowFatalException(ImageError,"NoImagesWereLoaded",filenames);
13729      return((Image *) NULL);
13730    }
13731  /*
13732    Create the Visual Image Directory.
13733  */
13734  montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13735  montage_info->pointsize=10;
13736  if (resource_info->font != (char *) NULL)
13737    (void) CloneString(&montage_info->font,resource_info->font);
13738  (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13739  montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13740    images),exception);
13741  images=DestroyImageList(images);
13742  montage_info=DestroyMontageInfo(montage_info);
13743  read_info=DestroyImageInfo(read_info);
13744  XSetCursorState(display,windows,MagickFalse);
13745  if (montage_image == (Image *) NULL)
13746    return(montage_image);
13747  XClientMessage(display,windows->image.id,windows->im_protocols,
13748    windows->im_next_image,CurrentTime);
13749  return(montage_image);
13750}
13751
13752/*
13753%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13754%                                                                             %
13755%                                                                             %
13756%                                                                             %
13757%   X D i s p l a y B a c k g r o u n d I m a g e                             %
13758%                                                                             %
13759%                                                                             %
13760%                                                                             %
13761%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13762%
13763%  XDisplayBackgroundImage() displays an image in the background of a window.
13764%
13765%  The format of the XDisplayBackgroundImage method is:
13766%
13767%      MagickBooleanType XDisplayBackgroundImage(Display *display,
13768%        XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13769%
13770%  A description of each parameter follows:
13771%
13772%    o display: Specifies a connection to an X server;  returned from
13773%      XOpenDisplay.
13774%
13775%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13776%
13777%    o image: the image.
13778%
13779%    o exception: return any errors or warnings in this structure.
13780%
13781*/
13782MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13783  XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13784{
13785  char
13786    geometry[MaxTextExtent],
13787    visual_type[MaxTextExtent];
13788
13789  int
13790    height,
13791    status,
13792    width;
13793
13794  RectangleInfo
13795    geometry_info;
13796
13797  static XPixelInfo
13798    pixel;
13799
13800  static XStandardColormap
13801    *map_info;
13802
13803  static XVisualInfo
13804    *visual_info = (XVisualInfo *) NULL;
13805
13806  static XWindowInfo
13807    window_info;
13808
13809  size_t
13810    delay;
13811
13812  Window
13813    root_window;
13814
13815  XGCValues
13816    context_values;
13817
13818  XResourceInfo
13819    resources;
13820
13821  XWindowAttributes
13822    window_attributes;
13823
13824  /*
13825    Determine target window.
13826  */
13827  assert(image != (Image *) NULL);
13828  assert(image->signature == MagickSignature);
13829  if (image->debug != MagickFalse)
13830    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13831  resources=(*resource_info);
13832  window_info.id=(Window) NULL;
13833  root_window=XRootWindow(display,XDefaultScreen(display));
13834  if (LocaleCompare(resources.window_id,"root") == 0)
13835    window_info.id=root_window;
13836  else
13837    {
13838      if (isdigit((unsigned char) *resources.window_id) != 0)
13839        window_info.id=XWindowByID(display,root_window,
13840          (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13841      if (window_info.id == (Window) NULL)
13842        window_info.id=XWindowByName(display,root_window,resources.window_id);
13843    }
13844  if (window_info.id == (Window) NULL)
13845    {
13846      ThrowXWindowFatalException(XServerError,"NoWindowWithSpecifiedIDExists",
13847        resources.window_id);
13848      return(MagickFalse);
13849    }
13850  /*
13851    Determine window visual id.
13852  */
13853  window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13854  window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13855  (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13856  status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13857  if (status != 0)
13858    (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
13859      XVisualIDFromVisual(window_attributes.visual));
13860  if (visual_info == (XVisualInfo *) NULL)
13861    {
13862      /*
13863        Allocate standard colormap.
13864      */
13865      map_info=XAllocStandardColormap();
13866      if (map_info == (XStandardColormap *) NULL)
13867        ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13868          image->filename);
13869      map_info->colormap=(Colormap) NULL;
13870      pixel.pixels=(unsigned long *) NULL;
13871      /*
13872        Initialize visual info.
13873      */
13874      resources.map_type=(char *) NULL;
13875      resources.visual_type=visual_type;
13876      visual_info=XBestVisualInfo(display,map_info,&resources);
13877      if (visual_info == (XVisualInfo *) NULL)
13878        ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13879          resources.visual_type);
13880      /*
13881        Initialize window info.
13882      */
13883      window_info.ximage=(XImage *) NULL;
13884      window_info.matte_image=(XImage *) NULL;
13885      window_info.pixmap=(Pixmap) NULL;
13886      window_info.matte_pixmap=(Pixmap) NULL;
13887    }
13888  /*
13889    Free previous root colors.
13890  */
13891  if (window_info.id == root_window)
13892    (void) XDestroyWindowColors(display,root_window);
13893  /*
13894    Initialize Standard Colormap.
13895  */
13896  resources.colormap=SharedColormap;
13897  XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel,
13898    exception);
13899  /*
13900    Graphic context superclass.
13901  */
13902  context_values.background=pixel.background_color.pixel;
13903  context_values.foreground=pixel.foreground_color.pixel;
13904  pixel.annotate_context=XCreateGC(display,window_info.id,
13905    (size_t) (GCBackground | GCForeground),&context_values);
13906  if (pixel.annotate_context == (GC) NULL)
13907    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13908      image->filename);
13909  /*
13910    Initialize Image window attributes.
13911  */
13912  window_info.name=AcquireString("\0");
13913  window_info.icon_name=AcquireString("\0");
13914  XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13915    &resources,&window_info);
13916  /*
13917    Create the X image.
13918  */
13919  window_info.width=(unsigned int) image->columns;
13920  window_info.height=(unsigned int) image->rows;
13921  if ((image->columns != window_info.width) ||
13922      (image->rows != window_info.height))
13923    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13924      image->filename);
13925  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
13926    window_attributes.width,window_attributes.height);
13927  geometry_info.width=window_info.width;
13928  geometry_info.height=window_info.height;
13929  geometry_info.x=(ssize_t) window_info.x;
13930  geometry_info.y=(ssize_t) window_info.y;
13931  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13932    &geometry_info.width,&geometry_info.height);
13933  window_info.width=(unsigned int) geometry_info.width;
13934  window_info.height=(unsigned int) geometry_info.height;
13935  window_info.x=(int) geometry_info.x;
13936  window_info.y=(int) geometry_info.y;
13937  status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13938    window_info.height,exception);
13939  if (status == MagickFalse)
13940    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13941      image->filename);
13942  window_info.x=0;
13943  window_info.y=0;
13944  if (image->debug != MagickFalse)
13945    {
13946      (void) LogMagickEvent(X11Event,GetMagickModule(),
13947        "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13948        (double) image->columns,(double) image->rows);
13949      if (image->colors != 0)
13950        (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13951          image->colors);
13952      (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13953    }
13954  /*
13955    Adjust image dimensions as specified by backdrop or geometry options.
13956  */
13957  width=(int) window_info.width;
13958  height=(int) window_info.height;
13959  if (resources.backdrop != MagickFalse)
13960    {
13961      /*
13962        Center image on window.
13963      */
13964      window_info.x=(window_attributes.width/2)-
13965        (window_info.ximage->width/2);
13966      window_info.y=(window_attributes.height/2)-
13967        (window_info.ximage->height/2);
13968      width=window_attributes.width;
13969      height=window_attributes.height;
13970    }
13971  if ((resources.image_geometry != (char *) NULL) &&
13972      (*resources.image_geometry != '\0'))
13973    {
13974      char
13975        default_geometry[MaxTextExtent];
13976
13977      int
13978        flags,
13979        gravity;
13980
13981      XSizeHints
13982        *size_hints;
13983
13984      /*
13985        User specified geometry.
13986      */
13987      size_hints=XAllocSizeHints();
13988      if (size_hints == (XSizeHints *) NULL)
13989        ThrowXWindowFatalException(ResourceLimitFatalError,
13990          "MemoryAllocationFailed",image->filename);
13991      size_hints->flags=0L;
13992      (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
13993        width,height);
13994      flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
13995        default_geometry,window_info.border_width,size_hints,&window_info.x,
13996        &window_info.y,&width,&height,&gravity);
13997      if (flags & (XValue | YValue))
13998        {
13999          width=window_attributes.width;
14000          height=window_attributes.height;
14001        }
14002      (void) XFree((void *) size_hints);
14003    }
14004  /*
14005    Create the X pixmap.
14006  */
14007  window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
14008    (unsigned int) height,window_info.depth);
14009  if (window_info.pixmap == (Pixmap) NULL)
14010    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
14011      image->filename);
14012  /*
14013    Display pixmap on the window.
14014  */
14015  if (((unsigned int) width > window_info.width) ||
14016      ((unsigned int) height > window_info.height))
14017    (void) XFillRectangle(display,window_info.pixmap,
14018      window_info.annotate_context,0,0,(unsigned int) width,
14019      (unsigned int) height);
14020  (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
14021    window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
14022    window_info.width,(unsigned int) window_info.height);
14023  (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
14024  (void) XClearWindow(display,window_info.id);
14025  delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
14026  XDelay(display,delay == 0UL ? 10UL : delay);
14027  (void) XSync(display,MagickFalse);
14028  return(window_info.id == root_window ? MagickTrue : MagickFalse);
14029}
14030
14031/*
14032%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14033%                                                                             %
14034%                                                                             %
14035%                                                                             %
14036+   X D i s p l a y I m a g e                                                 %
14037%                                                                             %
14038%                                                                             %
14039%                                                                             %
14040%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14041%
14042%  XDisplayImage() displays an image via X11.  A new image is created and
14043%  returned if the user interactively transforms the displayed image.
14044%
14045%  The format of the XDisplayImage method is:
14046%
14047%      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14048%        char **argv,int argc,Image **image,size_t *state,
14049%        ExceptionInfo *exception)
14050%
14051%  A description of each parameter follows:
14052%
14053%    o nexus:  Method XDisplayImage returns an image when the
14054%      user chooses 'Open Image' from the command menu or picks a tile
14055%      from the image directory.  Otherwise a null image is returned.
14056%
14057%    o display: Specifies a connection to an X server;  returned from
14058%      XOpenDisplay.
14059%
14060%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14061%
14062%    o argv: Specifies the application's argument list.
14063%
14064%    o argc: Specifies the number of arguments.
14065%
14066%    o image: Specifies an address to an address of an Image structure;
14067%
14068%    o exception: return any errors or warnings in this structure.
14069%
14070*/
14071MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14072  char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
14073{
14074#define MagnifySize  256  /* must be a power of 2 */
14075#define MagickMenus  10
14076#define MagickTitle  "Commands"
14077
14078  static const char
14079    *CommandMenu[] =
14080    {
14081      "File",
14082      "Edit",
14083      "View",
14084      "Transform",
14085      "Enhance",
14086      "Effects",
14087      "F/X",
14088      "Image Edit",
14089      "Miscellany",
14090      "Help",
14091      (char *) NULL
14092    },
14093    *FileMenu[] =
14094    {
14095      "Open...",
14096      "Next",
14097      "Former",
14098      "Select...",
14099      "Save...",
14100      "Print...",
14101      "Delete...",
14102      "New...",
14103      "Visual Directory...",
14104      "Quit",
14105      (char *) NULL
14106    },
14107    *EditMenu[] =
14108    {
14109      "Undo",
14110      "Redo",
14111      "Cut",
14112      "Copy",
14113      "Paste",
14114      (char *) NULL
14115    },
14116    *ViewMenu[] =
14117    {
14118      "Half Size",
14119      "Original Size",
14120      "Double Size",
14121      "Resize...",
14122      "Apply",
14123      "Refresh",
14124      "Restore",
14125      (char *) NULL
14126    },
14127    *TransformMenu[] =
14128    {
14129      "Crop",
14130      "Chop",
14131      "Flop",
14132      "Flip",
14133      "Rotate Right",
14134      "Rotate Left",
14135      "Rotate...",
14136      "Shear...",
14137      "Roll...",
14138      "Trim Edges",
14139      (char *) NULL
14140    },
14141    *EnhanceMenu[] =
14142    {
14143      "Hue...",
14144      "Saturation...",
14145      "Brightness...",
14146      "Gamma...",
14147      "Spiff",
14148      "Dull",
14149      "Contrast Stretch...",
14150      "Sigmoidal Contrast...",
14151      "Normalize",
14152      "Equalize",
14153      "Negate",
14154      "Grayscale",
14155      "Map...",
14156      "Quantize...",
14157      (char *) NULL
14158    },
14159    *EffectsMenu[] =
14160    {
14161      "Despeckle",
14162      "Emboss",
14163      "Reduce Noise",
14164      "Add Noise...",
14165      "Sharpen...",
14166      "Blur...",
14167      "Threshold...",
14168      "Edge Detect...",
14169      "Spread...",
14170      "Shade...",
14171      "Raise...",
14172      "Segment...",
14173      (char *) NULL
14174    },
14175    *FXMenu[] =
14176    {
14177      "Solarize...",
14178      "Sepia Tone...",
14179      "Swirl...",
14180      "Implode...",
14181      "Vignette...",
14182      "Wave...",
14183      "Oil Paint...",
14184      "Charcoal Draw...",
14185      (char *) NULL
14186    },
14187    *ImageEditMenu[] =
14188    {
14189      "Annotate...",
14190      "Draw...",
14191      "Color...",
14192      "Matte...",
14193      "Composite...",
14194      "Add Border...",
14195      "Add Frame...",
14196      "Comment...",
14197      "Launch...",
14198      "Region of Interest...",
14199      (char *) NULL
14200    },
14201    *MiscellanyMenu[] =
14202    {
14203      "Image Info",
14204      "Zoom Image",
14205      "Show Preview...",
14206      "Show Histogram",
14207      "Show Matte",
14208      "Background...",
14209      "Slide Show...",
14210      "Preferences...",
14211      (char *) NULL
14212    },
14213    *HelpMenu[] =
14214    {
14215      "Overview",
14216      "Browse Documentation",
14217      "About Display",
14218      (char *) NULL
14219    },
14220    *ShortCutsMenu[] =
14221    {
14222      "Next",
14223      "Former",
14224      "Open...",
14225      "Save...",
14226      "Print...",
14227      "Undo",
14228      "Restore",
14229      "Image Info",
14230      "Quit",
14231      (char *) NULL
14232    },
14233    *VirtualMenu[] =
14234    {
14235      "Image Info",
14236      "Print",
14237      "Next",
14238      "Quit",
14239      (char *) NULL
14240    };
14241
14242  static const char
14243    **Menus[MagickMenus] =
14244    {
14245      FileMenu,
14246      EditMenu,
14247      ViewMenu,
14248      TransformMenu,
14249      EnhanceMenu,
14250      EffectsMenu,
14251      FXMenu,
14252      ImageEditMenu,
14253      MiscellanyMenu,
14254      HelpMenu
14255    };
14256
14257  static CommandType
14258    CommandMenus[] =
14259    {
14260      NullCommand,
14261      NullCommand,
14262      NullCommand,
14263      NullCommand,
14264      NullCommand,
14265      NullCommand,
14266      NullCommand,
14267      NullCommand,
14268      NullCommand,
14269      NullCommand,
14270    },
14271    FileCommands[] =
14272    {
14273      OpenCommand,
14274      NextCommand,
14275      FormerCommand,
14276      SelectCommand,
14277      SaveCommand,
14278      PrintCommand,
14279      DeleteCommand,
14280      NewCommand,
14281      VisualDirectoryCommand,
14282      QuitCommand
14283    },
14284    EditCommands[] =
14285    {
14286      UndoCommand,
14287      RedoCommand,
14288      CutCommand,
14289      CopyCommand,
14290      PasteCommand
14291    },
14292    ViewCommands[] =
14293    {
14294      HalfSizeCommand,
14295      OriginalSizeCommand,
14296      DoubleSizeCommand,
14297      ResizeCommand,
14298      ApplyCommand,
14299      RefreshCommand,
14300      RestoreCommand
14301    },
14302    TransformCommands[] =
14303    {
14304      CropCommand,
14305      ChopCommand,
14306      FlopCommand,
14307      FlipCommand,
14308      RotateRightCommand,
14309      RotateLeftCommand,
14310      RotateCommand,
14311      ShearCommand,
14312      RollCommand,
14313      TrimCommand
14314    },
14315    EnhanceCommands[] =
14316    {
14317      HueCommand,
14318      SaturationCommand,
14319      BrightnessCommand,
14320      GammaCommand,
14321      SpiffCommand,
14322      DullCommand,
14323      ContrastStretchCommand,
14324      SigmoidalContrastCommand,
14325      NormalizeCommand,
14326      EqualizeCommand,
14327      NegateCommand,
14328      GrayscaleCommand,
14329      MapCommand,
14330      QuantizeCommand
14331    },
14332    EffectsCommands[] =
14333    {
14334      DespeckleCommand,
14335      EmbossCommand,
14336      ReduceNoiseCommand,
14337      AddNoiseCommand,
14338      SharpenCommand,
14339      BlurCommand,
14340      ThresholdCommand,
14341      EdgeDetectCommand,
14342      SpreadCommand,
14343      ShadeCommand,
14344      RaiseCommand,
14345      SegmentCommand
14346    },
14347    FXCommands[] =
14348    {
14349      SolarizeCommand,
14350      SepiaToneCommand,
14351      SwirlCommand,
14352      ImplodeCommand,
14353      VignetteCommand,
14354      WaveCommand,
14355      OilPaintCommand,
14356      CharcoalDrawCommand
14357    },
14358    ImageEditCommands[] =
14359    {
14360      AnnotateCommand,
14361      DrawCommand,
14362      ColorCommand,
14363      MatteCommand,
14364      CompositeCommand,
14365      AddBorderCommand,
14366      AddFrameCommand,
14367      CommentCommand,
14368      LaunchCommand,
14369      RegionofInterestCommand
14370    },
14371    MiscellanyCommands[] =
14372    {
14373      InfoCommand,
14374      ZoomCommand,
14375      ShowPreviewCommand,
14376      ShowHistogramCommand,
14377      ShowMatteCommand,
14378      BackgroundCommand,
14379      SlideShowCommand,
14380      PreferencesCommand
14381    },
14382    HelpCommands[] =
14383    {
14384      HelpCommand,
14385      BrowseDocumentationCommand,
14386      VersionCommand
14387    },
14388    ShortCutsCommands[] =
14389    {
14390      NextCommand,
14391      FormerCommand,
14392      OpenCommand,
14393      SaveCommand,
14394      PrintCommand,
14395      UndoCommand,
14396      RestoreCommand,
14397      InfoCommand,
14398      QuitCommand
14399    },
14400    VirtualCommands[] =
14401    {
14402      InfoCommand,
14403      PrintCommand,
14404      NextCommand,
14405      QuitCommand
14406    };
14407
14408  static CommandType
14409    *Commands[MagickMenus] =
14410    {
14411      FileCommands,
14412      EditCommands,
14413      ViewCommands,
14414      TransformCommands,
14415      EnhanceCommands,
14416      EffectsCommands,
14417      FXCommands,
14418      ImageEditCommands,
14419      MiscellanyCommands,
14420      HelpCommands
14421    };
14422
14423  char
14424    command[MaxTextExtent],
14425    *directory,
14426    geometry[MaxTextExtent],
14427    resource_name[MaxTextExtent];
14428
14429  CommandType
14430    command_type;
14431
14432  Image
14433    *display_image,
14434    *nexus;
14435
14436  int
14437    entry,
14438    id;
14439
14440  KeySym
14441    key_symbol;
14442
14443  MagickStatusType
14444    context_mask,
14445    status;
14446
14447  RectangleInfo
14448    geometry_info;
14449
14450  register int
14451    i;
14452
14453  static char
14454    working_directory[MaxTextExtent];
14455
14456  static XPoint
14457    vid_info;
14458
14459  static XWindowInfo
14460    *magick_windows[MaxXWindows];
14461
14462  static unsigned int
14463    number_windows;
14464
14465  struct stat
14466    attributes;
14467
14468  time_t
14469    timer,
14470    timestamp,
14471    update_time;
14472
14473  unsigned int
14474    height,
14475    width;
14476
14477  size_t
14478    delay;
14479
14480  WarningHandler
14481    warning_handler;
14482
14483  Window
14484    root_window;
14485
14486  XClassHint
14487    *class_hints;
14488
14489  XEvent
14490    event;
14491
14492  XFontStruct
14493    *font_info;
14494
14495  XGCValues
14496    context_values;
14497
14498  XPixelInfo
14499    *icon_pixel,
14500    *pixel;
14501
14502  XResourceInfo
14503    *icon_resources;
14504
14505  XStandardColormap
14506    *icon_map,
14507    *map_info;
14508
14509  XVisualInfo
14510    *icon_visual,
14511    *visual_info;
14512
14513  XWindowChanges
14514    window_changes;
14515
14516  XWindows
14517    *windows;
14518
14519  XWMHints
14520    *manager_hints;
14521
14522  assert(image != (Image **) NULL);
14523  assert((*image)->signature == MagickSignature);
14524  if ((*image)->debug != MagickFalse)
14525    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14526  display_image=(*image);
14527  warning_handler=(WarningHandler) NULL;
14528  windows=XSetWindows((XWindows *) ~0);
14529  if (windows != (XWindows *) NULL)
14530    {
14531      int
14532        status;
14533
14534      status=chdir(working_directory);
14535      if (status == -1)
14536        (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14537          "UnableToOpenFile","%s",working_directory);
14538      warning_handler=resource_info->display_warnings ?
14539        SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14540      warning_handler=resource_info->display_warnings ?
14541        SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14542    }
14543  else
14544    {
14545      /*
14546        Allocate windows structure.
14547      */
14548      resource_info->colors=display_image->colors;
14549      windows=XSetWindows(XInitializeWindows(display,resource_info));
14550      if (windows == (XWindows *) NULL)
14551        ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14552          (*image)->filename);
14553      /*
14554        Initialize window id's.
14555      */
14556      number_windows=0;
14557      magick_windows[number_windows++]=(&windows->icon);
14558      magick_windows[number_windows++]=(&windows->backdrop);
14559      magick_windows[number_windows++]=(&windows->image);
14560      magick_windows[number_windows++]=(&windows->info);
14561      magick_windows[number_windows++]=(&windows->command);
14562      magick_windows[number_windows++]=(&windows->widget);
14563      magick_windows[number_windows++]=(&windows->popup);
14564      magick_windows[number_windows++]=(&windows->magnify);
14565      magick_windows[number_windows++]=(&windows->pan);
14566      for (i=0; i < (int) number_windows; i++)
14567        magick_windows[i]->id=(Window) NULL;
14568      vid_info.x=0;
14569      vid_info.y=0;
14570    }
14571  /*
14572    Initialize font info.
14573  */
14574  if (windows->font_info != (XFontStruct *) NULL)
14575    (void) XFreeFont(display,windows->font_info);
14576  windows->font_info=XBestFont(display,resource_info,MagickFalse);
14577  if (windows->font_info == (XFontStruct *) NULL)
14578    ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14579      resource_info->font);
14580  /*
14581    Initialize Standard Colormap.
14582  */
14583  map_info=windows->map_info;
14584  icon_map=windows->icon_map;
14585  visual_info=windows->visual_info;
14586  icon_visual=windows->icon_visual;
14587  pixel=windows->pixel_info;
14588  icon_pixel=windows->icon_pixel;
14589  font_info=windows->font_info;
14590  icon_resources=windows->icon_resources;
14591  class_hints=windows->class_hints;
14592  manager_hints=windows->manager_hints;
14593  root_window=XRootWindow(display,visual_info->screen);
14594  nexus=NewImageList();
14595  if (display_image->debug != MagickFalse)
14596    {
14597      (void) LogMagickEvent(X11Event,GetMagickModule(),
14598        "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14599        (double) display_image->scene,(double) display_image->columns,
14600        (double) display_image->rows);
14601      if (display_image->colors != 0)
14602        (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14603          display_image->colors);
14604      (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14605        display_image->magick);
14606    }
14607  XMakeStandardColormap(display,visual_info,resource_info,display_image,
14608    map_info,pixel,exception);
14609  display_image->taint=MagickFalse;
14610  /*
14611    Initialize graphic context.
14612  */
14613  windows->context.id=(Window) NULL;
14614  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14615    resource_info,&windows->context);
14616  (void) CloneString(&class_hints->res_name,resource_info->client_name);
14617  (void) CloneString(&class_hints->res_class,resource_info->client_name);
14618  class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14619  manager_hints->flags=InputHint | StateHint;
14620  manager_hints->input=MagickFalse;
14621  manager_hints->initial_state=WithdrawnState;
14622  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14623    &windows->context);
14624  if (display_image->debug != MagickFalse)
14625    (void) LogMagickEvent(X11Event,GetMagickModule(),
14626      "Window id: 0x%lx (context)",windows->context.id);
14627  context_values.background=pixel->background_color.pixel;
14628  context_values.font=font_info->fid;
14629  context_values.foreground=pixel->foreground_color.pixel;
14630  context_values.graphics_exposures=MagickFalse;
14631  context_mask=(MagickStatusType)
14632    (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14633  if (pixel->annotate_context != (GC) NULL)
14634    (void) XFreeGC(display,pixel->annotate_context);
14635  pixel->annotate_context=XCreateGC(display,windows->context.id,
14636    context_mask,&context_values);
14637  if (pixel->annotate_context == (GC) NULL)
14638    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14639      display_image->filename);
14640  context_values.background=pixel->depth_color.pixel;
14641  if (pixel->widget_context != (GC) NULL)
14642    (void) XFreeGC(display,pixel->widget_context);
14643  pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14644    &context_values);
14645  if (pixel->widget_context == (GC) NULL)
14646    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14647      display_image->filename);
14648  context_values.background=pixel->foreground_color.pixel;
14649  context_values.foreground=pixel->background_color.pixel;
14650  context_values.plane_mask=context_values.background ^
14651    context_values.foreground;
14652  if (pixel->highlight_context != (GC) NULL)
14653    (void) XFreeGC(display,pixel->highlight_context);
14654  pixel->highlight_context=XCreateGC(display,windows->context.id,
14655    (size_t) (context_mask | GCPlaneMask),&context_values);
14656  if (pixel->highlight_context == (GC) NULL)
14657    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14658      display_image->filename);
14659  (void) XDestroyWindow(display,windows->context.id);
14660  /*
14661    Initialize icon window.
14662  */
14663  XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14664    icon_resources,&windows->icon);
14665  windows->icon.geometry=resource_info->icon_geometry;
14666  XBestIconSize(display,&windows->icon,display_image);
14667  windows->icon.attributes.colormap=XDefaultColormap(display,
14668    icon_visual->screen);
14669  windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14670  manager_hints->flags=InputHint | StateHint;
14671  manager_hints->input=MagickFalse;
14672  manager_hints->initial_state=IconicState;
14673  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14674    &windows->icon);
14675  if (display_image->debug != MagickFalse)
14676    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14677      windows->icon.id);
14678  /*
14679    Initialize graphic context for icon window.
14680  */
14681  if (icon_pixel->annotate_context != (GC) NULL)
14682    (void) XFreeGC(display,icon_pixel->annotate_context);
14683  context_values.background=icon_pixel->background_color.pixel;
14684  context_values.foreground=icon_pixel->foreground_color.pixel;
14685  icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14686    (size_t) (GCBackground | GCForeground),&context_values);
14687  if (icon_pixel->annotate_context == (GC) NULL)
14688    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14689      display_image->filename);
14690  windows->icon.annotate_context=icon_pixel->annotate_context;
14691  /*
14692    Initialize Image window.
14693  */
14694  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14695    &windows->image);
14696  windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14697  if (resource_info->use_shared_memory == MagickFalse)
14698    windows->image.shared_memory=MagickFalse;
14699  if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14700    {
14701      char
14702        *title;
14703
14704      title=InterpretImageProperties(resource_info->image_info,display_image,
14705        resource_info->title,exception);
14706      (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
14707      (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
14708      title=DestroyString(title);
14709    }
14710  else
14711    {
14712      char
14713        filename[MaxTextExtent];
14714
14715      /*
14716        Window name is the base of the filename.
14717      */
14718      GetPathComponent(display_image->magick_filename,TailPath,filename);
14719      if (display_image->scene == 0)
14720        (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14721          "%s: %s",MagickPackageName,filename);
14722      else
14723        (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14724          "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14725          (double) display_image->scene,(double) GetImageListLength(
14726          display_image));
14727      (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
14728    }
14729  if (resource_info->immutable)
14730    windows->image.immutable=MagickTrue;
14731  windows->image.use_pixmap=resource_info->use_pixmap;
14732  windows->image.geometry=resource_info->image_geometry;
14733  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14734    XDisplayWidth(display,visual_info->screen),
14735    XDisplayHeight(display,visual_info->screen));
14736  geometry_info.width=display_image->columns;
14737  geometry_info.height=display_image->rows;
14738  geometry_info.x=0;
14739  geometry_info.y=0;
14740  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14741    &geometry_info.width,&geometry_info.height);
14742  windows->image.width=(unsigned int) geometry_info.width;
14743  windows->image.height=(unsigned int) geometry_info.height;
14744  windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14745    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14746    KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14747    PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14748  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14749    resource_info,&windows->backdrop);
14750  if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14751    {
14752      /*
14753        Initialize backdrop window.
14754      */
14755      windows->backdrop.x=0;
14756      windows->backdrop.y=0;
14757      (void) CloneString(&windows->backdrop.name,"Backdrop");
14758      windows->backdrop.flags=(size_t) (USSize | USPosition);
14759      windows->backdrop.width=(unsigned int)
14760        XDisplayWidth(display,visual_info->screen);
14761      windows->backdrop.height=(unsigned int)
14762        XDisplayHeight(display,visual_info->screen);
14763      windows->backdrop.border_width=0;
14764      windows->backdrop.immutable=MagickTrue;
14765      windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14766        ButtonReleaseMask;
14767      windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14768        StructureNotifyMask;
14769      manager_hints->flags=IconWindowHint | InputHint | StateHint;
14770      manager_hints->icon_window=windows->icon.id;
14771      manager_hints->input=MagickTrue;
14772      manager_hints->initial_state=resource_info->iconic ? IconicState :
14773        NormalState;
14774      XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14775        &windows->backdrop);
14776      if (display_image->debug != MagickFalse)
14777        (void) LogMagickEvent(X11Event,GetMagickModule(),
14778          "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14779      (void) XMapWindow(display,windows->backdrop.id);
14780      (void) XClearWindow(display,windows->backdrop.id);
14781      if (windows->image.id != (Window) NULL)
14782        {
14783          (void) XDestroyWindow(display,windows->image.id);
14784          windows->image.id=(Window) NULL;
14785        }
14786      /*
14787        Position image in the center the backdrop.
14788      */
14789      windows->image.flags|=USPosition;
14790      windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14791        (windows->image.width/2);
14792      windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14793        (windows->image.height/2);
14794    }
14795  manager_hints->flags=IconWindowHint | InputHint | StateHint;
14796  manager_hints->icon_window=windows->icon.id;
14797  manager_hints->input=MagickTrue;
14798  manager_hints->initial_state=resource_info->iconic ? IconicState :
14799    NormalState;
14800  if (windows->group_leader.id != (Window) NULL)
14801    {
14802      /*
14803        Follow the leader.
14804      */
14805      manager_hints->flags|=WindowGroupHint;
14806      manager_hints->window_group=windows->group_leader.id;
14807      (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14808      if (display_image->debug != MagickFalse)
14809        (void) LogMagickEvent(X11Event,GetMagickModule(),
14810          "Window id: 0x%lx (group leader)",windows->group_leader.id);
14811    }
14812  XMakeWindow(display,
14813    (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14814    argv,argc,class_hints,manager_hints,&windows->image);
14815  (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14816    XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14817  if (windows->group_leader.id != (Window) NULL)
14818    (void) XSetTransientForHint(display,windows->image.id,
14819      windows->group_leader.id);
14820  if (display_image->debug != MagickFalse)
14821    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14822      windows->image.id);
14823  /*
14824    Initialize Info widget.
14825  */
14826  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14827    &windows->info);
14828  (void) CloneString(&windows->info.name,"Info");
14829  (void) CloneString(&windows->info.icon_name,"Info");
14830  windows->info.border_width=1;
14831  windows->info.x=2;
14832  windows->info.y=2;
14833  windows->info.flags|=PPosition;
14834  windows->info.attributes.win_gravity=UnmapGravity;
14835  windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14836    StructureNotifyMask;
14837  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14838  manager_hints->input=MagickFalse;
14839  manager_hints->initial_state=NormalState;
14840  manager_hints->window_group=windows->image.id;
14841  XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14842    &windows->info);
14843  windows->info.highlight_stipple=XCreateBitmapFromData(display,
14844    windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14845  windows->info.shadow_stipple=XCreateBitmapFromData(display,
14846    windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14847  (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14848  if (windows->image.mapped != MagickFalse)
14849    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14850  if (display_image->debug != MagickFalse)
14851    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14852      windows->info.id);
14853  /*
14854    Initialize Command widget.
14855  */
14856  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14857    resource_info,&windows->command);
14858  windows->command.data=MagickMenus;
14859  (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14860  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
14861    resource_info->client_name);
14862  windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14863    resource_name,"geometry",(char *) NULL);
14864  (void) CloneString(&windows->command.name,MagickTitle);
14865  windows->command.border_width=0;
14866  windows->command.flags|=PPosition;
14867  windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14868    ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14869    OwnerGrabButtonMask | StructureNotifyMask;
14870  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14871  manager_hints->input=MagickTrue;
14872  manager_hints->initial_state=NormalState;
14873  manager_hints->window_group=windows->image.id;
14874  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14875    &windows->command);
14876  windows->command.highlight_stipple=XCreateBitmapFromData(display,
14877    windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14878    HighlightHeight);
14879  windows->command.shadow_stipple=XCreateBitmapFromData(display,
14880    windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14881  (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14882  if (windows->command.mapped != MagickFalse)
14883    (void) XMapRaised(display,windows->command.id);
14884  if (display_image->debug != MagickFalse)
14885    (void) LogMagickEvent(X11Event,GetMagickModule(),
14886      "Window id: 0x%lx (command)",windows->command.id);
14887  /*
14888    Initialize Widget window.
14889  */
14890  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14891    resource_info,&windows->widget);
14892  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
14893    resource_info->client_name);
14894  windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14895    resource_name,"geometry",(char *) NULL);
14896  windows->widget.border_width=0;
14897  windows->widget.flags|=PPosition;
14898  windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14899    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14900    KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14901    StructureNotifyMask;
14902  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14903  manager_hints->input=MagickTrue;
14904  manager_hints->initial_state=NormalState;
14905  manager_hints->window_group=windows->image.id;
14906  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14907    &windows->widget);
14908  windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14909    windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14910  windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14911    windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14912  (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14913  if (display_image->debug != MagickFalse)
14914    (void) LogMagickEvent(X11Event,GetMagickModule(),
14915      "Window id: 0x%lx (widget)",windows->widget.id);
14916  /*
14917    Initialize popup window.
14918  */
14919  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14920    resource_info,&windows->popup);
14921  windows->popup.border_width=0;
14922  windows->popup.flags|=PPosition;
14923  windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14924    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14925    KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14926  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14927  manager_hints->input=MagickTrue;
14928  manager_hints->initial_state=NormalState;
14929  manager_hints->window_group=windows->image.id;
14930  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14931    &windows->popup);
14932  windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14933    windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14934  windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14935    windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14936  (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14937  if (display_image->debug != MagickFalse)
14938    (void) LogMagickEvent(X11Event,GetMagickModule(),
14939      "Window id: 0x%lx (pop up)",windows->popup.id);
14940  /*
14941    Initialize Magnify window and cursor.
14942  */
14943  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14944    resource_info,&windows->magnify);
14945  if (resource_info->use_shared_memory == MagickFalse)
14946    windows->magnify.shared_memory=MagickFalse;
14947  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
14948    resource_info->client_name);
14949  windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14950    resource_name,"geometry",(char *) NULL);
14951  (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14952    resource_info->magnify);
14953  if (windows->magnify.cursor != (Cursor) NULL)
14954    (void) XFreeCursor(display,windows->magnify.cursor);
14955  windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14956    map_info->colormap,resource_info->background_color,
14957    resource_info->foreground_color);
14958  if (windows->magnify.cursor == (Cursor) NULL)
14959    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14960      display_image->filename);
14961  windows->magnify.width=MagnifySize;
14962  windows->magnify.height=MagnifySize;
14963  windows->magnify.flags|=PPosition;
14964  windows->magnify.min_width=MagnifySize;
14965  windows->magnify.min_height=MagnifySize;
14966  windows->magnify.width_inc=MagnifySize;
14967  windows->magnify.height_inc=MagnifySize;
14968  windows->magnify.data=resource_info->magnify;
14969  windows->magnify.attributes.cursor=windows->magnify.cursor;
14970  windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14971    ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14972    StructureNotifyMask;
14973  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14974  manager_hints->input=MagickTrue;
14975  manager_hints->initial_state=NormalState;
14976  manager_hints->window_group=windows->image.id;
14977  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14978    &windows->magnify);
14979  if (display_image->debug != MagickFalse)
14980    (void) LogMagickEvent(X11Event,GetMagickModule(),
14981      "Window id: 0x%lx (magnify)",windows->magnify.id);
14982  (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14983  /*
14984    Initialize panning window.
14985  */
14986  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14987    resource_info,&windows->pan);
14988  (void) CloneString(&windows->pan.name,"Pan Icon");
14989  windows->pan.width=windows->icon.width;
14990  windows->pan.height=windows->icon.height;
14991  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
14992    resource_info->client_name);
14993  windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
14994    resource_name,"geometry",(char *) NULL);
14995  (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
14996    &windows->pan.width,&windows->pan.height);
14997  windows->pan.flags|=PPosition;
14998  windows->pan.immutable=MagickTrue;
14999  windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
15000    ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
15001    StructureNotifyMask;
15002  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
15003  manager_hints->input=MagickFalse;
15004  manager_hints->initial_state=NormalState;
15005  manager_hints->window_group=windows->image.id;
15006  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
15007    &windows->pan);
15008  if (display_image->debug != MagickFalse)
15009    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
15010      windows->pan.id);
15011  (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
15012  if (windows->info.mapped != MagickFalse)
15013    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15014  if ((windows->image.mapped == MagickFalse) ||
15015      (windows->backdrop.id != (Window) NULL))
15016    (void) XMapWindow(display,windows->image.id);
15017  /*
15018    Set our progress monitor and warning handlers.
15019  */
15020  if (warning_handler == (WarningHandler) NULL)
15021    {
15022      warning_handler=resource_info->display_warnings ?
15023        SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
15024      warning_handler=resource_info->display_warnings ?
15025        SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
15026    }
15027  /*
15028    Initialize Image and Magnify X images.
15029  */
15030  windows->image.x=0;
15031  windows->image.y=0;
15032  windows->magnify.shape=MagickFalse;
15033  width=(unsigned int) display_image->columns;
15034  height=(unsigned int) display_image->rows;
15035  if ((display_image->columns != width) || (display_image->rows != height))
15036    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15037      display_image->filename);
15038  status=XMakeImage(display,resource_info,&windows->image,display_image,
15039    width,height,exception);
15040  if (status == MagickFalse)
15041    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15042      display_image->filename);
15043  status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
15044    windows->magnify.width,windows->magnify.height,exception);
15045  if (status == MagickFalse)
15046    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15047      display_image->filename);
15048  if (windows->magnify.mapped != MagickFalse)
15049    (void) XMapRaised(display,windows->magnify.id);
15050  if (windows->pan.mapped != MagickFalse)
15051    (void) XMapRaised(display,windows->pan.id);
15052  windows->image.window_changes.width=(int) display_image->columns;
15053  windows->image.window_changes.height=(int) display_image->rows;
15054  (void) XConfigureImage(display,resource_info,windows,display_image,exception);
15055  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15056  (void) XSync(display,MagickFalse);
15057  /*
15058    Respond to events.
15059  */
15060  delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
15061  timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15062  update_time=0;
15063  if (resource_info->update != MagickFalse)
15064    {
15065      MagickBooleanType
15066        status;
15067
15068      /*
15069        Determine when file data was last modified.
15070      */
15071      status=GetPathAttributes(display_image->filename,&attributes);
15072      if (status != MagickFalse)
15073        update_time=attributes.st_mtime;
15074    }
15075  *state&=(~FormerImageState);
15076  *state&=(~MontageImageState);
15077  *state&=(~NextImageState);
15078  do
15079  {
15080    /*
15081      Handle a window event.
15082    */
15083    if (windows->image.mapped != MagickFalse)
15084      if ((display_image->delay != 0) || (resource_info->update != 0))
15085        {
15086          if (timer < time((time_t *) NULL))
15087            {
15088              if (resource_info->update == MagickFalse)
15089                *state|=NextImageState | ExitState;
15090              else
15091                {
15092                  MagickBooleanType
15093                    status;
15094
15095                  /*
15096                    Determine if image file was modified.
15097                  */
15098                  status=GetPathAttributes(display_image->filename,&attributes);
15099                  if (status != MagickFalse)
15100                    if (update_time != attributes.st_mtime)
15101                      {
15102                        /*
15103                          Redisplay image.
15104                        */
15105                        (void) FormatLocaleString(
15106                          resource_info->image_info->filename,MaxTextExtent,
15107                          "%s:%s",display_image->magick,
15108                          display_image->filename);
15109                        nexus=ReadImage(resource_info->image_info,exception);
15110                        if (nexus != (Image *) NULL)
15111                          {
15112                            nexus=DestroyImage(nexus);
15113                            *state|=NextImageState | ExitState;
15114                          }
15115                      }
15116                  delay=display_image->delay/MagickMax(
15117                    display_image->ticks_per_second,1L);
15118                  timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15119                }
15120            }
15121          if (XEventsQueued(display,QueuedAfterFlush) == 0)
15122            {
15123              /*
15124                Do not block if delay > 0.
15125              */
15126              XDelay(display,SuspendTime << 2);
15127              continue;
15128            }
15129        }
15130    timestamp=time((time_t *) NULL);
15131    (void) XNextEvent(display,&event);
15132    if (windows->image.stasis == MagickFalse)
15133      windows->image.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15134        MagickTrue : MagickFalse;
15135    if (windows->magnify.stasis == MagickFalse)
15136      windows->magnify.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15137        MagickTrue : MagickFalse;
15138    if (event.xany.window == windows->command.id)
15139      {
15140        /*
15141          Select a command from the Command widget.
15142        */
15143        id=XCommandWidget(display,windows,CommandMenu,&event);
15144        if (id < 0)
15145          continue;
15146        (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15147        command_type=CommandMenus[id];
15148        if (id < MagickMenus)
15149          {
15150            /*
15151              Select a command from a pop-up menu.
15152            */
15153            entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15154              command);
15155            if (entry < 0)
15156              continue;
15157            (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15158            command_type=Commands[id][entry];
15159          }
15160        if (command_type != NullCommand)
15161          nexus=XMagickCommand(display,resource_info,windows,command_type,
15162            &display_image,exception);
15163        continue;
15164      }
15165    switch (event.type)
15166    {
15167      case ButtonPress:
15168      {
15169        if (display_image->debug != MagickFalse)
15170          (void) LogMagickEvent(X11Event,GetMagickModule(),
15171            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15172            event.xbutton.button,event.xbutton.x,event.xbutton.y);
15173        if ((event.xbutton.button == Button3) &&
15174            (event.xbutton.state & Mod1Mask))
15175          {
15176            /*
15177              Convert Alt-Button3 to Button2.
15178            */
15179            event.xbutton.button=Button2;
15180            event.xbutton.state&=(~Mod1Mask);
15181          }
15182        if (event.xbutton.window == windows->backdrop.id)
15183          {
15184            (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15185              event.xbutton.time);
15186            break;
15187          }
15188        if (event.xbutton.window == windows->image.id)
15189          {
15190            switch (event.xbutton.button)
15191            {
15192              case Button1:
15193              {
15194                if (resource_info->immutable)
15195                  {
15196                    /*
15197                      Select a command from the Virtual menu.
15198                    */
15199                    entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15200                      command);
15201                    if (entry >= 0)
15202                      nexus=XMagickCommand(display,resource_info,windows,
15203                        VirtualCommands[entry],&display_image,exception);
15204                    break;
15205                  }
15206                /*
15207                  Map/unmap Command widget.
15208                */
15209                if (windows->command.mapped != MagickFalse)
15210                  (void) XWithdrawWindow(display,windows->command.id,
15211                    windows->command.screen);
15212                else
15213                  {
15214                    (void) XCommandWidget(display,windows,CommandMenu,
15215                      (XEvent *) NULL);
15216                    (void) XMapRaised(display,windows->command.id);
15217                  }
15218                break;
15219              }
15220              case Button2:
15221              {
15222                /*
15223                  User pressed the image magnify button.
15224                */
15225                (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15226                  &display_image,exception);
15227                XMagnifyImage(display,windows,&event,exception);
15228                break;
15229              }
15230              case Button3:
15231              {
15232                if (resource_info->immutable)
15233                  {
15234                    /*
15235                      Select a command from the Virtual menu.
15236                    */
15237                    entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15238                      command);
15239                    if (entry >= 0)
15240                      nexus=XMagickCommand(display,resource_info,windows,
15241                        VirtualCommands[entry],&display_image,exception);
15242                    break;
15243                  }
15244                if (display_image->montage != (char *) NULL)
15245                  {
15246                    /*
15247                      Open or delete a tile from a visual image directory.
15248                    */
15249                    nexus=XTileImage(display,resource_info,windows,
15250                      display_image,&event,exception);
15251                    if (nexus != (Image *) NULL)
15252                      *state|=MontageImageState | NextImageState | ExitState;
15253                    vid_info.x=(short int) windows->image.x;
15254                    vid_info.y=(short int) windows->image.y;
15255                    break;
15256                  }
15257                /*
15258                  Select a command from the Short Cuts menu.
15259                */
15260                entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15261                  command);
15262                if (entry >= 0)
15263                  nexus=XMagickCommand(display,resource_info,windows,
15264                    ShortCutsCommands[entry],&display_image,exception);
15265                break;
15266              }
15267              case Button4:
15268              {
15269                /*
15270                  Wheel up.
15271                */
15272                XTranslateImage(display,windows,*image,XK_Up);
15273                break;
15274              }
15275              case Button5:
15276              {
15277                /*
15278                  Wheel down.
15279                */
15280                XTranslateImage(display,windows,*image,XK_Down);
15281                break;
15282              }
15283              default:
15284                break;
15285            }
15286            break;
15287          }
15288        if (event.xbutton.window == windows->magnify.id)
15289          {
15290            int
15291              factor;
15292
15293            static const char
15294              *MagnifyMenu[] =
15295              {
15296                "2",
15297                "4",
15298                "5",
15299                "6",
15300                "7",
15301                "8",
15302                "9",
15303                "3",
15304                (char *) NULL,
15305              };
15306
15307            static KeySym
15308              MagnifyCommands[] =
15309              {
15310                XK_2,
15311                XK_4,
15312                XK_5,
15313                XK_6,
15314                XK_7,
15315                XK_8,
15316                XK_9,
15317                XK_3
15318              };
15319
15320            /*
15321              Select a magnify factor from the pop-up menu.
15322            */
15323            factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15324            if (factor >= 0)
15325              XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor],
15326                exception);
15327            break;
15328          }
15329        if (event.xbutton.window == windows->pan.id)
15330          {
15331            switch (event.xbutton.button)
15332            {
15333              case Button4:
15334              {
15335                /*
15336                  Wheel up.
15337                */
15338                XTranslateImage(display,windows,*image,XK_Up);
15339                break;
15340              }
15341              case Button5:
15342              {
15343                /*
15344                  Wheel down.
15345                */
15346                XTranslateImage(display,windows,*image,XK_Down);
15347                break;
15348              }
15349              default:
15350              {
15351                XPanImage(display,windows,&event,exception);
15352                break;
15353              }
15354            }
15355            break;
15356          }
15357        delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15358          1L);
15359        timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15360        break;
15361      }
15362      case ButtonRelease:
15363      {
15364        if (display_image->debug != MagickFalse)
15365          (void) LogMagickEvent(X11Event,GetMagickModule(),
15366            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15367            event.xbutton.button,event.xbutton.x,event.xbutton.y);
15368        break;
15369      }
15370      case ClientMessage:
15371      {
15372        if (display_image->debug != MagickFalse)
15373          (void) LogMagickEvent(X11Event,GetMagickModule(),
15374            "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15375            event.xclient.message_type,event.xclient.format,(unsigned long)
15376            event.xclient.data.l[0]);
15377        if (event.xclient.message_type == windows->im_protocols)
15378          {
15379            if (*event.xclient.data.l == (long) windows->im_update_widget)
15380              {
15381                (void) CloneString(&windows->command.name,MagickTitle);
15382                windows->command.data=MagickMenus;
15383                (void) XCommandWidget(display,windows,CommandMenu,
15384                  (XEvent *) NULL);
15385                break;
15386              }
15387            if (*event.xclient.data.l == (long) windows->im_update_colormap)
15388              {
15389                /*
15390                  Update graphic context and window colormap.
15391                */
15392                for (i=0; i < (int) number_windows; i++)
15393                {
15394                  if (magick_windows[i]->id == windows->icon.id)
15395                    continue;
15396                  context_values.background=pixel->background_color.pixel;
15397                  context_values.foreground=pixel->foreground_color.pixel;
15398                  (void) XChangeGC(display,magick_windows[i]->annotate_context,
15399                    context_mask,&context_values);
15400                  (void) XChangeGC(display,magick_windows[i]->widget_context,
15401                    context_mask,&context_values);
15402                  context_values.background=pixel->foreground_color.pixel;
15403                  context_values.foreground=pixel->background_color.pixel;
15404                  context_values.plane_mask=context_values.background ^
15405                    context_values.foreground;
15406                  (void) XChangeGC(display,magick_windows[i]->highlight_context,
15407                    (size_t) (context_mask | GCPlaneMask),
15408                    &context_values);
15409                  magick_windows[i]->attributes.background_pixel=
15410                    pixel->background_color.pixel;
15411                  magick_windows[i]->attributes.border_pixel=
15412                    pixel->border_color.pixel;
15413                  magick_windows[i]->attributes.colormap=map_info->colormap;
15414                  (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15415                    (unsigned long) magick_windows[i]->mask,
15416                    &magick_windows[i]->attributes);
15417                }
15418                if (windows->pan.mapped != MagickFalse)
15419                  {
15420                    (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15421                      windows->pan.pixmap);
15422                    (void) XClearWindow(display,windows->pan.id);
15423                    XDrawPanRectangle(display,windows);
15424                  }
15425                if (windows->backdrop.id != (Window) NULL)
15426                  (void) XInstallColormap(display,map_info->colormap);
15427                break;
15428              }
15429            if (*event.xclient.data.l == (long) windows->im_former_image)
15430              {
15431                *state|=FormerImageState | ExitState;
15432                break;
15433              }
15434            if (*event.xclient.data.l == (long) windows->im_next_image)
15435              {
15436                *state|=NextImageState | ExitState;
15437                break;
15438              }
15439            if (*event.xclient.data.l == (long) windows->im_retain_colors)
15440              {
15441                *state|=RetainColorsState;
15442                break;
15443              }
15444            if (*event.xclient.data.l == (long) windows->im_exit)
15445              {
15446                *state|=ExitState;
15447                break;
15448              }
15449            break;
15450          }
15451        if (event.xclient.message_type == windows->dnd_protocols)
15452          {
15453            Atom
15454              selection,
15455              type;
15456
15457            int
15458              format,
15459              status;
15460
15461            unsigned char
15462              *data;
15463
15464            unsigned long
15465              after,
15466              length;
15467
15468            /*
15469              Display image named by the Drag-and-Drop selection.
15470            */
15471            if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15472              break;
15473            selection=XInternAtom(display,"DndSelection",MagickFalse);
15474            status=XGetWindowProperty(display,root_window,selection,0L,(long)
15475              MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15476              &length,&after,&data);
15477            if ((status != Success) || (length == 0))
15478              break;
15479            if (*event.xclient.data.l == 2)
15480              {
15481                /*
15482                  Offix DND.
15483                */
15484                (void) CopyMagickString(resource_info->image_info->filename,
15485                  (char *) data,MaxTextExtent);
15486              }
15487            else
15488              {
15489                /*
15490                  XDND.
15491                */
15492                if (strncmp((char *) data, "file:", 5) != 0)
15493                  {
15494                    (void) XFree((void *) data);
15495                    break;
15496                  }
15497                (void) CopyMagickString(resource_info->image_info->filename,
15498                  ((char *) data)+5,MaxTextExtent);
15499              }
15500            nexus=ReadImage(resource_info->image_info,exception);
15501            CatchException(exception);
15502            if (nexus != (Image *) NULL)
15503              *state|=NextImageState | ExitState;
15504            (void) XFree((void *) data);
15505            break;
15506          }
15507        /*
15508          If client window delete message, exit.
15509        */
15510        if (event.xclient.message_type != windows->wm_protocols)
15511          break;
15512        if (*event.xclient.data.l != (long) windows->wm_delete_window)
15513          break;
15514        (void) XWithdrawWindow(display,event.xclient.window,
15515          visual_info->screen);
15516        if (event.xclient.window == windows->image.id)
15517          {
15518            *state|=ExitState;
15519            break;
15520          }
15521        if (event.xclient.window == windows->pan.id)
15522          {
15523            /*
15524              Restore original image size when pan window is deleted.
15525            */
15526            windows->image.window_changes.width=windows->image.ximage->width;
15527            windows->image.window_changes.height=windows->image.ximage->height;
15528            (void) XConfigureImage(display,resource_info,windows,
15529              display_image,exception);
15530          }
15531        break;
15532      }
15533      case ConfigureNotify:
15534      {
15535        if (display_image->debug != MagickFalse)
15536          (void) LogMagickEvent(X11Event,GetMagickModule(),
15537            "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15538            event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15539            event.xconfigure.y,event.xconfigure.send_event);
15540        if (event.xconfigure.window == windows->image.id)
15541          {
15542            /*
15543              Image window has a new configuration.
15544            */
15545            if (event.xconfigure.send_event != 0)
15546              {
15547                XWindowChanges
15548                  window_changes;
15549
15550                /*
15551                  Position the transient windows relative of the Image window.
15552                */
15553                if (windows->command.geometry == (char *) NULL)
15554                  if (windows->command.mapped == MagickFalse)
15555                    {
15556                      windows->command.x=event.xconfigure.x-
15557                        windows->command.width-25;
15558                      windows->command.y=event.xconfigure.y;
15559                      XConstrainWindowPosition(display,&windows->command);
15560                      window_changes.x=windows->command.x;
15561                      window_changes.y=windows->command.y;
15562                      (void) XReconfigureWMWindow(display,windows->command.id,
15563                        windows->command.screen,(unsigned int) (CWX | CWY),
15564                        &window_changes);
15565                    }
15566                if (windows->widget.geometry == (char *) NULL)
15567                  if (windows->widget.mapped == MagickFalse)
15568                    {
15569                      windows->widget.x=event.xconfigure.x+
15570                        event.xconfigure.width/10;
15571                      windows->widget.y=event.xconfigure.y+
15572                        event.xconfigure.height/10;
15573                      XConstrainWindowPosition(display,&windows->widget);
15574                      window_changes.x=windows->widget.x;
15575                      window_changes.y=windows->widget.y;
15576                      (void) XReconfigureWMWindow(display,windows->widget.id,
15577                        windows->widget.screen,(unsigned int) (CWX | CWY),
15578                        &window_changes);
15579                    }
15580                if (windows->magnify.geometry == (char *) NULL)
15581                  if (windows->magnify.mapped == MagickFalse)
15582                    {
15583                      windows->magnify.x=event.xconfigure.x+
15584                        event.xconfigure.width+25;
15585                      windows->magnify.y=event.xconfigure.y;
15586                      XConstrainWindowPosition(display,&windows->magnify);
15587                      window_changes.x=windows->magnify.x;
15588                      window_changes.y=windows->magnify.y;
15589                      (void) XReconfigureWMWindow(display,windows->magnify.id,
15590                        windows->magnify.screen,(unsigned int) (CWX | CWY),
15591                        &window_changes);
15592                    }
15593                if (windows->pan.geometry == (char *) NULL)
15594                  if (windows->pan.mapped == MagickFalse)
15595                    {
15596                      windows->pan.x=event.xconfigure.x+
15597                        event.xconfigure.width+25;
15598                      windows->pan.y=event.xconfigure.y+
15599                        windows->magnify.height+50;
15600                      XConstrainWindowPosition(display,&windows->pan);
15601                      window_changes.x=windows->pan.x;
15602                      window_changes.y=windows->pan.y;
15603                      (void) XReconfigureWMWindow(display,windows->pan.id,
15604                        windows->pan.screen,(unsigned int) (CWX | CWY),
15605                        &window_changes);
15606                    }
15607              }
15608            if ((event.xconfigure.width == (int) windows->image.width) &&
15609                (event.xconfigure.height == (int) windows->image.height))
15610              break;
15611            windows->image.width=(unsigned int) event.xconfigure.width;
15612            windows->image.height=(unsigned int) event.xconfigure.height;
15613            windows->image.x=0;
15614            windows->image.y=0;
15615            if (display_image->montage != (char *) NULL)
15616              {
15617                windows->image.x=vid_info.x;
15618                windows->image.y=vid_info.y;
15619              }
15620            if ((windows->image.mapped != MagickFalse) &&
15621                (windows->image.stasis != MagickFalse))
15622              {
15623                /*
15624                  Update image window configuration.
15625                */
15626                windows->image.window_changes.width=event.xconfigure.width;
15627                windows->image.window_changes.height=event.xconfigure.height;
15628                (void) XConfigureImage(display,resource_info,windows,
15629                  display_image,exception);
15630              }
15631            /*
15632              Update pan window configuration.
15633            */
15634            if ((event.xconfigure.width < windows->image.ximage->width) ||
15635                (event.xconfigure.height < windows->image.ximage->height))
15636              {
15637                (void) XMapRaised(display,windows->pan.id);
15638                XDrawPanRectangle(display,windows);
15639              }
15640            else
15641              if (windows->pan.mapped != MagickFalse)
15642                (void) XWithdrawWindow(display,windows->pan.id,
15643                  windows->pan.screen);
15644            break;
15645          }
15646        if (event.xconfigure.window == windows->magnify.id)
15647          {
15648            unsigned int
15649              magnify;
15650
15651            /*
15652              Magnify window has a new configuration.
15653            */
15654            windows->magnify.width=(unsigned int) event.xconfigure.width;
15655            windows->magnify.height=(unsigned int) event.xconfigure.height;
15656            if (windows->magnify.mapped == MagickFalse)
15657              break;
15658            magnify=1;
15659            while ((int) magnify <= event.xconfigure.width)
15660              magnify<<=1;
15661            while ((int) magnify <= event.xconfigure.height)
15662              magnify<<=1;
15663            magnify>>=1;
15664            if (((int) magnify != event.xconfigure.width) ||
15665                ((int) magnify != event.xconfigure.height))
15666              {
15667                window_changes.width=(int) magnify;
15668                window_changes.height=(int) magnify;
15669                (void) XReconfigureWMWindow(display,windows->magnify.id,
15670                  windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15671                  &window_changes);
15672                break;
15673              }
15674            if ((windows->magnify.mapped != MagickFalse) &&
15675                (windows->magnify.stasis != MagickFalse))
15676              {
15677                status=XMakeImage(display,resource_info,&windows->magnify,
15678                  display_image,windows->magnify.width,windows->magnify.height,
15679                  exception);
15680                XMakeMagnifyImage(display,windows,exception);
15681              }
15682            break;
15683          }
15684        if ((windows->magnify.mapped != MagickFalse) &&
15685            (event.xconfigure.window == windows->pan.id))
15686          {
15687            /*
15688              Pan icon window has a new configuration.
15689            */
15690            if (event.xconfigure.send_event != 0)
15691              {
15692                windows->pan.x=event.xconfigure.x;
15693                windows->pan.y=event.xconfigure.y;
15694              }
15695            windows->pan.width=(unsigned int) event.xconfigure.width;
15696            windows->pan.height=(unsigned int) event.xconfigure.height;
15697            break;
15698          }
15699        if (event.xconfigure.window == windows->icon.id)
15700          {
15701            /*
15702              Icon window has a new configuration.
15703            */
15704            windows->icon.width=(unsigned int) event.xconfigure.width;
15705            windows->icon.height=(unsigned int) event.xconfigure.height;
15706            break;
15707          }
15708        break;
15709      }
15710      case DestroyNotify:
15711      {
15712        /*
15713          Group leader has exited.
15714        */
15715        if (display_image->debug != MagickFalse)
15716          (void) LogMagickEvent(X11Event,GetMagickModule(),
15717            "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15718        if (event.xdestroywindow.window == windows->group_leader.id)
15719          {
15720            *state|=ExitState;
15721            break;
15722          }
15723        break;
15724      }
15725      case EnterNotify:
15726      {
15727        /*
15728          Selectively install colormap.
15729        */
15730        if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15731          if (event.xcrossing.mode != NotifyUngrab)
15732            XInstallColormap(display,map_info->colormap);
15733        break;
15734      }
15735      case Expose:
15736      {
15737        if (display_image->debug != MagickFalse)
15738          (void) LogMagickEvent(X11Event,GetMagickModule(),
15739            "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15740            event.xexpose.width,event.xexpose.height,event.xexpose.x,
15741            event.xexpose.y);
15742        /*
15743          Refresh windows that are now exposed.
15744        */
15745        if ((event.xexpose.window == windows->image.id) &&
15746            (windows->image.mapped != MagickFalse))
15747          {
15748            XRefreshWindow(display,&windows->image,&event);
15749            delay=display_image->delay/MagickMax(
15750              display_image->ticks_per_second,1L);
15751            timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15752            break;
15753          }
15754        if ((event.xexpose.window == windows->magnify.id) &&
15755            (windows->magnify.mapped != MagickFalse))
15756          {
15757            XMakeMagnifyImage(display,windows,exception);
15758            break;
15759          }
15760        if (event.xexpose.window == windows->pan.id)
15761          {
15762            XDrawPanRectangle(display,windows);
15763            break;
15764          }
15765        if (event.xexpose.window == windows->icon.id)
15766          {
15767            XRefreshWindow(display,&windows->icon,&event);
15768            break;
15769          }
15770        break;
15771      }
15772      case KeyPress:
15773      {
15774        int
15775          length;
15776
15777        /*
15778          Respond to a user key press.
15779        */
15780        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15781          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15782        *(command+length)='\0';
15783        if (display_image->debug != MagickFalse)
15784          (void) LogMagickEvent(X11Event,GetMagickModule(),
15785            "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15786            key_symbol,command);
15787        if (event.xkey.window == windows->image.id)
15788          {
15789            command_type=XImageWindowCommand(display,resource_info,windows,
15790              event.xkey.state,key_symbol,&display_image,exception);
15791            if (command_type != NullCommand)
15792              nexus=XMagickCommand(display,resource_info,windows,command_type,
15793                &display_image,exception);
15794          }
15795        if (event.xkey.window == windows->magnify.id)
15796          XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol,
15797            exception);
15798        if (event.xkey.window == windows->pan.id)
15799          {
15800            if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15801              (void) XWithdrawWindow(display,windows->pan.id,
15802                windows->pan.screen);
15803            else
15804              if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15805                XTextViewWidget(display,resource_info,windows,MagickFalse,
15806                  "Help Viewer - Image Pan",ImagePanHelp);
15807              else
15808                XTranslateImage(display,windows,*image,key_symbol);
15809          }
15810        delay=display_image->delay/MagickMax(
15811          display_image->ticks_per_second,1L);
15812        timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15813        break;
15814      }
15815      case KeyRelease:
15816      {
15817        /*
15818          Respond to a user key release.
15819        */
15820        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15821          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15822        if (display_image->debug != MagickFalse)
15823          (void) LogMagickEvent(X11Event,GetMagickModule(),
15824            "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15825        break;
15826      }
15827      case LeaveNotify:
15828      {
15829        /*
15830          Selectively uninstall colormap.
15831        */
15832        if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15833          if (event.xcrossing.mode != NotifyUngrab)
15834            XUninstallColormap(display,map_info->colormap);
15835        break;
15836      }
15837      case MapNotify:
15838      {
15839        if (display_image->debug != MagickFalse)
15840          (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15841            event.xmap.window);
15842        if (event.xmap.window == windows->backdrop.id)
15843          {
15844            (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15845              CurrentTime);
15846            windows->backdrop.mapped=MagickTrue;
15847            break;
15848          }
15849        if (event.xmap.window == windows->image.id)
15850          {
15851            if (windows->backdrop.id != (Window) NULL)
15852              (void) XInstallColormap(display,map_info->colormap);
15853            if (LocaleCompare(display_image->magick,"LOGO") == 0)
15854              {
15855                if (LocaleCompare(display_image->filename,"LOGO") == 0)
15856                  nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15857              }
15858            if (((int) windows->image.width < windows->image.ximage->width) ||
15859                ((int) windows->image.height < windows->image.ximage->height))
15860              (void) XMapRaised(display,windows->pan.id);
15861            windows->image.mapped=MagickTrue;
15862            break;
15863          }
15864        if (event.xmap.window == windows->magnify.id)
15865          {
15866            XMakeMagnifyImage(display,windows,exception);
15867            windows->magnify.mapped=MagickTrue;
15868            (void) XWithdrawWindow(display,windows->info.id,
15869              windows->info.screen);
15870            break;
15871          }
15872        if (event.xmap.window == windows->pan.id)
15873          {
15874            XMakePanImage(display,resource_info,windows,display_image,
15875              exception);
15876            windows->pan.mapped=MagickTrue;
15877            break;
15878          }
15879        if (event.xmap.window == windows->info.id)
15880          {
15881            windows->info.mapped=MagickTrue;
15882            break;
15883          }
15884        if (event.xmap.window == windows->icon.id)
15885          {
15886            MagickBooleanType
15887              taint;
15888
15889            /*
15890              Create an icon image.
15891            */
15892            taint=display_image->taint;
15893            XMakeStandardColormap(display,icon_visual,icon_resources,
15894              display_image,icon_map,icon_pixel,exception);
15895            (void) XMakeImage(display,icon_resources,&windows->icon,
15896              display_image,windows->icon.width,windows->icon.height,
15897              exception);
15898            display_image->taint=taint;
15899            (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15900              windows->icon.pixmap);
15901            (void) XClearWindow(display,windows->icon.id);
15902            (void) XWithdrawWindow(display,windows->info.id,
15903              windows->info.screen);
15904            windows->icon.mapped=MagickTrue;
15905            break;
15906          }
15907        if (event.xmap.window == windows->command.id)
15908          {
15909            windows->command.mapped=MagickTrue;
15910            break;
15911          }
15912        if (event.xmap.window == windows->popup.id)
15913          {
15914            windows->popup.mapped=MagickTrue;
15915            break;
15916          }
15917        if (event.xmap.window == windows->widget.id)
15918          {
15919            windows->widget.mapped=MagickTrue;
15920            break;
15921          }
15922        break;
15923      }
15924      case MappingNotify:
15925      {
15926        (void) XRefreshKeyboardMapping(&event.xmapping);
15927        break;
15928      }
15929      case NoExpose:
15930        break;
15931      case PropertyNotify:
15932      {
15933        Atom
15934          type;
15935
15936        int
15937          format,
15938          status;
15939
15940        unsigned char
15941          *data;
15942
15943        unsigned long
15944          after,
15945          length;
15946
15947        if (display_image->debug != MagickFalse)
15948          (void) LogMagickEvent(X11Event,GetMagickModule(),
15949            "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15950            event.xproperty.atom,event.xproperty.state);
15951        if (event.xproperty.atom != windows->im_remote_command)
15952          break;
15953        /*
15954          Display image named by the remote command protocol.
15955        */
15956        status=XGetWindowProperty(display,event.xproperty.window,
15957          event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15958          AnyPropertyType,&type,&format,&length,&after,&data);
15959        if ((status != Success) || (length == 0))
15960          break;
15961        if (LocaleCompare((char *) data,"-quit") == 0)
15962          {
15963            XClientMessage(display,windows->image.id,windows->im_protocols,
15964              windows->im_exit,CurrentTime);
15965            (void) XFree((void *) data);
15966            break;
15967          }
15968        (void) CopyMagickString(resource_info->image_info->filename,
15969          (char *) data,MaxTextExtent);
15970        (void) XFree((void *) data);
15971        nexus=ReadImage(resource_info->image_info,exception);
15972        CatchException(exception);
15973        if (nexus != (Image *) NULL)
15974          *state|=NextImageState | ExitState;
15975        break;
15976      }
15977      case ReparentNotify:
15978      {
15979        if (display_image->debug != MagickFalse)
15980          (void) LogMagickEvent(X11Event,GetMagickModule(),
15981            "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15982            event.xreparent.window);
15983        break;
15984      }
15985      case UnmapNotify:
15986      {
15987        if (display_image->debug != MagickFalse)
15988          (void) LogMagickEvent(X11Event,GetMagickModule(),
15989            "Unmap Notify: 0x%lx",event.xunmap.window);
15990        if (event.xunmap.window == windows->backdrop.id)
15991          {
15992            windows->backdrop.mapped=MagickFalse;
15993            break;
15994          }
15995        if (event.xunmap.window == windows->image.id)
15996          {
15997            windows->image.mapped=MagickFalse;
15998            break;
15999          }
16000        if (event.xunmap.window == windows->magnify.id)
16001          {
16002            windows->magnify.mapped=MagickFalse;
16003            break;
16004          }
16005        if (event.xunmap.window == windows->pan.id)
16006          {
16007            windows->pan.mapped=MagickFalse;
16008            break;
16009          }
16010        if (event.xunmap.window == windows->info.id)
16011          {
16012            windows->info.mapped=MagickFalse;
16013            break;
16014          }
16015        if (event.xunmap.window == windows->icon.id)
16016          {
16017            if (map_info->colormap == icon_map->colormap)
16018              XConfigureImageColormap(display,resource_info,windows,
16019                display_image,exception);
16020            (void) XFreeStandardColormap(display,icon_visual,icon_map,
16021              icon_pixel);
16022            windows->icon.mapped=MagickFalse;
16023            break;
16024          }
16025        if (event.xunmap.window == windows->command.id)
16026          {
16027            windows->command.mapped=MagickFalse;
16028            break;
16029          }
16030        if (event.xunmap.window == windows->popup.id)
16031          {
16032            if (windows->backdrop.id != (Window) NULL)
16033              (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16034                CurrentTime);
16035            windows->popup.mapped=MagickFalse;
16036            break;
16037          }
16038        if (event.xunmap.window == windows->widget.id)
16039          {
16040            if (windows->backdrop.id != (Window) NULL)
16041              (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16042                CurrentTime);
16043            windows->widget.mapped=MagickFalse;
16044            break;
16045          }
16046        break;
16047      }
16048      default:
16049      {
16050        if (display_image->debug != MagickFalse)
16051          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
16052            event.type);
16053        break;
16054      }
16055    }
16056  } while (!(*state & ExitState));
16057  if ((*state & ExitState) == 0)
16058    (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
16059      &display_image,exception);
16060  else
16061    if (resource_info->confirm_edit != MagickFalse)
16062      {
16063        /*
16064          Query user if image has changed.
16065        */
16066        if ((resource_info->immutable == MagickFalse) &&
16067            (display_image->taint != MagickFalse))
16068          {
16069            int
16070              status;
16071
16072            status=XConfirmWidget(display,windows,"Your image changed.",
16073              "Do you want to save it");
16074            if (status == 0)
16075              *state&=(~ExitState);
16076            else
16077              if (status > 0)
16078                (void) XMagickCommand(display,resource_info,windows,SaveCommand,
16079                  &display_image,exception);
16080          }
16081      }
16082  if ((windows->visual_info->klass == GrayScale) ||
16083      (windows->visual_info->klass == PseudoColor) ||
16084      (windows->visual_info->klass == DirectColor))
16085    {
16086      /*
16087        Withdraw pan and Magnify window.
16088      */
16089      if (windows->info.mapped != MagickFalse)
16090        (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16091      if (windows->magnify.mapped != MagickFalse)
16092        (void) XWithdrawWindow(display,windows->magnify.id,
16093          windows->magnify.screen);
16094      if (windows->command.mapped != MagickFalse)
16095        (void) XWithdrawWindow(display,windows->command.id,
16096          windows->command.screen);
16097    }
16098  if (windows->pan.mapped != MagickFalse)
16099    (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16100  if (resource_info->backdrop == MagickFalse)
16101    if (windows->backdrop.mapped)
16102      {
16103        (void) XWithdrawWindow(display,windows->backdrop.id,
16104          windows->backdrop.screen);
16105        (void) XDestroyWindow(display,windows->backdrop.id);
16106        windows->backdrop.id=(Window) NULL;
16107        (void) XWithdrawWindow(display,windows->image.id,
16108          windows->image.screen);
16109        (void) XDestroyWindow(display,windows->image.id);
16110        windows->image.id=(Window) NULL;
16111      }
16112  XSetCursorState(display,windows,MagickTrue);
16113  XCheckRefreshWindows(display,windows);
16114  if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16115    *state&=(~ExitState);
16116  if (*state & ExitState)
16117    {
16118      /*
16119        Free Standard Colormap.
16120      */
16121      (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16122      if (resource_info->map_type == (char *) NULL)
16123        (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16124      /*
16125        Free X resources.
16126      */
16127      if (resource_info->copy_image != (Image *) NULL)
16128        {
16129          resource_info->copy_image=DestroyImage(resource_info->copy_image);
16130          resource_info->copy_image=NewImageList();
16131        }
16132      DestroyXResources();
16133    }
16134  (void) XSync(display,MagickFalse);
16135  /*
16136    Restore our progress monitor and warning handlers.
16137  */
16138  (void) SetErrorHandler(warning_handler);
16139  (void) SetWarningHandler(warning_handler);
16140  /*
16141    Change to home directory.
16142  */
16143  directory=getcwd(working_directory,MaxTextExtent);
16144  (void) directory;
16145  {
16146    int
16147      status;
16148
16149    status=chdir(resource_info->home_directory);
16150    if (status == -1)
16151      (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
16152        "UnableToOpenFile","%s",resource_info->home_directory);
16153  }
16154  *image=display_image;
16155  return(nexus);
16156}
16157#else
16158
16159/*
16160%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16161%                                                                             %
16162%                                                                             %
16163%                                                                             %
16164+   D i s p l a y I m a g e s                                                 %
16165%                                                                             %
16166%                                                                             %
16167%                                                                             %
16168%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16169%
16170%  DisplayImages() displays an image sequence to any X window screen.  It
16171%  returns a value other than 0 if successful.  Check the exception member
16172%  of image to determine the reason for any failure.
16173%
16174%  The format of the DisplayImages method is:
16175%
16176%      MagickBooleanType DisplayImages(const ImageInfo *image_info,
16177%        Image *images,ExceptionInfo *exception)
16178%
16179%  A description of each parameter follows:
16180%
16181%    o image_info: the image info.
16182%
16183%    o image: the image.
16184%
16185%    o exception: return any errors or warnings in this structure.
16186%
16187*/
16188MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16189  Image *image,ExceptionInfo *exception)
16190{
16191  assert(image_info != (const ImageInfo *) NULL);
16192  assert(image_info->signature == MagickSignature);
16193  assert(image != (Image *) NULL);
16194  assert(image->signature == MagickSignature);
16195  if (image->debug != MagickFalse)
16196    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16197  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16198    "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image->filename);
16199  return(MagickFalse);
16200}
16201
16202/*
16203%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16204%                                                                             %
16205%                                                                             %
16206%                                                                             %
16207+   R e m o t e D i s p l a y C o m m a n d                                   %
16208%                                                                             %
16209%                                                                             %
16210%                                                                             %
16211%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16212%
16213%  RemoteDisplayCommand() encourages a remote display program to display the
16214%  specified image filename.
16215%
16216%  The format of the RemoteDisplayCommand method is:
16217%
16218%      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16219%        const char *window,const char *filename,ExceptionInfo *exception)
16220%
16221%  A description of each parameter follows:
16222%
16223%    o image_info: the image info.
16224%
16225%    o window: Specifies the name or id of an X window.
16226%
16227%    o filename: the name of the image filename to display.
16228%
16229%    o exception: return any errors or warnings in this structure.
16230%
16231*/
16232MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16233  const char *window,const char *filename,ExceptionInfo *exception)
16234{
16235  assert(image_info != (const ImageInfo *) NULL);
16236  assert(image_info->signature == MagickSignature);
16237  assert(filename != (char *) NULL);
16238  (void) window;
16239  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16240  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16241    "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image_info->filename);
16242  return(MagickFalse);
16243}
16244#endif
16245