display.c revision 9dc4c51125c7242f63ea032e209ea65fe855f82f
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-2013 ImageMagick Studio LLC, a non-profit organization      %
21%  dedicated to making software imaging solutions freely available.           %
22%                                                                             %
23%  You may not use this file except in compliance with the License.  You may  %
24%  obtain a copy of the License at                                            %
25%                                                                             %
26%    http://www.imagemagick.org/script/license.php                            %
27%                                                                             %
28%  Unless required by applicable law or agreed to in writing, software        %
29%  distributed under the License is distributed on an "AS IS" BASIS,          %
30%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31%  See the License for the specific language governing permissions and        %
32%  limitations under the License.                                             %
33%                                                                             %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37*/
38
39/*
40  Include declarations.
41*/
42#include "MagickCore/studio.h"
43#include "MagickCore/artifact.h"
44#include "MagickCore/attribute.h"
45#include "MagickCore/blob.h"
46#include "MagickCore/cache.h"
47#include "MagickCore/cache-private.h"
48#include "MagickCore/client.h"
49#include "MagickCore/color.h"
50#include "MagickCore/colorspace.h"
51#include "MagickCore/composite.h"
52#include "MagickCore/constitute.h"
53#include "MagickCore/decorate.h"
54#include "MagickCore/delegate.h"
55#include "MagickCore/display.h"
56#include "MagickCore/display-private.h"
57#include "MagickCore/distort.h"
58#include "MagickCore/draw.h"
59#include "MagickCore/effect.h"
60#include "MagickCore/enhance.h"
61#include "MagickCore/exception.h"
62#include "MagickCore/exception-private.h"
63#include "MagickCore/fx.h"
64#include "MagickCore/geometry.h"
65#include "MagickCore/image.h"
66#include "MagickCore/image-private.h"
67#include "MagickCore/list.h"
68#include "MagickCore/log.h"
69#include "MagickCore/magick.h"
70#include "MagickCore/memory_.h"
71#include "MagickCore/monitor.h"
72#include "MagickCore/monitor-private.h"
73#include "MagickCore/montage.h"
74#include "MagickCore/option.h"
75#include "MagickCore/paint.h"
76#include "MagickCore/pixel.h"
77#include "MagickCore/pixel-accessor.h"
78#include "MagickCore/PreRvIcccm.h"
79#include "MagickCore/property.h"
80#include "MagickCore/quantum.h"
81#include "MagickCore/quantum-private.h"
82#include "MagickCore/resize.h"
83#include "MagickCore/resource_.h"
84#include "MagickCore/shear.h"
85#include "MagickCore/segment.h"
86#include "MagickCore/statistic.h"
87#include "MagickCore/string_.h"
88#include "MagickCore/string-private.h"
89#include "MagickCore/transform.h"
90#include "MagickCore/threshold.h"
91#include "MagickCore/utility.h"
92#include "MagickCore/utility-private.h"
93#include "MagickCore/version.h"
94#include "MagickCore/widget.h"
95#include "MagickCore/widget-private.h"
96#include "MagickCore/xwindow.h"
97#include "MagickCore/xwindow-private.h"
98
99#if defined(MAGICKCORE_X11_DELEGATE)
100/*
101  Define declarations.
102*/
103#define MaxColors  MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
104
105/*
106  Constant declarations.
107*/
108static const unsigned char
109  HighlightBitmap[8] =
110  {
111    0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
112  },
113  OpaqueBitmap[8] =
114  {
115    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
116  },
117  ShadowBitmap[8] =
118  {
119    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
120  };
121
122static const char
123  *PageSizes[] =
124  {
125    "Letter",
126    "Tabloid",
127    "Ledger",
128    "Legal",
129    "Statement",
130    "Executive",
131    "A3",
132    "A4",
133    "A5",
134    "B4",
135    "B5",
136    "Folio",
137    "Quarto",
138    "10x14",
139    (char *) NULL
140  };
141
142/*
143  Help widget declarations.
144*/
145static const char
146  *ImageAnnotateHelp[] =
147  {
148    "In annotate mode, the Command widget has these options:",
149    "",
150    "    Font Name",
151    "      fixed",
152    "      variable",
153    "      5x8",
154    "      6x10",
155    "      7x13bold",
156    "      8x13bold",
157    "      9x15bold",
158    "      10x20",
159    "      12x24",
160    "      Browser...",
161    "    Font Color",
162    "      black",
163    "      blue",
164    "      cyan",
165    "      green",
166    "      gray",
167    "      red",
168    "      magenta",
169    "      yellow",
170    "      white",
171    "      transparent",
172    "      Browser...",
173    "    Font Color",
174    "      black",
175    "      blue",
176    "      cyan",
177    "      green",
178    "      gray",
179    "      red",
180    "      magenta",
181    "      yellow",
182    "      white",
183    "      transparent",
184    "      Browser...",
185    "    Rotate Text",
186    "      -90",
187    "      -45",
188    "      -30",
189    "      0",
190    "      30",
191    "      45",
192    "      90",
193    "      180",
194    "      Dialog...",
195    "    Help",
196    "    Dismiss",
197    "",
198    "Choose a font name from the Font Name sub-menu.  Additional",
199    "font names can be specified with the font browser.  You can",
200    "change the menu names by setting the X resources font1",
201    "through font9.",
202    "",
203    "Choose a font color from the Font Color sub-menu.",
204    "Additional font colors can be specified with the color",
205    "browser.  You can change the menu colors by setting the X",
206    "resources pen1 through pen9.",
207    "",
208    "If you select the color browser and press Grab, you can",
209    "choose the font color by moving the pointer to the desired",
210    "color on the screen and press any button.",
211    "",
212    "If you choose to rotate the text, choose Rotate Text from the",
213    "menu and select an angle.  Typically you will only want to",
214    "rotate one line of text at a time.  Depending on the angle you",
215    "choose, subsequent lines may end up overwriting each other.",
216    "",
217    "Choosing a font and its color is optional.  The default font",
218    "is fixed and the default color is black.  However, you must",
219    "choose a location to begin entering text and press button 1.",
220    "An underscore character will appear at the location of the",
221    "pointer.  The cursor changes to a pencil to indicate you are",
222    "in text mode.  To exit immediately, press Dismiss.",
223    "",
224    "In text mode, any key presses will display the character at",
225    "the location of the underscore and advance the underscore",
226    "cursor.  Enter your text and once completed press Apply to",
227    "finish your image annotation.  To correct errors press BACK",
228    "SPACE.  To delete an entire line of text, press DELETE.  Any",
229    "text that exceeds the boundaries of the image window is",
230    "automagically continued onto the next line.",
231    "",
232    "The actual color you request for the font is saved in the",
233    "image.  However, the color that appears in your image window",
234    "may be different.  For example, on a monochrome screen the",
235    "text will appear black or white even if you choose the color",
236    "red as the font color.  However, the image saved to a file",
237    "with -write is written with red lettering.  To assure the",
238    "correct color text in the final image, any PseudoClass image",
239    "is promoted to DirectClass (see miff(5)).  To force a",
240    "PseudoClass image to remain PseudoClass, use -colors.",
241    (char *) NULL,
242  },
243  *ImageChopHelp[] =
244  {
245    "In chop mode, the Command widget has these options:",
246    "",
247    "    Direction",
248    "      horizontal",
249    "      vertical",
250    "    Help",
251    "    Dismiss",
252    "",
253    "If the you choose the horizontal direction (this the",
254    "default), the area of the image between the two horizontal",
255    "endpoints of the chop line is removed.  Otherwise, the area",
256    "of the image between the two vertical endpoints of the chop",
257    "line is removed.",
258    "",
259    "Select a location within the image window to begin your chop,",
260    "press and hold any button.  Next, move the pointer to",
261    "another location in the image.  As you move a line will",
262    "connect the initial location and the pointer.  When you",
263    "release the button, the area within the image to chop is",
264    "determined by which direction you choose from the Command",
265    "widget.",
266    "",
267    "To cancel the image chopping, move the pointer back to the",
268    "starting point of the line and release the button.",
269    (char *) NULL,
270  },
271  *ImageColorEditHelp[] =
272  {
273    "In color edit mode, the Command widget has these options:",
274    "",
275    "    Method",
276    "      point",
277    "      replace",
278    "      floodfill",
279    "      filltoborder",
280    "      reset",
281    "    Pixel Color",
282    "      black",
283    "      blue",
284    "      cyan",
285    "      green",
286    "      gray",
287    "      red",
288    "      magenta",
289    "      yellow",
290    "      white",
291    "      Browser...",
292    "    Border Color",
293    "      black",
294    "      blue",
295    "      cyan",
296    "      green",
297    "      gray",
298    "      red",
299    "      magenta",
300    "      yellow",
301    "      white",
302    "      Browser...",
303    "    Fuzz",
304    "      0%",
305    "      2%",
306    "      5%",
307    "      10%",
308    "      15%",
309    "      Dialog...",
310    "    Undo",
311    "    Help",
312    "    Dismiss",
313    "",
314    "Choose a color editing method from the Method sub-menu",
315    "of the Command widget.  The point method recolors any pixel",
316    "selected with the pointer until the button is released.  The",
317    "replace method recolors any pixel that matches the color of",
318    "the pixel you select with a button press.  Floodfill recolors",
319    "any pixel that matches the color of the pixel you select with",
320    "a button press and is a neighbor.  Whereas filltoborder recolors",
321    "any neighbor pixel that is not the border color.  Finally reset",
322    "changes the entire image to the designated color.",
323    "",
324    "Next, choose a pixel color from the Pixel Color sub-menu.",
325    "Additional pixel colors can be specified with the color",
326    "browser.  You can change the menu colors by setting the X",
327    "resources pen1 through pen9.",
328    "",
329    "Now press button 1 to select a pixel within the image window",
330    "to change its color.  Additional pixels may be recolored as",
331    "prescribed by the method you choose.",
332    "",
333    "If the Magnify widget is mapped, it can be helpful in positioning",
334    "your pointer within the image (refer to button 2).",
335    "",
336    "The actual color you request for the pixels is saved in the",
337    "image.  However, the color that appears in your image window",
338    "may be different.  For example, on a monochrome screen the",
339    "pixel will appear black or white even if you choose the",
340    "color red as the pixel color.  However, the image saved to a",
341    "file with -write is written with red pixels.  To assure the",
342    "correct color text in the final image, any PseudoClass image",
343    "is promoted to DirectClass (see miff(5)).  To force a",
344    "PseudoClass image to remain PseudoClass, use -colors.",
345    (char *) NULL,
346  },
347  *ImageCompositeHelp[] =
348  {
349    "First a widget window is displayed requesting you to enter an",
350    "image name. Press Composite, Grab or type a file name.",
351    "Press Cancel if you choose not to create a composite image.",
352    "When you choose Grab, move the pointer to the desired window",
353    "and press any button.",
354    "",
355    "If the Composite image does not have any matte information,",
356    "you are informed and the file browser is displayed again.",
357    "Enter the name of a mask image.  The image is typically",
358    "grayscale and the same size as the composite image.  If the",
359    "image is not grayscale, it is converted to grayscale and the",
360    "resulting intensities are used as matte information.",
361    "",
362    "A small window appears showing the location of the cursor in",
363    "the image window. You are now in composite mode.  To exit",
364    "immediately, press Dismiss.  In composite mode, the Command",
365    "widget has these options:",
366    "",
367    "    Operators",
368    "      Over",
369    "      In",
370    "      Out",
371    "      Atop",
372    "      Xor",
373    "      Plus",
374    "      Minus",
375    "      Add",
376    "      Subtract",
377    "      Difference",
378    "      Multiply",
379    "      Bumpmap",
380    "      Copy",
381    "      CopyRed",
382    "      CopyGreen",
383    "      CopyBlue",
384    "      CopyOpacity",
385    "      Clear",
386    "    Dissolve",
387    "    Displace",
388    "    Help",
389    "    Dismiss",
390    "",
391    "Choose a composite operation from the Operators sub-menu of",
392    "the Command widget.  How each operator behaves is described",
393    "below.  Image window is the image currently displayed on",
394    "your X server and image is the image obtained with the File",
395    "Browser widget.",
396    "",
397    "Over     The result is the union of the two image shapes,",
398    "         with image obscuring image window in the region of",
399    "         overlap.",
400    "",
401    "In       The result is simply image cut by the shape of",
402    "         image window.  None of the image data of image",
403    "         window is in the result.",
404    "",
405    "Out      The resulting image is image with the shape of",
406    "         image window cut out.",
407    "",
408    "Atop     The result is the same shape as image image window,",
409    "         with image obscuring image window where the image",
410    "         shapes overlap.  Note this differs from over",
411    "         because the portion of image outside image window's",
412    "         shape does not appear in the result.",
413    "",
414    "Xor      The result is the image data from both image and",
415    "         image window that is outside the overlap region.",
416    "         The overlap region is blank.",
417    "",
418    "Plus     The result is just the sum of the image data.",
419    "         Output values are cropped to QuantumRange (no overflow).",
420    "",
421    "Minus    The result of image - image window, with underflow",
422    "         cropped to zero.",
423    "",
424    "Add      The result of image + image window, with overflow",
425    "         wrapping around (mod 256).",
426    "",
427    "Subtract The result of image - image window, with underflow",
428    "         wrapping around (mod 256).  The add and subtract",
429    "         operators can be used to perform reversible",
430    "         transformations.",
431    "",
432    "Difference",
433    "         The result of abs(image - image window).  This",
434    "         useful for comparing two very similar images.",
435    "",
436    "Multiply",
437    "         The result of image * image window.  This",
438    "         useful for the creation of drop-shadows.",
439    "",
440    "Bumpmap  The result of surface normals from image * image",
441    "         window.",
442    "",
443    "Copy     The resulting image is image window replaced with",
444    "         image.  Here the matte information is ignored.",
445    "",
446    "CopyRed  The red layer of the image window is replace with",
447    "         the red layer of the image.  The other layers are",
448    "         untouched.",
449    "",
450    "CopyGreen",
451    "         The green layer of the image window is replace with",
452    "         the green layer of the image.  The other layers are",
453    "         untouched.",
454    "",
455    "CopyBlue The blue layer of the image window is replace with",
456    "         the blue layer of the image.  The other layers are",
457    "         untouched.",
458    "",
459    "CopyOpacity",
460    "         The matte layer of the image window is replace with",
461    "         the matte layer of the image.  The other layers are",
462    "         untouched.",
463    "",
464    "The image compositor requires a matte, or alpha channel in",
465    "the image for some operations.  This extra channel usually",
466    "defines a mask which represents a sort of a cookie-cutter",
467    "for the image.  This the case when matte is opaque (full",
468    "coverage) for pixels inside the shape, zero outside, and",
469    "between 0 and QuantumRange on the boundary.  If image does not",
470    "have a matte channel, it is initialized with 0 for any pixel",
471    "matching in color to pixel location (0,0), otherwise QuantumRange.",
472    "",
473    "If you choose Dissolve, the composite operator becomes Over.  The",
474    "image matte channel percent transparency is initialized to factor.",
475    "The image window is initialized to (100-factor). Where factor is the",
476    "value you specify in the Dialog widget.",
477    "",
478    "Displace shifts the image pixels as defined by a displacement",
479    "map.  With this option, image is used as a displacement map.",
480    "Black, within the displacement map, is a maximum positive",
481    "displacement.  White is a maximum negative displacement and",
482    "middle gray is neutral.  The displacement is scaled to determine",
483    "the pixel shift.  By default, the displacement applies in both the",
484    "horizontal and vertical directions.  However, if you specify a mask,",
485    "image is the horizontal X displacement and mask the vertical Y",
486    "displacement.",
487    "",
488    "Note that matte information for image window is not retained",
489    "for colormapped X server visuals (e.g. StaticColor,",
490    "StaticColor, GrayScale, PseudoColor).  Correct compositing",
491    "behavior may require a TrueColor or DirectColor visual or a",
492    "Standard Colormap.",
493    "",
494    "Choosing a composite operator is optional.  The default",
495    "operator is replace.  However, you must choose a location to",
496    "composite your image and press button 1.  Press and hold the",
497    "button before releasing and an outline of the image will",
498    "appear to help you identify your location.",
499    "",
500    "The actual colors of the composite image is saved.  However,",
501    "the color that appears in image window may be different.",
502    "For example, on a monochrome screen image window will appear",
503    "black or white even though your composited image may have",
504    "many colors.  If the image is saved to a file it is written",
505    "with the correct colors.  To assure the correct colors are",
506    "saved in the final image, any PseudoClass image is promoted",
507    "to DirectClass (see miff(5)).  To force a PseudoClass image",
508    "to remain PseudoClass, use -colors.",
509    (char *) NULL,
510  },
511  *ImageCutHelp[] =
512  {
513    "In cut mode, the Command widget has these options:",
514    "",
515    "    Help",
516    "    Dismiss",
517    "",
518    "To define a cut region, press button 1 and drag.  The",
519    "cut region is defined by a highlighted rectangle that",
520    "expands or contracts as it follows the pointer.  Once you",
521    "are satisfied with the cut region, release the button.",
522    "You are now in rectify mode.  In rectify mode, the Command",
523    "widget has these options:",
524    "",
525    "    Cut",
526    "    Help",
527    "    Dismiss",
528    "",
529    "You can make adjustments by moving the pointer to one of the",
530    "cut rectangle corners, pressing a button, and dragging.",
531    "Finally, press Cut to commit your copy region.  To",
532    "exit without cutting the image, press Dismiss.",
533    (char *) NULL,
534  },
535  *ImageCopyHelp[] =
536  {
537    "In copy mode, the Command widget has these options:",
538    "",
539    "    Help",
540    "    Dismiss",
541    "",
542    "To define a copy region, press button 1 and drag.  The",
543    "copy region is defined by a highlighted rectangle that",
544    "expands or contracts as it follows the pointer.  Once you",
545    "are satisfied with the copy region, release the button.",
546    "You are now in rectify mode.  In rectify mode, the Command",
547    "widget has these options:",
548    "",
549    "    Copy",
550    "    Help",
551    "    Dismiss",
552    "",
553    "You can make adjustments by moving the pointer to one of the",
554    "copy rectangle corners, pressing a button, and dragging.",
555    "Finally, press Copy to commit your copy region.  To",
556    "exit without copying the image, press Dismiss.",
557    (char *) NULL,
558  },
559  *ImageCropHelp[] =
560  {
561    "In crop mode, the Command widget has these options:",
562    "",
563    "    Help",
564    "    Dismiss",
565    "",
566    "To define a cropping region, press button 1 and drag.  The",
567    "cropping region is defined by a highlighted rectangle that",
568    "expands or contracts as it follows the pointer.  Once you",
569    "are satisfied with the cropping region, release the button.",
570    "You are now in rectify mode.  In rectify mode, the Command",
571    "widget has these options:",
572    "",
573    "    Crop",
574    "    Help",
575    "    Dismiss",
576    "",
577    "You can make adjustments by moving the pointer to one of the",
578    "cropping rectangle corners, pressing a button, and dragging.",
579    "Finally, press Crop to commit your cropping region.  To",
580    "exit without cropping the image, press Dismiss.",
581    (char *) NULL,
582  },
583  *ImageDrawHelp[] =
584  {
585    "The cursor changes to a crosshair to indicate you are in",
586    "draw mode.  To exit immediately, press Dismiss.  In draw mode,",
587    "the Command widget has these options:",
588    "",
589    "    Element",
590    "      point",
591    "      line",
592    "      rectangle",
593    "      fill rectangle",
594    "      circle",
595    "      fill circle",
596    "      ellipse",
597    "      fill ellipse",
598    "      polygon",
599    "      fill polygon",
600    "    Color",
601    "      black",
602    "      blue",
603    "      cyan",
604    "      green",
605    "      gray",
606    "      red",
607    "      magenta",
608    "      yellow",
609    "      white",
610    "      transparent",
611    "      Browser...",
612    "    Stipple",
613    "      Brick",
614    "      Diagonal",
615    "      Scales",
616    "      Vertical",
617    "      Wavy",
618    "      Translucent",
619    "      Opaque",
620    "      Open...",
621    "    Width",
622    "      1",
623    "      2",
624    "      4",
625    "      8",
626    "      16",
627    "      Dialog...",
628    "    Undo",
629    "    Help",
630    "    Dismiss",
631    "",
632    "Choose a drawing primitive from the Element sub-menu.",
633    "",
634    "Choose a color from the Color sub-menu.  Additional",
635    "colors can be specified with the color browser.",
636    "",
637    "If you choose the color browser and press Grab, you can",
638    "select the color by moving the pointer to the desired",
639    "color on the screen and press any button.  The transparent",
640    "color updates the image matte channel and is useful for",
641    "image compositing.",
642    "",
643    "Choose a stipple, if appropriate, from the Stipple sub-menu.",
644    "Additional stipples can be specified with the file browser.",
645    "Stipples obtained from the file browser must be on disk in the",
646    "X11 bitmap format.",
647    "",
648    "Choose a width, if appropriate, from the Width sub-menu.  To",
649    "choose a specific width select the Dialog widget.",
650    "",
651    "Choose a point in the Image window and press button 1 and",
652    "hold.  Next, move the pointer to another location in the",
653    "image.  As you move, a line connects the initial location and",
654    "the pointer.  When you release the button, the image is",
655    "updated with the primitive you just drew.  For polygons, the",
656    "image is updated when you press and release the button without",
657    "moving the pointer.",
658    "",
659    "To cancel image drawing, move the pointer back to the",
660    "starting point of the line and release the button.",
661    (char *) NULL,
662  },
663  *DisplayHelp[] =
664  {
665    "BUTTONS",
666    "  The effects of each button press is described below.  Three",
667    "  buttons are required.  If you have a two button mouse,",
668    "  button 1 and 3 are returned.  Press ALT and button 3 to",
669    "  simulate button 2.",
670    "",
671    "  1    Press this button to map or unmap the Command widget.",
672    "",
673    "  2    Press and drag to define a region of the image to",
674    "       magnify.",
675    "",
676    "  3    Press and drag to choose from a select set of commands.",
677    "       This button behaves differently if the image being",
678    "       displayed is a visual image directory.  Here, choose a",
679    "       particular tile of the directory and press this button and",
680    "       drag to select a command from a pop-up menu.  Choose from",
681    "       these menu items:",
682    "",
683    "           Open",
684    "           Next",
685    "           Former",
686    "           Delete",
687    "           Update",
688    "",
689    "       If you choose Open, the image represented by the tile is",
690    "       displayed.  To return to the visual image directory, choose",
691    "       Next from the Command widget.  Next and Former moves to the",
692    "       next or former image respectively.  Choose Delete to delete",
693    "       a particular image tile.  Finally, choose Update to",
694    "       synchronize all the image tiles with their respective",
695    "       images.",
696    "",
697    "COMMAND WIDGET",
698    "  The Command widget lists a number of sub-menus and commands.",
699    "  They are",
700    "",
701    "      File",
702    "        Open...",
703    "        Next",
704    "        Former",
705    "        Select...",
706    "        Save...",
707    "        Print...",
708    "        Delete...",
709    "        New...",
710    "        Visual Directory...",
711    "        Quit",
712    "      Edit",
713    "        Undo",
714    "        Redo",
715    "        Cut",
716    "        Copy",
717    "        Paste",
718    "      View",
719    "        Half Size",
720    "        Original Size",
721    "        Double Size",
722    "        Resize...",
723    "        Apply",
724    "        Refresh",
725    "        Restore",
726    "      Transform",
727    "        Crop",
728    "        Chop",
729    "        Flop",
730    "        Flip",
731    "        Rotate Right",
732    "        Rotate Left",
733    "        Rotate...",
734    "        Shear...",
735    "        Roll...",
736    "        Trim Edges",
737    "      Enhance",
738    "        Brightness...",
739    "        Saturation...",
740    "        Hue...",
741    "        Gamma...",
742    "        Sharpen...",
743    "        Dull",
744    "        Contrast Stretch...",
745    "        Sigmoidal Contrast...",
746    "        Normalize",
747    "        Equalize",
748    "        Negate",
749    "        Grayscale",
750    "        Map...",
751    "        Quantize...",
752    "      Effects",
753    "        Despeckle",
754    "        Emboss",
755    "        Reduce Noise",
756    "        Add Noise",
757    "        Sharpen...",
758    "        Blur...",
759    "        Threshold...",
760    "        Edge Detect...",
761    "        Spread...",
762    "        Shade...",
763    "        Painting...",
764    "        Segment...",
765    "      F/X",
766    "        Solarize...",
767    "        Sepia Tone...",
768    "        Swirl...",
769    "        Implode...",
770    "        Vignette...",
771    "        Wave...",
772    "        Oil Painting...",
773    "        Charcoal Drawing...",
774    "      Image Edit",
775    "        Annotate...",
776    "        Draw...",
777    "        Color...",
778    "        Matte...",
779    "        Composite...",
780    "        Add Border...",
781    "        Add Frame...",
782    "        Comment...",
783    "        Launch...",
784    "        Region of Interest...",
785    "      Miscellany",
786    "        Image Info",
787    "        Zoom Image",
788    "        Show Preview...",
789    "        Show Histogram",
790    "        Show Matte",
791    "        Background...",
792    "        Slide Show",
793    "        Preferences...",
794    "      Help",
795    "        Overview",
796    "        Browse Documentation",
797    "        About Display",
798    "",
799    "  Menu items with a indented triangle have a sub-menu.  They",
800    "  are represented above as the indented items.  To access a",
801    "  sub-menu item, move the pointer to the appropriate menu and",
802    "  press a button and drag.  When you find the desired sub-menu",
803    "  item, release the button and the command is executed.  Move",
804    "  the pointer away from the sub-menu if you decide not to",
805    "  execute a particular command.",
806    "",
807    "KEYBOARD ACCELERATORS",
808    "  Accelerators are one or two key presses that effect a",
809    "  particular command.  The keyboard accelerators that",
810    "  display(1) understands is:",
811    "",
812    "  Ctl+O     Press to open an image from a file.",
813    "",
814    "  space     Press to display the next image.",
815    "",
816    "            If the image is a multi-paged document such as a Postscript",
817    "            document, you can skip ahead several pages by preceding",
818    "            this command with a number.  For example to display the",
819    "            third page beyond the current page, press 3<space>.",
820    "",
821    "  backspace Press to display the former image.",
822    "",
823    "            If the image is a multi-paged document such as a Postscript",
824    "            document, you can skip behind several pages by preceding",
825    "            this command with a number.  For example to display the",
826    "            third page preceding the current page, press 3<backspace>.",
827    "",
828    "  Ctl+S     Press to write the image to a file.",
829    "",
830    "  Ctl+P     Press to print the image to a Postscript printer.",
831    "",
832    "  Ctl+D     Press to delete an image file.",
833    "",
834    "  Ctl+N     Press to create a blank canvas.",
835    "",
836    "  Ctl+Q     Press to discard all images and exit program.",
837    "",
838    "  Ctl+Z     Press to undo last image transformation.",
839    "",
840    "  Ctl+R     Press to redo last image transformation.",
841    "",
842    "  Ctl+X     Press to cut a region of the image.",
843    "",
844    "  Ctl+C     Press to copy a region of the image.",
845    "",
846    "  Ctl+V     Press to paste a region to the image.",
847    "",
848    "  <         Press to half the image size.",
849    "",
850    "  -         Press to return to the original image size.",
851    "",
852    "  >         Press to double the image size.",
853    "",
854    "  %         Press to resize the image to a width and height you",
855    "            specify.",
856    "",
857    "Cmd-A       Press to make any image transformations permanent."
858    "",
859    "            By default, any image size transformations are applied",
860    "            to the original image to create the image displayed on",
861    "            the X server.  However, the transformations are not",
862    "            permanent (i.e. the original image does not change",
863    "            size only the X image does).  For example, if you",
864    "            press > the X image will appear to double in size,",
865    "            but the original image will in fact remain the same size.",
866    "            To force the original image to double in size, press >",
867    "            followed by Cmd-A.",
868    "",
869    "  @         Press to refresh the image window.",
870    "",
871    "  C         Press to cut out a rectangular region of the image.",
872    "",
873    "  [         Press to chop the image.",
874    "",
875    "  H         Press to flop image in the horizontal direction.",
876    "",
877    "  V         Press to flip image in the vertical direction.",
878    "",
879    "  /         Press to rotate the image 90 degrees clockwise.",
880    "",
881    " \\         Press to rotate the image 90 degrees counter-clockwise.",
882    "",
883    "  *         Press to rotate the image the number of degrees you",
884    "            specify.",
885    "",
886    "  S         Press to shear the image the number of degrees you",
887    "            specify.",
888    "",
889    "  R         Press to roll the image.",
890    "",
891    "  T         Press to trim the image edges.",
892    "",
893    "  Shft-H    Press to vary the image hue.",
894    "",
895    "  Shft-S    Press to vary the color saturation.",
896    "",
897    "  Shft-L    Press to vary the color brightness.",
898    "",
899    "  Shft-G    Press to gamma correct the image.",
900    "",
901    "  Shft-C    Press to sharpen the image contrast.",
902    "",
903    "  Shft-Z    Press to dull the image contrast.",
904    "",
905    "  =         Press to perform histogram equalization on the image.",
906    "",
907    "  Shft-N    Press to perform histogram normalization on the image.",
908    "",
909    "  Shft-~    Press to negate the colors of the image.",
910    "",
911    "  .         Press to convert the image colors to gray.",
912    "",
913    "  Shft-#    Press to set the maximum number of unique colors in the",
914    "            image.",
915    "",
916    "  F2        Press to reduce the speckles in an image.",
917    "",
918    "  F3        Press to eliminate peak noise from an image.",
919    "",
920    "  F4        Press to add noise to an image.",
921    "",
922    "  F5        Press to sharpen an image.",
923    "",
924    "  F6        Press to delete an image file.",
925    "",
926    "  F7        Press to threshold the image.",
927    "",
928    "  F8        Press to detect edges within an image.",
929    "",
930    "  F9        Press to emboss an image.",
931    "",
932    "  F10       Press to displace pixels by a random amount.",
933    "",
934    "  F11       Press to negate all pixels above the threshold level.",
935    "",
936    "  F12       Press to shade the image using a distant light source.",
937    "",
938    "  F13       Press to lighten or darken image edges to create a 3-D effect.",
939    "",
940    "  F14       Press to segment the image by color.",
941    "",
942    "  Meta-S    Press to swirl image pixels about the center.",
943    "",
944    "  Meta-I    Press to implode image pixels about the center.",
945    "",
946    "  Meta-W    Press to alter an image along a sine wave.",
947    "",
948    "  Meta-P    Press to simulate an oil painting.",
949    "",
950    "  Meta-C    Press to simulate a charcoal drawing.",
951    "",
952    "  Alt-A     Press to annotate the image with text.",
953    "",
954    "  Alt-D     Press to draw on an image.",
955    "",
956    "  Alt-P     Press to edit an image pixel color.",
957    "",
958    "  Alt-M     Press to edit the image matte information.",
959    "",
960    "  Alt-V     Press to composite the image with another.",
961    "",
962    "  Alt-B     Press to add a border to the image.",
963    "",
964    "  Alt-F     Press to add an ornamental border to the image.",
965    "",
966    "  Alt-Shft-!",
967    "            Press to add an image comment.",
968    "",
969    "  Ctl-A     Press to apply image processing techniques to a region",
970    "            of interest.",
971    "",
972    "  Shft-?    Press to display information about the image.",
973    "",
974    "  Shft-+    Press to map the zoom image window.",
975    "",
976    "  Shft-P    Press to preview an image enhancement, effect, or f/x.",
977    "",
978    "  F1        Press to display helpful information about display(1).",
979    "",
980    "  Find      Press to browse documentation about ImageMagick.",
981    "",
982    "  1-9       Press to change the level of magnification.",
983    "",
984    "  Use the arrow keys to move the image one pixel up, down,",
985    "  left, or right within the magnify window.  Be sure to first",
986    "  map the magnify window by pressing button 2.",
987    "",
988    "  Press ALT and one of the arrow keys to trim off one pixel",
989    "  from any side of the image.",
990    (char *) NULL,
991  },
992  *ImageMatteEditHelp[] =
993  {
994    "Matte information within an image is useful for some",
995    "operations such as image compositing (See IMAGE",
996    "COMPOSITING).  This extra channel usually defines a mask",
997    "which represents a sort of a cookie-cutter for the image.",
998    "This the case when matte is opaque (full coverage) for",
999    "pixels inside the shape, zero outside, and between 0 and",
1000    "QuantumRange on the boundary.",
1001    "",
1002    "A small window appears showing the location of the cursor in",
1003    "the image window. You are now in matte edit mode.  To exit",
1004    "immediately, press Dismiss.  In matte edit mode, the Command",
1005    "widget has these options:",
1006    "",
1007    "    Method",
1008    "      point",
1009    "      replace",
1010    "      floodfill",
1011    "      filltoborder",
1012    "      reset",
1013    "    Border Color",
1014    "      black",
1015    "      blue",
1016    "      cyan",
1017    "      green",
1018    "      gray",
1019    "      red",
1020    "      magenta",
1021    "      yellow",
1022    "      white",
1023    "      Browser...",
1024    "    Fuzz",
1025    "      0%",
1026    "      2%",
1027    "      5%",
1028    "      10%",
1029    "      15%",
1030    "      Dialog...",
1031    "    Matte",
1032    "      Opaque",
1033    "      Transparent",
1034    "      Dialog...",
1035    "    Undo",
1036    "    Help",
1037    "    Dismiss",
1038    "",
1039    "Choose a matte editing method from the Method sub-menu of",
1040    "the Command widget.  The point method changes the matte value",
1041    "of any pixel selected with the pointer until the button is",
1042    "is released.  The replace method changes the matte value of",
1043    "any pixel that matches the color of the pixel you select with",
1044    "a button press.  Floodfill changes the matte value of any pixel",
1045    "that matches the color of the pixel you select with a button",
1046    "press and is a neighbor.  Whereas filltoborder changes the matte",
1047    "value any neighbor pixel that is not the border color.  Finally",
1048    "reset changes the entire image to the designated matte value.",
1049    "",
1050    "Choose Matte Value and pick Opaque or Transarent.  For other values",
1051    "select the Dialog entry.  Here a dialog appears requesting a matte",
1052    "value.  The value you select is assigned as the opacity value of the",
1053    "selected pixel or pixels.",
1054    "",
1055    "Now, press any button to select a pixel within the image",
1056    "window to change its matte value.",
1057    "",
1058    "If the Magnify widget is mapped, it can be helpful in positioning",
1059    "your pointer within the image (refer to button 2).",
1060    "",
1061    "Matte information is only valid in a DirectClass image.",
1062    "Therefore, any PseudoClass image is promoted to DirectClass",
1063    "(see miff(5)).  Note that matte information for PseudoClass",
1064    "is not retained for colormapped X server visuals (e.g.",
1065    "StaticColor, StaticColor, GrayScale, PseudoColor) unless you",
1066    "immediately save your image to a file (refer to Write).",
1067    "Correct matte editing behavior may require a TrueColor or",
1068    "DirectColor visual or a Standard Colormap.",
1069    (char *) NULL,
1070  },
1071  *ImagePanHelp[] =
1072  {
1073    "When an image exceeds the width or height of the X server",
1074    "screen, display maps a small panning icon.  The rectangle",
1075    "within the panning icon shows the area that is currently",
1076    "displayed in the image window.  To pan about the image,",
1077    "press any button and drag the pointer within the panning",
1078    "icon.  The pan rectangle moves with the pointer and the",
1079    "image window is updated to reflect the location of the",
1080    "rectangle within the panning icon.  When you have selected",
1081    "the area of the image you wish to view, release the button.",
1082    "",
1083    "Use the arrow keys to pan the image one pixel up, down,",
1084    "left, or right within the image window.",
1085    "",
1086    "The panning icon is withdrawn if the image becomes smaller",
1087    "than the dimensions of the X server screen.",
1088    (char *) NULL,
1089  },
1090  *ImagePasteHelp[] =
1091  {
1092    "A small window appears showing the location of the cursor in",
1093    "the image window. You are now in paste mode.  To exit",
1094    "immediately, press Dismiss.  In paste mode, the Command",
1095    "widget has these options:",
1096    "",
1097    "    Operators",
1098    "      over",
1099    "      in",
1100    "      out",
1101    "      atop",
1102    "      xor",
1103    "      plus",
1104    "      minus",
1105    "      add",
1106    "      subtract",
1107    "      difference",
1108    "      replace",
1109    "    Help",
1110    "    Dismiss",
1111    "",
1112    "Choose a composite operation from the Operators sub-menu of",
1113    "the Command widget.  How each operator behaves is described",
1114    "below.  Image window is the image currently displayed on",
1115    "your X server and image is the image obtained with the File",
1116    "Browser widget.",
1117    "",
1118    "Over     The result is the union of the two image shapes,",
1119    "         with image obscuring image window in the region of",
1120    "         overlap.",
1121    "",
1122    "In       The result is simply image cut by the shape of",
1123    "         image window.  None of the image data of image",
1124    "         window is in the result.",
1125    "",
1126    "Out      The resulting image is image with the shape of",
1127    "         image window cut out.",
1128    "",
1129    "Atop     The result is the same shape as image image window,",
1130    "         with image obscuring image window where the image",
1131    "         shapes overlap.  Note this differs from over",
1132    "         because the portion of image outside image window's",
1133    "         shape does not appear in the result.",
1134    "",
1135    "Xor      The result is the image data from both image and",
1136    "         image window that is outside the overlap region.",
1137    "         The overlap region is blank.",
1138    "",
1139    "Plus     The result is just the sum of the image data.",
1140    "         Output values are cropped to QuantumRange (no overflow).",
1141    "         This operation is independent of the matte",
1142    "         channels.",
1143    "",
1144    "Minus    The result of image - image window, with underflow",
1145    "         cropped to zero.",
1146    "",
1147    "Add      The result of image + image window, with overflow",
1148    "         wrapping around (mod 256).",
1149    "",
1150    "Subtract The result of image - image window, with underflow",
1151    "         wrapping around (mod 256).  The add and subtract",
1152    "         operators can be used to perform reversible",
1153    "         transformations.",
1154    "",
1155    "Difference",
1156    "         The result of abs(image - image window).  This",
1157    "         useful for comparing two very similar images.",
1158    "",
1159    "Copy     The resulting image is image window replaced with",
1160    "         image.  Here the matte information is ignored.",
1161    "",
1162    "CopyRed  The red layer of the image window is replace with",
1163    "         the red layer of the image.  The other layers are",
1164    "         untouched.",
1165    "",
1166    "CopyGreen",
1167    "         The green layer of the image window is replace with",
1168    "         the green layer of the image.  The other layers are",
1169    "         untouched.",
1170    "",
1171    "CopyBlue The blue layer of the image window is replace with",
1172    "         the blue layer of the image.  The other layers are",
1173    "         untouched.",
1174    "",
1175    "CopyOpacity",
1176    "         The matte layer of the image window is replace with",
1177    "         the matte layer of the image.  The other layers are",
1178    "         untouched.",
1179    "",
1180    "The image compositor requires a matte, or alpha channel in",
1181    "the image for some operations.  This extra channel usually",
1182    "defines a mask which represents a sort of a cookie-cutter",
1183    "for the image.  This the case when matte is opaque (full",
1184    "coverage) for pixels inside the shape, zero outside, and",
1185    "between 0 and QuantumRange on the boundary.  If image does not",
1186    "have a matte channel, it is initialized with 0 for any pixel",
1187    "matching in color to pixel location (0,0), otherwise QuantumRange.",
1188    "",
1189    "Note that matte information for image window is not retained",
1190    "for colormapped X server visuals (e.g. StaticColor,",
1191    "StaticColor, GrayScale, PseudoColor).  Correct compositing",
1192    "behavior may require a TrueColor or DirectColor visual or a",
1193    "Standard Colormap.",
1194    "",
1195    "Choosing a composite operator is optional.  The default",
1196    "operator is replace.  However, you must choose a location to",
1197    "paste your image and press button 1.  Press and hold the",
1198    "button before releasing and an outline of the image will",
1199    "appear to help you identify your location.",
1200    "",
1201    "The actual colors of the pasted image is saved.  However,",
1202    "the color that appears in image window may be different.",
1203    "For example, on a monochrome screen image window will appear",
1204    "black or white even though your pasted image may have",
1205    "many colors.  If the image is saved to a file it is written",
1206    "with the correct colors.  To assure the correct colors are",
1207    "saved in the final image, any PseudoClass image is promoted",
1208    "to DirectClass (see miff(5)).  To force a PseudoClass image",
1209    "to remain PseudoClass, use -colors.",
1210    (char *) NULL,
1211  },
1212  *ImageROIHelp[] =
1213  {
1214    "In region of interest mode, the Command widget has these",
1215    "options:",
1216    "",
1217    "    Help",
1218    "    Dismiss",
1219    "",
1220    "To define a region of interest, press button 1 and drag.",
1221    "The region of interest is defined by a highlighted rectangle",
1222    "that expands or contracts as it follows the pointer.  Once",
1223    "you are satisfied with the region of interest, release the",
1224    "button.  You are now in apply mode.  In apply mode the",
1225    "Command widget has these options:",
1226    "",
1227    "      File",
1228    "        Save...",
1229    "        Print...",
1230    "      Edit",
1231    "        Undo",
1232    "        Redo",
1233    "      Transform",
1234    "        Flop",
1235    "        Flip",
1236    "        Rotate Right",
1237    "        Rotate Left",
1238    "      Enhance",
1239    "        Hue...",
1240    "        Saturation...",
1241    "        Brightness...",
1242    "        Gamma...",
1243    "        Spiff",
1244    "        Dull",
1245    "        Contrast Stretch",
1246    "        Sigmoidal Contrast...",
1247    "        Normalize",
1248    "        Equalize",
1249    "        Negate",
1250    "        Grayscale",
1251    "        Map...",
1252    "        Quantize...",
1253    "      Effects",
1254    "        Despeckle",
1255    "        Emboss",
1256    "        Reduce Noise",
1257    "        Sharpen...",
1258    "        Blur...",
1259    "        Threshold...",
1260    "        Edge Detect...",
1261    "        Spread...",
1262    "        Shade...",
1263    "        Raise...",
1264    "        Segment...",
1265    "      F/X",
1266    "        Solarize...",
1267    "        Sepia Tone...",
1268    "        Swirl...",
1269    "        Implode...",
1270    "        Vignette...",
1271    "        Wave...",
1272    "        Oil Painting...",
1273    "        Charcoal Drawing...",
1274    "      Miscellany",
1275    "        Image Info",
1276    "        Zoom Image",
1277    "        Show Preview...",
1278    "        Show Histogram",
1279    "        Show Matte",
1280    "      Help",
1281    "      Dismiss",
1282    "",
1283    "You can make adjustments to the region of interest by moving",
1284    "the pointer to one of the rectangle corners, pressing a",
1285    "button, and dragging.  Finally, choose an image processing",
1286    "technique from the Command widget.  You can choose more than",
1287    "one image processing technique to apply to an area.",
1288    "Alternatively, you can move the region of interest before",
1289    "applying another image processing technique.  To exit, press",
1290    "Dismiss.",
1291    (char *) NULL,
1292  },
1293  *ImageRotateHelp[] =
1294  {
1295    "In rotate mode, the Command widget has these options:",
1296    "",
1297    "    Pixel Color",
1298    "      black",
1299    "      blue",
1300    "      cyan",
1301    "      green",
1302    "      gray",
1303    "      red",
1304    "      magenta",
1305    "      yellow",
1306    "      white",
1307    "      Browser...",
1308    "    Direction",
1309    "      horizontal",
1310    "      vertical",
1311    "    Help",
1312    "    Dismiss",
1313    "",
1314    "Choose a background color from the Pixel Color sub-menu.",
1315    "Additional background colors can be specified with the color",
1316    "browser.  You can change the menu colors by setting the X",
1317    "resources pen1 through pen9.",
1318    "",
1319    "If you choose the color browser and press Grab, you can",
1320    "select the background color by moving the pointer to the",
1321    "desired color on the screen and press any button.",
1322    "",
1323    "Choose a point in the image window and press this button and",
1324    "hold.  Next, move the pointer to another location in the",
1325    "image.  As you move a line connects the initial location and",
1326    "the pointer.  When you release the button, the degree of",
1327    "image rotation is determined by the slope of the line you",
1328    "just drew.  The slope is relative to the direction you",
1329    "choose from the Direction sub-menu of the Command widget.",
1330    "",
1331    "To cancel the image rotation, move the pointer back to the",
1332    "starting point of the line and release the button.",
1333    (char *) NULL,
1334  };
1335
1336/*
1337  Enumeration declarations.
1338*/
1339typedef enum
1340{
1341  CopyMode,
1342  CropMode,
1343  CutMode
1344} ClipboardMode;
1345
1346typedef enum
1347{
1348  OpenCommand,
1349  NextCommand,
1350  FormerCommand,
1351  SelectCommand,
1352  SaveCommand,
1353  PrintCommand,
1354  DeleteCommand,
1355  NewCommand,
1356  VisualDirectoryCommand,
1357  QuitCommand,
1358  UndoCommand,
1359  RedoCommand,
1360  CutCommand,
1361  CopyCommand,
1362  PasteCommand,
1363  HalfSizeCommand,
1364  OriginalSizeCommand,
1365  DoubleSizeCommand,
1366  ResizeCommand,
1367  ApplyCommand,
1368  RefreshCommand,
1369  RestoreCommand,
1370  CropCommand,
1371  ChopCommand,
1372  FlopCommand,
1373  FlipCommand,
1374  RotateRightCommand,
1375  RotateLeftCommand,
1376  RotateCommand,
1377  ShearCommand,
1378  RollCommand,
1379  TrimCommand,
1380  HueCommand,
1381  SaturationCommand,
1382  BrightnessCommand,
1383  GammaCommand,
1384  SpiffCommand,
1385  DullCommand,
1386  ContrastStretchCommand,
1387  SigmoidalContrastCommand,
1388  NormalizeCommand,
1389  EqualizeCommand,
1390  NegateCommand,
1391  GrayscaleCommand,
1392  MapCommand,
1393  QuantizeCommand,
1394  DespeckleCommand,
1395  EmbossCommand,
1396  ReduceNoiseCommand,
1397  AddNoiseCommand,
1398  SharpenCommand,
1399  BlurCommand,
1400  ThresholdCommand,
1401  EdgeDetectCommand,
1402  SpreadCommand,
1403  ShadeCommand,
1404  RaiseCommand,
1405  SegmentCommand,
1406  SolarizeCommand,
1407  SepiaToneCommand,
1408  SwirlCommand,
1409  ImplodeCommand,
1410  VignetteCommand,
1411  WaveCommand,
1412  OilPaintCommand,
1413  CharcoalDrawCommand,
1414  AnnotateCommand,
1415  DrawCommand,
1416  ColorCommand,
1417  MatteCommand,
1418  CompositeCommand,
1419  AddBorderCommand,
1420  AddFrameCommand,
1421  CommentCommand,
1422  LaunchCommand,
1423  RegionofInterestCommand,
1424  ROIHelpCommand,
1425  ROIDismissCommand,
1426  InfoCommand,
1427  ZoomCommand,
1428  ShowPreviewCommand,
1429  ShowHistogramCommand,
1430  ShowMatteCommand,
1431  BackgroundCommand,
1432  SlideShowCommand,
1433  PreferencesCommand,
1434  HelpCommand,
1435  BrowseDocumentationCommand,
1436  VersionCommand,
1437  SaveToUndoBufferCommand,
1438  FreeBuffersCommand,
1439  NullCommand
1440} CommandType;
1441
1442typedef enum
1443{
1444  AnnotateNameCommand,
1445  AnnotateFontColorCommand,
1446  AnnotateBackgroundColorCommand,
1447  AnnotateRotateCommand,
1448  AnnotateHelpCommand,
1449  AnnotateDismissCommand,
1450  TextHelpCommand,
1451  TextApplyCommand,
1452  ChopDirectionCommand,
1453  ChopHelpCommand,
1454  ChopDismissCommand,
1455  HorizontalChopCommand,
1456  VerticalChopCommand,
1457  ColorEditMethodCommand,
1458  ColorEditColorCommand,
1459  ColorEditBorderCommand,
1460  ColorEditFuzzCommand,
1461  ColorEditUndoCommand,
1462  ColorEditHelpCommand,
1463  ColorEditDismissCommand,
1464  CompositeOperatorsCommand,
1465  CompositeDissolveCommand,
1466  CompositeDisplaceCommand,
1467  CompositeHelpCommand,
1468  CompositeDismissCommand,
1469  CropHelpCommand,
1470  CropDismissCommand,
1471  RectifyCopyCommand,
1472  RectifyHelpCommand,
1473  RectifyDismissCommand,
1474  DrawElementCommand,
1475  DrawColorCommand,
1476  DrawStippleCommand,
1477  DrawWidthCommand,
1478  DrawUndoCommand,
1479  DrawHelpCommand,
1480  DrawDismissCommand,
1481  MatteEditMethod,
1482  MatteEditBorderCommand,
1483  MatteEditFuzzCommand,
1484  MatteEditValueCommand,
1485  MatteEditUndoCommand,
1486  MatteEditHelpCommand,
1487  MatteEditDismissCommand,
1488  PasteOperatorsCommand,
1489  PasteHelpCommand,
1490  PasteDismissCommand,
1491  RotateColorCommand,
1492  RotateDirectionCommand,
1493  RotateCropCommand,
1494  RotateSharpenCommand,
1495  RotateHelpCommand,
1496  RotateDismissCommand,
1497  HorizontalRotateCommand,
1498  VerticalRotateCommand,
1499  TileLoadCommand,
1500  TileNextCommand,
1501  TileFormerCommand,
1502  TileDeleteCommand,
1503  TileUpdateCommand
1504} ModeType;
1505
1506/*
1507  Stipples.
1508*/
1509#define BricksWidth  20
1510#define BricksHeight  20
1511#define DiagonalWidth  16
1512#define DiagonalHeight  16
1513#define HighlightWidth  8
1514#define HighlightHeight  8
1515#define OpaqueWidth  8
1516#define OpaqueHeight  8
1517#define ScalesWidth  16
1518#define ScalesHeight  16
1519#define ShadowWidth  8
1520#define ShadowHeight  8
1521#define VerticalWidth  16
1522#define VerticalHeight  16
1523#define WavyWidth  16
1524#define WavyHeight  16
1525
1526/*
1527  Constant declaration.
1528*/
1529static const int
1530  RoiDelta = 8;
1531
1532static const unsigned char
1533  BricksBitmap[] =
1534  {
1535    0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1536    0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1537    0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1538    0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1539    0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1540  },
1541  DiagonalBitmap[] =
1542  {
1543    0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1544    0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1545    0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1546  },
1547  ScalesBitmap[] =
1548  {
1549    0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1550    0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1551    0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1552  },
1553  VerticalBitmap[] =
1554  {
1555    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1556    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1557    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1558  },
1559  WavyBitmap[] =
1560  {
1561    0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1562    0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1563    0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1564  };
1565
1566/*
1567  Function prototypes.
1568*/
1569static CommandType
1570  XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1571    const MagickStatusType,KeySym,Image **,ExceptionInfo *);
1572
1573static Image
1574  *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
1575    Image **,ExceptionInfo *),
1576  *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1577  *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *,
1578    ExceptionInfo *),
1579  *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *,
1580    ExceptionInfo *);
1581
1582static MagickBooleanType
1583  XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *,
1584    ExceptionInfo *),
1585  XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **,
1586    ExceptionInfo *),
1587  XChopImage(Display *,XResourceInfo *,XWindows *,Image **,
1588    ExceptionInfo *),
1589  XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode,
1590    ExceptionInfo *),
1591  XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1592    ExceptionInfo *),
1593  XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *,
1594    ExceptionInfo *),
1595  XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1596  XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1597    ExceptionInfo *),
1598  XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1599    ExceptionInfo *),
1600  XPasteImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1601  XPrintImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1602  XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **,
1603    ExceptionInfo *),
1604  XROIImage(Display *,XResourceInfo *,XWindows *,Image **,ExceptionInfo *),
1605  XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1606  XTrimImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
1607
1608static void
1609  XDrawPanRectangle(Display *,XWindows *),
1610  XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **,
1611    ExceptionInfo *),
1612  XMagnifyImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1613  XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1614  XPanImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1615  XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1616    const KeySym,ExceptionInfo *),
1617  XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1618  XScreenEvent(Display *,XWindows *,XEvent *,ExceptionInfo *),
1619  XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1620
1621/*
1622%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1623%                                                                             %
1624%                                                                             %
1625%                                                                             %
1626%   D i s p l a y I m a g e s                                                 %
1627%                                                                             %
1628%                                                                             %
1629%                                                                             %
1630%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1631%
1632%  DisplayImages() displays an image sequence to any X window screen.  It
1633%  returns a value other than 0 if successful.  Check the exception member
1634%  of image to determine the reason for any failure.
1635%
1636%  The format of the DisplayImages method is:
1637%
1638%      MagickBooleanType DisplayImages(const ImageInfo *image_info,
1639%        Image *images,ExceptionInfo *exception)
1640%
1641%  A description of each parameter follows:
1642%
1643%    o image_info: the image info.
1644%
1645%    o image: the image.
1646%
1647%    o exception: return any errors or warnings in this structure.
1648%
1649*/
1650MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1651  Image *images,ExceptionInfo *exception)
1652{
1653  char
1654    *argv[1];
1655
1656  Display
1657    *display;
1658
1659  Image
1660    *image;
1661
1662  register ssize_t
1663    i;
1664
1665  size_t
1666    state;
1667
1668  XrmDatabase
1669    resource_database;
1670
1671  XResourceInfo
1672    resource_info;
1673
1674  assert(image_info != (const ImageInfo *) NULL);
1675  assert(image_info->signature == MagickSignature);
1676  assert(images != (Image *) NULL);
1677  assert(images->signature == MagickSignature);
1678  if( IfMagickTrue(images->debug) )
1679    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1680  display=XOpenDisplay(image_info->server_name);
1681  if (display == (Display *) NULL)
1682    {
1683      (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1684        "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1685      return(MagickFalse);
1686    }
1687  if (exception->severity != UndefinedException)
1688    CatchException(exception);
1689  (void) XSetErrorHandler(XError);
1690  resource_database=XGetResourceDatabase(display,GetClientName());
1691  (void) ResetMagickMemory(&resource_info,0,sizeof(resource_info));
1692  XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1693  if (image_info->page != (char *) NULL)
1694    resource_info.image_geometry=AcquireString(image_info->page);
1695  resource_info.immutable=MagickTrue;
1696  argv[0]=AcquireString(GetClientName());
1697  state=DefaultState;
1698  for (i=0; (state & ExitState) == 0; i++)
1699  {
1700    if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1701      break;
1702    image=GetImageFromList(images,i % GetImageListLength(images));
1703    (void) XDisplayImage(display,&resource_info,argv,1,&image,&state,exception);
1704  }
1705  (void) SetErrorHandler((ErrorHandler) NULL);
1706  (void) SetWarningHandler((WarningHandler) NULL);
1707  argv[0]=DestroyString(argv[0]);
1708  (void) XCloseDisplay(display);
1709  XDestroyResourceInfo(&resource_info);
1710  if (exception->severity != UndefinedException)
1711    return(MagickFalse);
1712  return(MagickTrue);
1713}
1714
1715/*
1716%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1717%                                                                             %
1718%                                                                             %
1719%                                                                             %
1720%   R e m o t e D i s p l a y C o m m a n d                                   %
1721%                                                                             %
1722%                                                                             %
1723%                                                                             %
1724%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1725%
1726%  RemoteDisplayCommand() encourages a remote display program to display the
1727%  specified image filename.
1728%
1729%  The format of the RemoteDisplayCommand method is:
1730%
1731%      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1732%        const char *window,const char *filename,ExceptionInfo *exception)
1733%
1734%  A description of each parameter follows:
1735%
1736%    o image_info: the image info.
1737%
1738%    o window: Specifies the name or id of an X window.
1739%
1740%    o filename: the name of the image filename to display.
1741%
1742%    o exception: return any errors or warnings in this structure.
1743%
1744*/
1745MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1746  const char *window,const char *filename,ExceptionInfo *exception)
1747{
1748  Display
1749    *display;
1750
1751  MagickStatusType
1752    status;
1753
1754  assert(image_info != (const ImageInfo *) NULL);
1755  assert(image_info->signature == MagickSignature);
1756  assert(filename != (char *) NULL);
1757  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1758  display=XOpenDisplay(image_info->server_name);
1759  if (display == (Display *) NULL)
1760    {
1761      (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1762        "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1763      return(MagickFalse);
1764    }
1765  (void) XSetErrorHandler(XError);
1766  status=XRemoteCommand(display,window,filename);
1767  (void) XCloseDisplay(display);
1768  return(IsMagickTrue(status));
1769}
1770
1771/*
1772%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1773%                                                                             %
1774%                                                                             %
1775%                                                                             %
1776+   X A n n o t a t e E d i t I m a g e                                       %
1777%                                                                             %
1778%                                                                             %
1779%                                                                             %
1780%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1781%
1782%  XAnnotateEditImage() annotates the image with text.
1783%
1784%  The format of the XAnnotateEditImage method is:
1785%
1786%      MagickBooleanType XAnnotateEditImage(Display *display,
1787%        XResourceInfo *resource_info,XWindows *windows,Image *image,
1788%        ExceptionInfo *exception)
1789%
1790%  A description of each parameter follows:
1791%
1792%    o display: Specifies a connection to an X server;  returned from
1793%      XOpenDisplay.
1794%
1795%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1796%
1797%    o windows: Specifies a pointer to a XWindows structure.
1798%
1799%    o image: the image; returned from ReadImage.
1800%
1801*/
1802
1803static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
1804{
1805  if (x > y)
1806    return(x);
1807  return(y);
1808}
1809
1810static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
1811{
1812  if (x < y)
1813    return(x);
1814  return(y);
1815}
1816
1817static MagickBooleanType XAnnotateEditImage(Display *display,
1818  XResourceInfo *resource_info,XWindows *windows,Image *image,
1819  ExceptionInfo *exception)
1820{
1821  static const char
1822    *AnnotateMenu[] =
1823    {
1824      "Font Name",
1825      "Font Color",
1826      "Box Color",
1827      "Rotate Text",
1828      "Help",
1829      "Dismiss",
1830      (char *) NULL
1831    },
1832    *TextMenu[] =
1833    {
1834      "Help",
1835      "Apply",
1836      (char *) NULL
1837    };
1838
1839  static const ModeType
1840    AnnotateCommands[] =
1841    {
1842      AnnotateNameCommand,
1843      AnnotateFontColorCommand,
1844      AnnotateBackgroundColorCommand,
1845      AnnotateRotateCommand,
1846      AnnotateHelpCommand,
1847      AnnotateDismissCommand
1848    },
1849    TextCommands[] =
1850    {
1851      TextHelpCommand,
1852      TextApplyCommand
1853    };
1854
1855  static MagickBooleanType
1856    transparent_box = MagickTrue,
1857    transparent_pen = MagickFalse;
1858
1859  static double
1860    degrees = 0.0;
1861
1862  static unsigned int
1863    box_id = MaxNumberPens-2,
1864    font_id = 0,
1865    pen_id = 0;
1866
1867  char
1868    command[MaxTextExtent],
1869    text[MaxTextExtent];
1870
1871  const char
1872    *ColorMenu[MaxNumberPens+1];
1873
1874  Cursor
1875    cursor;
1876
1877  GC
1878    annotate_context;
1879
1880  int
1881    id,
1882    pen_number,
1883    status,
1884    x,
1885    y;
1886
1887  KeySym
1888    key_symbol;
1889
1890  register char
1891    *p;
1892
1893  register ssize_t
1894    i;
1895
1896  unsigned int
1897    height,
1898    width;
1899
1900  size_t
1901    state;
1902
1903  XAnnotateInfo
1904    *annotate_info,
1905    *previous_info;
1906
1907  XColor
1908    color;
1909
1910  XFontStruct
1911    *font_info;
1912
1913  XEvent
1914    event,
1915    text_event;
1916
1917  /*
1918    Map Command widget.
1919  */
1920  (void) CloneString(&windows->command.name,"Annotate");
1921  windows->command.data=4;
1922  (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1923  (void) XMapRaised(display,windows->command.id);
1924  XClientMessage(display,windows->image.id,windows->im_protocols,
1925    windows->im_update_widget,CurrentTime);
1926  /*
1927    Track pointer until button 1 is pressed.
1928  */
1929  XQueryPosition(display,windows->image.id,&x,&y);
1930  (void) XSelectInput(display,windows->image.id,
1931    windows->image.attributes.event_mask | PointerMotionMask);
1932  cursor=XCreateFontCursor(display,XC_left_side);
1933  (void) XCheckDefineCursor(display,windows->image.id,cursor);
1934  state=DefaultState;
1935  do
1936  {
1937    if( IfMagickTrue(windows->info.mapped) )
1938      {
1939        /*
1940          Display pointer position.
1941        */
1942        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
1943          x+windows->image.x,y+windows->image.y);
1944        XInfoWidget(display,windows,text);
1945      }
1946    /*
1947      Wait for next event.
1948    */
1949    XScreenEvent(display,windows,&event,exception);
1950    if (event.xany.window == windows->command.id)
1951      {
1952        /*
1953          Select a command from the Command widget.
1954        */
1955        id=XCommandWidget(display,windows,AnnotateMenu,&event);
1956        (void) XCheckDefineCursor(display,windows->image.id,cursor);
1957        if (id < 0)
1958          continue;
1959        switch (AnnotateCommands[id])
1960        {
1961          case AnnotateNameCommand:
1962          {
1963            const char
1964              *FontMenu[MaxNumberFonts];
1965
1966            int
1967              font_number;
1968
1969            /*
1970              Initialize menu selections.
1971            */
1972            for (i=0; i < MaxNumberFonts; i++)
1973              FontMenu[i]=resource_info->font_name[i];
1974            FontMenu[MaxNumberFonts-2]="Browser...";
1975            FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1976            /*
1977              Select a font name from the pop-up menu.
1978            */
1979            font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1980              (const char **) FontMenu,command);
1981            if (font_number < 0)
1982              break;
1983            if (font_number == (MaxNumberFonts-2))
1984              {
1985                static char
1986                  font_name[MaxTextExtent] = "fixed";
1987
1988                /*
1989                  Select a font name from a browser.
1990                */
1991                resource_info->font_name[font_number]=font_name;
1992                XFontBrowserWidget(display,windows,"Select",font_name);
1993                if (*font_name == '\0')
1994                  break;
1995              }
1996            /*
1997              Initialize font info.
1998            */
1999            font_info=XLoadQueryFont(display,resource_info->font_name[
2000              font_number]);
2001            if (font_info == (XFontStruct *) NULL)
2002              {
2003                XNoticeWidget(display,windows,"Unable to load font:",
2004                  resource_info->font_name[font_number]);
2005                break;
2006              }
2007            font_id=(unsigned int) font_number;
2008            (void) XFreeFont(display,font_info);
2009            break;
2010          }
2011          case AnnotateFontColorCommand:
2012          {
2013            /*
2014              Initialize menu selections.
2015            */
2016            for (i=0; i < (int) (MaxNumberPens-2); i++)
2017              ColorMenu[i]=resource_info->pen_colors[i];
2018            ColorMenu[MaxNumberPens-2]="transparent";
2019            ColorMenu[MaxNumberPens-1]="Browser...";
2020            ColorMenu[MaxNumberPens]=(const char *) NULL;
2021            /*
2022              Select a pen color from the pop-up menu.
2023            */
2024            pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2025              (const char **) ColorMenu,command);
2026            if (pen_number < 0)
2027              break;
2028            transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
2029              MagickFalse;
2030            if( IfMagickTrue(transparent_pen) )
2031              break;
2032            if (pen_number == (MaxNumberPens-1))
2033              {
2034                static char
2035                  color_name[MaxTextExtent] = "gray";
2036
2037                /*
2038                  Select a pen color from a dialog.
2039                */
2040                resource_info->pen_colors[pen_number]=color_name;
2041                XColorBrowserWidget(display,windows,"Select",color_name);
2042                if (*color_name == '\0')
2043                  break;
2044              }
2045            /*
2046              Set pen color.
2047            */
2048            (void) XParseColor(display,windows->map_info->colormap,
2049              resource_info->pen_colors[pen_number],&color);
2050            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2051              (unsigned int) MaxColors,&color);
2052            windows->pixel_info->pen_colors[pen_number]=color;
2053            pen_id=(unsigned int) pen_number;
2054            break;
2055          }
2056          case AnnotateBackgroundColorCommand:
2057          {
2058            /*
2059              Initialize menu selections.
2060            */
2061            for (i=0; i < (int) (MaxNumberPens-2); i++)
2062              ColorMenu[i]=resource_info->pen_colors[i];
2063            ColorMenu[MaxNumberPens-2]="transparent";
2064            ColorMenu[MaxNumberPens-1]="Browser...";
2065            ColorMenu[MaxNumberPens]=(const char *) NULL;
2066            /*
2067              Select a pen color from the pop-up menu.
2068            */
2069            pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2070              (const char **) ColorMenu,command);
2071            if (pen_number < 0)
2072              break;
2073            transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2074              MagickFalse;
2075            if( IfMagickTrue(transparent_box) )
2076              break;
2077            if (pen_number == (MaxNumberPens-1))
2078              {
2079                static char
2080                  color_name[MaxTextExtent] = "gray";
2081
2082                /*
2083                  Select a pen color from a dialog.
2084                */
2085                resource_info->pen_colors[pen_number]=color_name;
2086                XColorBrowserWidget(display,windows,"Select",color_name);
2087                if (*color_name == '\0')
2088                  break;
2089              }
2090            /*
2091              Set pen color.
2092            */
2093            (void) XParseColor(display,windows->map_info->colormap,
2094              resource_info->pen_colors[pen_number],&color);
2095            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2096              (unsigned int) MaxColors,&color);
2097            windows->pixel_info->pen_colors[pen_number]=color;
2098            box_id=(unsigned int) pen_number;
2099            break;
2100          }
2101          case AnnotateRotateCommand:
2102          {
2103            int
2104              entry;
2105
2106            static char
2107              angle[MaxTextExtent] = "30.0";
2108
2109            static const char
2110              *RotateMenu[] =
2111              {
2112                "-90",
2113                "-45",
2114                "-30",
2115                "0",
2116                "30",
2117                "45",
2118                "90",
2119                "180",
2120                "Dialog...",
2121                (char *) NULL,
2122              };
2123
2124            /*
2125              Select a command from the pop-up menu.
2126            */
2127            entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2128              command);
2129            if (entry < 0)
2130              break;
2131            if (entry != 8)
2132              {
2133                degrees=StringToDouble(RotateMenu[entry],(char **) NULL);
2134                break;
2135              }
2136            (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2137              angle);
2138            if (*angle == '\0')
2139              break;
2140            degrees=StringToDouble(angle,(char **) NULL);
2141            break;
2142          }
2143          case AnnotateHelpCommand:
2144          {
2145            XTextViewWidget(display,resource_info,windows,MagickFalse,
2146              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2147            break;
2148          }
2149          case AnnotateDismissCommand:
2150          {
2151            /*
2152              Prematurely exit.
2153            */
2154            state|=EscapeState;
2155            state|=ExitState;
2156            break;
2157          }
2158          default:
2159            break;
2160        }
2161        continue;
2162      }
2163    switch (event.type)
2164    {
2165      case ButtonPress:
2166      {
2167        if (event.xbutton.button != Button1)
2168          break;
2169        if (event.xbutton.window != windows->image.id)
2170          break;
2171        /*
2172          Change to text entering mode.
2173        */
2174        x=event.xbutton.x;
2175        y=event.xbutton.y;
2176        state|=ExitState;
2177        break;
2178      }
2179      case ButtonRelease:
2180        break;
2181      case Expose:
2182        break;
2183      case KeyPress:
2184      {
2185        if (event.xkey.window != windows->image.id)
2186          break;
2187        /*
2188          Respond to a user key press.
2189        */
2190        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2191          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2192        switch ((int) key_symbol)
2193        {
2194          case XK_Escape:
2195          case XK_F20:
2196          {
2197            /*
2198              Prematurely exit.
2199            */
2200            state|=EscapeState;
2201            state|=ExitState;
2202            break;
2203          }
2204          case XK_F1:
2205          case XK_Help:
2206          {
2207            XTextViewWidget(display,resource_info,windows,MagickFalse,
2208              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2209            break;
2210          }
2211          default:
2212          {
2213            (void) XBell(display,0);
2214            break;
2215          }
2216        }
2217        break;
2218      }
2219      case MotionNotify:
2220      {
2221        /*
2222          Map and unmap Info widget as cursor crosses its boundaries.
2223        */
2224        x=event.xmotion.x;
2225        y=event.xmotion.y;
2226        if( IfMagickTrue(windows->info.mapped) )
2227          {
2228            if ((x < (int) (windows->info.x+windows->info.width)) &&
2229                (y < (int) (windows->info.y+windows->info.height)))
2230              (void) XWithdrawWindow(display,windows->info.id,
2231                windows->info.screen);
2232          }
2233        else
2234          if ((x > (int) (windows->info.x+windows->info.width)) ||
2235              (y > (int) (windows->info.y+windows->info.height)))
2236            (void) XMapWindow(display,windows->info.id);
2237        break;
2238      }
2239      default:
2240        break;
2241    }
2242  } while ((state & ExitState) == 0);
2243  (void) XSelectInput(display,windows->image.id,
2244    windows->image.attributes.event_mask);
2245  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2246  if ((state & EscapeState) != 0)
2247    return(MagickTrue);
2248  /*
2249    Set font info and check boundary conditions.
2250  */
2251  font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2252  if (font_info == (XFontStruct *) NULL)
2253    {
2254      XNoticeWidget(display,windows,"Unable to load font:",
2255        resource_info->font_name[font_id]);
2256      font_info=windows->font_info;
2257    }
2258  if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2259    x=(int) windows->image.width-font_info->max_bounds.width;
2260  if (y < (int) (font_info->ascent+font_info->descent))
2261    y=(int) font_info->ascent+font_info->descent;
2262  if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2263      ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2264    return(MagickFalse);
2265  /*
2266    Initialize annotate structure.
2267  */
2268  annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2269  if (annotate_info == (XAnnotateInfo *) NULL)
2270    return(MagickFalse);
2271  XGetAnnotateInfo(annotate_info);
2272  annotate_info->x=x;
2273  annotate_info->y=y;
2274  if( IfMagickFalse(transparent_box) && IfMagickFalse(transparent_pen))
2275    annotate_info->stencil=OpaqueStencil;
2276  else
2277    if( IfMagickFalse(transparent_box) )
2278      annotate_info->stencil=BackgroundStencil;
2279    else
2280      annotate_info->stencil=ForegroundStencil;
2281  annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2282  annotate_info->degrees=degrees;
2283  annotate_info->font_info=font_info;
2284  annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2285    windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
2286    sizeof(*annotate_info->text));
2287  if (annotate_info->text == (char *) NULL)
2288    return(MagickFalse);
2289  /*
2290    Create cursor and set graphic context.
2291  */
2292  cursor=XCreateFontCursor(display,XC_pencil);
2293  (void) XCheckDefineCursor(display,windows->image.id,cursor);
2294  annotate_context=windows->image.annotate_context;
2295  (void) XSetFont(display,annotate_context,font_info->fid);
2296  (void) XSetBackground(display,annotate_context,
2297    windows->pixel_info->pen_colors[box_id].pixel);
2298  (void) XSetForeground(display,annotate_context,
2299    windows->pixel_info->pen_colors[pen_id].pixel);
2300  /*
2301    Begin annotating the image with text.
2302  */
2303  (void) CloneString(&windows->command.name,"Text");
2304  windows->command.data=0;
2305  (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2306  state=DefaultState;
2307  (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2308  text_event.xexpose.width=(int) font_info->max_bounds.width;
2309  text_event.xexpose.height=font_info->max_bounds.ascent+
2310    font_info->max_bounds.descent;
2311  p=annotate_info->text;
2312  do
2313  {
2314    /*
2315      Display text cursor.
2316    */
2317    *p='\0';
2318    (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2319    /*
2320      Wait for next event.
2321    */
2322    XScreenEvent(display,windows,&event,exception);
2323    if (event.xany.window == windows->command.id)
2324      {
2325        /*
2326          Select a command from the Command widget.
2327        */
2328        (void) XSetBackground(display,annotate_context,
2329          windows->pixel_info->background_color.pixel);
2330        (void) XSetForeground(display,annotate_context,
2331          windows->pixel_info->foreground_color.pixel);
2332        id=XCommandWidget(display,windows,AnnotateMenu,&event);
2333        (void) XSetBackground(display,annotate_context,
2334          windows->pixel_info->pen_colors[box_id].pixel);
2335        (void) XSetForeground(display,annotate_context,
2336          windows->pixel_info->pen_colors[pen_id].pixel);
2337        if (id < 0)
2338          continue;
2339        switch (TextCommands[id])
2340        {
2341          case TextHelpCommand:
2342          {
2343            XTextViewWidget(display,resource_info,windows,MagickFalse,
2344              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2345            (void) XCheckDefineCursor(display,windows->image.id,cursor);
2346            break;
2347          }
2348          case TextApplyCommand:
2349          {
2350            /*
2351              Finished annotating.
2352            */
2353            annotate_info->width=(unsigned int) XTextWidth(font_info,
2354              annotate_info->text,(int) strlen(annotate_info->text));
2355            XRefreshWindow(display,&windows->image,&text_event);
2356            state|=ExitState;
2357            break;
2358          }
2359          default:
2360            break;
2361        }
2362        continue;
2363      }
2364    /*
2365      Erase text cursor.
2366    */
2367    text_event.xexpose.x=x;
2368    text_event.xexpose.y=y-font_info->max_bounds.ascent;
2369    (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2370      (unsigned int) text_event.xexpose.width,(unsigned int)
2371      text_event.xexpose.height,MagickFalse);
2372    XRefreshWindow(display,&windows->image,&text_event);
2373    switch (event.type)
2374    {
2375      case ButtonPress:
2376      {
2377        if (event.xbutton.window != windows->image.id)
2378          break;
2379        if (event.xbutton.button == Button2)
2380          {
2381            /*
2382              Request primary selection.
2383            */
2384            (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2385              windows->image.id,CurrentTime);
2386            break;
2387          }
2388        break;
2389      }
2390      case Expose:
2391      {
2392        if (event.xexpose.count == 0)
2393          {
2394            XAnnotateInfo
2395              *text_info;
2396
2397            /*
2398              Refresh Image window.
2399            */
2400            XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2401            text_info=annotate_info;
2402            while (text_info != (XAnnotateInfo *) NULL)
2403            {
2404              if (annotate_info->stencil == ForegroundStencil)
2405                (void) XDrawString(display,windows->image.id,annotate_context,
2406                  text_info->x,text_info->y,text_info->text,
2407                  (int) strlen(text_info->text));
2408              else
2409                (void) XDrawImageString(display,windows->image.id,
2410                  annotate_context,text_info->x,text_info->y,text_info->text,
2411                  (int) strlen(text_info->text));
2412              text_info=text_info->previous;
2413            }
2414            (void) XDrawString(display,windows->image.id,annotate_context,
2415              x,y,"_",1);
2416          }
2417        break;
2418      }
2419      case KeyPress:
2420      {
2421        int
2422          length;
2423
2424        if (event.xkey.window != windows->image.id)
2425          break;
2426        /*
2427          Respond to a user key press.
2428        */
2429        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2430          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2431        *(command+length)='\0';
2432        if (((event.xkey.state & ControlMask) != 0) ||
2433            ((event.xkey.state & Mod1Mask) != 0))
2434          state|=ModifierState;
2435        if ((state & ModifierState) != 0)
2436          switch ((int) key_symbol)
2437          {
2438            case XK_u:
2439            case XK_U:
2440            {
2441              key_symbol=DeleteCommand;
2442              break;
2443            }
2444            default:
2445              break;
2446          }
2447        switch ((int) key_symbol)
2448        {
2449          case XK_BackSpace:
2450          {
2451            /*
2452              Erase one character.
2453            */
2454            if (p == annotate_info->text)
2455              {
2456                if (annotate_info->previous == (XAnnotateInfo *) NULL)
2457                  break;
2458                else
2459                  {
2460                    /*
2461                      Go to end of the previous line of text.
2462                    */
2463                    annotate_info=annotate_info->previous;
2464                    p=annotate_info->text;
2465                    x=annotate_info->x+annotate_info->width;
2466                    y=annotate_info->y;
2467                    if (annotate_info->width != 0)
2468                      p+=strlen(annotate_info->text);
2469                    break;
2470                  }
2471              }
2472            p--;
2473            x-=XTextWidth(font_info,p,1);
2474            text_event.xexpose.x=x;
2475            text_event.xexpose.y=y-font_info->max_bounds.ascent;
2476            XRefreshWindow(display,&windows->image,&text_event);
2477            break;
2478          }
2479          case XK_bracketleft:
2480          {
2481            key_symbol=XK_Escape;
2482            break;
2483          }
2484          case DeleteCommand:
2485          {
2486            /*
2487              Erase the entire line of text.
2488            */
2489            while (p != annotate_info->text)
2490            {
2491              p--;
2492              x-=XTextWidth(font_info,p,1);
2493              text_event.xexpose.x=x;
2494              XRefreshWindow(display,&windows->image,&text_event);
2495            }
2496            break;
2497          }
2498          case XK_Escape:
2499          case XK_F20:
2500          {
2501            /*
2502              Finished annotating.
2503            */
2504            annotate_info->width=(unsigned int) XTextWidth(font_info,
2505              annotate_info->text,(int) strlen(annotate_info->text));
2506            XRefreshWindow(display,&windows->image,&text_event);
2507            state|=ExitState;
2508            break;
2509          }
2510          default:
2511          {
2512            /*
2513              Draw a single character on the Image window.
2514            */
2515            if ((state & ModifierState) != 0)
2516              break;
2517            if (*command == '\0')
2518              break;
2519            *p=(*command);
2520            if (annotate_info->stencil == ForegroundStencil)
2521              (void) XDrawString(display,windows->image.id,annotate_context,
2522                x,y,p,1);
2523            else
2524              (void) XDrawImageString(display,windows->image.id,
2525                annotate_context,x,y,p,1);
2526            x+=XTextWidth(font_info,p,1);
2527            p++;
2528            if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2529              break;
2530          }
2531          case XK_Return:
2532          case XK_KP_Enter:
2533          {
2534            /*
2535              Advance to the next line of text.
2536            */
2537            *p='\0';
2538            annotate_info->width=(unsigned int) XTextWidth(font_info,
2539              annotate_info->text,(int) strlen(annotate_info->text));
2540            if (annotate_info->next != (XAnnotateInfo *) NULL)
2541              {
2542                /*
2543                  Line of text already exists.
2544                */
2545                annotate_info=annotate_info->next;
2546                x=annotate_info->x;
2547                y=annotate_info->y;
2548                p=annotate_info->text;
2549                break;
2550              }
2551            annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2552              sizeof(*annotate_info->next));
2553            if (annotate_info->next == (XAnnotateInfo *) NULL)
2554              return(MagickFalse);
2555            *annotate_info->next=(*annotate_info);
2556            annotate_info->next->previous=annotate_info;
2557            annotate_info=annotate_info->next;
2558            annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2559              windows->image.width/MagickMax((ssize_t)
2560              font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2561            if (annotate_info->text == (char *) NULL)
2562              return(MagickFalse);
2563            annotate_info->y+=annotate_info->height;
2564            if (annotate_info->y > (int) windows->image.height)
2565              annotate_info->y=(int) annotate_info->height;
2566            annotate_info->next=(XAnnotateInfo *) NULL;
2567            x=annotate_info->x;
2568            y=annotate_info->y;
2569            p=annotate_info->text;
2570            break;
2571          }
2572        }
2573        break;
2574      }
2575      case KeyRelease:
2576      {
2577        /*
2578          Respond to a user key release.
2579        */
2580        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2581          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2582        state&=(~ModifierState);
2583        break;
2584      }
2585      case SelectionNotify:
2586      {
2587        Atom
2588          type;
2589
2590        int
2591          format;
2592
2593        unsigned char
2594          *data;
2595
2596        unsigned long
2597          after,
2598          length;
2599
2600        /*
2601          Obtain response from primary selection.
2602        */
2603        if (event.xselection.property == (Atom) None)
2604          break;
2605        status=XGetWindowProperty(display,event.xselection.requestor,
2606          event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
2607          &type,&format,&length,&after,&data);
2608        if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2609            (length == 0))
2610          break;
2611        /*
2612          Annotate Image window with primary selection.
2613        */
2614        for (i=0; i < (ssize_t) length; i++)
2615        {
2616          if ((char) data[i] != '\n')
2617            {
2618              /*
2619                Draw a single character on the Image window.
2620              */
2621              *p=(char) data[i];
2622              (void) XDrawString(display,windows->image.id,annotate_context,
2623                x,y,p,1);
2624              x+=XTextWidth(font_info,p,1);
2625              p++;
2626              if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2627                continue;
2628            }
2629          /*
2630            Advance to the next line of text.
2631          */
2632          *p='\0';
2633          annotate_info->width=(unsigned int) XTextWidth(font_info,
2634            annotate_info->text,(int) strlen(annotate_info->text));
2635          if (annotate_info->next != (XAnnotateInfo *) NULL)
2636            {
2637              /*
2638                Line of text already exists.
2639              */
2640              annotate_info=annotate_info->next;
2641              x=annotate_info->x;
2642              y=annotate_info->y;
2643              p=annotate_info->text;
2644              continue;
2645            }
2646          annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2647            sizeof(*annotate_info->next));
2648          if (annotate_info->next == (XAnnotateInfo *) NULL)
2649            return(MagickFalse);
2650          *annotate_info->next=(*annotate_info);
2651          annotate_info->next->previous=annotate_info;
2652          annotate_info=annotate_info->next;
2653          annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2654            windows->image.width/MagickMax((ssize_t)
2655            font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2656          if (annotate_info->text == (char *) NULL)
2657            return(MagickFalse);
2658          annotate_info->y+=annotate_info->height;
2659          if (annotate_info->y > (int) windows->image.height)
2660            annotate_info->y=(int) annotate_info->height;
2661          annotate_info->next=(XAnnotateInfo *) NULL;
2662          x=annotate_info->x;
2663          y=annotate_info->y;
2664          p=annotate_info->text;
2665        }
2666        (void) XFree((void *) data);
2667        break;
2668      }
2669      default:
2670        break;
2671    }
2672  } while ((state & ExitState) == 0);
2673  (void) XFreeCursor(display,cursor);
2674  /*
2675    Annotation is relative to image configuration.
2676  */
2677  width=(unsigned int) image->columns;
2678  height=(unsigned int) image->rows;
2679  x=0;
2680  y=0;
2681  if (windows->image.crop_geometry != (char *) NULL)
2682    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2683  /*
2684    Initialize annotated image.
2685  */
2686  XSetCursorState(display,windows,MagickTrue);
2687  XCheckRefreshWindows(display,windows);
2688  while (annotate_info != (XAnnotateInfo *) NULL)
2689  {
2690    if (annotate_info->width == 0)
2691      {
2692        /*
2693          No text on this line--  go to the next line of text.
2694        */
2695        previous_info=annotate_info->previous;
2696        annotate_info->text=(char *)
2697          RelinquishMagickMemory(annotate_info->text);
2698        annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2699        annotate_info=previous_info;
2700        continue;
2701      }
2702    /*
2703      Determine pixel index for box and pen color.
2704    */
2705    windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2706    if (windows->pixel_info->colors != 0)
2707      for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2708        if (windows->pixel_info->pixels[i] ==
2709            windows->pixel_info->pen_colors[box_id].pixel)
2710          {
2711            windows->pixel_info->box_index=(unsigned short) i;
2712            break;
2713          }
2714    windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2715    if (windows->pixel_info->colors != 0)
2716      for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2717        if (windows->pixel_info->pixels[i] ==
2718            windows->pixel_info->pen_colors[pen_id].pixel)
2719          {
2720            windows->pixel_info->pen_index=(unsigned short) i;
2721            break;
2722          }
2723    /*
2724      Define the annotate geometry string.
2725    */
2726    annotate_info->x=(int)
2727      width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2728    annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2729      windows->image.y)/windows->image.ximage->height;
2730    (void) FormatLocaleString(annotate_info->geometry,MaxTextExtent,
2731      "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2732      height*annotate_info->height/windows->image.ximage->height,
2733      annotate_info->x+x,annotate_info->y+y);
2734    /*
2735      Annotate image with text.
2736    */
2737    status=XAnnotateImage(display,windows->pixel_info,annotate_info,image,
2738      exception);
2739    if (status == 0)
2740      return(MagickFalse);
2741    /*
2742      Free up memory.
2743    */
2744    previous_info=annotate_info->previous;
2745    annotate_info->text=DestroyString(annotate_info->text);
2746    annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2747    annotate_info=previous_info;
2748  }
2749  (void) XSetForeground(display,annotate_context,
2750    windows->pixel_info->foreground_color.pixel);
2751  (void) XSetBackground(display,annotate_context,
2752    windows->pixel_info->background_color.pixel);
2753  (void) XSetFont(display,annotate_context,windows->font_info->fid);
2754  XSetCursorState(display,windows,MagickFalse);
2755  (void) XFreeFont(display,font_info);
2756  /*
2757    Update image configuration.
2758  */
2759  XConfigureImageColormap(display,resource_info,windows,image,exception);
2760  (void) XConfigureImage(display,resource_info,windows,image,exception);
2761  return(MagickTrue);
2762}
2763
2764/*
2765%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2766%                                                                             %
2767%                                                                             %
2768%                                                                             %
2769+   X B a c k g r o u n d I m a g e                                           %
2770%                                                                             %
2771%                                                                             %
2772%                                                                             %
2773%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2774%
2775%  XBackgroundImage() displays the image in the background of a window.
2776%
2777%  The format of the XBackgroundImage method is:
2778%
2779%      MagickBooleanType XBackgroundImage(Display *display,
2780%        XResourceInfo *resource_info,XWindows *windows,Image **image,
2781%        ExceptionInfo *exception)
2782%
2783%  A description of each parameter follows:
2784%
2785%    o display: Specifies a connection to an X server; returned from
2786%      XOpenDisplay.
2787%
2788%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2789%
2790%    o windows: Specifies a pointer to a XWindows structure.
2791%
2792%    o image: the image.
2793%
2794%    o exception: return any errors or warnings in this structure.
2795%
2796*/
2797static MagickBooleanType XBackgroundImage(Display *display,
2798  XResourceInfo *resource_info,XWindows *windows,Image **image,
2799  ExceptionInfo *exception)
2800{
2801#define BackgroundImageTag  "Background/Image"
2802
2803  int
2804    status;
2805
2806  static char
2807    window_id[MaxTextExtent] = "root";
2808
2809  XResourceInfo
2810    background_resources;
2811
2812  /*
2813    Put image in background.
2814  */
2815  status=XDialogWidget(display,windows,"Background",
2816    "Enter window id (id 0x00 selects window with pointer):",window_id);
2817  if (*window_id == '\0')
2818    return(MagickFalse);
2819  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
2820    exception);
2821  XInfoWidget(display,windows,BackgroundImageTag);
2822  XSetCursorState(display,windows,MagickTrue);
2823  XCheckRefreshWindows(display,windows);
2824  background_resources=(*resource_info);
2825  background_resources.window_id=window_id;
2826  background_resources.backdrop=IsMagickTrue(status);
2827  status=XDisplayBackgroundImage(display,&background_resources,*image,
2828    exception);
2829  if (IfMagickTrue(status))
2830    XClientMessage(display,windows->image.id,windows->im_protocols,
2831      windows->im_retain_colors,CurrentTime);
2832  XSetCursorState(display,windows,MagickFalse);
2833  (void) XMagickCommand(display,resource_info,windows,UndoCommand,image,
2834    exception);
2835  return(MagickTrue);
2836}
2837
2838/*
2839%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2840%                                                                             %
2841%                                                                             %
2842%                                                                             %
2843+   X C h o p I m a g e                                                       %
2844%                                                                             %
2845%                                                                             %
2846%                                                                             %
2847%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2848%
2849%  XChopImage() chops the X image.
2850%
2851%  The format of the XChopImage method is:
2852%
2853%    MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2854%      XWindows *windows,Image **image,ExceptionInfo *exception)
2855%
2856%  A description of each parameter follows:
2857%
2858%    o display: Specifies a connection to an X server; returned from
2859%      XOpenDisplay.
2860%
2861%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2862%
2863%    o windows: Specifies a pointer to a XWindows structure.
2864%
2865%    o image: the image.
2866%
2867%    o exception: return any errors or warnings in this structure.
2868%
2869*/
2870static MagickBooleanType XChopImage(Display *display,
2871  XResourceInfo *resource_info,XWindows *windows,Image **image,
2872  ExceptionInfo *exception)
2873{
2874  static const char
2875    *ChopMenu[] =
2876    {
2877      "Direction",
2878      "Help",
2879      "Dismiss",
2880      (char *) NULL
2881    };
2882
2883  static ModeType
2884    direction = HorizontalChopCommand;
2885
2886  static const ModeType
2887    ChopCommands[] =
2888    {
2889      ChopDirectionCommand,
2890      ChopHelpCommand,
2891      ChopDismissCommand
2892    },
2893    DirectionCommands[] =
2894    {
2895      HorizontalChopCommand,
2896      VerticalChopCommand
2897    };
2898
2899  char
2900    text[MaxTextExtent];
2901
2902  Image
2903    *chop_image;
2904
2905  int
2906    id,
2907    x,
2908    y;
2909
2910  double
2911    scale_factor;
2912
2913  RectangleInfo
2914    chop_info;
2915
2916  unsigned int
2917    distance,
2918    height,
2919    width;
2920
2921  size_t
2922    state;
2923
2924  XEvent
2925    event;
2926
2927  XSegment
2928    segment_info;
2929
2930  /*
2931    Map Command widget.
2932  */
2933  (void) CloneString(&windows->command.name,"Chop");
2934  windows->command.data=1;
2935  (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2936  (void) XMapRaised(display,windows->command.id);
2937  XClientMessage(display,windows->image.id,windows->im_protocols,
2938    windows->im_update_widget,CurrentTime);
2939  /*
2940    Track pointer until button 1 is pressed.
2941  */
2942  XQueryPosition(display,windows->image.id,&x,&y);
2943  (void) XSelectInput(display,windows->image.id,
2944    windows->image.attributes.event_mask | PointerMotionMask);
2945  state=DefaultState;
2946  do
2947  {
2948    if( IfMagickTrue(windows->info.mapped) )
2949      {
2950        /*
2951          Display pointer position.
2952        */
2953        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
2954          x+windows->image.x,y+windows->image.y);
2955        XInfoWidget(display,windows,text);
2956      }
2957    /*
2958      Wait for next event.
2959    */
2960    XScreenEvent(display,windows,&event,exception);
2961    if (event.xany.window == windows->command.id)
2962      {
2963        /*
2964          Select a command from the Command widget.
2965        */
2966        id=XCommandWidget(display,windows,ChopMenu,&event);
2967        if (id < 0)
2968          continue;
2969        switch (ChopCommands[id])
2970        {
2971          case ChopDirectionCommand:
2972          {
2973            char
2974              command[MaxTextExtent];
2975
2976            static const char
2977              *Directions[] =
2978              {
2979                "horizontal",
2980                "vertical",
2981                (char *) NULL,
2982              };
2983
2984            /*
2985              Select a command from the pop-up menu.
2986            */
2987            id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2988            if (id >= 0)
2989              direction=DirectionCommands[id];
2990            break;
2991          }
2992          case ChopHelpCommand:
2993          {
2994            XTextViewWidget(display,resource_info,windows,MagickFalse,
2995              "Help Viewer - Image Chop",ImageChopHelp);
2996            break;
2997          }
2998          case ChopDismissCommand:
2999          {
3000            /*
3001              Prematurely exit.
3002            */
3003            state|=EscapeState;
3004            state|=ExitState;
3005            break;
3006          }
3007          default:
3008            break;
3009        }
3010        continue;
3011      }
3012    switch (event.type)
3013    {
3014      case ButtonPress:
3015      {
3016        if (event.xbutton.button != Button1)
3017          break;
3018        if (event.xbutton.window != windows->image.id)
3019          break;
3020        /*
3021          User has committed to start point of chopping line.
3022        */
3023        segment_info.x1=(short int) event.xbutton.x;
3024        segment_info.x2=(short int) event.xbutton.x;
3025        segment_info.y1=(short int) event.xbutton.y;
3026        segment_info.y2=(short int) event.xbutton.y;
3027        state|=ExitState;
3028        break;
3029      }
3030      case ButtonRelease:
3031        break;
3032      case Expose:
3033        break;
3034      case KeyPress:
3035      {
3036        char
3037          command[MaxTextExtent];
3038
3039        KeySym
3040          key_symbol;
3041
3042        if (event.xkey.window != windows->image.id)
3043          break;
3044        /*
3045          Respond to a user key press.
3046        */
3047        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3048          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3049        switch ((int) key_symbol)
3050        {
3051          case XK_Escape:
3052          case XK_F20:
3053          {
3054            /*
3055              Prematurely exit.
3056            */
3057            state|=EscapeState;
3058            state|=ExitState;
3059            break;
3060          }
3061          case XK_F1:
3062          case XK_Help:
3063          {
3064            (void) XSetFunction(display,windows->image.highlight_context,
3065              GXcopy);
3066            XTextViewWidget(display,resource_info,windows,MagickFalse,
3067              "Help Viewer - Image Chop",ImageChopHelp);
3068            (void) XSetFunction(display,windows->image.highlight_context,
3069              GXinvert);
3070            break;
3071          }
3072          default:
3073          {
3074            (void) XBell(display,0);
3075            break;
3076          }
3077        }
3078        break;
3079      }
3080      case MotionNotify:
3081      {
3082        /*
3083          Map and unmap Info widget as text cursor crosses its boundaries.
3084        */
3085        x=event.xmotion.x;
3086        y=event.xmotion.y;
3087        if( IfMagickTrue(windows->info.mapped) )
3088          {
3089            if ((x < (int) (windows->info.x+windows->info.width)) &&
3090                (y < (int) (windows->info.y+windows->info.height)))
3091              (void) XWithdrawWindow(display,windows->info.id,
3092                windows->info.screen);
3093          }
3094        else
3095          if ((x > (int) (windows->info.x+windows->info.width)) ||
3096              (y > (int) (windows->info.y+windows->info.height)))
3097            (void) XMapWindow(display,windows->info.id);
3098      }
3099    }
3100  } while ((state & ExitState) == 0);
3101  (void) XSelectInput(display,windows->image.id,
3102    windows->image.attributes.event_mask);
3103  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3104  if ((state & EscapeState) != 0)
3105    return(MagickTrue);
3106  /*
3107    Draw line as pointer moves until the mouse button is released.
3108  */
3109  chop_info.width=0;
3110  chop_info.height=0;
3111  chop_info.x=0;
3112  chop_info.y=0;
3113  distance=0;
3114  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3115  state=DefaultState;
3116  do
3117  {
3118    if (distance > 9)
3119      {
3120        /*
3121          Display info and draw chopping line.
3122        */
3123        if( IfMagickFalse(windows->info.mapped) )
3124          (void) XMapWindow(display,windows->info.id);
3125        (void) FormatLocaleString(text,MaxTextExtent,
3126          " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3127          chop_info.height,(double) chop_info.x,(double) chop_info.y);
3128        XInfoWidget(display,windows,text);
3129        XHighlightLine(display,windows->image.id,
3130          windows->image.highlight_context,&segment_info);
3131      }
3132    else
3133      if( IfMagickTrue(windows->info.mapped) )
3134        (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3135    /*
3136      Wait for next event.
3137    */
3138    XScreenEvent(display,windows,&event,exception);
3139    if (distance > 9)
3140      XHighlightLine(display,windows->image.id,
3141        windows->image.highlight_context,&segment_info);
3142    switch (event.type)
3143    {
3144      case ButtonPress:
3145      {
3146        segment_info.x2=(short int) event.xmotion.x;
3147        segment_info.y2=(short int) event.xmotion.y;
3148        break;
3149      }
3150      case ButtonRelease:
3151      {
3152        /*
3153          User has committed to chopping line.
3154        */
3155        segment_info.x2=(short int) event.xbutton.x;
3156        segment_info.y2=(short int) event.xbutton.y;
3157        state|=ExitState;
3158        break;
3159      }
3160      case Expose:
3161        break;
3162      case MotionNotify:
3163      {
3164        segment_info.x2=(short int) event.xmotion.x;
3165        segment_info.y2=(short int) event.xmotion.y;
3166      }
3167      default:
3168        break;
3169    }
3170    /*
3171      Check boundary conditions.
3172    */
3173    if (segment_info.x2 < 0)
3174      segment_info.x2=0;
3175    else
3176      if (segment_info.x2 > windows->image.ximage->width)
3177        segment_info.x2=windows->image.ximage->width;
3178    if (segment_info.y2 < 0)
3179      segment_info.y2=0;
3180    else
3181      if (segment_info.y2 > windows->image.ximage->height)
3182        segment_info.y2=windows->image.ximage->height;
3183    distance=(unsigned int)
3184      (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3185       ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3186    /*
3187      Compute chopping geometry.
3188    */
3189    if (direction == HorizontalChopCommand)
3190      {
3191        chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3192        chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3193        chop_info.height=0;
3194        chop_info.y=0;
3195        if (segment_info.x1 > (int) segment_info.x2)
3196          {
3197            chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3198            chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3199          }
3200      }
3201    else
3202      {
3203        chop_info.width=0;
3204        chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3205        chop_info.x=0;
3206        chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3207        if (segment_info.y1 > segment_info.y2)
3208          {
3209            chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3210            chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3211          }
3212      }
3213  } while ((state & ExitState) == 0);
3214  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3215  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3216  if (distance <= 9)
3217    return(MagickTrue);
3218  /*
3219    Image chopping is relative to image configuration.
3220  */
3221  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
3222    exception);
3223  XSetCursorState(display,windows,MagickTrue);
3224  XCheckRefreshWindows(display,windows);
3225  windows->image.window_changes.width=windows->image.ximage->width-
3226    (unsigned int) chop_info.width;
3227  windows->image.window_changes.height=windows->image.ximage->height-
3228    (unsigned int) chop_info.height;
3229  width=(unsigned int) (*image)->columns;
3230  height=(unsigned int) (*image)->rows;
3231  x=0;
3232  y=0;
3233  if (windows->image.crop_geometry != (char *) NULL)
3234    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3235  scale_factor=(double) width/windows->image.ximage->width;
3236  chop_info.x+=x;
3237  chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3238  chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3239  scale_factor=(double) height/windows->image.ximage->height;
3240  chop_info.y+=y;
3241  chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3242  chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3243  /*
3244    Chop image.
3245  */
3246  chop_image=ChopImage(*image,&chop_info,exception);
3247  XSetCursorState(display,windows,MagickFalse);
3248  if (chop_image == (Image *) NULL)
3249    return(MagickFalse);
3250  *image=DestroyImage(*image);
3251  *image=chop_image;
3252  /*
3253    Update image configuration.
3254  */
3255  XConfigureImageColormap(display,resource_info,windows,*image,exception);
3256  (void) XConfigureImage(display,resource_info,windows,*image,exception);
3257  return(MagickTrue);
3258}
3259
3260/*
3261%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3262%                                                                             %
3263%                                                                             %
3264%                                                                             %
3265+   X C o l o r E d i t I m a g e                                             %
3266%                                                                             %
3267%                                                                             %
3268%                                                                             %
3269%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3270%
3271%  XColorEditImage() allows the user to interactively change the color of one
3272%  pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3273%
3274%  The format of the XColorEditImage method is:
3275%
3276%      MagickBooleanType XColorEditImage(Display *display,
3277%        XResourceInfo *resource_info,XWindows *windows,Image **image,
3278%          ExceptionInfo *exception)
3279%
3280%  A description of each parameter follows:
3281%
3282%    o display: Specifies a connection to an X server;  returned from
3283%      XOpenDisplay.
3284%
3285%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3286%
3287%    o windows: Specifies a pointer to a XWindows structure.
3288%
3289%    o image: the image; returned from ReadImage.
3290%
3291%    o exception: return any errors or warnings in this structure.
3292%
3293*/
3294static MagickBooleanType XColorEditImage(Display *display,
3295  XResourceInfo *resource_info,XWindows *windows,Image **image,
3296  ExceptionInfo *exception)
3297{
3298  static const char
3299    *ColorEditMenu[] =
3300    {
3301      "Method",
3302      "Pixel Color",
3303      "Border Color",
3304      "Fuzz",
3305      "Undo",
3306      "Help",
3307      "Dismiss",
3308      (char *) NULL
3309    };
3310
3311  static const ModeType
3312    ColorEditCommands[] =
3313    {
3314      ColorEditMethodCommand,
3315      ColorEditColorCommand,
3316      ColorEditBorderCommand,
3317      ColorEditFuzzCommand,
3318      ColorEditUndoCommand,
3319      ColorEditHelpCommand,
3320      ColorEditDismissCommand
3321    };
3322
3323  static PaintMethod
3324    method = PointMethod;
3325
3326  static unsigned int
3327    pen_id = 0;
3328
3329  static XColor
3330    border_color = { 0, 0, 0, 0, 0, 0 };
3331
3332  char
3333    command[MaxTextExtent],
3334    text[MaxTextExtent];
3335
3336  Cursor
3337    cursor;
3338
3339  int
3340    entry,
3341    id,
3342    x,
3343    x_offset,
3344    y,
3345    y_offset;
3346
3347  register Quantum
3348    *q;
3349
3350  register ssize_t
3351    i;
3352
3353  unsigned int
3354    height,
3355    width;
3356
3357  size_t
3358    state;
3359
3360  XColor
3361    color;
3362
3363  XEvent
3364    event;
3365
3366  /*
3367    Map Command widget.
3368  */
3369  (void) CloneString(&windows->command.name,"Color Edit");
3370  windows->command.data=4;
3371  (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3372  (void) XMapRaised(display,windows->command.id);
3373  XClientMessage(display,windows->image.id,windows->im_protocols,
3374    windows->im_update_widget,CurrentTime);
3375  /*
3376    Make cursor.
3377  */
3378  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3379    resource_info->background_color,resource_info->foreground_color);
3380  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3381  /*
3382    Track pointer until button 1 is pressed.
3383  */
3384  XQueryPosition(display,windows->image.id,&x,&y);
3385  (void) XSelectInput(display,windows->image.id,
3386    windows->image.attributes.event_mask | PointerMotionMask);
3387  state=DefaultState;
3388  do
3389  {
3390    if( IfMagickTrue(windows->info.mapped) )
3391      {
3392        /*
3393          Display pointer position.
3394        */
3395        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
3396          x+windows->image.x,y+windows->image.y);
3397        XInfoWidget(display,windows,text);
3398      }
3399    /*
3400      Wait for next event.
3401    */
3402    XScreenEvent(display,windows,&event,exception);
3403    if (event.xany.window == windows->command.id)
3404      {
3405        /*
3406          Select a command from the Command widget.
3407        */
3408        id=XCommandWidget(display,windows,ColorEditMenu,&event);
3409        if (id < 0)
3410          {
3411            (void) XCheckDefineCursor(display,windows->image.id,cursor);
3412            continue;
3413          }
3414        switch (ColorEditCommands[id])
3415        {
3416          case ColorEditMethodCommand:
3417          {
3418            char
3419              **methods;
3420
3421            /*
3422              Select a method from the pop-up menu.
3423            */
3424            methods=(char **) GetCommandOptions(MagickMethodOptions);
3425            if (methods == (char **) NULL)
3426              break;
3427            entry=XMenuWidget(display,windows,ColorEditMenu[id],
3428              (const char **) methods,command);
3429            if (entry >= 0)
3430              method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3431                MagickFalse,methods[entry]);
3432            methods=DestroyStringList(methods);
3433            break;
3434          }
3435          case ColorEditColorCommand:
3436          {
3437            const char
3438              *ColorMenu[MaxNumberPens];
3439
3440            int
3441              pen_number;
3442
3443            /*
3444              Initialize menu selections.
3445            */
3446            for (i=0; i < (int) (MaxNumberPens-2); i++)
3447              ColorMenu[i]=resource_info->pen_colors[i];
3448            ColorMenu[MaxNumberPens-2]="Browser...";
3449            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3450            /*
3451              Select a pen color from the pop-up menu.
3452            */
3453            pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3454              (const char **) ColorMenu,command);
3455            if (pen_number < 0)
3456              break;
3457            if (pen_number == (MaxNumberPens-2))
3458              {
3459                static char
3460                  color_name[MaxTextExtent] = "gray";
3461
3462                /*
3463                  Select a pen color from a dialog.
3464                */
3465                resource_info->pen_colors[pen_number]=color_name;
3466                XColorBrowserWidget(display,windows,"Select",color_name);
3467                if (*color_name == '\0')
3468                  break;
3469              }
3470            /*
3471              Set pen color.
3472            */
3473            (void) XParseColor(display,windows->map_info->colormap,
3474              resource_info->pen_colors[pen_number],&color);
3475            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3476              (unsigned int) MaxColors,&color);
3477            windows->pixel_info->pen_colors[pen_number]=color;
3478            pen_id=(unsigned int) pen_number;
3479            break;
3480          }
3481          case ColorEditBorderCommand:
3482          {
3483            const char
3484              *ColorMenu[MaxNumberPens];
3485
3486            int
3487              pen_number;
3488
3489            /*
3490              Initialize menu selections.
3491            */
3492            for (i=0; i < (int) (MaxNumberPens-2); i++)
3493              ColorMenu[i]=resource_info->pen_colors[i];
3494            ColorMenu[MaxNumberPens-2]="Browser...";
3495            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3496            /*
3497              Select a pen color from the pop-up menu.
3498            */
3499            pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3500              (const char **) ColorMenu,command);
3501            if (pen_number < 0)
3502              break;
3503            if (pen_number == (MaxNumberPens-2))
3504              {
3505                static char
3506                  color_name[MaxTextExtent] = "gray";
3507
3508                /*
3509                  Select a pen color from a dialog.
3510                */
3511                resource_info->pen_colors[pen_number]=color_name;
3512                XColorBrowserWidget(display,windows,"Select",color_name);
3513                if (*color_name == '\0')
3514                  break;
3515              }
3516            /*
3517              Set border color.
3518            */
3519            (void) XParseColor(display,windows->map_info->colormap,
3520              resource_info->pen_colors[pen_number],&border_color);
3521            break;
3522          }
3523          case ColorEditFuzzCommand:
3524          {
3525            static char
3526              fuzz[MaxTextExtent];
3527
3528            static const char
3529              *FuzzMenu[] =
3530              {
3531                "0%",
3532                "2%",
3533                "5%",
3534                "10%",
3535                "15%",
3536                "Dialog...",
3537                (char *) NULL,
3538              };
3539
3540            /*
3541              Select a command from the pop-up menu.
3542            */
3543            entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3544              command);
3545            if (entry < 0)
3546              break;
3547            if (entry != 5)
3548              {
3549                (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
3550                  QuantumRange+1.0);
3551                break;
3552              }
3553            (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
3554            (void) XDialogWidget(display,windows,"Ok",
3555              "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3556            if (*fuzz == '\0')
3557              break;
3558            (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
3559            (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
3560              1.0);
3561            break;
3562          }
3563          case ColorEditUndoCommand:
3564          {
3565            (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3566              image,exception);
3567            break;
3568          }
3569          case ColorEditHelpCommand:
3570          default:
3571          {
3572            XTextViewWidget(display,resource_info,windows,MagickFalse,
3573              "Help Viewer - Image Annotation",ImageColorEditHelp);
3574            break;
3575          }
3576          case ColorEditDismissCommand:
3577          {
3578            /*
3579              Prematurely exit.
3580            */
3581            state|=EscapeState;
3582            state|=ExitState;
3583            break;
3584          }
3585        }
3586        (void) XCheckDefineCursor(display,windows->image.id,cursor);
3587        continue;
3588      }
3589    switch (event.type)
3590    {
3591      case ButtonPress:
3592      {
3593        if (event.xbutton.button != Button1)
3594          break;
3595        if ((event.xbutton.window != windows->image.id) &&
3596            (event.xbutton.window != windows->magnify.id))
3597          break;
3598        /*
3599          exit loop.
3600        */
3601        x=event.xbutton.x;
3602        y=event.xbutton.y;
3603        (void) XMagickCommand(display,resource_info,windows,
3604          SaveToUndoBufferCommand,image,exception);
3605        state|=UpdateConfigurationState;
3606        break;
3607      }
3608      case ButtonRelease:
3609      {
3610        if (event.xbutton.button != Button1)
3611          break;
3612        if ((event.xbutton.window != windows->image.id) &&
3613            (event.xbutton.window != windows->magnify.id))
3614          break;
3615        /*
3616          Update colormap information.
3617        */
3618        x=event.xbutton.x;
3619        y=event.xbutton.y;
3620        XConfigureImageColormap(display,resource_info,windows,*image,exception);
3621        (void) XConfigureImage(display,resource_info,windows,*image,exception);
3622        XInfoWidget(display,windows,text);
3623        (void) XCheckDefineCursor(display,windows->image.id,cursor);
3624        state&=(~UpdateConfigurationState);
3625        break;
3626      }
3627      case Expose:
3628        break;
3629      case KeyPress:
3630      {
3631        KeySym
3632          key_symbol;
3633
3634        if (event.xkey.window == windows->magnify.id)
3635          {
3636            Window
3637              window;
3638
3639            window=windows->magnify.id;
3640            while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3641          }
3642        if (event.xkey.window != windows->image.id)
3643          break;
3644        /*
3645          Respond to a user key press.
3646        */
3647        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3648          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3649        switch ((int) key_symbol)
3650        {
3651          case XK_Escape:
3652          case XK_F20:
3653          {
3654            /*
3655              Prematurely exit.
3656            */
3657            state|=ExitState;
3658            break;
3659          }
3660          case XK_F1:
3661          case XK_Help:
3662          {
3663            XTextViewWidget(display,resource_info,windows,MagickFalse,
3664              "Help Viewer - Image Annotation",ImageColorEditHelp);
3665            break;
3666          }
3667          default:
3668          {
3669            (void) XBell(display,0);
3670            break;
3671          }
3672        }
3673        break;
3674      }
3675      case MotionNotify:
3676      {
3677        /*
3678          Map and unmap Info widget as cursor crosses its boundaries.
3679        */
3680        x=event.xmotion.x;
3681        y=event.xmotion.y;
3682        if( IfMagickTrue(windows->info.mapped) )
3683          {
3684            if ((x < (int) (windows->info.x+windows->info.width)) &&
3685                (y < (int) (windows->info.y+windows->info.height)))
3686              (void) XWithdrawWindow(display,windows->info.id,
3687                windows->info.screen);
3688          }
3689        else
3690          if ((x > (int) (windows->info.x+windows->info.width)) ||
3691              (y > (int) (windows->info.y+windows->info.height)))
3692            (void) XMapWindow(display,windows->info.id);
3693        break;
3694      }
3695      default:
3696        break;
3697    }
3698    if (event.xany.window == windows->magnify.id)
3699      {
3700        x=windows->magnify.x-windows->image.x;
3701        y=windows->magnify.y-windows->image.y;
3702      }
3703    x_offset=x;
3704    y_offset=y;
3705    if ((state & UpdateConfigurationState) != 0)
3706      {
3707        CacheView
3708          *image_view;
3709
3710        int
3711          x,
3712          y;
3713
3714        /*
3715          Pixel edit is relative to image configuration.
3716        */
3717        (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3718          MagickTrue);
3719        color=windows->pixel_info->pen_colors[pen_id];
3720        XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3721        width=(unsigned int) (*image)->columns;
3722        height=(unsigned int) (*image)->rows;
3723        x=0;
3724        y=0;
3725        if (windows->image.crop_geometry != (char *) NULL)
3726          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3727            &width,&height);
3728        x_offset=(int)
3729          (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3730        y_offset=(int)
3731          (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3732        if ((x_offset < 0) || (y_offset < 0))
3733          continue;
3734        if ((x_offset >= (int) (*image)->columns) ||
3735            (y_offset >= (int) (*image)->rows))
3736          continue;
3737        image_view=AcquireAuthenticCacheView(*image,exception);
3738        switch (method)
3739        {
3740          case PointMethod:
3741          default:
3742          {
3743            /*
3744              Update color information using point algorithm.
3745            */
3746            if( IfMagickFalse(SetImageStorageClass(*image,DirectClass,exception)) )
3747              return(MagickFalse);
3748            q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3749              (ssize_t) y_offset,1,1,exception);
3750            if (q == (Quantum *) NULL)
3751              break;
3752            SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3753            SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3754            SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3755            (void) SyncCacheViewAuthenticPixels(image_view,exception);
3756            break;
3757          }
3758          case ReplaceMethod:
3759          {
3760            PixelInfo
3761              pixel,
3762              target;
3763
3764            /*
3765              Update color information using replace algorithm.
3766            */
3767            (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
3768              x_offset,(ssize_t) y_offset,&target,exception);
3769            if ((*image)->storage_class == DirectClass)
3770              {
3771                for (y=0; y < (int) (*image)->rows; y++)
3772                {
3773                  q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3774                    (*image)->columns,1,exception);
3775                  if (q == (Quantum *) NULL)
3776                    break;
3777                  for (x=0; x < (int) (*image)->columns; x++)
3778                  {
3779                    GetPixelInfoPixel(*image,q,&pixel);
3780                    if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
3781                      {
3782                        SetPixelRed(*image,ScaleShortToQuantum(
3783                          color.red),q);
3784                        SetPixelGreen(*image,ScaleShortToQuantum(
3785                          color.green),q);
3786                        SetPixelBlue(*image,ScaleShortToQuantum(
3787                          color.blue),q);
3788                      }
3789                    q+=GetPixelChannels(*image);
3790                  }
3791                  if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
3792                    break;
3793                }
3794              }
3795            else
3796              {
3797                for (i=0; i < (ssize_t) (*image)->colors; i++)
3798                  if (IsFuzzyEquivalencePixelInfo((*image)->colormap+i,&target))
3799                    {
3800                      (*image)->colormap[i].red=(double) ScaleShortToQuantum(
3801                        color.red);
3802                      (*image)->colormap[i].green=(double) ScaleShortToQuantum(
3803                        color.green);
3804                      (*image)->colormap[i].blue=(double) ScaleShortToQuantum(
3805                        color.blue);
3806                    }
3807                (void) SyncImage(*image,exception);
3808              }
3809            break;
3810          }
3811          case FloodfillMethod:
3812          case FillToBorderMethod:
3813          {
3814            DrawInfo
3815              *draw_info;
3816
3817            PixelInfo
3818              target;
3819
3820            /*
3821              Update color information using floodfill algorithm.
3822            */
3823            (void) GetOneVirtualPixelInfo(*image,
3824              GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
3825              y_offset,&target,exception);
3826            if (method == FillToBorderMethod)
3827              {
3828                target.red=(double)
3829                  ScaleShortToQuantum(border_color.red);
3830                target.green=(double)
3831                  ScaleShortToQuantum(border_color.green);
3832                target.blue=(double)
3833                  ScaleShortToQuantum(border_color.blue);
3834              }
3835            draw_info=CloneDrawInfo(resource_info->image_info,
3836              (DrawInfo *) NULL);
3837            (void) QueryColorCompliance(resource_info->pen_colors[pen_id],
3838              AllCompliance,&draw_info->fill,exception);
3839            (void) FloodfillPaintImage(*image,draw_info,&target,
3840              (ssize_t)x_offset,(ssize_t)y_offset,
3841              IsMagickFalse(method == FloodfillMethod),exception);
3842            draw_info=DestroyDrawInfo(draw_info);
3843            break;
3844          }
3845          case ResetMethod:
3846          {
3847            /*
3848              Update color information using reset algorithm.
3849            */
3850            if( IfMagickFalse(SetImageStorageClass(*image,DirectClass,exception)) )
3851              return(MagickFalse);
3852            for (y=0; y < (int) (*image)->rows; y++)
3853            {
3854              q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3855                (*image)->columns,1,exception);
3856              if (q == (Quantum *) NULL)
3857                break;
3858              for (x=0; x < (int) (*image)->columns; x++)
3859              {
3860                SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3861                SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3862                SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3863                q+=GetPixelChannels(*image);
3864              }
3865              if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
3866                break;
3867            }
3868            break;
3869          }
3870        }
3871        image_view=DestroyCacheView(image_view);
3872        state&=(~UpdateConfigurationState);
3873      }
3874  } while ((state & ExitState) == 0);
3875  (void) XSelectInput(display,windows->image.id,
3876    windows->image.attributes.event_mask);
3877  XSetCursorState(display,windows,MagickFalse);
3878  (void) XFreeCursor(display,cursor);
3879  return(MagickTrue);
3880}
3881
3882/*
3883%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3884%                                                                             %
3885%                                                                             %
3886%                                                                             %
3887+   X C o m p o s i t e I m a g e                                             %
3888%                                                                             %
3889%                                                                             %
3890%                                                                             %
3891%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3892%
3893%  XCompositeImage() requests an image name from the user, reads the image and
3894%  composites it with the X window image at a location the user chooses with
3895%  the pointer.
3896%
3897%  The format of the XCompositeImage method is:
3898%
3899%      MagickBooleanType XCompositeImage(Display *display,
3900%        XResourceInfo *resource_info,XWindows *windows,Image *image,
3901%        ExceptionInfo *exception)
3902%
3903%  A description of each parameter follows:
3904%
3905%    o display: Specifies a connection to an X server;  returned from
3906%      XOpenDisplay.
3907%
3908%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3909%
3910%    o windows: Specifies a pointer to a XWindows structure.
3911%
3912%    o image: the image; returned from ReadImage.
3913%
3914%    o exception: return any errors or warnings in this structure.
3915%
3916*/
3917static MagickBooleanType XCompositeImage(Display *display,
3918  XResourceInfo *resource_info,XWindows *windows,Image *image,
3919  ExceptionInfo *exception)
3920{
3921  static char
3922    displacement_geometry[MaxTextExtent] = "30x30",
3923    filename[MaxTextExtent] = "\0";
3924
3925  static const char
3926    *CompositeMenu[] =
3927    {
3928      "Operators",
3929      "Dissolve",
3930      "Displace",
3931      "Help",
3932      "Dismiss",
3933      (char *) NULL
3934    };
3935
3936  static CompositeOperator
3937    compose = CopyCompositeOp;
3938
3939  static const ModeType
3940    CompositeCommands[] =
3941    {
3942      CompositeOperatorsCommand,
3943      CompositeDissolveCommand,
3944      CompositeDisplaceCommand,
3945      CompositeHelpCommand,
3946      CompositeDismissCommand
3947    };
3948
3949  char
3950    text[MaxTextExtent];
3951
3952  Cursor
3953    cursor;
3954
3955  Image
3956    *composite_image;
3957
3958  int
3959    entry,
3960    id,
3961    x,
3962    y;
3963
3964  double
3965    blend,
3966    scale_factor;
3967
3968  RectangleInfo
3969    highlight_info,
3970    composite_info;
3971
3972  unsigned int
3973    height,
3974    width;
3975
3976  size_t
3977    state;
3978
3979  XEvent
3980    event;
3981
3982  /*
3983    Request image file name from user.
3984  */
3985  XFileBrowserWidget(display,windows,"Composite",filename);
3986  if (*filename == '\0')
3987    return(MagickTrue);
3988  /*
3989    Read image.
3990  */
3991  XSetCursorState(display,windows,MagickTrue);
3992  XCheckRefreshWindows(display,windows);
3993  (void) CopyMagickString(resource_info->image_info->filename,filename,
3994    MaxTextExtent);
3995  composite_image=ReadImage(resource_info->image_info,exception);
3996  CatchException(exception);
3997  XSetCursorState(display,windows,MagickFalse);
3998  if (composite_image == (Image *) NULL)
3999    return(MagickFalse);
4000  /*
4001    Map Command widget.
4002  */
4003  (void) CloneString(&windows->command.name,"Composite");
4004  windows->command.data=1;
4005  (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
4006  (void) XMapRaised(display,windows->command.id);
4007  XClientMessage(display,windows->image.id,windows->im_protocols,
4008    windows->im_update_widget,CurrentTime);
4009  /*
4010    Track pointer until button 1 is pressed.
4011  */
4012  XQueryPosition(display,windows->image.id,&x,&y);
4013  (void) XSelectInput(display,windows->image.id,
4014    windows->image.attributes.event_mask | PointerMotionMask);
4015  composite_info.x=(ssize_t) windows->image.x+x;
4016  composite_info.y=(ssize_t) windows->image.y+y;
4017  composite_info.width=0;
4018  composite_info.height=0;
4019  cursor=XCreateFontCursor(display,XC_ul_angle);
4020  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4021  blend=0.0;
4022  state=DefaultState;
4023  do
4024  {
4025    if( IfMagickTrue(windows->info.mapped) )
4026      {
4027        /*
4028          Display pointer position.
4029        */
4030        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4031          (long) composite_info.x,(long) composite_info.y);
4032        XInfoWidget(display,windows,text);
4033      }
4034    highlight_info=composite_info;
4035    highlight_info.x=composite_info.x-windows->image.x;
4036    highlight_info.y=composite_info.y-windows->image.y;
4037    XHighlightRectangle(display,windows->image.id,
4038      windows->image.highlight_context,&highlight_info);
4039    /*
4040      Wait for next event.
4041    */
4042    XScreenEvent(display,windows,&event,exception);
4043    XHighlightRectangle(display,windows->image.id,
4044      windows->image.highlight_context,&highlight_info);
4045    if (event.xany.window == windows->command.id)
4046      {
4047        /*
4048          Select a command from the Command widget.
4049        */
4050        id=XCommandWidget(display,windows,CompositeMenu,&event);
4051        if (id < 0)
4052          continue;
4053        switch (CompositeCommands[id])
4054        {
4055          case CompositeOperatorsCommand:
4056          {
4057            char
4058              command[MaxTextExtent],
4059              **operators;
4060
4061            /*
4062              Select a command from the pop-up menu.
4063            */
4064            operators=GetCommandOptions(MagickComposeOptions);
4065            if (operators == (char **) NULL)
4066              break;
4067            entry=XMenuWidget(display,windows,CompositeMenu[id],
4068              (const char **) operators,command);
4069            if (entry >= 0)
4070              compose=(CompositeOperator) ParseCommandOption(
4071                MagickComposeOptions,MagickFalse,operators[entry]);
4072            operators=DestroyStringList(operators);
4073            break;
4074          }
4075          case CompositeDissolveCommand:
4076          {
4077            static char
4078              factor[MaxTextExtent] = "20.0";
4079
4080            /*
4081              Dissolve the two images a given percent.
4082            */
4083            (void) XSetFunction(display,windows->image.highlight_context,
4084              GXcopy);
4085            (void) XDialogWidget(display,windows,"Dissolve",
4086              "Enter the blend factor (0.0 - 99.9%):",factor);
4087            (void) XSetFunction(display,windows->image.highlight_context,
4088              GXinvert);
4089            if (*factor == '\0')
4090              break;
4091            blend=StringToDouble(factor,(char **) NULL);
4092            compose=DissolveCompositeOp;
4093            break;
4094          }
4095          case CompositeDisplaceCommand:
4096          {
4097            /*
4098              Get horizontal and vertical scale displacement geometry.
4099            */
4100            (void) XSetFunction(display,windows->image.highlight_context,
4101              GXcopy);
4102            (void) XDialogWidget(display,windows,"Displace",
4103              "Enter the horizontal and vertical scale:",displacement_geometry);
4104            (void) XSetFunction(display,windows->image.highlight_context,
4105              GXinvert);
4106            if (*displacement_geometry == '\0')
4107              break;
4108            compose=DisplaceCompositeOp;
4109            break;
4110          }
4111          case CompositeHelpCommand:
4112          {
4113            (void) XSetFunction(display,windows->image.highlight_context,
4114              GXcopy);
4115            XTextViewWidget(display,resource_info,windows,MagickFalse,
4116              "Help Viewer - Image Composite",ImageCompositeHelp);
4117            (void) XSetFunction(display,windows->image.highlight_context,
4118              GXinvert);
4119            break;
4120          }
4121          case CompositeDismissCommand:
4122          {
4123            /*
4124              Prematurely exit.
4125            */
4126            state|=EscapeState;
4127            state|=ExitState;
4128            break;
4129          }
4130          default:
4131            break;
4132        }
4133        continue;
4134      }
4135    switch (event.type)
4136    {
4137      case ButtonPress:
4138      {
4139        if( IfMagickTrue(image->debug) )
4140          (void) LogMagickEvent(X11Event,GetMagickModule(),
4141            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4142            event.xbutton.button,event.xbutton.x,event.xbutton.y);
4143        if (event.xbutton.button != Button1)
4144          break;
4145        if (event.xbutton.window != windows->image.id)
4146          break;
4147        /*
4148          Change cursor.
4149        */
4150        composite_info.width=composite_image->columns;
4151        composite_info.height=composite_image->rows;
4152        (void) XCheckDefineCursor(display,windows->image.id,cursor);
4153        composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4154        composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4155        break;
4156      }
4157      case ButtonRelease:
4158      {
4159        if( IfMagickTrue(image->debug) )
4160          (void) LogMagickEvent(X11Event,GetMagickModule(),
4161            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4162            event.xbutton.button,event.xbutton.x,event.xbutton.y);
4163        if (event.xbutton.button != Button1)
4164          break;
4165        if (event.xbutton.window != windows->image.id)
4166          break;
4167        if ((composite_info.width != 0) && (composite_info.height != 0))
4168          {
4169            /*
4170              User has selected the location of the composite image.
4171            */
4172            composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4173            composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4174            state|=ExitState;
4175          }
4176        break;
4177      }
4178      case Expose:
4179        break;
4180      case KeyPress:
4181      {
4182        char
4183          command[MaxTextExtent];
4184
4185        KeySym
4186          key_symbol;
4187
4188        int
4189          length;
4190
4191        if (event.xkey.window != windows->image.id)
4192          break;
4193        /*
4194          Respond to a user key press.
4195        */
4196        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4197          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4198        *(command+length)='\0';
4199        if( IfMagickTrue(image->debug) )
4200          (void) LogMagickEvent(X11Event,GetMagickModule(),
4201            "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4202        switch ((int) key_symbol)
4203        {
4204          case XK_Escape:
4205          case XK_F20:
4206          {
4207            /*
4208              Prematurely exit.
4209            */
4210            composite_image=DestroyImage(composite_image);
4211            state|=EscapeState;
4212            state|=ExitState;
4213            break;
4214          }
4215          case XK_F1:
4216          case XK_Help:
4217          {
4218            (void) XSetFunction(display,windows->image.highlight_context,
4219              GXcopy);
4220            XTextViewWidget(display,resource_info,windows,MagickFalse,
4221              "Help Viewer - Image Composite",ImageCompositeHelp);
4222            (void) XSetFunction(display,windows->image.highlight_context,
4223              GXinvert);
4224            break;
4225          }
4226          default:
4227          {
4228            (void) XBell(display,0);
4229            break;
4230          }
4231        }
4232        break;
4233      }
4234      case MotionNotify:
4235      {
4236        /*
4237          Map and unmap Info widget as text cursor crosses its boundaries.
4238        */
4239        x=event.xmotion.x;
4240        y=event.xmotion.y;
4241        if( IfMagickTrue(windows->info.mapped) )
4242          {
4243            if ((x < (int) (windows->info.x+windows->info.width)) &&
4244                (y < (int) (windows->info.y+windows->info.height)))
4245              (void) XWithdrawWindow(display,windows->info.id,
4246                windows->info.screen);
4247          }
4248        else
4249          if ((x > (int) (windows->info.x+windows->info.width)) ||
4250              (y > (int) (windows->info.y+windows->info.height)))
4251            (void) XMapWindow(display,windows->info.id);
4252        composite_info.x=(ssize_t) windows->image.x+x;
4253        composite_info.y=(ssize_t) windows->image.y+y;
4254        break;
4255      }
4256      default:
4257      {
4258        if( IfMagickTrue(image->debug) )
4259          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4260            event.type);
4261        break;
4262      }
4263    }
4264  } while ((state & ExitState) == 0);
4265  (void) XSelectInput(display,windows->image.id,
4266    windows->image.attributes.event_mask);
4267  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4268  XSetCursorState(display,windows,MagickFalse);
4269  (void) XFreeCursor(display,cursor);
4270  if ((state & EscapeState) != 0)
4271    return(MagickTrue);
4272  /*
4273    Image compositing is relative to image configuration.
4274  */
4275  XSetCursorState(display,windows,MagickTrue);
4276  XCheckRefreshWindows(display,windows);
4277  width=(unsigned int) image->columns;
4278  height=(unsigned int) image->rows;
4279  x=0;
4280  y=0;
4281  if (windows->image.crop_geometry != (char *) NULL)
4282    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4283  scale_factor=(double) width/windows->image.ximage->width;
4284  composite_info.x+=x;
4285  composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4286  composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4287  scale_factor=(double) height/windows->image.ximage->height;
4288  composite_info.y+=y;
4289  composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4290  composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4291  if ((composite_info.width != composite_image->columns) ||
4292      (composite_info.height != composite_image->rows))
4293    {
4294      Image
4295        *resize_image;
4296
4297      /*
4298        Scale composite image.
4299      */
4300      resize_image=ResizeImage(composite_image,composite_info.width,
4301        composite_info.height,composite_image->filter,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(QuantumRange)-
4335        ((ssize_t) ScaleQuantumToChar(QuantumRange)*blend)/100);
4336      if( IfMagickFalse(SetImageStorageClass(image,DirectClass,exception)) )
4337        return(MagickFalse);
4338      image->alpha_trait=BlendPixelTrait;
4339      image_view=AcquireAuthenticCacheView(image,exception);
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( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
4352          break;
4353      }
4354      image_view=DestroyCacheView(image_view);
4355    }
4356  /*
4357    Composite image with X Image window.
4358  */
4359  (void) CompositeImage(image,composite_image,compose,MagickTrue,
4360    composite_info.x,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( IfMagickTrue(image->debug) )
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( IfMagickFalse(status) )
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( IfMagickTrue(windows->magnify.mapped) )
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(IsMagickTrue(status));
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  double
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( IfMagickTrue(windows->info.mapped) )
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( IfMagickTrue(windows->info.mapped) )
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( IfMagickFalse(windows->info.mapped) )
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( IfMagickTrue(windows->info.mapped) )
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( IfMagickTrue(windows->info.mapped) )
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( IfMagickTrue(windows->info.mapped) )
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=(double) 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=(double) 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( IfMagickFalse(SetImageStorageClass(image,DirectClass,exception)) )
5358    return(MagickFalse);
5359  image->alpha_trait=BlendPixelTrait;
5360  image_view=AcquireAuthenticCacheView(image,exception);
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( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
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  double
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( IfMagickTrue(windows->info.mapped) )
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( IfMagickTrue(transparent) )
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( IfMagickTrue(windows->info.mapped) )
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( IfMagickTrue(windows->info.mapped) )
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( IfMagickTrue(windows->info.mapped) )
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( IfMagickTrue(windows->info.mapped) )
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( IfMagickTrue(windows->info.mapped) )
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(IsMagickTrue(status));
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  double
6375    scale_factor;
6376
6377  RectangleInfo
6378    highlight_info;
6379
6380  /*
6381    Determine dimensions of the panning rectangle.
6382  */
6383  scale_factor=(double) 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=(double)
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( IfMagickTrue(windows->image.orphan) )
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( IfMagickTrue(windows->image.orphan) )
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      if (*resource_info->home_directory == '\0')
7205        (void) CopyMagickString(resource_info->home_directory,".",
7206          MaxTextExtent);
7207      status=chdir(resource_info->home_directory);
7208      if (status == -1)
7209        (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
7210          "UnableToOpenFile","%s",resource_info->home_directory);
7211      nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7212      break;
7213    }
7214    case SaveCommand:
7215    {
7216      /*
7217        Save image.
7218      */
7219      status=XSaveImage(display,resource_info,windows,*image,exception);
7220      if( IfMagickFalse(status) )
7221        {
7222          char
7223            message[MaxTextExtent];
7224
7225          (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7226            exception->reason != (char *) NULL ? exception->reason : "",
7227            exception->description != (char *) NULL ? exception->description :
7228            "");
7229          XNoticeWidget(display,windows,"Unable to save file:",message);
7230          break;
7231        }
7232      break;
7233    }
7234    case PrintCommand:
7235    {
7236      /*
7237        Print image.
7238      */
7239      status=XPrintImage(display,resource_info,windows,*image,exception);
7240      if( IfMagickFalse(status) )
7241        {
7242          char
7243            message[MaxTextExtent];
7244
7245          (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7246            exception->reason != (char *) NULL ? exception->reason : "",
7247            exception->description != (char *) NULL ? exception->description :
7248            "");
7249          XNoticeWidget(display,windows,"Unable to print file:",message);
7250          break;
7251        }
7252      break;
7253    }
7254    case DeleteCommand:
7255    {
7256      static char
7257        filename[MaxTextExtent] = "\0";
7258
7259      /*
7260        Delete image file.
7261      */
7262      XFileBrowserWidget(display,windows,"Delete",filename);
7263      if (*filename == '\0')
7264        break;
7265      status=IsMagickTrue(remove_utf8(filename));
7266      if( IfMagickTrue(status) )
7267        XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7268      break;
7269    }
7270    case NewCommand:
7271    {
7272      int
7273        status;
7274
7275      static char
7276        color[MaxTextExtent] = "gray",
7277        geometry[MaxTextExtent] = "640x480";
7278
7279      static const char
7280        *format = "gradient";
7281
7282      /*
7283        Query user for canvas geometry.
7284      */
7285      status=XDialogWidget(display,windows,"New","Enter image geometry:",
7286        geometry);
7287      if (*geometry == '\0')
7288        break;
7289      if (status == 0)
7290        format="xc";
7291      XColorBrowserWidget(display,windows,"Select",color);
7292      if (*color == '\0')
7293        break;
7294      /*
7295        Create canvas.
7296      */
7297      (void) FormatLocaleString(image_info->filename,MaxTextExtent,
7298        "%s:%s",format,color);
7299      (void) CloneString(&image_info->size,geometry);
7300      nexus=ReadImage(image_info,exception);
7301      CatchException(exception);
7302      XClientMessage(display,windows->image.id,windows->im_protocols,
7303        windows->im_next_image,CurrentTime);
7304      break;
7305    }
7306    case VisualDirectoryCommand:
7307    {
7308      /*
7309        Visual Image directory.
7310      */
7311      nexus=XVisualDirectoryImage(display,resource_info,windows,exception);
7312      break;
7313    }
7314    case QuitCommand:
7315    {
7316      /*
7317        exit program.
7318      */
7319      if( IfMagickFalse(resource_info->confirm_exit) )
7320        XClientMessage(display,windows->image.id,windows->im_protocols,
7321          windows->im_exit,CurrentTime);
7322      else
7323        {
7324          int
7325            status;
7326
7327          /*
7328            Confirm program exit.
7329          */
7330          status=XConfirmWidget(display,windows,"Do you really want to exit",
7331            resource_info->client_name);
7332          if (status > 0)
7333            XClientMessage(display,windows->image.id,windows->im_protocols,
7334              windows->im_exit,CurrentTime);
7335        }
7336      break;
7337    }
7338    case CutCommand:
7339    {
7340      /*
7341        Cut image.
7342      */
7343      (void) XCropImage(display,resource_info,windows,*image,CutMode,exception);
7344      break;
7345    }
7346    case CopyCommand:
7347    {
7348      /*
7349        Copy image.
7350      */
7351      (void) XCropImage(display,resource_info,windows,*image,CopyMode,
7352        exception);
7353      break;
7354    }
7355    case PasteCommand:
7356    {
7357      /*
7358        Paste image.
7359      */
7360      status=XPasteImage(display,resource_info,windows,*image,exception);
7361      if( IfMagickFalse(status) )
7362        {
7363          XNoticeWidget(display,windows,"Unable to paste X image",
7364            (*image)->filename);
7365          break;
7366        }
7367      break;
7368    }
7369    case HalfSizeCommand:
7370    {
7371      /*
7372        Half image size.
7373      */
7374      windows->image.window_changes.width=windows->image.ximage->width/2;
7375      windows->image.window_changes.height=windows->image.ximage->height/2;
7376      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7377      break;
7378    }
7379    case OriginalSizeCommand:
7380    {
7381      /*
7382        Original image size.
7383      */
7384      windows->image.window_changes.width=(int) (*image)->columns;
7385      windows->image.window_changes.height=(int) (*image)->rows;
7386      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7387      break;
7388    }
7389    case DoubleSizeCommand:
7390    {
7391      /*
7392        Double the image size.
7393      */
7394      windows->image.window_changes.width=windows->image.ximage->width << 1;
7395      windows->image.window_changes.height=windows->image.ximage->height << 1;
7396      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7397      break;
7398    }
7399    case ResizeCommand:
7400    {
7401      int
7402        status;
7403
7404      size_t
7405        height,
7406        width;
7407
7408      ssize_t
7409        x,
7410        y;
7411
7412      /*
7413        Resize image.
7414      */
7415      width=(size_t) windows->image.ximage->width;
7416      height=(size_t) windows->image.ximage->height;
7417      x=0;
7418      y=0;
7419      (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g+0+0",
7420        (double) width,(double) height);
7421      status=XDialogWidget(display,windows,"Resize",
7422        "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7423      if (*geometry == '\0')
7424        break;
7425      if (status == 0)
7426        (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7427      (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7428      windows->image.window_changes.width=(int) width;
7429      windows->image.window_changes.height=(int) height;
7430      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7431      break;
7432    }
7433    case ApplyCommand:
7434    {
7435      char
7436        image_geometry[MaxTextExtent];
7437
7438      if ((windows->image.crop_geometry == (char *) NULL) &&
7439          ((int) (*image)->columns == windows->image.ximage->width) &&
7440          ((int) (*image)->rows == windows->image.ximage->height))
7441        break;
7442      /*
7443        Apply size transforms to image.
7444      */
7445      XSetCursorState(display,windows,MagickTrue);
7446      XCheckRefreshWindows(display,windows);
7447      /*
7448        Crop and/or scale displayed image.
7449      */
7450      (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
7451        windows->image.ximage->width,windows->image.ximage->height);
7452      (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
7453        exception);
7454      if (windows->image.crop_geometry != (char *) NULL)
7455        windows->image.crop_geometry=(char *) RelinquishMagickMemory(
7456          windows->image.crop_geometry);
7457      windows->image.x=0;
7458      windows->image.y=0;
7459      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7460      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7461      break;
7462    }
7463    case RefreshCommand:
7464    {
7465      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7466      break;
7467    }
7468    case RestoreCommand:
7469    {
7470      /*
7471        Restore Image window to its original size.
7472      */
7473      if ((windows->image.width == (unsigned int) (*image)->columns) &&
7474          (windows->image.height == (unsigned int) (*image)->rows) &&
7475          (windows->image.crop_geometry == (char *) NULL))
7476        {
7477          (void) XBell(display,0);
7478          break;
7479        }
7480      windows->image.window_changes.width=(int) (*image)->columns;
7481      windows->image.window_changes.height=(int) (*image)->rows;
7482      if (windows->image.crop_geometry != (char *) NULL)
7483        {
7484          windows->image.crop_geometry=(char *)
7485            RelinquishMagickMemory(windows->image.crop_geometry);
7486          windows->image.crop_geometry=(char *) NULL;
7487          windows->image.x=0;
7488          windows->image.y=0;
7489        }
7490      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7491      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7492      break;
7493    }
7494    case CropCommand:
7495    {
7496      /*
7497        Crop image.
7498      */
7499      (void) XCropImage(display,resource_info,windows,*image,CropMode,
7500        exception);
7501      break;
7502    }
7503    case ChopCommand:
7504    {
7505      /*
7506        Chop image.
7507      */
7508      status=XChopImage(display,resource_info,windows,image,exception);
7509      if( IfMagickFalse(status) )
7510        {
7511          XNoticeWidget(display,windows,"Unable to cut X image",
7512            (*image)->filename);
7513          break;
7514        }
7515      break;
7516    }
7517    case FlopCommand:
7518    {
7519      Image
7520        *flop_image;
7521
7522      /*
7523        Flop image scanlines.
7524      */
7525      XSetCursorState(display,windows,MagickTrue);
7526      XCheckRefreshWindows(display,windows);
7527      flop_image=FlopImage(*image,exception);
7528      if (flop_image != (Image *) NULL)
7529        {
7530          *image=DestroyImage(*image);
7531          *image=flop_image;
7532        }
7533      CatchException(exception);
7534      XSetCursorState(display,windows,MagickFalse);
7535      if (windows->image.crop_geometry != (char *) NULL)
7536        {
7537          /*
7538            Flop crop geometry.
7539          */
7540          width=(unsigned int) (*image)->columns;
7541          height=(unsigned int) (*image)->rows;
7542          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7543            &width,&height);
7544          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7545            "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7546        }
7547      if( IfMagickTrue(windows->image.orphan) )
7548        break;
7549      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7550      break;
7551    }
7552    case FlipCommand:
7553    {
7554      Image
7555        *flip_image;
7556
7557      /*
7558        Flip image scanlines.
7559      */
7560      XSetCursorState(display,windows,MagickTrue);
7561      XCheckRefreshWindows(display,windows);
7562      flip_image=FlipImage(*image,exception);
7563      if (flip_image != (Image *) NULL)
7564        {
7565          *image=DestroyImage(*image);
7566          *image=flip_image;
7567        }
7568      CatchException(exception);
7569      XSetCursorState(display,windows,MagickFalse);
7570      if (windows->image.crop_geometry != (char *) NULL)
7571        {
7572          /*
7573            Flip crop geometry.
7574          */
7575          width=(unsigned int) (*image)->columns;
7576          height=(unsigned int) (*image)->rows;
7577          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7578            &width,&height);
7579          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7580            "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7581        }
7582      if( IfMagickTrue(windows->image.orphan) )
7583        break;
7584      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7585      break;
7586    }
7587    case RotateRightCommand:
7588    {
7589      /*
7590        Rotate image 90 degrees clockwise.
7591      */
7592      status=XRotateImage(display,resource_info,windows,90.0,image,exception);
7593      if( IfMagickFalse(status) )
7594        {
7595          XNoticeWidget(display,windows,"Unable to rotate X image",
7596            (*image)->filename);
7597          break;
7598        }
7599      break;
7600    }
7601    case RotateLeftCommand:
7602    {
7603      /*
7604        Rotate image 90 degrees counter-clockwise.
7605      */
7606      status=XRotateImage(display,resource_info,windows,-90.0,image,exception);
7607      if( IfMagickFalse(status) )
7608        {
7609          XNoticeWidget(display,windows,"Unable to rotate X image",
7610            (*image)->filename);
7611          break;
7612        }
7613      break;
7614    }
7615    case RotateCommand:
7616    {
7617      /*
7618        Rotate image.
7619      */
7620      status=XRotateImage(display,resource_info,windows,0.0,image,exception);
7621      if( IfMagickFalse(status) )
7622        {
7623          XNoticeWidget(display,windows,"Unable to rotate X image",
7624            (*image)->filename);
7625          break;
7626        }
7627      break;
7628    }
7629    case ShearCommand:
7630    {
7631      Image
7632        *shear_image;
7633
7634      static char
7635        geometry[MaxTextExtent] = "45.0x45.0";
7636
7637      /*
7638        Query user for shear color and geometry.
7639      */
7640      XColorBrowserWidget(display,windows,"Select",color);
7641      if (*color == '\0')
7642        break;
7643      (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7644        geometry);
7645      if (*geometry == '\0')
7646        break;
7647      /*
7648        Shear image.
7649      */
7650      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7651        exception);
7652      XSetCursorState(display,windows,MagickTrue);
7653      XCheckRefreshWindows(display,windows);
7654      (void) QueryColorCompliance(color,AllCompliance,
7655        &(*image)->background_color,exception);
7656      flags=ParseGeometry(geometry,&geometry_info);
7657      if ((flags & SigmaValue) == 0)
7658        geometry_info.sigma=geometry_info.rho;
7659      shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7660        exception);
7661      if (shear_image != (Image *) NULL)
7662        {
7663          *image=DestroyImage(*image);
7664          *image=shear_image;
7665        }
7666      CatchException(exception);
7667      XSetCursorState(display,windows,MagickFalse);
7668      if( IfMagickTrue(windows->image.orphan) )
7669        break;
7670      windows->image.window_changes.width=(int) (*image)->columns;
7671      windows->image.window_changes.height=(int) (*image)->rows;
7672      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7673      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7674      break;
7675    }
7676    case RollCommand:
7677    {
7678      Image
7679        *roll_image;
7680
7681      static char
7682        geometry[MaxTextExtent] = "+2+2";
7683
7684      /*
7685        Query user for the roll geometry.
7686      */
7687      (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7688        geometry);
7689      if (*geometry == '\0')
7690        break;
7691      /*
7692        Roll image.
7693      */
7694      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7695        exception);
7696      XSetCursorState(display,windows,MagickTrue);
7697      XCheckRefreshWindows(display,windows);
7698      (void) ParsePageGeometry(*image,geometry,&page_geometry,
7699        exception);
7700      roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7701        exception);
7702      if (roll_image != (Image *) NULL)
7703        {
7704          *image=DestroyImage(*image);
7705          *image=roll_image;
7706        }
7707      CatchException(exception);
7708      XSetCursorState(display,windows,MagickFalse);
7709      if( IfMagickTrue(windows->image.orphan) )
7710        break;
7711      windows->image.window_changes.width=(int) (*image)->columns;
7712      windows->image.window_changes.height=(int) (*image)->rows;
7713      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7714      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7715      break;
7716    }
7717    case TrimCommand:
7718    {
7719      static char
7720        fuzz[MaxTextExtent];
7721
7722      /*
7723        Query user for the fuzz factor.
7724      */
7725      (void) FormatLocaleString(fuzz,MaxTextExtent,"%g%%",100.0*
7726        (*image)->fuzz/(QuantumRange+1.0));
7727      (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7728      if (*fuzz == '\0')
7729        break;
7730      (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+1.0);
7731      /*
7732        Trim image.
7733      */
7734      status=XTrimImage(display,resource_info,windows,*image,exception);
7735      if( IfMagickFalse(status) )
7736        {
7737          XNoticeWidget(display,windows,"Unable to trim X image",
7738            (*image)->filename);
7739          break;
7740        }
7741      break;
7742    }
7743    case HueCommand:
7744    {
7745      static char
7746        hue_percent[MaxTextExtent] = "110";
7747
7748      /*
7749        Query user for percent hue change.
7750      */
7751      (void) XDialogWidget(display,windows,"Apply",
7752        "Enter percent change in image hue (0-200):",hue_percent);
7753      if (*hue_percent == '\0')
7754        break;
7755      /*
7756        Vary the image hue.
7757      */
7758      XSetCursorState(display,windows,MagickTrue);
7759      XCheckRefreshWindows(display,windows);
7760      (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7761      (void) ConcatenateMagickString(modulate_factors,hue_percent,
7762        MaxTextExtent);
7763      (void) ModulateImage(*image,modulate_factors,exception);
7764      XSetCursorState(display,windows,MagickFalse);
7765      if( IfMagickTrue(windows->image.orphan) )
7766        break;
7767      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7768      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7769      break;
7770    }
7771    case SaturationCommand:
7772    {
7773      static char
7774        saturation_percent[MaxTextExtent] = "110";
7775
7776      /*
7777        Query user for percent saturation change.
7778      */
7779      (void) XDialogWidget(display,windows,"Apply",
7780        "Enter percent change in color saturation (0-200):",saturation_percent);
7781      if (*saturation_percent == '\0')
7782        break;
7783      /*
7784        Vary color saturation.
7785      */
7786      XSetCursorState(display,windows,MagickTrue);
7787      XCheckRefreshWindows(display,windows);
7788      (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7789      (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7790        MaxTextExtent);
7791      (void) ModulateImage(*image,modulate_factors,exception);
7792      XSetCursorState(display,windows,MagickFalse);
7793      if( IfMagickTrue(windows->image.orphan) )
7794        break;
7795      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7796      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7797      break;
7798    }
7799    case BrightnessCommand:
7800    {
7801      static char
7802        brightness_percent[MaxTextExtent] = "110";
7803
7804      /*
7805        Query user for percent brightness change.
7806      */
7807      (void) XDialogWidget(display,windows,"Apply",
7808        "Enter percent change in color brightness (0-200):",brightness_percent);
7809      if (*brightness_percent == '\0')
7810        break;
7811      /*
7812        Vary the color brightness.
7813      */
7814      XSetCursorState(display,windows,MagickTrue);
7815      XCheckRefreshWindows(display,windows);
7816      (void) CopyMagickString(modulate_factors,brightness_percent,
7817        MaxTextExtent);
7818      (void) ModulateImage(*image,modulate_factors,exception);
7819      XSetCursorState(display,windows,MagickFalse);
7820      if( IfMagickTrue(windows->image.orphan) )
7821        break;
7822      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7823      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7824      break;
7825    }
7826    case GammaCommand:
7827    {
7828      static char
7829        factor[MaxTextExtent] = "1.6";
7830
7831      /*
7832        Query user for gamma value.
7833      */
7834      (void) XDialogWidget(display,windows,"Gamma",
7835        "Enter gamma value (e.g. 1.2):",factor);
7836      if (*factor == '\0')
7837        break;
7838      /*
7839        Gamma correct image.
7840      */
7841      XSetCursorState(display,windows,MagickTrue);
7842      XCheckRefreshWindows(display,windows);
7843      (void) GammaImage(*image,atof(factor),exception);
7844      XSetCursorState(display,windows,MagickFalse);
7845      if( IfMagickTrue(windows->image.orphan) )
7846        break;
7847      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7848      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7849      break;
7850    }
7851    case SpiffCommand:
7852    {
7853      /*
7854        Sharpen the image contrast.
7855      */
7856      XSetCursorState(display,windows,MagickTrue);
7857      XCheckRefreshWindows(display,windows);
7858      (void) ContrastImage(*image,MagickTrue,exception);
7859      XSetCursorState(display,windows,MagickFalse);
7860      if( IfMagickTrue(windows->image.orphan) )
7861        break;
7862      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7863      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7864      break;
7865    }
7866    case DullCommand:
7867    {
7868      /*
7869        Dull the image contrast.
7870      */
7871      XSetCursorState(display,windows,MagickTrue);
7872      XCheckRefreshWindows(display,windows);
7873      (void) ContrastImage(*image,MagickFalse,exception);
7874      XSetCursorState(display,windows,MagickFalse);
7875      if( IfMagickTrue(windows->image.orphan) )
7876        break;
7877      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7878      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7879      break;
7880    }
7881    case ContrastStretchCommand:
7882    {
7883      double
7884        black_point,
7885        white_point;
7886
7887      static char
7888        levels[MaxTextExtent] = "1%";
7889
7890      /*
7891        Query user for gamma value.
7892      */
7893      (void) XDialogWidget(display,windows,"Contrast Stretch",
7894        "Enter black and white points:",levels);
7895      if (*levels == '\0')
7896        break;
7897      /*
7898        Contrast stretch image.
7899      */
7900      XSetCursorState(display,windows,MagickTrue);
7901      XCheckRefreshWindows(display,windows);
7902      flags=ParseGeometry(levels,&geometry_info);
7903      black_point=geometry_info.rho;
7904      white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7905      if ((flags & PercentValue) != 0)
7906        {
7907          black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7908          white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7909        }
7910      white_point=(double) (*image)->columns*(*image)->rows-white_point;
7911      (void) ContrastStretchImage(*image,black_point,white_point,
7912        exception);
7913      XSetCursorState(display,windows,MagickFalse);
7914      if( IfMagickTrue(windows->image.orphan) )
7915        break;
7916      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7917      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7918      break;
7919    }
7920    case SigmoidalContrastCommand:
7921    {
7922      GeometryInfo
7923        geometry_info;
7924
7925      MagickStatusType
7926        flags;
7927
7928      static char
7929        levels[MaxTextExtent] = "3x50%";
7930
7931      /*
7932        Query user for gamma value.
7933      */
7934      (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7935        "Enter contrast and midpoint:",levels);
7936      if (*levels == '\0')
7937        break;
7938      /*
7939        Contrast stretch image.
7940      */
7941      XSetCursorState(display,windows,MagickTrue);
7942      XCheckRefreshWindows(display,windows);
7943      flags=ParseGeometry(levels,&geometry_info);
7944      if ((flags & SigmaValue) == 0)
7945        geometry_info.sigma=1.0*QuantumRange/2.0;
7946      if ((flags & PercentValue) != 0)
7947        geometry_info.sigma=1.0*QuantumRange*geometry_info.sigma/100.0;
7948      (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
7949        geometry_info.sigma,exception);
7950      XSetCursorState(display,windows,MagickFalse);
7951      if( IfMagickTrue(windows->image.orphan) )
7952        break;
7953      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7954      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7955      break;
7956    }
7957    case NormalizeCommand:
7958    {
7959      /*
7960        Perform histogram normalization on the image.
7961      */
7962      XSetCursorState(display,windows,MagickTrue);
7963      XCheckRefreshWindows(display,windows);
7964      (void) NormalizeImage(*image,exception);
7965      XSetCursorState(display,windows,MagickFalse);
7966      if( IfMagickTrue(windows->image.orphan) )
7967        break;
7968      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7969      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7970      break;
7971    }
7972    case EqualizeCommand:
7973    {
7974      /*
7975        Perform histogram equalization on the image.
7976      */
7977      XSetCursorState(display,windows,MagickTrue);
7978      XCheckRefreshWindows(display,windows);
7979      (void) EqualizeImage(*image,exception);
7980      XSetCursorState(display,windows,MagickFalse);
7981      if( IfMagickTrue(windows->image.orphan) )
7982        break;
7983      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7984      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7985      break;
7986    }
7987    case NegateCommand:
7988    {
7989      /*
7990        Negate colors in image.
7991      */
7992      XSetCursorState(display,windows,MagickTrue);
7993      XCheckRefreshWindows(display,windows);
7994      (void) NegateImage(*image,MagickFalse,exception);
7995      XSetCursorState(display,windows,MagickFalse);
7996      if( IfMagickTrue(windows->image.orphan) )
7997        break;
7998      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7999      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8000      break;
8001    }
8002    case GrayscaleCommand:
8003    {
8004      /*
8005        Convert image to grayscale.
8006      */
8007      XSetCursorState(display,windows,MagickTrue);
8008      XCheckRefreshWindows(display,windows);
8009      (void) SetImageType(*image,(*image)->alpha_trait != BlendPixelTrait ?
8010        GrayscaleType : GrayscaleMatteType,exception);
8011      XSetCursorState(display,windows,MagickFalse);
8012      if( IfMagickTrue(windows->image.orphan) )
8013        break;
8014      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8015      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8016      break;
8017    }
8018    case MapCommand:
8019    {
8020      Image
8021        *affinity_image;
8022
8023      static char
8024        filename[MaxTextExtent] = "\0";
8025
8026      /*
8027        Request image file name from user.
8028      */
8029      XFileBrowserWidget(display,windows,"Map",filename);
8030      if (*filename == '\0')
8031        break;
8032      /*
8033        Map image.
8034      */
8035      XSetCursorState(display,windows,MagickTrue);
8036      XCheckRefreshWindows(display,windows);
8037      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
8038      affinity_image=ReadImage(image_info,exception);
8039      if (affinity_image != (Image *) NULL)
8040        {
8041          (void) RemapImage(&quantize_info,*image,affinity_image,exception);
8042          affinity_image=DestroyImage(affinity_image);
8043        }
8044      CatchException(exception);
8045      XSetCursorState(display,windows,MagickFalse);
8046      if( IfMagickTrue(windows->image.orphan) )
8047        break;
8048      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8049      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8050      break;
8051    }
8052    case QuantizeCommand:
8053    {
8054      int
8055        status;
8056
8057      static char
8058        colors[MaxTextExtent] = "256";
8059
8060      /*
8061        Query user for maximum number of colors.
8062      */
8063      status=XDialogWidget(display,windows,"Quantize",
8064        "Maximum number of colors:",colors);
8065      if (*colors == '\0')
8066        break;
8067      /*
8068        Color reduce the image.
8069      */
8070      XSetCursorState(display,windows,MagickTrue);
8071      XCheckRefreshWindows(display,windows);
8072      quantize_info.number_colors=StringToUnsignedLong(colors);
8073      quantize_info.dither_method=status != 0 ? RiemersmaDitherMethod :
8074        NoDitherMethod;
8075      (void) QuantizeImage(&quantize_info,*image,exception);
8076      XSetCursorState(display,windows,MagickFalse);
8077      if( IfMagickTrue(windows->image.orphan) )
8078        break;
8079      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8080      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8081      break;
8082    }
8083    case DespeckleCommand:
8084    {
8085      Image
8086        *despeckle_image;
8087
8088      /*
8089        Despeckle image.
8090      */
8091      XSetCursorState(display,windows,MagickTrue);
8092      XCheckRefreshWindows(display,windows);
8093      despeckle_image=DespeckleImage(*image,exception);
8094      if (despeckle_image != (Image *) NULL)
8095        {
8096          *image=DestroyImage(*image);
8097          *image=despeckle_image;
8098        }
8099      CatchException(exception);
8100      XSetCursorState(display,windows,MagickFalse);
8101      if( IfMagickTrue(windows->image.orphan) )
8102        break;
8103      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8104      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8105      break;
8106    }
8107    case EmbossCommand:
8108    {
8109      Image
8110        *emboss_image;
8111
8112      static char
8113        radius[MaxTextExtent] = "0.0x1.0";
8114
8115      /*
8116        Query user for emboss radius.
8117      */
8118      (void) XDialogWidget(display,windows,"Emboss",
8119        "Enter the emboss radius and standard deviation:",radius);
8120      if (*radius == '\0')
8121        break;
8122      /*
8123        Reduce noise in the image.
8124      */
8125      XSetCursorState(display,windows,MagickTrue);
8126      XCheckRefreshWindows(display,windows);
8127      flags=ParseGeometry(radius,&geometry_info);
8128      if ((flags & SigmaValue) == 0)
8129        geometry_info.sigma=1.0;
8130      emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8131        exception);
8132      if (emboss_image != (Image *) NULL)
8133        {
8134          *image=DestroyImage(*image);
8135          *image=emboss_image;
8136        }
8137      CatchException(exception);
8138      XSetCursorState(display,windows,MagickFalse);
8139      if( IfMagickTrue(windows->image.orphan) )
8140        break;
8141      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8142      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8143      break;
8144    }
8145    case ReduceNoiseCommand:
8146    {
8147      Image
8148        *noise_image;
8149
8150      static char
8151        radius[MaxTextExtent] = "0";
8152
8153      /*
8154        Query user for noise radius.
8155      */
8156      (void) XDialogWidget(display,windows,"Reduce Noise",
8157        "Enter the noise radius:",radius);
8158      if (*radius == '\0')
8159        break;
8160      /*
8161        Reduce noise in the image.
8162      */
8163      XSetCursorState(display,windows,MagickTrue);
8164      XCheckRefreshWindows(display,windows);
8165      flags=ParseGeometry(radius,&geometry_info);
8166      noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8167        geometry_info.rho,(size_t) geometry_info.rho,exception);
8168      if (noise_image != (Image *) NULL)
8169        {
8170          *image=DestroyImage(*image);
8171          *image=noise_image;
8172        }
8173      CatchException(exception);
8174      XSetCursorState(display,windows,MagickFalse);
8175      if( IfMagickTrue(windows->image.orphan) )
8176        break;
8177      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8178      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8179      break;
8180    }
8181    case AddNoiseCommand:
8182    {
8183      char
8184        **noises;
8185
8186      Image
8187        *noise_image;
8188
8189      static char
8190        noise_type[MaxTextExtent] = "Gaussian";
8191
8192      /*
8193        Add noise to the image.
8194      */
8195      noises=GetCommandOptions(MagickNoiseOptions);
8196      if (noises == (char **) NULL)
8197        break;
8198      XListBrowserWidget(display,windows,&windows->widget,
8199        (const char **) noises,"Add Noise",
8200        "Select a type of noise to add to your image:",noise_type);
8201      noises=DestroyStringList(noises);
8202      if (*noise_type == '\0')
8203        break;
8204      XSetCursorState(display,windows,MagickTrue);
8205      XCheckRefreshWindows(display,windows);
8206      noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8207        MagickNoiseOptions,MagickFalse,noise_type),1.0,exception);
8208      if (noise_image != (Image *) NULL)
8209        {
8210          *image=DestroyImage(*image);
8211          *image=noise_image;
8212        }
8213      CatchException(exception);
8214      XSetCursorState(display,windows,MagickFalse);
8215      if( IfMagickTrue(windows->image.orphan) )
8216        break;
8217      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8218      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8219      break;
8220    }
8221    case SharpenCommand:
8222    {
8223      Image
8224        *sharp_image;
8225
8226      static char
8227        radius[MaxTextExtent] = "0.0x1.0";
8228
8229      /*
8230        Query user for sharpen radius.
8231      */
8232      (void) XDialogWidget(display,windows,"Sharpen",
8233        "Enter the sharpen radius and standard deviation:",radius);
8234      if (*radius == '\0')
8235        break;
8236      /*
8237        Sharpen image scanlines.
8238      */
8239      XSetCursorState(display,windows,MagickTrue);
8240      XCheckRefreshWindows(display,windows);
8241      flags=ParseGeometry(radius,&geometry_info);
8242      sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8243        exception);
8244      if (sharp_image != (Image *) NULL)
8245        {
8246          *image=DestroyImage(*image);
8247          *image=sharp_image;
8248        }
8249      CatchException(exception);
8250      XSetCursorState(display,windows,MagickFalse);
8251      if( IfMagickTrue(windows->image.orphan) )
8252        break;
8253      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8254      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8255      break;
8256    }
8257    case BlurCommand:
8258    {
8259      Image
8260        *blur_image;
8261
8262      static char
8263        radius[MaxTextExtent] = "0.0x1.0";
8264
8265      /*
8266        Query user for blur radius.
8267      */
8268      (void) XDialogWidget(display,windows,"Blur",
8269        "Enter the blur radius and standard deviation:",radius);
8270      if (*radius == '\0')
8271        break;
8272      /*
8273        Blur an image.
8274      */
8275      XSetCursorState(display,windows,MagickTrue);
8276      XCheckRefreshWindows(display,windows);
8277      flags=ParseGeometry(radius,&geometry_info);
8278      blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8279        exception);
8280      if (blur_image != (Image *) NULL)
8281        {
8282          *image=DestroyImage(*image);
8283          *image=blur_image;
8284        }
8285      CatchException(exception);
8286      XSetCursorState(display,windows,MagickFalse);
8287      if( IfMagickTrue(windows->image.orphan) )
8288        break;
8289      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8290      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8291      break;
8292    }
8293    case ThresholdCommand:
8294    {
8295      double
8296        threshold;
8297
8298      static char
8299        factor[MaxTextExtent] = "128";
8300
8301      /*
8302        Query user for threshold value.
8303      */
8304      (void) XDialogWidget(display,windows,"Threshold",
8305        "Enter threshold value:",factor);
8306      if (*factor == '\0')
8307        break;
8308      /*
8309        Gamma correct image.
8310      */
8311      XSetCursorState(display,windows,MagickTrue);
8312      XCheckRefreshWindows(display,windows);
8313      threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8314      (void) BilevelImage(*image,threshold,exception);
8315      XSetCursorState(display,windows,MagickFalse);
8316      if( IfMagickTrue(windows->image.orphan) )
8317        break;
8318      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8319      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8320      break;
8321    }
8322    case EdgeDetectCommand:
8323    {
8324      Image
8325        *edge_image;
8326
8327      static char
8328        radius[MaxTextExtent] = "0";
8329
8330      /*
8331        Query user for edge factor.
8332      */
8333      (void) XDialogWidget(display,windows,"Detect Edges",
8334        "Enter the edge detect radius:",radius);
8335      if (*radius == '\0')
8336        break;
8337      /*
8338        Detect edge in image.
8339      */
8340      XSetCursorState(display,windows,MagickTrue);
8341      XCheckRefreshWindows(display,windows);
8342      flags=ParseGeometry(radius,&geometry_info);
8343      edge_image=EdgeImage(*image,geometry_info.rho,exception);
8344      if (edge_image != (Image *) NULL)
8345        {
8346          *image=DestroyImage(*image);
8347          *image=edge_image;
8348        }
8349      CatchException(exception);
8350      XSetCursorState(display,windows,MagickFalse);
8351      if( IfMagickTrue(windows->image.orphan) )
8352        break;
8353      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8354      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8355      break;
8356    }
8357    case SpreadCommand:
8358    {
8359      Image
8360        *spread_image;
8361
8362      static char
8363        amount[MaxTextExtent] = "2";
8364
8365      /*
8366        Query user for spread amount.
8367      */
8368      (void) XDialogWidget(display,windows,"Spread",
8369        "Enter the displacement amount:",amount);
8370      if (*amount == '\0')
8371        break;
8372      /*
8373        Displace image pixels by a random amount.
8374      */
8375      XSetCursorState(display,windows,MagickTrue);
8376      XCheckRefreshWindows(display,windows);
8377      flags=ParseGeometry(amount,&geometry_info);
8378      spread_image=EdgeImage(*image,geometry_info.rho,exception);
8379      if (spread_image != (Image *) NULL)
8380        {
8381          *image=DestroyImage(*image);
8382          *image=spread_image;
8383        }
8384      CatchException(exception);
8385      XSetCursorState(display,windows,MagickFalse);
8386      if( IfMagickTrue(windows->image.orphan) )
8387        break;
8388      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8389      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8390      break;
8391    }
8392    case ShadeCommand:
8393    {
8394      Image
8395        *shade_image;
8396
8397      int
8398        status;
8399
8400      static char
8401        geometry[MaxTextExtent] = "30x30";
8402
8403      /*
8404        Query user for the shade geometry.
8405      */
8406      status=XDialogWidget(display,windows,"Shade",
8407        "Enter the azimuth and elevation of the light source:",geometry);
8408      if (*geometry == '\0')
8409        break;
8410      /*
8411        Shade image pixels.
8412      */
8413      XSetCursorState(display,windows,MagickTrue);
8414      XCheckRefreshWindows(display,windows);
8415      flags=ParseGeometry(geometry,&geometry_info);
8416      if ((flags & SigmaValue) == 0)
8417        geometry_info.sigma=1.0;
8418      shade_image=ShadeImage(*image,IsMagickTrue(status),
8419        geometry_info.rho,geometry_info.sigma,exception);
8420      if (shade_image != (Image *) NULL)
8421        {
8422          *image=DestroyImage(*image);
8423          *image=shade_image;
8424        }
8425      CatchException(exception);
8426      XSetCursorState(display,windows,MagickFalse);
8427      if( IfMagickTrue(windows->image.orphan) )
8428        break;
8429      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8430      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8431      break;
8432    }
8433    case RaiseCommand:
8434    {
8435      static char
8436        bevel_width[MaxTextExtent] = "10";
8437
8438      /*
8439        Query user for bevel width.
8440      */
8441      (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8442      if (*bevel_width == '\0')
8443        break;
8444      /*
8445        Raise an image.
8446      */
8447      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8448        exception);
8449      XSetCursorState(display,windows,MagickTrue);
8450      XCheckRefreshWindows(display,windows);
8451      (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8452        exception);
8453      (void) RaiseImage(*image,&page_geometry,MagickTrue,exception);
8454      XSetCursorState(display,windows,MagickFalse);
8455      if( IfMagickTrue(windows->image.orphan) )
8456        break;
8457      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8458      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8459      break;
8460    }
8461    case SegmentCommand:
8462    {
8463      static char
8464        threshold[MaxTextExtent] = "1.0x1.5";
8465
8466      /*
8467        Query user for smoothing threshold.
8468      */
8469      (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8470        threshold);
8471      if (*threshold == '\0')
8472        break;
8473      /*
8474        Segment an image.
8475      */
8476      XSetCursorState(display,windows,MagickTrue);
8477      XCheckRefreshWindows(display,windows);
8478      flags=ParseGeometry(threshold,&geometry_info);
8479      if ((flags & SigmaValue) == 0)
8480        geometry_info.sigma=1.0;
8481      (void) SegmentImage(*image,sRGBColorspace,MagickFalse,geometry_info.rho,
8482        geometry_info.sigma,exception);
8483      XSetCursorState(display,windows,MagickFalse);
8484      if( IfMagickTrue(windows->image.orphan) )
8485        break;
8486      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8487      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8488      break;
8489    }
8490    case SepiaToneCommand:
8491    {
8492      double
8493        threshold;
8494
8495      Image
8496        *sepia_image;
8497
8498      static char
8499        factor[MaxTextExtent] = "80%";
8500
8501      /*
8502        Query user for sepia-tone factor.
8503      */
8504      (void) XDialogWidget(display,windows,"Sepia Tone",
8505        "Enter the sepia tone factor (0 - 99.9%):",factor);
8506      if (*factor == '\0')
8507        break;
8508      /*
8509        Sepia tone image pixels.
8510      */
8511      XSetCursorState(display,windows,MagickTrue);
8512      XCheckRefreshWindows(display,windows);
8513      threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8514      sepia_image=SepiaToneImage(*image,threshold,exception);
8515      if (sepia_image != (Image *) NULL)
8516        {
8517          *image=DestroyImage(*image);
8518          *image=sepia_image;
8519        }
8520      CatchException(exception);
8521      XSetCursorState(display,windows,MagickFalse);
8522      if( IfMagickTrue(windows->image.orphan) )
8523        break;
8524      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8525      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8526      break;
8527    }
8528    case SolarizeCommand:
8529    {
8530      double
8531        threshold;
8532
8533      static char
8534        factor[MaxTextExtent] = "60%";
8535
8536      /*
8537        Query user for solarize factor.
8538      */
8539      (void) XDialogWidget(display,windows,"Solarize",
8540        "Enter the solarize factor (0 - 99.9%):",factor);
8541      if (*factor == '\0')
8542        break;
8543      /*
8544        Solarize image pixels.
8545      */
8546      XSetCursorState(display,windows,MagickTrue);
8547      XCheckRefreshWindows(display,windows);
8548      threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8549      (void) SolarizeImage(*image,threshold,exception);
8550      XSetCursorState(display,windows,MagickFalse);
8551      if( IfMagickTrue(windows->image.orphan) )
8552        break;
8553      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8554      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8555      break;
8556    }
8557    case SwirlCommand:
8558    {
8559      Image
8560        *swirl_image;
8561
8562      static char
8563        degrees[MaxTextExtent] = "60";
8564
8565      /*
8566        Query user for swirl angle.
8567      */
8568      (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8569        degrees);
8570      if (*degrees == '\0')
8571        break;
8572      /*
8573        Swirl image pixels about the center.
8574      */
8575      XSetCursorState(display,windows,MagickTrue);
8576      XCheckRefreshWindows(display,windows);
8577      flags=ParseGeometry(degrees,&geometry_info);
8578      swirl_image=SwirlImage(*image,geometry_info.rho,(*image)->interpolate,
8579        exception);
8580      if (swirl_image != (Image *) NULL)
8581        {
8582          *image=DestroyImage(*image);
8583          *image=swirl_image;
8584        }
8585      CatchException(exception);
8586      XSetCursorState(display,windows,MagickFalse);
8587      if( IfMagickTrue(windows->image.orphan) )
8588        break;
8589      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8590      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8591      break;
8592    }
8593    case ImplodeCommand:
8594    {
8595      Image
8596        *implode_image;
8597
8598      static char
8599        factor[MaxTextExtent] = "0.3";
8600
8601      /*
8602        Query user for implode factor.
8603      */
8604      (void) XDialogWidget(display,windows,"Implode",
8605        "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8606      if (*factor == '\0')
8607        break;
8608      /*
8609        Implode image pixels about the center.
8610      */
8611      XSetCursorState(display,windows,MagickTrue);
8612      XCheckRefreshWindows(display,windows);
8613      flags=ParseGeometry(factor,&geometry_info);
8614      implode_image=ImplodeImage(*image,geometry_info.rho,(*image)->interpolate,
8615        exception);
8616      if (implode_image != (Image *) NULL)
8617        {
8618          *image=DestroyImage(*image);
8619          *image=implode_image;
8620        }
8621      CatchException(exception);
8622      XSetCursorState(display,windows,MagickFalse);
8623      if( IfMagickTrue(windows->image.orphan) )
8624        break;
8625      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8626      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8627      break;
8628    }
8629    case VignetteCommand:
8630    {
8631      Image
8632        *vignette_image;
8633
8634      static char
8635        geometry[MaxTextExtent] = "0x20";
8636
8637      /*
8638        Query user for the vignette geometry.
8639      */
8640      (void) XDialogWidget(display,windows,"Vignette",
8641        "Enter the radius, sigma, and x and y offsets:",geometry);
8642      if (*geometry == '\0')
8643        break;
8644      /*
8645        Soften the edges of the image in vignette style
8646      */
8647      XSetCursorState(display,windows,MagickTrue);
8648      XCheckRefreshWindows(display,windows);
8649      flags=ParseGeometry(geometry,&geometry_info);
8650      if ((flags & SigmaValue) == 0)
8651        geometry_info.sigma=1.0;
8652      if ((flags & XiValue) == 0)
8653        geometry_info.xi=0.1*(*image)->columns;
8654      if ((flags & PsiValue) == 0)
8655        geometry_info.psi=0.1*(*image)->rows;
8656      vignette_image=VignetteImage(*image,geometry_info.rho,0.0,(ssize_t)
8657        ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-0.5),
8658        exception);
8659      if (vignette_image != (Image *) NULL)
8660        {
8661          *image=DestroyImage(*image);
8662          *image=vignette_image;
8663        }
8664      CatchException(exception);
8665      XSetCursorState(display,windows,MagickFalse);
8666      if( IfMagickTrue(windows->image.orphan) )
8667        break;
8668      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8669      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8670      break;
8671    }
8672    case WaveCommand:
8673    {
8674      Image
8675        *wave_image;
8676
8677      static char
8678        geometry[MaxTextExtent] = "25x150";
8679
8680      /*
8681        Query user for the wave geometry.
8682      */
8683      (void) XDialogWidget(display,windows,"Wave",
8684        "Enter the amplitude and length of the wave:",geometry);
8685      if (*geometry == '\0')
8686        break;
8687      /*
8688        Alter an image along a sine wave.
8689      */
8690      XSetCursorState(display,windows,MagickTrue);
8691      XCheckRefreshWindows(display,windows);
8692      flags=ParseGeometry(geometry,&geometry_info);
8693      if ((flags & SigmaValue) == 0)
8694        geometry_info.sigma=1.0;
8695      wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8696        (*image)->interpolate,exception);
8697      if (wave_image != (Image *) NULL)
8698        {
8699          *image=DestroyImage(*image);
8700          *image=wave_image;
8701        }
8702      CatchException(exception);
8703      XSetCursorState(display,windows,MagickFalse);
8704      if( IfMagickTrue(windows->image.orphan) )
8705        break;
8706      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8707      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8708      break;
8709    }
8710    case OilPaintCommand:
8711    {
8712      Image
8713        *paint_image;
8714
8715      static char
8716        radius[MaxTextExtent] = "0";
8717
8718      /*
8719        Query user for circular neighborhood radius.
8720      */
8721      (void) XDialogWidget(display,windows,"Oil Paint",
8722        "Enter the mask radius:",radius);
8723      if (*radius == '\0')
8724        break;
8725      /*
8726        OilPaint image scanlines.
8727      */
8728      XSetCursorState(display,windows,MagickTrue);
8729      XCheckRefreshWindows(display,windows);
8730      flags=ParseGeometry(radius,&geometry_info);
8731      paint_image=OilPaintImage(*image,geometry_info.rho,geometry_info.sigma,
8732        exception);
8733      if (paint_image != (Image *) NULL)
8734        {
8735          *image=DestroyImage(*image);
8736          *image=paint_image;
8737        }
8738      CatchException(exception);
8739      XSetCursorState(display,windows,MagickFalse);
8740      if( IfMagickTrue(windows->image.orphan) )
8741        break;
8742      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8743      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8744      break;
8745    }
8746    case CharcoalDrawCommand:
8747    {
8748      Image
8749        *charcoal_image;
8750
8751      static char
8752        radius[MaxTextExtent] = "0x1";
8753
8754      /*
8755        Query user for charcoal radius.
8756      */
8757      (void) XDialogWidget(display,windows,"Charcoal Draw",
8758        "Enter the charcoal radius and sigma:",radius);
8759      if (*radius == '\0')
8760        break;
8761      /*
8762        Charcoal the image.
8763      */
8764      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8765        exception);
8766      XSetCursorState(display,windows,MagickTrue);
8767      XCheckRefreshWindows(display,windows);
8768      flags=ParseGeometry(radius,&geometry_info);
8769      if ((flags & SigmaValue) == 0)
8770        geometry_info.sigma=geometry_info.rho;
8771      charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8772        exception);
8773      if (charcoal_image != (Image *) NULL)
8774        {
8775          *image=DestroyImage(*image);
8776          *image=charcoal_image;
8777        }
8778      CatchException(exception);
8779      XSetCursorState(display,windows,MagickFalse);
8780      if( IfMagickTrue(windows->image.orphan) )
8781        break;
8782      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8783      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8784      break;
8785    }
8786    case AnnotateCommand:
8787    {
8788      /*
8789        Annotate the image with text.
8790      */
8791      status=XAnnotateEditImage(display,resource_info,windows,*image,exception);
8792      if( IfMagickFalse(status) )
8793        {
8794          XNoticeWidget(display,windows,"Unable to annotate X image",
8795            (*image)->filename);
8796          break;
8797        }
8798      break;
8799    }
8800    case DrawCommand:
8801    {
8802      /*
8803        Draw image.
8804      */
8805      status=XDrawEditImage(display,resource_info,windows,image,exception);
8806      if( IfMagickFalse(status) )
8807        {
8808          XNoticeWidget(display,windows,"Unable to draw on the X image",
8809            (*image)->filename);
8810          break;
8811        }
8812      break;
8813    }
8814    case ColorCommand:
8815    {
8816      /*
8817        Color edit.
8818      */
8819      status=XColorEditImage(display,resource_info,windows,image,exception);
8820      if( IfMagickFalse(status) )
8821        {
8822          XNoticeWidget(display,windows,"Unable to pixel edit X image",
8823            (*image)->filename);
8824          break;
8825        }
8826      break;
8827    }
8828    case MatteCommand:
8829    {
8830      /*
8831        Matte edit.
8832      */
8833      status=XMatteEditImage(display,resource_info,windows,image,exception);
8834      if( IfMagickFalse(status) )
8835        {
8836          XNoticeWidget(display,windows,"Unable to matte edit X image",
8837            (*image)->filename);
8838          break;
8839        }
8840      break;
8841    }
8842    case CompositeCommand:
8843    {
8844      /*
8845        Composite image.
8846      */
8847      status=XCompositeImage(display,resource_info,windows,*image,
8848        exception);
8849      if( IfMagickFalse(status) )
8850        {
8851          XNoticeWidget(display,windows,"Unable to composite X image",
8852            (*image)->filename);
8853          break;
8854        }
8855      break;
8856    }
8857    case AddBorderCommand:
8858    {
8859      Image
8860        *border_image;
8861
8862      static char
8863        geometry[MaxTextExtent] = "6x6";
8864
8865      /*
8866        Query user for border color and geometry.
8867      */
8868      XColorBrowserWidget(display,windows,"Select",color);
8869      if (*color == '\0')
8870        break;
8871      (void) XDialogWidget(display,windows,"Add Border",
8872        "Enter border geometry:",geometry);
8873      if (*geometry == '\0')
8874        break;
8875      /*
8876        Add a border to the image.
8877      */
8878      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8879        exception);
8880      XSetCursorState(display,windows,MagickTrue);
8881      XCheckRefreshWindows(display,windows);
8882      (void) QueryColorCompliance(color,AllCompliance,&(*image)->border_color,
8883        exception);
8884      (void) ParsePageGeometry(*image,geometry,&page_geometry,
8885        exception);
8886      border_image=BorderImage(*image,&page_geometry,(*image)->compose,
8887        exception);
8888      if (border_image != (Image *) NULL)
8889        {
8890          *image=DestroyImage(*image);
8891          *image=border_image;
8892        }
8893      CatchException(exception);
8894      XSetCursorState(display,windows,MagickFalse);
8895      if( IfMagickTrue(windows->image.orphan) )
8896        break;
8897      windows->image.window_changes.width=(int) (*image)->columns;
8898      windows->image.window_changes.height=(int) (*image)->rows;
8899      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8900      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8901      break;
8902    }
8903    case AddFrameCommand:
8904    {
8905      FrameInfo
8906        frame_info;
8907
8908      Image
8909        *frame_image;
8910
8911      static char
8912        geometry[MaxTextExtent] = "6x6";
8913
8914      /*
8915        Query user for frame color and geometry.
8916      */
8917      XColorBrowserWidget(display,windows,"Select",color);
8918      if (*color == '\0')
8919        break;
8920      (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8921        geometry);
8922      if (*geometry == '\0')
8923        break;
8924      /*
8925        Surround image with an ornamental border.
8926      */
8927      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8928        exception);
8929      XSetCursorState(display,windows,MagickTrue);
8930      XCheckRefreshWindows(display,windows);
8931      (void) QueryColorCompliance(color,AllCompliance,&(*image)->matte_color,
8932        exception);
8933      (void) ParsePageGeometry(*image,geometry,&page_geometry,
8934        exception);
8935      frame_info.width=page_geometry.width;
8936      frame_info.height=page_geometry.height;
8937      frame_info.outer_bevel=page_geometry.x;
8938      frame_info.inner_bevel=page_geometry.y;
8939      frame_info.x=(ssize_t) frame_info.width;
8940      frame_info.y=(ssize_t) frame_info.height;
8941      frame_info.width=(*image)->columns+2*frame_info.width;
8942      frame_info.height=(*image)->rows+2*frame_info.height;
8943      frame_image=FrameImage(*image,&frame_info,(*image)->compose,exception);
8944      if (frame_image != (Image *) NULL)
8945        {
8946          *image=DestroyImage(*image);
8947          *image=frame_image;
8948        }
8949      CatchException(exception);
8950      XSetCursorState(display,windows,MagickFalse);
8951      if( IfMagickTrue(windows->image.orphan) )
8952        break;
8953      windows->image.window_changes.width=(int) (*image)->columns;
8954      windows->image.window_changes.height=(int) (*image)->rows;
8955      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8956      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8957      break;
8958    }
8959    case CommentCommand:
8960    {
8961      const char
8962        *value;
8963
8964      FILE
8965        *file;
8966
8967      int
8968        unique_file;
8969
8970      /*
8971        Edit image comment.
8972      */
8973      unique_file=AcquireUniqueFileResource(image_info->filename);
8974      if (unique_file == -1)
8975        XNoticeWidget(display,windows,"Unable to edit image comment",
8976          image_info->filename);
8977      value=GetImageProperty(*image,"comment",exception);
8978      if (value == (char *) NULL)
8979        unique_file=close(unique_file)-1;
8980      else
8981        {
8982          register const char
8983            *p;
8984
8985          file=fdopen(unique_file,"w");
8986          if (file == (FILE *) NULL)
8987            {
8988              XNoticeWidget(display,windows,"Unable to edit image comment",
8989                image_info->filename);
8990              break;
8991            }
8992          for (p=value; *p != '\0'; p++)
8993            (void) fputc((int) *p,file);
8994          (void) fputc('\n',file);
8995          (void) fclose(file);
8996        }
8997      XSetCursorState(display,windows,MagickTrue);
8998      XCheckRefreshWindows(display,windows);
8999      status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
9000        exception);
9001      if( IfMagickFalse(status) )
9002        XNoticeWidget(display,windows,"Unable to edit image comment",
9003          (char *) NULL);
9004      else
9005        {
9006          char
9007            *comment;
9008
9009          comment=FileToString(image_info->filename,~0UL,exception);
9010          if (comment != (char *) NULL)
9011            {
9012              (void) SetImageProperty(*image,"comment",comment,exception);
9013              (*image)->taint=MagickTrue;
9014            }
9015        }
9016      (void) RelinquishUniqueFileResource(image_info->filename);
9017      XSetCursorState(display,windows,MagickFalse);
9018      break;
9019    }
9020    case LaunchCommand:
9021    {
9022      /*
9023        Launch program.
9024      */
9025      XSetCursorState(display,windows,MagickTrue);
9026      XCheckRefreshWindows(display,windows);
9027      (void) AcquireUniqueFilename(filename);
9028      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"launch:%s",
9029        filename);
9030      status=WriteImage(image_info,*image,exception);
9031      if( IfMagickFalse(status) )
9032        XNoticeWidget(display,windows,"Unable to launch image editor",
9033          (char *) NULL);
9034      else
9035        {
9036          nexus=ReadImage(resource_info->image_info,exception);
9037          CatchException(exception);
9038          XClientMessage(display,windows->image.id,windows->im_protocols,
9039            windows->im_next_image,CurrentTime);
9040        }
9041      (void) RelinquishUniqueFileResource(filename);
9042      XSetCursorState(display,windows,MagickFalse);
9043      break;
9044    }
9045    case RegionofInterestCommand:
9046    {
9047      /*
9048        Apply an image processing technique to a region of interest.
9049      */
9050      (void) XROIImage(display,resource_info,windows,image,exception);
9051      break;
9052    }
9053    case InfoCommand:
9054      break;
9055    case ZoomCommand:
9056    {
9057      /*
9058        Zoom image.
9059      */
9060      if( IfMagickTrue(windows->magnify.mapped) )
9061        (void) XRaiseWindow(display,windows->magnify.id);
9062      else
9063        {
9064          /*
9065            Make magnify image.
9066          */
9067          XSetCursorState(display,windows,MagickTrue);
9068          (void) XMapRaised(display,windows->magnify.id);
9069          XSetCursorState(display,windows,MagickFalse);
9070        }
9071      break;
9072    }
9073    case ShowPreviewCommand:
9074    {
9075      char
9076        **previews;
9077
9078      Image
9079        *preview_image;
9080
9081      static char
9082        preview_type[MaxTextExtent] = "Gamma";
9083
9084      /*
9085        Select preview type from menu.
9086      */
9087      previews=GetCommandOptions(MagickPreviewOptions);
9088      if (previews == (char **) NULL)
9089        break;
9090      XListBrowserWidget(display,windows,&windows->widget,
9091        (const char **) previews,"Preview",
9092        "Select an enhancement, effect, or F/X:",preview_type);
9093      previews=DestroyStringList(previews);
9094      if (*preview_type == '\0')
9095        break;
9096      /*
9097        Show image preview.
9098      */
9099      XSetCursorState(display,windows,MagickTrue);
9100      XCheckRefreshWindows(display,windows);
9101      image_info->preview_type=(PreviewType)
9102        ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
9103      image_info->group=(ssize_t) windows->image.id;
9104      (void) DeleteImageProperty(*image,"label");
9105      (void) SetImageProperty(*image,"label","Preview",exception);
9106      (void) AcquireUniqueFilename(filename);
9107      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"preview:%s",
9108        filename);
9109      status=WriteImage(image_info,*image,exception);
9110      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9111      preview_image=ReadImage(image_info,exception);
9112      (void) RelinquishUniqueFileResource(filename);
9113      if (preview_image == (Image *) NULL)
9114        break;
9115      (void) FormatLocaleString(preview_image->filename,MaxTextExtent,"show:%s",
9116        filename);
9117      status=WriteImage(image_info,preview_image,exception);
9118      preview_image=DestroyImage(preview_image);
9119      if( IfMagickFalse(status) )
9120        XNoticeWidget(display,windows,"Unable to show image preview",
9121          (*image)->filename);
9122      XDelay(display,1500);
9123      XSetCursorState(display,windows,MagickFalse);
9124      break;
9125    }
9126    case ShowHistogramCommand:
9127    {
9128      Image
9129        *histogram_image;
9130
9131      /*
9132        Show image histogram.
9133      */
9134      XSetCursorState(display,windows,MagickTrue);
9135      XCheckRefreshWindows(display,windows);
9136      image_info->group=(ssize_t) windows->image.id;
9137      (void) DeleteImageProperty(*image,"label");
9138      (void) SetImageProperty(*image,"label","Histogram",exception);
9139      (void) AcquireUniqueFilename(filename);
9140      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"histogram:%s",
9141        filename);
9142      status=WriteImage(image_info,*image,exception);
9143      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9144      histogram_image=ReadImage(image_info,exception);
9145      (void) RelinquishUniqueFileResource(filename);
9146      if (histogram_image == (Image *) NULL)
9147        break;
9148      (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
9149        "show:%s",filename);
9150      status=WriteImage(image_info,histogram_image,exception);
9151      histogram_image=DestroyImage(histogram_image);
9152      if( IfMagickFalse(status) )
9153        XNoticeWidget(display,windows,"Unable to show histogram",
9154          (*image)->filename);
9155      XDelay(display,1500);
9156      XSetCursorState(display,windows,MagickFalse);
9157      break;
9158    }
9159    case ShowMatteCommand:
9160    {
9161      Image
9162        *matte_image;
9163
9164      if ((*image)->alpha_trait != BlendPixelTrait)
9165        {
9166          XNoticeWidget(display,windows,
9167            "Image does not have any matte information",(*image)->filename);
9168          break;
9169        }
9170      /*
9171        Show image matte.
9172      */
9173      XSetCursorState(display,windows,MagickTrue);
9174      XCheckRefreshWindows(display,windows);
9175      image_info->group=(ssize_t) windows->image.id;
9176      (void) DeleteImageProperty(*image,"label");
9177      (void) SetImageProperty(*image,"label","Matte",exception);
9178      (void) AcquireUniqueFilename(filename);
9179      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"matte:%s",
9180        filename);
9181      status=WriteImage(image_info,*image,exception);
9182      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9183      matte_image=ReadImage(image_info,exception);
9184      (void) RelinquishUniqueFileResource(filename);
9185      if (matte_image == (Image *) NULL)
9186        break;
9187      (void) FormatLocaleString(matte_image->filename,MaxTextExtent,"show:%s",
9188        filename);
9189      status=WriteImage(image_info,matte_image,exception);
9190      matte_image=DestroyImage(matte_image);
9191      if( IfMagickFalse(status) )
9192        XNoticeWidget(display,windows,"Unable to show matte",
9193          (*image)->filename);
9194      XDelay(display,1500);
9195      XSetCursorState(display,windows,MagickFalse);
9196      break;
9197    }
9198    case BackgroundCommand:
9199    {
9200      /*
9201        Background image.
9202      */
9203      status=XBackgroundImage(display,resource_info,windows,image,exception);
9204      if( IfMagickFalse(status) )
9205        break;
9206      nexus=CloneImage(*image,0,0,MagickTrue,exception);
9207      if (nexus != (Image *) NULL)
9208        XClientMessage(display,windows->image.id,windows->im_protocols,
9209          windows->im_next_image,CurrentTime);
9210      break;
9211    }
9212    case SlideShowCommand:
9213    {
9214      static char
9215        delay[MaxTextExtent] = "5";
9216
9217      /*
9218        Display next image after pausing.
9219      */
9220      (void) XDialogWidget(display,windows,"Slide Show",
9221        "Pause how many 1/100ths of a second between images:",delay);
9222      if (*delay == '\0')
9223        break;
9224      resource_info->delay=StringToUnsignedLong(delay);
9225      XClientMessage(display,windows->image.id,windows->im_protocols,
9226        windows->im_next_image,CurrentTime);
9227      break;
9228    }
9229    case PreferencesCommand:
9230    {
9231      /*
9232        Set user preferences.
9233      */
9234      status=XPreferencesWidget(display,resource_info,windows);
9235      if( IfMagickFalse(status) )
9236        break;
9237      nexus=CloneImage(*image,0,0,MagickTrue,exception);
9238      if (nexus != (Image *) NULL)
9239        XClientMessage(display,windows->image.id,windows->im_protocols,
9240          windows->im_next_image,CurrentTime);
9241      break;
9242    }
9243    case HelpCommand:
9244    {
9245      /*
9246        User requested help.
9247      */
9248      XTextViewWidget(display,resource_info,windows,MagickFalse,
9249        "Help Viewer - Display",DisplayHelp);
9250      break;
9251    }
9252    case BrowseDocumentationCommand:
9253    {
9254      Atom
9255        mozilla_atom;
9256
9257      Window
9258        mozilla_window,
9259        root_window;
9260
9261      /*
9262        Browse the ImageMagick documentation.
9263      */
9264      root_window=XRootWindow(display,XDefaultScreen(display));
9265      mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9266      mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9267      if (mozilla_window != (Window) NULL)
9268        {
9269          char
9270            command[MaxTextExtent],
9271            *url;
9272
9273          /*
9274            Display documentation using Netscape remote control.
9275          */
9276          url=GetMagickHomeURL();
9277          (void) FormatLocaleString(command,MaxTextExtent,
9278            "openurl(%s,new-tab)",url);
9279          url=DestroyString(url);
9280          mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9281          (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9282            8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9283          XSetCursorState(display,windows,MagickFalse);
9284          break;
9285        }
9286      XSetCursorState(display,windows,MagickTrue);
9287      XCheckRefreshWindows(display,windows);
9288      status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9289        exception);
9290      if( IfMagickFalse(status) )
9291        XNoticeWidget(display,windows,"Unable to browse documentation",
9292          (char *) NULL);
9293      XDelay(display,1500);
9294      XSetCursorState(display,windows,MagickFalse);
9295      break;
9296    }
9297    case VersionCommand:
9298    {
9299      XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9300        GetMagickCopyright());
9301      break;
9302    }
9303    case SaveToUndoBufferCommand:
9304      break;
9305    default:
9306    {
9307      (void) XBell(display,0);
9308      break;
9309    }
9310  }
9311  image_info=DestroyImageInfo(image_info);
9312  return(nexus);
9313}
9314
9315/*
9316%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9317%                                                                             %
9318%                                                                             %
9319%                                                                             %
9320+   X M a g n i f y I m a g e                                                 %
9321%                                                                             %
9322%                                                                             %
9323%                                                                             %
9324%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9325%
9326%  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9327%  The magnified portion is displayed in a separate window.
9328%
9329%  The format of the XMagnifyImage method is:
9330%
9331%      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9332%        ExceptionInfo *exception)
9333%
9334%  A description of each parameter follows:
9335%
9336%    o display: Specifies a connection to an X server;  returned from
9337%      XOpenDisplay.
9338%
9339%    o windows: Specifies a pointer to a XWindows structure.
9340%
9341%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
9342%      the entire image is refreshed.
9343%
9344%    o exception: return any errors or warnings in this structure.
9345%
9346*/
9347static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9348  ExceptionInfo *exception)
9349{
9350  char
9351    text[MaxTextExtent];
9352
9353  register int
9354    x,
9355    y;
9356
9357  size_t
9358    state;
9359
9360  /*
9361    Update magnified image until the mouse button is released.
9362  */
9363  (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9364  state=DefaultState;
9365  x=event->xbutton.x;
9366  y=event->xbutton.y;
9367  windows->magnify.x=(int) windows->image.x+x;
9368  windows->magnify.y=(int) windows->image.y+y;
9369  do
9370  {
9371    /*
9372      Map and unmap Info widget as text cursor crosses its boundaries.
9373    */
9374    if( IfMagickTrue(windows->info.mapped) )
9375      {
9376        if ((x < (int) (windows->info.x+windows->info.width)) &&
9377            (y < (int) (windows->info.y+windows->info.height)))
9378          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9379      }
9380    else
9381      if ((x > (int) (windows->info.x+windows->info.width)) ||
9382          (y > (int) (windows->info.y+windows->info.height)))
9383        (void) XMapWindow(display,windows->info.id);
9384    if( IfMagickTrue(windows->info.mapped) )
9385      {
9386        /*
9387          Display pointer position.
9388        */
9389        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9390          windows->magnify.x,windows->magnify.y);
9391        XInfoWidget(display,windows,text);
9392      }
9393    /*
9394      Wait for next event.
9395    */
9396    XScreenEvent(display,windows,event,exception);
9397    switch (event->type)
9398    {
9399      case ButtonPress:
9400        break;
9401      case ButtonRelease:
9402      {
9403        /*
9404          User has finished magnifying image.
9405        */
9406        x=event->xbutton.x;
9407        y=event->xbutton.y;
9408        state|=ExitState;
9409        break;
9410      }
9411      case Expose:
9412        break;
9413      case MotionNotify:
9414      {
9415        x=event->xmotion.x;
9416        y=event->xmotion.y;
9417        break;
9418      }
9419      default:
9420        break;
9421    }
9422    /*
9423      Check boundary conditions.
9424    */
9425    if (x < 0)
9426      x=0;
9427    else
9428      if (x >= (int) windows->image.width)
9429        x=(int) windows->image.width-1;
9430    if (y < 0)
9431      y=0;
9432    else
9433     if (y >= (int) windows->image.height)
9434       y=(int) windows->image.height-1;
9435  } while ((state & ExitState) == 0);
9436  /*
9437    Display magnified image.
9438  */
9439  XSetCursorState(display,windows,MagickFalse);
9440}
9441
9442/*
9443%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9444%                                                                             %
9445%                                                                             %
9446%                                                                             %
9447+   X M a g n i f y W i n d o w C o m m a n d                                 %
9448%                                                                             %
9449%                                                                             %
9450%                                                                             %
9451%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9452%
9453%  XMagnifyWindowCommand() moves the image within an Magnify window by one
9454%  pixel as specified by the key symbol.
9455%
9456%  The format of the XMagnifyWindowCommand method is:
9457%
9458%      void XMagnifyWindowCommand(Display *display,XWindows *windows,
9459%        const MagickStatusType state,const KeySym key_symbol,
9460%        ExceptionInfo *exception)
9461%
9462%  A description of each parameter follows:
9463%
9464%    o display: Specifies a connection to an X server; returned from
9465%      XOpenDisplay.
9466%
9467%    o windows: Specifies a pointer to a XWindows structure.
9468%
9469%    o state: key mask.
9470%
9471%    o key_symbol: Specifies a KeySym which indicates which side of the image
9472%      to trim.
9473%
9474%    o exception: return any errors or warnings in this structure.
9475%
9476*/
9477static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9478  const MagickStatusType state,const KeySym key_symbol,ExceptionInfo *exception)
9479{
9480  unsigned int
9481    quantum;
9482
9483  /*
9484    User specified a magnify factor or position.
9485  */
9486  quantum=1;
9487  if ((state & Mod1Mask) != 0)
9488    quantum=10;
9489  switch ((int) key_symbol)
9490  {
9491    case QuitCommand:
9492    {
9493      (void) XWithdrawWindow(display,windows->magnify.id,
9494        windows->magnify.screen);
9495      break;
9496    }
9497    case XK_Home:
9498    case XK_KP_Home:
9499    {
9500      windows->magnify.x=(int) windows->image.width/2;
9501      windows->magnify.y=(int) windows->image.height/2;
9502      break;
9503    }
9504    case XK_Left:
9505    case XK_KP_Left:
9506    {
9507      if (windows->magnify.x > 0)
9508        windows->magnify.x-=quantum;
9509      break;
9510    }
9511    case XK_Up:
9512    case XK_KP_Up:
9513    {
9514      if (windows->magnify.y > 0)
9515        windows->magnify.y-=quantum;
9516      break;
9517    }
9518    case XK_Right:
9519    case XK_KP_Right:
9520    {
9521      if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9522        windows->magnify.x+=quantum;
9523      break;
9524    }
9525    case XK_Down:
9526    case XK_KP_Down:
9527    {
9528      if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9529        windows->magnify.y+=quantum;
9530      break;
9531    }
9532    case XK_0:
9533    case XK_1:
9534    case XK_2:
9535    case XK_3:
9536    case XK_4:
9537    case XK_5:
9538    case XK_6:
9539    case XK_7:
9540    case XK_8:
9541    case XK_9:
9542    {
9543      windows->magnify.data=(key_symbol-XK_0);
9544      break;
9545    }
9546    case XK_KP_0:
9547    case XK_KP_1:
9548    case XK_KP_2:
9549    case XK_KP_3:
9550    case XK_KP_4:
9551    case XK_KP_5:
9552    case XK_KP_6:
9553    case XK_KP_7:
9554    case XK_KP_8:
9555    case XK_KP_9:
9556    {
9557      windows->magnify.data=(key_symbol-XK_KP_0);
9558      break;
9559    }
9560    default:
9561      break;
9562  }
9563  XMakeMagnifyImage(display,windows,exception);
9564}
9565
9566/*
9567%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9568%                                                                             %
9569%                                                                             %
9570%                                                                             %
9571+   X M a k e P a n I m a g e                                                 %
9572%                                                                             %
9573%                                                                             %
9574%                                                                             %
9575%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9576%
9577%  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9578%  icon window.
9579%
9580%  The format of the XMakePanImage method is:
9581%
9582%        void XMakePanImage(Display *display,XResourceInfo *resource_info,
9583%          XWindows *windows,Image *image,ExceptionInfo *exception)
9584%
9585%  A description of each parameter follows:
9586%
9587%    o display: Specifies a connection to an X server;  returned from
9588%      XOpenDisplay.
9589%
9590%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9591%
9592%    o windows: Specifies a pointer to a XWindows structure.
9593%
9594%    o image: the image.
9595%
9596%    o exception: return any errors or warnings in this structure.
9597%
9598*/
9599static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9600  XWindows *windows,Image *image,ExceptionInfo *exception)
9601{
9602  MagickStatusType
9603    status;
9604
9605  /*
9606    Create and display image for panning icon.
9607  */
9608  XSetCursorState(display,windows,MagickTrue);
9609  XCheckRefreshWindows(display,windows);
9610  windows->pan.x=(int) windows->image.x;
9611  windows->pan.y=(int) windows->image.y;
9612  status=XMakeImage(display,resource_info,&windows->pan,image,
9613    windows->pan.width,windows->pan.height,exception);
9614  if( IfMagickFalse(status) )
9615    ThrowXWindowFatalException(ResourceLimitError,
9616     "MemoryAllocationFailed",image->filename);
9617  (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9618    windows->pan.pixmap);
9619  (void) XClearWindow(display,windows->pan.id);
9620  XDrawPanRectangle(display,windows);
9621  XSetCursorState(display,windows,MagickFalse);
9622}
9623
9624/*
9625%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9626%                                                                             %
9627%                                                                             %
9628%                                                                             %
9629+   X M a t t a E d i t I m a g e                                             %
9630%                                                                             %
9631%                                                                             %
9632%                                                                             %
9633%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9634%
9635%  XMatteEditImage() allows the user to interactively change the Matte channel
9636%  of an image.  If the image is PseudoClass it is promoted to DirectClass
9637%  before the matte information is stored.
9638%
9639%  The format of the XMatteEditImage method is:
9640%
9641%      MagickBooleanType XMatteEditImage(Display *display,
9642%        XResourceInfo *resource_info,XWindows *windows,Image **image,
9643%        ExceptionInfo *exception)
9644%
9645%  A description of each parameter follows:
9646%
9647%    o display: Specifies a connection to an X server;  returned from
9648%      XOpenDisplay.
9649%
9650%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9651%
9652%    o windows: Specifies a pointer to a XWindows structure.
9653%
9654%    o image: the image; returned from ReadImage.
9655%
9656%    o exception: return any errors or warnings in this structure.
9657%
9658*/
9659static MagickBooleanType XMatteEditImage(Display *display,
9660  XResourceInfo *resource_info,XWindows *windows,Image **image,
9661  ExceptionInfo *exception)
9662{
9663  static char
9664    matte[MaxTextExtent] = "0";
9665
9666  static const char
9667    *MatteEditMenu[] =
9668    {
9669      "Method",
9670      "Border Color",
9671      "Fuzz",
9672      "Matte Value",
9673      "Undo",
9674      "Help",
9675      "Dismiss",
9676      (char *) NULL
9677    };
9678
9679  static const ModeType
9680    MatteEditCommands[] =
9681    {
9682      MatteEditMethod,
9683      MatteEditBorderCommand,
9684      MatteEditFuzzCommand,
9685      MatteEditValueCommand,
9686      MatteEditUndoCommand,
9687      MatteEditHelpCommand,
9688      MatteEditDismissCommand
9689    };
9690
9691  static PaintMethod
9692    method = PointMethod;
9693
9694  static XColor
9695    border_color = { 0, 0, 0, 0, 0, 0 };
9696
9697  char
9698    command[MaxTextExtent],
9699    text[MaxTextExtent];
9700
9701  Cursor
9702    cursor;
9703
9704  int
9705    entry,
9706    id,
9707    x,
9708    x_offset,
9709    y,
9710    y_offset;
9711
9712  register int
9713    i;
9714
9715  register Quantum
9716    *q;
9717
9718  unsigned int
9719    height,
9720    width;
9721
9722  size_t
9723    state;
9724
9725  XEvent
9726    event;
9727
9728  /*
9729    Map Command widget.
9730  */
9731  (void) CloneString(&windows->command.name,"Matte Edit");
9732  windows->command.data=4;
9733  (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9734  (void) XMapRaised(display,windows->command.id);
9735  XClientMessage(display,windows->image.id,windows->im_protocols,
9736    windows->im_update_widget,CurrentTime);
9737  /*
9738    Make cursor.
9739  */
9740  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9741    resource_info->background_color,resource_info->foreground_color);
9742  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9743  /*
9744    Track pointer until button 1 is pressed.
9745  */
9746  XQueryPosition(display,windows->image.id,&x,&y);
9747  (void) XSelectInput(display,windows->image.id,
9748    windows->image.attributes.event_mask | PointerMotionMask);
9749  state=DefaultState;
9750  do
9751  {
9752    if( IfMagickTrue(windows->info.mapped) )
9753      {
9754        /*
9755          Display pointer position.
9756        */
9757        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9758          x+windows->image.x,y+windows->image.y);
9759        XInfoWidget(display,windows,text);
9760      }
9761    /*
9762      Wait for next event.
9763    */
9764    XScreenEvent(display,windows,&event,exception);
9765    if (event.xany.window == windows->command.id)
9766      {
9767        /*
9768          Select a command from the Command widget.
9769        */
9770        id=XCommandWidget(display,windows,MatteEditMenu,&event);
9771        if (id < 0)
9772          {
9773            (void) XCheckDefineCursor(display,windows->image.id,cursor);
9774            continue;
9775          }
9776        switch (MatteEditCommands[id])
9777        {
9778          case MatteEditMethod:
9779          {
9780            char
9781              **methods;
9782
9783            /*
9784              Select a method from the pop-up menu.
9785            */
9786            methods=GetCommandOptions(MagickMethodOptions);
9787            if (methods == (char **) NULL)
9788              break;
9789            entry=XMenuWidget(display,windows,MatteEditMenu[id],
9790              (const char **) methods,command);
9791            if (entry >= 0)
9792              method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9793                MagickFalse,methods[entry]);
9794            methods=DestroyStringList(methods);
9795            break;
9796          }
9797          case MatteEditBorderCommand:
9798          {
9799            const char
9800              *ColorMenu[MaxNumberPens];
9801
9802            int
9803              pen_number;
9804
9805            /*
9806              Initialize menu selections.
9807            */
9808            for (i=0; i < (int) (MaxNumberPens-2); i++)
9809              ColorMenu[i]=resource_info->pen_colors[i];
9810            ColorMenu[MaxNumberPens-2]="Browser...";
9811            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9812            /*
9813              Select a pen color from the pop-up menu.
9814            */
9815            pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9816              (const char **) ColorMenu,command);
9817            if (pen_number < 0)
9818              break;
9819            if (pen_number == (MaxNumberPens-2))
9820              {
9821                static char
9822                  color_name[MaxTextExtent] = "gray";
9823
9824                /*
9825                  Select a pen color from a dialog.
9826                */
9827                resource_info->pen_colors[pen_number]=color_name;
9828                XColorBrowserWidget(display,windows,"Select",color_name);
9829                if (*color_name == '\0')
9830                  break;
9831              }
9832            /*
9833              Set border color.
9834            */
9835            (void) XParseColor(display,windows->map_info->colormap,
9836              resource_info->pen_colors[pen_number],&border_color);
9837            break;
9838          }
9839          case MatteEditFuzzCommand:
9840          {
9841            static char
9842              fuzz[MaxTextExtent];
9843
9844            static const char
9845              *FuzzMenu[] =
9846              {
9847                "0%",
9848                "2%",
9849                "5%",
9850                "10%",
9851                "15%",
9852                "Dialog...",
9853                (char *) NULL,
9854              };
9855
9856            /*
9857              Select a command from the pop-up menu.
9858            */
9859            entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9860              command);
9861            if (entry < 0)
9862              break;
9863            if (entry != 5)
9864              {
9865                (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
9866                  QuantumRange+1.0);
9867                break;
9868              }
9869            (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9870            (void) XDialogWidget(display,windows,"Ok",
9871              "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9872            if (*fuzz == '\0')
9873              break;
9874            (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
9875            (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
9876              1.0);
9877            break;
9878          }
9879          case MatteEditValueCommand:
9880          {
9881            static char
9882              message[MaxTextExtent];
9883
9884            static const char
9885              *MatteMenu[] =
9886              {
9887                "Opaque",
9888                "Transparent",
9889                "Dialog...",
9890                (char *) NULL,
9891              };
9892
9893            /*
9894              Select a command from the pop-up menu.
9895            */
9896            entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9897              command);
9898            if (entry < 0)
9899              break;
9900            if (entry != 2)
9901              {
9902                (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9903                  OpaqueAlpha);
9904                if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9905                  (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9906                    (Quantum) TransparentAlpha);
9907                break;
9908              }
9909            (void) FormatLocaleString(message,MaxTextExtent,
9910              "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9911              QuantumRange);
9912            (void) XDialogWidget(display,windows,"Matte",message,matte);
9913            if (*matte == '\0')
9914              break;
9915            break;
9916          }
9917          case MatteEditUndoCommand:
9918          {
9919            (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9920              image,exception);
9921            break;
9922          }
9923          case MatteEditHelpCommand:
9924          {
9925            XTextViewWidget(display,resource_info,windows,MagickFalse,
9926              "Help Viewer - Matte Edit",ImageMatteEditHelp);
9927            break;
9928          }
9929          case MatteEditDismissCommand:
9930          {
9931            /*
9932              Prematurely exit.
9933            */
9934            state|=EscapeState;
9935            state|=ExitState;
9936            break;
9937          }
9938          default:
9939            break;
9940        }
9941        (void) XCheckDefineCursor(display,windows->image.id,cursor);
9942        continue;
9943      }
9944    switch (event.type)
9945    {
9946      case ButtonPress:
9947      {
9948        if (event.xbutton.button != Button1)
9949          break;
9950        if ((event.xbutton.window != windows->image.id) &&
9951            (event.xbutton.window != windows->magnify.id))
9952          break;
9953        /*
9954          Update matte data.
9955        */
9956        x=event.xbutton.x;
9957        y=event.xbutton.y;
9958        (void) XMagickCommand(display,resource_info,windows,
9959          SaveToUndoBufferCommand,image,exception);
9960        state|=UpdateConfigurationState;
9961        break;
9962      }
9963      case ButtonRelease:
9964      {
9965        if (event.xbutton.button != Button1)
9966          break;
9967        if ((event.xbutton.window != windows->image.id) &&
9968            (event.xbutton.window != windows->magnify.id))
9969          break;
9970        /*
9971          Update colormap information.
9972        */
9973        x=event.xbutton.x;
9974        y=event.xbutton.y;
9975        XConfigureImageColormap(display,resource_info,windows,*image,exception);
9976        (void) XConfigureImage(display,resource_info,windows,*image,exception);
9977        XInfoWidget(display,windows,text);
9978        (void) XCheckDefineCursor(display,windows->image.id,cursor);
9979        state&=(~UpdateConfigurationState);
9980        break;
9981      }
9982      case Expose:
9983        break;
9984      case KeyPress:
9985      {
9986        char
9987          command[MaxTextExtent];
9988
9989        KeySym
9990          key_symbol;
9991
9992        if (event.xkey.window == windows->magnify.id)
9993          {
9994            Window
9995              window;
9996
9997            window=windows->magnify.id;
9998            while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9999          }
10000        if (event.xkey.window != windows->image.id)
10001          break;
10002        /*
10003          Respond to a user key press.
10004        */
10005        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
10006          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10007        switch ((int) key_symbol)
10008        {
10009          case XK_Escape:
10010          case XK_F20:
10011          {
10012            /*
10013              Prematurely exit.
10014            */
10015            state|=ExitState;
10016            break;
10017          }
10018          case XK_F1:
10019          case XK_Help:
10020          {
10021            XTextViewWidget(display,resource_info,windows,MagickFalse,
10022              "Help Viewer - Matte Edit",ImageMatteEditHelp);
10023            break;
10024          }
10025          default:
10026          {
10027            (void) XBell(display,0);
10028            break;
10029          }
10030        }
10031        break;
10032      }
10033      case MotionNotify:
10034      {
10035        /*
10036          Map and unmap Info widget as cursor crosses its boundaries.
10037        */
10038        x=event.xmotion.x;
10039        y=event.xmotion.y;
10040        if( IfMagickTrue(windows->info.mapped) )
10041          {
10042            if ((x < (int) (windows->info.x+windows->info.width)) &&
10043                (y < (int) (windows->info.y+windows->info.height)))
10044              (void) XWithdrawWindow(display,windows->info.id,
10045                windows->info.screen);
10046          }
10047        else
10048          if ((x > (int) (windows->info.x+windows->info.width)) ||
10049              (y > (int) (windows->info.y+windows->info.height)))
10050            (void) XMapWindow(display,windows->info.id);
10051        break;
10052      }
10053      default:
10054        break;
10055    }
10056    if (event.xany.window == windows->magnify.id)
10057      {
10058        x=windows->magnify.x-windows->image.x;
10059        y=windows->magnify.y-windows->image.y;
10060      }
10061    x_offset=x;
10062    y_offset=y;
10063    if ((state & UpdateConfigurationState) != 0)
10064      {
10065        CacheView
10066          *image_view;
10067
10068        int
10069          x,
10070          y;
10071
10072        /*
10073          Matte edit is relative to image configuration.
10074        */
10075        (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
10076          MagickTrue);
10077        XPutPixel(windows->image.ximage,x_offset,y_offset,
10078          windows->pixel_info->background_color.pixel);
10079        width=(unsigned int) (*image)->columns;
10080        height=(unsigned int) (*image)->rows;
10081        x=0;
10082        y=0;
10083        if (windows->image.crop_geometry != (char *) NULL)
10084          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
10085            &height);
10086        x_offset=(int) (width*(windows->image.x+x_offset)/
10087          windows->image.ximage->width+x);
10088        y_offset=(int) (height*(windows->image.y+y_offset)/
10089          windows->image.ximage->height+y);
10090        if ((x_offset < 0) || (y_offset < 0))
10091          continue;
10092        if ((x_offset >= (int) (*image)->columns) ||
10093            (y_offset >= (int) (*image)->rows))
10094          continue;
10095        if( IfMagickFalse(SetImageStorageClass(*image,DirectClass,exception)) )
10096          return(MagickFalse);
10097        if ((*image)->alpha_trait != BlendPixelTrait)
10098          (void) SetImageAlphaChannel(*image,OpaqueAlphaChannel,exception);
10099        image_view=AcquireAuthenticCacheView(*image,exception);
10100        switch (method)
10101        {
10102          case PointMethod:
10103          default:
10104          {
10105            /*
10106              Update matte information using point algorithm.
10107            */
10108            q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10109              (ssize_t) y_offset,1,1,exception);
10110            if (q == (Quantum *) NULL)
10111              break;
10112            SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10113            (void) SyncCacheViewAuthenticPixels(image_view,exception);
10114            break;
10115          }
10116          case ReplaceMethod:
10117          {
10118            PixelInfo
10119              pixel,
10120              target;
10121
10122            /*
10123              Update matte information using replace algorithm.
10124            */
10125            (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
10126              x_offset,(ssize_t) y_offset,&target,exception);
10127            for (y=0; y < (int) (*image)->rows; y++)
10128            {
10129              q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10130                (*image)->columns,1,exception);
10131              if (q == (Quantum *) NULL)
10132                break;
10133              for (x=0; x < (int) (*image)->columns; x++)
10134              {
10135                GetPixelInfoPixel(*image,q,&pixel);
10136                if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
10137                  SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10138                q+=GetPixelChannels(*image);
10139              }
10140              if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
10141                break;
10142            }
10143            break;
10144          }
10145          case FloodfillMethod:
10146          case FillToBorderMethod:
10147          {
10148            ChannelType
10149              channel_mask;
10150
10151            DrawInfo
10152              *draw_info;
10153
10154            PixelInfo
10155              target;
10156
10157            /*
10158              Update matte information using floodfill algorithm.
10159            */
10160            (void) GetOneVirtualPixelInfo(*image,
10161              GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
10162              y_offset,&target,exception);
10163            if (method == FillToBorderMethod)
10164              {
10165                target.red=(double) ScaleShortToQuantum(
10166                  border_color.red);
10167                target.green=(double) ScaleShortToQuantum(
10168                  border_color.green);
10169                target.blue=(double) ScaleShortToQuantum(
10170                  border_color.blue);
10171              }
10172            draw_info=CloneDrawInfo(resource_info->image_info,
10173              (DrawInfo *) NULL);
10174            draw_info->fill.alpha=(double) ClampToQuantum(
10175              StringToDouble(matte,(char **) NULL));
10176            channel_mask=SetImageChannelMask(*image,AlphaChannel);
10177            (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10178              x_offset,(ssize_t) y_offset,
10179              IsMagickFalse(method == FloodfillMethod),exception);
10180            (void) SetPixelChannelMask(*image,channel_mask);
10181            draw_info=DestroyDrawInfo(draw_info);
10182            break;
10183          }
10184          case ResetMethod:
10185          {
10186            /*
10187              Update matte information using reset algorithm.
10188            */
10189            if( IfMagickFalse(SetImageStorageClass(*image,DirectClass,exception)) )
10190              return(MagickFalse);
10191            for (y=0; y < (int) (*image)->rows; y++)
10192            {
10193              q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10194                (*image)->columns,1,exception);
10195              if (q == (Quantum *) NULL)
10196                break;
10197              for (x=0; x < (int) (*image)->columns; x++)
10198              {
10199                SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10200                q+=GetPixelChannels(*image);
10201              }
10202              if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
10203                break;
10204            }
10205            if (StringToLong(matte) == (long) OpaqueAlpha)
10206              (*image)->alpha_trait=UndefinedPixelTrait;
10207            break;
10208          }
10209        }
10210        image_view=DestroyCacheView(image_view);
10211        state&=(~UpdateConfigurationState);
10212      }
10213  } while ((state & ExitState) == 0);
10214  (void) XSelectInput(display,windows->image.id,
10215    windows->image.attributes.event_mask);
10216  XSetCursorState(display,windows,MagickFalse);
10217  (void) XFreeCursor(display,cursor);
10218  return(MagickTrue);
10219}
10220
10221/*
10222%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10223%                                                                             %
10224%                                                                             %
10225%                                                                             %
10226+   X O p e n I m a g e                                                       %
10227%                                                                             %
10228%                                                                             %
10229%                                                                             %
10230%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10231%
10232%  XOpenImage() loads an image from a file.
10233%
10234%  The format of the XOpenImage method is:
10235%
10236%     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10237%       XWindows *windows,const unsigned int command)
10238%
10239%  A description of each parameter follows:
10240%
10241%    o display: Specifies a connection to an X server; returned from
10242%      XOpenDisplay.
10243%
10244%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10245%
10246%    o windows: Specifies a pointer to a XWindows structure.
10247%
10248%    o command: A value other than zero indicates that the file is selected
10249%      from the command line argument list.
10250%
10251*/
10252static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10253  XWindows *windows,const MagickBooleanType command)
10254{
10255  const MagickInfo
10256    *magick_info;
10257
10258  ExceptionInfo
10259    *exception;
10260
10261  Image
10262    *nexus;
10263
10264  ImageInfo
10265    *image_info;
10266
10267  static char
10268    filename[MaxTextExtent] = "\0";
10269
10270  /*
10271    Request file name from user.
10272  */
10273  if( IfMagickFalse(command) )
10274    XFileBrowserWidget(display,windows,"Open",filename);
10275  else
10276    {
10277      char
10278        **filelist,
10279        **files;
10280
10281      int
10282        count,
10283        status;
10284
10285      register int
10286        i,
10287        j;
10288
10289      /*
10290        Select next image from the command line.
10291      */
10292      status=XGetCommand(display,windows->image.id,&files,&count);
10293      if (status == 0)
10294        {
10295          ThrowXWindowFatalException(XServerError,"UnableToGetProperty","...");
10296          return((Image *) NULL);
10297        }
10298      filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10299      if (filelist == (char **) NULL)
10300        {
10301          ThrowXWindowFatalException(ResourceLimitError,
10302            "MemoryAllocationFailed","...");
10303          (void) XFreeStringList(files);
10304          return((Image *) NULL);
10305        }
10306      j=0;
10307      for (i=1; i < count; i++)
10308        if (*files[i] != '-')
10309          filelist[j++]=files[i];
10310      filelist[j]=(char *) NULL;
10311      XListBrowserWidget(display,windows,&windows->widget,
10312        (const char **) filelist,"Load","Select Image to Load:",filename);
10313      filelist=(char **) RelinquishMagickMemory(filelist);
10314      (void) XFreeStringList(files);
10315    }
10316  if (*filename == '\0')
10317    return((Image *) NULL);
10318  image_info=CloneImageInfo(resource_info->image_info);
10319  (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10320    (void *) NULL);
10321  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10322  exception=AcquireExceptionInfo();
10323  (void) SetImageInfo(image_info,0,exception);
10324  if (LocaleCompare(image_info->magick,"X") == 0)
10325    {
10326      char
10327        seconds[MaxTextExtent];
10328
10329      /*
10330        User may want to delay the X server screen grab.
10331      */
10332      (void) CopyMagickString(seconds,"0",MaxTextExtent);
10333      (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10334        seconds);
10335      if (*seconds == '\0')
10336        return((Image *) NULL);
10337      XDelay(display,(size_t) (1000*StringToLong(seconds)));
10338    }
10339  magick_info=GetMagickInfo(image_info->magick,exception);
10340  if ((magick_info != (const MagickInfo *) NULL) &&
10341      IfMagickTrue(magick_info->raw))
10342    {
10343      char
10344        geometry[MaxTextExtent];
10345
10346      /*
10347        Request image size from the user.
10348      */
10349      (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10350      if (image_info->size != (char *) NULL)
10351        (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10352      (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10353        geometry);
10354      (void) CloneString(&image_info->size,geometry);
10355    }
10356  /*
10357    Load the image.
10358  */
10359  XSetCursorState(display,windows,MagickTrue);
10360  XCheckRefreshWindows(display,windows);
10361  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10362  nexus=ReadImage(image_info,exception);
10363  CatchException(exception);
10364  XSetCursorState(display,windows,MagickFalse);
10365  if (nexus != (Image *) NULL)
10366    XClientMessage(display,windows->image.id,windows->im_protocols,
10367      windows->im_next_image,CurrentTime);
10368  else
10369    {
10370      char
10371        *text,
10372        **textlist;
10373
10374      /*
10375        Unknown image format.
10376      */
10377      text=FileToString(filename,~0,exception);
10378      if (text == (char *) NULL)
10379        return((Image *) NULL);
10380      textlist=StringToList(text);
10381      if (textlist != (char **) NULL)
10382        {
10383          char
10384            title[MaxTextExtent];
10385
10386          register int
10387            i;
10388
10389          (void) FormatLocaleString(title,MaxTextExtent,
10390            "Unknown format: %s",filename);
10391          XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10392            (const char **) textlist);
10393          for (i=0; textlist[i] != (char *) NULL; i++)
10394            textlist[i]=DestroyString(textlist[i]);
10395          textlist=(char **) RelinquishMagickMemory(textlist);
10396        }
10397      text=DestroyString(text);
10398    }
10399  exception=DestroyExceptionInfo(exception);
10400  image_info=DestroyImageInfo(image_info);
10401  return(nexus);
10402}
10403
10404/*
10405%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10406%                                                                             %
10407%                                                                             %
10408%                                                                             %
10409+   X P a n I m a g e                                                         %
10410%                                                                             %
10411%                                                                             %
10412%                                                                             %
10413%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10414%
10415%  XPanImage() pans the image until the mouse button is released.
10416%
10417%  The format of the XPanImage method is:
10418%
10419%      void XPanImage(Display *display,XWindows *windows,XEvent *event,
10420%        ExceptionInfo *exception)
10421%
10422%  A description of each parameter follows:
10423%
10424%    o display: Specifies a connection to an X server;  returned from
10425%      XOpenDisplay.
10426%
10427%    o windows: Specifies a pointer to a XWindows structure.
10428%
10429%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10430%      the entire image is refreshed.
10431%
10432%    o exception: return any errors or warnings in this structure.
10433%
10434*/
10435static void XPanImage(Display *display,XWindows *windows,XEvent *event,
10436  ExceptionInfo *exception)
10437{
10438  char
10439    text[MaxTextExtent];
10440
10441  Cursor
10442    cursor;
10443
10444  double
10445    x_factor,
10446    y_factor;
10447
10448  RectangleInfo
10449    pan_info;
10450
10451  size_t
10452    state;
10453
10454  /*
10455    Define cursor.
10456  */
10457  if ((windows->image.ximage->width > (int) windows->image.width) &&
10458      (windows->image.ximage->height > (int) windows->image.height))
10459    cursor=XCreateFontCursor(display,XC_fleur);
10460  else
10461    if (windows->image.ximage->width > (int) windows->image.width)
10462      cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10463    else
10464      if (windows->image.ximage->height > (int) windows->image.height)
10465        cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10466      else
10467        cursor=XCreateFontCursor(display,XC_arrow);
10468  (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10469  /*
10470    Pan image as pointer moves until the mouse button is released.
10471  */
10472  x_factor=(double) windows->image.ximage->width/windows->pan.width;
10473  y_factor=(double) windows->image.ximage->height/windows->pan.height;
10474  pan_info.width=windows->pan.width*windows->image.width/
10475    windows->image.ximage->width;
10476  pan_info.height=windows->pan.height*windows->image.height/
10477    windows->image.ximage->height;
10478  pan_info.x=0;
10479  pan_info.y=0;
10480  state=UpdateConfigurationState;
10481  do
10482  {
10483    switch (event->type)
10484    {
10485      case ButtonPress:
10486      {
10487        /*
10488          User choose an initial pan location.
10489        */
10490        pan_info.x=(ssize_t) event->xbutton.x;
10491        pan_info.y=(ssize_t) event->xbutton.y;
10492        state|=UpdateConfigurationState;
10493        break;
10494      }
10495      case ButtonRelease:
10496      {
10497        /*
10498          User has finished panning the image.
10499        */
10500        pan_info.x=(ssize_t) event->xbutton.x;
10501        pan_info.y=(ssize_t) event->xbutton.y;
10502        state|=UpdateConfigurationState | ExitState;
10503        break;
10504      }
10505      case MotionNotify:
10506      {
10507        pan_info.x=(ssize_t) event->xmotion.x;
10508        pan_info.y=(ssize_t) event->xmotion.y;
10509        state|=UpdateConfigurationState;
10510      }
10511      default:
10512        break;
10513    }
10514    if ((state & UpdateConfigurationState) != 0)
10515      {
10516        /*
10517          Check boundary conditions.
10518        */
10519        if (pan_info.x < (ssize_t) (pan_info.width/2))
10520          pan_info.x=0;
10521        else
10522          pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10523        if (pan_info.x < 0)
10524          pan_info.x=0;
10525        else
10526          if ((int) (pan_info.x+windows->image.width) >
10527              windows->image.ximage->width)
10528            pan_info.x=(ssize_t)
10529              (windows->image.ximage->width-windows->image.width);
10530        if (pan_info.y < (ssize_t) (pan_info.height/2))
10531          pan_info.y=0;
10532        else
10533          pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10534        if (pan_info.y < 0)
10535          pan_info.y=0;
10536        else
10537          if ((int) (pan_info.y+windows->image.height) >
10538              windows->image.ximage->height)
10539            pan_info.y=(ssize_t)
10540              (windows->image.ximage->height-windows->image.height);
10541        if ((windows->image.x != (int) pan_info.x) ||
10542            (windows->image.y != (int) pan_info.y))
10543          {
10544            /*
10545              Display image pan offset.
10546            */
10547            windows->image.x=(int) pan_info.x;
10548            windows->image.y=(int) pan_info.y;
10549            (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
10550              windows->image.width,windows->image.height,windows->image.x,
10551              windows->image.y);
10552            XInfoWidget(display,windows,text);
10553            /*
10554              Refresh Image window.
10555            */
10556            XDrawPanRectangle(display,windows);
10557            XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10558          }
10559        state&=(~UpdateConfigurationState);
10560      }
10561    /*
10562      Wait for next event.
10563    */
10564    if ((state & ExitState) == 0)
10565      XScreenEvent(display,windows,event,exception);
10566  } while ((state & ExitState) == 0);
10567  /*
10568    Restore cursor.
10569  */
10570  (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10571  (void) XFreeCursor(display,cursor);
10572  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10573}
10574
10575/*
10576%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10577%                                                                             %
10578%                                                                             %
10579%                                                                             %
10580+   X P a s t e I m a g e                                                     %
10581%                                                                             %
10582%                                                                             %
10583%                                                                             %
10584%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10585%
10586%  XPasteImage() pastes an image previously saved with XCropImage in the X
10587%  window image at a location the user chooses with the pointer.
10588%
10589%  The format of the XPasteImage method is:
10590%
10591%      MagickBooleanType XPasteImage(Display *display,
10592%        XResourceInfo *resource_info,XWindows *windows,Image *image,
10593%        ExceptionInfo *exception)
10594%
10595%  A description of each parameter follows:
10596%
10597%    o display: Specifies a connection to an X server;  returned from
10598%      XOpenDisplay.
10599%
10600%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10601%
10602%    o windows: Specifies a pointer to a XWindows structure.
10603%
10604%    o image: the image; returned from ReadImage.
10605%
10606%    o exception: return any errors or warnings in this structure.
10607%
10608*/
10609static MagickBooleanType XPasteImage(Display *display,
10610  XResourceInfo *resource_info,XWindows *windows,Image *image,
10611  ExceptionInfo *exception)
10612{
10613  static const char
10614    *PasteMenu[] =
10615    {
10616      "Operator",
10617      "Help",
10618      "Dismiss",
10619      (char *) NULL
10620    };
10621
10622  static const ModeType
10623    PasteCommands[] =
10624    {
10625      PasteOperatorsCommand,
10626      PasteHelpCommand,
10627      PasteDismissCommand
10628    };
10629
10630  static CompositeOperator
10631    compose = CopyCompositeOp;
10632
10633  char
10634    text[MaxTextExtent];
10635
10636  Cursor
10637    cursor;
10638
10639  Image
10640    *paste_image;
10641
10642  int
10643    entry,
10644    id,
10645    x,
10646    y;
10647
10648  double
10649    scale_factor;
10650
10651  RectangleInfo
10652    highlight_info,
10653    paste_info;
10654
10655  unsigned int
10656    height,
10657    width;
10658
10659  size_t
10660    state;
10661
10662  XEvent
10663    event;
10664
10665  /*
10666    Copy image.
10667  */
10668  if (resource_info->copy_image == (Image *) NULL)
10669    return(MagickFalse);
10670  paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,exception);
10671  /*
10672    Map Command widget.
10673  */
10674  (void) CloneString(&windows->command.name,"Paste");
10675  windows->command.data=1;
10676  (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10677  (void) XMapRaised(display,windows->command.id);
10678  XClientMessage(display,windows->image.id,windows->im_protocols,
10679    windows->im_update_widget,CurrentTime);
10680  /*
10681    Track pointer until button 1 is pressed.
10682  */
10683  XSetCursorState(display,windows,MagickFalse);
10684  XQueryPosition(display,windows->image.id,&x,&y);
10685  (void) XSelectInput(display,windows->image.id,
10686    windows->image.attributes.event_mask | PointerMotionMask);
10687  paste_info.x=(ssize_t) windows->image.x+x;
10688  paste_info.y=(ssize_t) windows->image.y+y;
10689  paste_info.width=0;
10690  paste_info.height=0;
10691  cursor=XCreateFontCursor(display,XC_ul_angle);
10692  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10693  state=DefaultState;
10694  do
10695  {
10696    if( IfMagickTrue(windows->info.mapped) )
10697      {
10698        /*
10699          Display pointer position.
10700        */
10701        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
10702          (long) paste_info.x,(long) paste_info.y);
10703        XInfoWidget(display,windows,text);
10704      }
10705    highlight_info=paste_info;
10706    highlight_info.x=paste_info.x-windows->image.x;
10707    highlight_info.y=paste_info.y-windows->image.y;
10708    XHighlightRectangle(display,windows->image.id,
10709      windows->image.highlight_context,&highlight_info);
10710    /*
10711      Wait for next event.
10712    */
10713    XScreenEvent(display,windows,&event,exception);
10714    XHighlightRectangle(display,windows->image.id,
10715      windows->image.highlight_context,&highlight_info);
10716    if (event.xany.window == windows->command.id)
10717      {
10718        /*
10719          Select a command from the Command widget.
10720        */
10721        id=XCommandWidget(display,windows,PasteMenu,&event);
10722        if (id < 0)
10723          continue;
10724        switch (PasteCommands[id])
10725        {
10726          case PasteOperatorsCommand:
10727          {
10728            char
10729              command[MaxTextExtent],
10730              **operators;
10731
10732            /*
10733              Select a command from the pop-up menu.
10734            */
10735            operators=GetCommandOptions(MagickComposeOptions);
10736            if (operators == (char **) NULL)
10737              break;
10738            entry=XMenuWidget(display,windows,PasteMenu[id],
10739              (const char **) operators,command);
10740            if (entry >= 0)
10741              compose=(CompositeOperator) ParseCommandOption(
10742                MagickComposeOptions,MagickFalse,operators[entry]);
10743            operators=DestroyStringList(operators);
10744            break;
10745          }
10746          case PasteHelpCommand:
10747          {
10748            XTextViewWidget(display,resource_info,windows,MagickFalse,
10749              "Help Viewer - Image Composite",ImagePasteHelp);
10750            break;
10751          }
10752          case PasteDismissCommand:
10753          {
10754            /*
10755              Prematurely exit.
10756            */
10757            state|=EscapeState;
10758            state|=ExitState;
10759            break;
10760          }
10761          default:
10762            break;
10763        }
10764        continue;
10765      }
10766    switch (event.type)
10767    {
10768      case ButtonPress:
10769      {
10770        if( IfMagickTrue(image->debug) )
10771          (void) LogMagickEvent(X11Event,GetMagickModule(),
10772            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10773            event.xbutton.button,event.xbutton.x,event.xbutton.y);
10774        if (event.xbutton.button != Button1)
10775          break;
10776        if (event.xbutton.window != windows->image.id)
10777          break;
10778        /*
10779          Paste rectangle is relative to image configuration.
10780        */
10781        width=(unsigned int) image->columns;
10782        height=(unsigned int) image->rows;
10783        x=0;
10784        y=0;
10785        if (windows->image.crop_geometry != (char *) NULL)
10786          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10787            &width,&height);
10788        scale_factor=(double) windows->image.ximage->width/width;
10789        paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10790        scale_factor=(double) windows->image.ximage->height/height;
10791        paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10792        (void) XCheckDefineCursor(display,windows->image.id,cursor);
10793        paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10794        paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10795        break;
10796      }
10797      case ButtonRelease:
10798      {
10799        if( IfMagickTrue(image->debug) )
10800          (void) LogMagickEvent(X11Event,GetMagickModule(),
10801            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10802            event.xbutton.button,event.xbutton.x,event.xbutton.y);
10803        if (event.xbutton.button != Button1)
10804          break;
10805        if (event.xbutton.window != windows->image.id)
10806          break;
10807        if ((paste_info.width != 0) && (paste_info.height != 0))
10808          {
10809            /*
10810              User has selected the location of the paste image.
10811            */
10812            paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10813            paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10814            state|=ExitState;
10815          }
10816        break;
10817      }
10818      case Expose:
10819        break;
10820      case KeyPress:
10821      {
10822        char
10823          command[MaxTextExtent];
10824
10825        KeySym
10826          key_symbol;
10827
10828        int
10829          length;
10830
10831        if (event.xkey.window != windows->image.id)
10832          break;
10833        /*
10834          Respond to a user key press.
10835        */
10836        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10837          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10838        *(command+length)='\0';
10839        if( IfMagickTrue(image->debug) )
10840          (void) LogMagickEvent(X11Event,GetMagickModule(),
10841            "Key press: 0x%lx (%s)",(long) key_symbol,command);
10842        switch ((int) key_symbol)
10843        {
10844          case XK_Escape:
10845          case XK_F20:
10846          {
10847            /*
10848              Prematurely exit.
10849            */
10850            paste_image=DestroyImage(paste_image);
10851            state|=EscapeState;
10852            state|=ExitState;
10853            break;
10854          }
10855          case XK_F1:
10856          case XK_Help:
10857          {
10858            (void) XSetFunction(display,windows->image.highlight_context,
10859              GXcopy);
10860            XTextViewWidget(display,resource_info,windows,MagickFalse,
10861              "Help Viewer - Image Composite",ImagePasteHelp);
10862            (void) XSetFunction(display,windows->image.highlight_context,
10863              GXinvert);
10864            break;
10865          }
10866          default:
10867          {
10868            (void) XBell(display,0);
10869            break;
10870          }
10871        }
10872        break;
10873      }
10874      case MotionNotify:
10875      {
10876        /*
10877          Map and unmap Info widget as text cursor crosses its boundaries.
10878        */
10879        x=event.xmotion.x;
10880        y=event.xmotion.y;
10881        if( IfMagickTrue(windows->info.mapped) )
10882          {
10883            if ((x < (int) (windows->info.x+windows->info.width)) &&
10884                (y < (int) (windows->info.y+windows->info.height)))
10885              (void) XWithdrawWindow(display,windows->info.id,
10886                windows->info.screen);
10887          }
10888        else
10889          if ((x > (int) (windows->info.x+windows->info.width)) ||
10890              (y > (int) (windows->info.y+windows->info.height)))
10891            (void) XMapWindow(display,windows->info.id);
10892        paste_info.x=(ssize_t) windows->image.x+x;
10893        paste_info.y=(ssize_t) windows->image.y+y;
10894        break;
10895      }
10896      default:
10897      {
10898        if( IfMagickTrue(image->debug) )
10899          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10900            event.type);
10901        break;
10902      }
10903    }
10904  } while ((state & ExitState) == 0);
10905  (void) XSelectInput(display,windows->image.id,
10906    windows->image.attributes.event_mask);
10907  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10908  XSetCursorState(display,windows,MagickFalse);
10909  (void) XFreeCursor(display,cursor);
10910  if ((state & EscapeState) != 0)
10911    return(MagickTrue);
10912  /*
10913    Image pasting is relative to image configuration.
10914  */
10915  XSetCursorState(display,windows,MagickTrue);
10916  XCheckRefreshWindows(display,windows);
10917  width=(unsigned int) image->columns;
10918  height=(unsigned int) image->rows;
10919  x=0;
10920  y=0;
10921  if (windows->image.crop_geometry != (char *) NULL)
10922    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10923  scale_factor=(double) width/windows->image.ximage->width;
10924  paste_info.x+=x;
10925  paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10926  paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10927  scale_factor=(double) height/windows->image.ximage->height;
10928  paste_info.y+=y;
10929  paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10930  paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10931  /*
10932    Paste image with X Image window.
10933  */
10934  (void) CompositeImage(image,paste_image,compose,MagickTrue,paste_info.x,
10935    paste_info.y,exception);
10936  paste_image=DestroyImage(paste_image);
10937  XSetCursorState(display,windows,MagickFalse);
10938  /*
10939    Update image colormap.
10940  */
10941  XConfigureImageColormap(display,resource_info,windows,image,exception);
10942  (void) XConfigureImage(display,resource_info,windows,image,exception);
10943  return(MagickTrue);
10944}
10945
10946/*
10947%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10948%                                                                             %
10949%                                                                             %
10950%                                                                             %
10951+   X P r i n t I m a g e                                                     %
10952%                                                                             %
10953%                                                                             %
10954%                                                                             %
10955%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10956%
10957%  XPrintImage() prints an image to a Postscript printer.
10958%
10959%  The format of the XPrintImage method is:
10960%
10961%      MagickBooleanType XPrintImage(Display *display,
10962%        XResourceInfo *resource_info,XWindows *windows,Image *image,
10963%        ExceptionInfo *exception)
10964%
10965%  A description of each parameter follows:
10966%
10967%    o display: Specifies a connection to an X server; returned from
10968%      XOpenDisplay.
10969%
10970%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10971%
10972%    o windows: Specifies a pointer to a XWindows structure.
10973%
10974%    o image: the image.
10975%
10976%    o exception: return any errors or warnings in this structure.
10977%
10978*/
10979static MagickBooleanType XPrintImage(Display *display,
10980  XResourceInfo *resource_info,XWindows *windows,Image *image,
10981  ExceptionInfo *exception)
10982{
10983  char
10984    filename[MaxTextExtent],
10985    geometry[MaxTextExtent];
10986
10987  Image
10988    *print_image;
10989
10990  ImageInfo
10991    *image_info;
10992
10993  MagickStatusType
10994    status;
10995
10996  /*
10997    Request Postscript page geometry from user.
10998  */
10999  image_info=CloneImageInfo(resource_info->image_info);
11000  (void) FormatLocaleString(geometry,MaxTextExtent,"Letter");
11001  if (image_info->page != (char *) NULL)
11002    (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
11003  XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
11004    "Select Postscript Page Geometry:",geometry);
11005  if (*geometry == '\0')
11006    return(MagickTrue);
11007  image_info->page=GetPageGeometry(geometry);
11008  /*
11009    Apply image transforms.
11010  */
11011  XSetCursorState(display,windows,MagickTrue);
11012  XCheckRefreshWindows(display,windows);
11013  print_image=CloneImage(image,0,0,MagickTrue,exception);
11014  if (print_image == (Image *) NULL)
11015    return(MagickFalse);
11016  (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
11017    windows->image.ximage->width,windows->image.ximage->height);
11018  (void) TransformImage(&print_image,windows->image.crop_geometry,geometry,
11019    exception);
11020  /*
11021    Print image.
11022  */
11023  (void) AcquireUniqueFilename(filename);
11024  (void) FormatLocaleString(print_image->filename,MaxTextExtent,"print:%s",
11025    filename);
11026  status=WriteImage(image_info,print_image,exception);
11027  (void) RelinquishUniqueFileResource(filename);
11028  print_image=DestroyImage(print_image);
11029  image_info=DestroyImageInfo(image_info);
11030  XSetCursorState(display,windows,MagickFalse);
11031  return(IsMagickTrue(status));
11032}
11033
11034/*
11035%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11036%                                                                             %
11037%                                                                             %
11038%                                                                             %
11039+   X R O I I m a g e                                                         %
11040%                                                                             %
11041%                                                                             %
11042%                                                                             %
11043%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11044%
11045%  XROIImage() applies an image processing technique to a region of interest.
11046%
11047%  The format of the XROIImage method is:
11048%
11049%      MagickBooleanType XROIImage(Display *display,
11050%        XResourceInfo *resource_info,XWindows *windows,Image **image,
11051%        ExceptionInfo *exception)
11052%
11053%  A description of each parameter follows:
11054%
11055%    o display: Specifies a connection to an X server; returned from
11056%      XOpenDisplay.
11057%
11058%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11059%
11060%    o windows: Specifies a pointer to a XWindows structure.
11061%
11062%    o image: the image; returned from ReadImage.
11063%
11064%    o exception: return any errors or warnings in this structure.
11065%
11066*/
11067static MagickBooleanType XROIImage(Display *display,
11068  XResourceInfo *resource_info,XWindows *windows,Image **image,
11069  ExceptionInfo *exception)
11070{
11071#define ApplyMenus  7
11072
11073  static const char
11074    *ROIMenu[] =
11075    {
11076      "Help",
11077      "Dismiss",
11078      (char *) NULL
11079    },
11080    *ApplyMenu[] =
11081    {
11082      "File",
11083      "Edit",
11084      "Transform",
11085      "Enhance",
11086      "Effects",
11087      "F/X",
11088      "Miscellany",
11089      "Help",
11090      "Dismiss",
11091      (char *) NULL
11092    },
11093    *FileMenu[] =
11094    {
11095      "Save...",
11096      "Print...",
11097      (char *) NULL
11098    },
11099    *EditMenu[] =
11100    {
11101      "Undo",
11102      "Redo",
11103      (char *) NULL
11104    },
11105    *TransformMenu[] =
11106    {
11107      "Flop",
11108      "Flip",
11109      "Rotate Right",
11110      "Rotate Left",
11111      (char *) NULL
11112    },
11113    *EnhanceMenu[] =
11114    {
11115      "Hue...",
11116      "Saturation...",
11117      "Brightness...",
11118      "Gamma...",
11119      "Spiff",
11120      "Dull",
11121      "Contrast Stretch...",
11122      "Sigmoidal Contrast...",
11123      "Normalize",
11124      "Equalize",
11125      "Negate",
11126      "Grayscale",
11127      "Map...",
11128      "Quantize...",
11129      (char *) NULL
11130    },
11131    *EffectsMenu[] =
11132    {
11133      "Despeckle",
11134      "Emboss",
11135      "Reduce Noise",
11136      "Add Noise",
11137      "Sharpen...",
11138      "Blur...",
11139      "Threshold...",
11140      "Edge Detect...",
11141      "Spread...",
11142      "Shade...",
11143      "Raise...",
11144      "Segment...",
11145      (char *) NULL
11146    },
11147    *FXMenu[] =
11148    {
11149      "Solarize...",
11150      "Sepia Tone...",
11151      "Swirl...",
11152      "Implode...",
11153      "Vignette...",
11154      "Wave...",
11155      "Oil Paint...",
11156      "Charcoal Draw...",
11157      (char *) NULL
11158    },
11159    *MiscellanyMenu[] =
11160    {
11161      "Image Info",
11162      "Zoom Image",
11163      "Show Preview...",
11164      "Show Histogram",
11165      "Show Matte",
11166      (char *) NULL
11167    };
11168
11169  static const char
11170    **Menus[ApplyMenus] =
11171    {
11172      FileMenu,
11173      EditMenu,
11174      TransformMenu,
11175      EnhanceMenu,
11176      EffectsMenu,
11177      FXMenu,
11178      MiscellanyMenu
11179    };
11180
11181  static const CommandType
11182    ApplyCommands[] =
11183    {
11184      NullCommand,
11185      NullCommand,
11186      NullCommand,
11187      NullCommand,
11188      NullCommand,
11189      NullCommand,
11190      NullCommand,
11191      HelpCommand,
11192      QuitCommand
11193    },
11194    FileCommands[] =
11195    {
11196      SaveCommand,
11197      PrintCommand
11198    },
11199    EditCommands[] =
11200    {
11201      UndoCommand,
11202      RedoCommand
11203    },
11204    TransformCommands[] =
11205    {
11206      FlopCommand,
11207      FlipCommand,
11208      RotateRightCommand,
11209      RotateLeftCommand
11210    },
11211    EnhanceCommands[] =
11212    {
11213      HueCommand,
11214      SaturationCommand,
11215      BrightnessCommand,
11216      GammaCommand,
11217      SpiffCommand,
11218      DullCommand,
11219      ContrastStretchCommand,
11220      SigmoidalContrastCommand,
11221      NormalizeCommand,
11222      EqualizeCommand,
11223      NegateCommand,
11224      GrayscaleCommand,
11225      MapCommand,
11226      QuantizeCommand
11227    },
11228    EffectsCommands[] =
11229    {
11230      DespeckleCommand,
11231      EmbossCommand,
11232      ReduceNoiseCommand,
11233      AddNoiseCommand,
11234      SharpenCommand,
11235      BlurCommand,
11236      EdgeDetectCommand,
11237      SpreadCommand,
11238      ShadeCommand,
11239      RaiseCommand,
11240      SegmentCommand
11241    },
11242    FXCommands[] =
11243    {
11244      SolarizeCommand,
11245      SepiaToneCommand,
11246      SwirlCommand,
11247      ImplodeCommand,
11248      VignetteCommand,
11249      WaveCommand,
11250      OilPaintCommand,
11251      CharcoalDrawCommand
11252    },
11253    MiscellanyCommands[] =
11254    {
11255      InfoCommand,
11256      ZoomCommand,
11257      ShowPreviewCommand,
11258      ShowHistogramCommand,
11259      ShowMatteCommand
11260    },
11261    ROICommands[] =
11262    {
11263      ROIHelpCommand,
11264      ROIDismissCommand
11265    };
11266
11267  static const CommandType
11268    *Commands[ApplyMenus] =
11269    {
11270      FileCommands,
11271      EditCommands,
11272      TransformCommands,
11273      EnhanceCommands,
11274      EffectsCommands,
11275      FXCommands,
11276      MiscellanyCommands
11277    };
11278
11279  char
11280    command[MaxTextExtent],
11281    text[MaxTextExtent];
11282
11283  CommandType
11284    command_type;
11285
11286  Cursor
11287    cursor;
11288
11289  Image
11290    *roi_image;
11291
11292  int
11293    entry,
11294    id,
11295    x,
11296    y;
11297
11298  double
11299    scale_factor;
11300
11301  MagickProgressMonitor
11302    progress_monitor;
11303
11304  RectangleInfo
11305    crop_info,
11306    highlight_info,
11307    roi_info;
11308
11309  unsigned int
11310    height,
11311    width;
11312
11313  size_t
11314    state;
11315
11316  XEvent
11317    event;
11318
11319  /*
11320    Map Command widget.
11321  */
11322  (void) CloneString(&windows->command.name,"ROI");
11323  windows->command.data=0;
11324  (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11325  (void) XMapRaised(display,windows->command.id);
11326  XClientMessage(display,windows->image.id,windows->im_protocols,
11327    windows->im_update_widget,CurrentTime);
11328  /*
11329    Track pointer until button 1 is pressed.
11330  */
11331  XQueryPosition(display,windows->image.id,&x,&y);
11332  (void) XSelectInput(display,windows->image.id,
11333    windows->image.attributes.event_mask | PointerMotionMask);
11334  roi_info.x=(ssize_t) windows->image.x+x;
11335  roi_info.y=(ssize_t) windows->image.y+y;
11336  roi_info.width=0;
11337  roi_info.height=0;
11338  cursor=XCreateFontCursor(display,XC_fleur);
11339  state=DefaultState;
11340  do
11341  {
11342    if( IfMagickTrue(windows->info.mapped) )
11343      {
11344        /*
11345          Display pointer position.
11346        */
11347        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
11348          (long) roi_info.x,(long) roi_info.y);
11349        XInfoWidget(display,windows,text);
11350      }
11351    /*
11352      Wait for next event.
11353    */
11354    XScreenEvent(display,windows,&event,exception);
11355    if (event.xany.window == windows->command.id)
11356      {
11357        /*
11358          Select a command from the Command widget.
11359        */
11360        id=XCommandWidget(display,windows,ROIMenu,&event);
11361        if (id < 0)
11362          continue;
11363        switch (ROICommands[id])
11364        {
11365          case ROIHelpCommand:
11366          {
11367            XTextViewWidget(display,resource_info,windows,MagickFalse,
11368              "Help Viewer - Region of Interest",ImageROIHelp);
11369            break;
11370          }
11371          case ROIDismissCommand:
11372          {
11373            /*
11374              Prematurely exit.
11375            */
11376            state|=EscapeState;
11377            state|=ExitState;
11378            break;
11379          }
11380          default:
11381            break;
11382        }
11383        continue;
11384      }
11385    switch (event.type)
11386    {
11387      case ButtonPress:
11388      {
11389        if (event.xbutton.button != Button1)
11390          break;
11391        if (event.xbutton.window != windows->image.id)
11392          break;
11393        /*
11394          Note first corner of region of interest rectangle-- exit loop.
11395        */
11396        (void) XCheckDefineCursor(display,windows->image.id,cursor);
11397        roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11398        roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11399        state|=ExitState;
11400        break;
11401      }
11402      case ButtonRelease:
11403        break;
11404      case Expose:
11405        break;
11406      case KeyPress:
11407      {
11408        KeySym
11409          key_symbol;
11410
11411        if (event.xkey.window != windows->image.id)
11412          break;
11413        /*
11414          Respond to a user key press.
11415        */
11416        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11417          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11418        switch ((int) key_symbol)
11419        {
11420          case XK_Escape:
11421          case XK_F20:
11422          {
11423            /*
11424              Prematurely exit.
11425            */
11426            state|=EscapeState;
11427            state|=ExitState;
11428            break;
11429          }
11430          case XK_F1:
11431          case XK_Help:
11432          {
11433            XTextViewWidget(display,resource_info,windows,MagickFalse,
11434              "Help Viewer - Region of Interest",ImageROIHelp);
11435            break;
11436          }
11437          default:
11438          {
11439            (void) XBell(display,0);
11440            break;
11441          }
11442        }
11443        break;
11444      }
11445      case MotionNotify:
11446      {
11447        /*
11448          Map and unmap Info widget as text cursor crosses its boundaries.
11449        */
11450        x=event.xmotion.x;
11451        y=event.xmotion.y;
11452        if( IfMagickTrue(windows->info.mapped) )
11453          {
11454            if ((x < (int) (windows->info.x+windows->info.width)) &&
11455                (y < (int) (windows->info.y+windows->info.height)))
11456              (void) XWithdrawWindow(display,windows->info.id,
11457                windows->info.screen);
11458          }
11459        else
11460          if ((x > (int) (windows->info.x+windows->info.width)) ||
11461              (y > (int) (windows->info.y+windows->info.height)))
11462            (void) XMapWindow(display,windows->info.id);
11463        roi_info.x=(ssize_t) windows->image.x+x;
11464        roi_info.y=(ssize_t) windows->image.y+y;
11465        break;
11466      }
11467      default:
11468        break;
11469    }
11470  } while ((state & ExitState) == 0);
11471  (void) XSelectInput(display,windows->image.id,
11472    windows->image.attributes.event_mask);
11473  if ((state & EscapeState) != 0)
11474    {
11475      /*
11476        User want to exit without region of interest.
11477      */
11478      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11479      (void) XFreeCursor(display,cursor);
11480      return(MagickTrue);
11481    }
11482  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11483  do
11484  {
11485    /*
11486      Size rectangle as pointer moves until the mouse button is released.
11487    */
11488    x=(int) roi_info.x;
11489    y=(int) roi_info.y;
11490    roi_info.width=0;
11491    roi_info.height=0;
11492    state=DefaultState;
11493    do
11494    {
11495      highlight_info=roi_info;
11496      highlight_info.x=roi_info.x-windows->image.x;
11497      highlight_info.y=roi_info.y-windows->image.y;
11498      if ((highlight_info.width > 3) && (highlight_info.height > 3))
11499        {
11500          /*
11501            Display info and draw region of interest rectangle.
11502          */
11503          if( IfMagickFalse(windows->info.mapped) )
11504            (void) XMapWindow(display,windows->info.id);
11505          (void) FormatLocaleString(text,MaxTextExtent,
11506            " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11507            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11508          XInfoWidget(display,windows,text);
11509          XHighlightRectangle(display,windows->image.id,
11510            windows->image.highlight_context,&highlight_info);
11511        }
11512      else
11513        if( IfMagickTrue(windows->info.mapped) )
11514          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11515      /*
11516        Wait for next event.
11517      */
11518      XScreenEvent(display,windows,&event,exception);
11519      if ((highlight_info.width > 3) && (highlight_info.height > 3))
11520        XHighlightRectangle(display,windows->image.id,
11521          windows->image.highlight_context,&highlight_info);
11522      switch (event.type)
11523      {
11524        case ButtonPress:
11525        {
11526          roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11527          roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11528          break;
11529        }
11530        case ButtonRelease:
11531        {
11532          /*
11533            User has committed to region of interest rectangle.
11534          */
11535          roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11536          roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11537          XSetCursorState(display,windows,MagickFalse);
11538          state|=ExitState;
11539          if (LocaleCompare(windows->command.name,"Apply") == 0)
11540            break;
11541          (void) CloneString(&windows->command.name,"Apply");
11542          windows->command.data=ApplyMenus;
11543          (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11544          break;
11545        }
11546        case Expose:
11547          break;
11548        case MotionNotify:
11549        {
11550          roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11551          roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11552        }
11553        default:
11554          break;
11555      }
11556      if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11557          ((state & ExitState) != 0))
11558        {
11559          /*
11560            Check boundary conditions.
11561          */
11562          if (roi_info.x < 0)
11563            roi_info.x=0;
11564          else
11565            if (roi_info.x > (ssize_t) windows->image.ximage->width)
11566              roi_info.x=(ssize_t) windows->image.ximage->width;
11567          if ((int) roi_info.x < x)
11568            roi_info.width=(unsigned int) (x-roi_info.x);
11569          else
11570            {
11571              roi_info.width=(unsigned int) (roi_info.x-x);
11572              roi_info.x=(ssize_t) x;
11573            }
11574          if (roi_info.y < 0)
11575            roi_info.y=0;
11576          else
11577            if (roi_info.y > (ssize_t) windows->image.ximage->height)
11578              roi_info.y=(ssize_t) windows->image.ximage->height;
11579          if ((int) roi_info.y < y)
11580            roi_info.height=(unsigned int) (y-roi_info.y);
11581          else
11582            {
11583              roi_info.height=(unsigned int) (roi_info.y-y);
11584              roi_info.y=(ssize_t) y;
11585            }
11586        }
11587    } while ((state & ExitState) == 0);
11588    /*
11589      Wait for user to grab a corner of the rectangle or press return.
11590    */
11591    state=DefaultState;
11592    command_type=NullCommand;
11593    (void) XMapWindow(display,windows->info.id);
11594    do
11595    {
11596      if( IfMagickTrue(windows->info.mapped) )
11597        {
11598          /*
11599            Display pointer position.
11600          */
11601          (void) FormatLocaleString(text,MaxTextExtent,
11602            " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11603            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11604          XInfoWidget(display,windows,text);
11605        }
11606      highlight_info=roi_info;
11607      highlight_info.x=roi_info.x-windows->image.x;
11608      highlight_info.y=roi_info.y-windows->image.y;
11609      if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11610        {
11611          state|=EscapeState;
11612          state|=ExitState;
11613          break;
11614        }
11615      if ((state & UpdateRegionState) != 0)
11616        {
11617          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11618          switch (command_type)
11619          {
11620            case UndoCommand:
11621            case RedoCommand:
11622            {
11623              (void) XMagickCommand(display,resource_info,windows,command_type,
11624                image,exception);
11625              break;
11626            }
11627            default:
11628            {
11629              /*
11630                Region of interest is relative to image configuration.
11631              */
11632              progress_monitor=SetImageProgressMonitor(*image,
11633                (MagickProgressMonitor) NULL,(*image)->client_data);
11634              crop_info=roi_info;
11635              width=(unsigned int) (*image)->columns;
11636              height=(unsigned int) (*image)->rows;
11637              x=0;
11638              y=0;
11639              if (windows->image.crop_geometry != (char *) NULL)
11640                (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11641                  &width,&height);
11642              scale_factor=(double) width/windows->image.ximage->width;
11643              crop_info.x+=x;
11644              crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11645              crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11646              scale_factor=(double)
11647                height/windows->image.ximage->height;
11648              crop_info.y+=y;
11649              crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11650              crop_info.height=(unsigned int)
11651                (scale_factor*crop_info.height+0.5);
11652              roi_image=CropImage(*image,&crop_info,exception);
11653              (void) SetImageProgressMonitor(*image,progress_monitor,
11654                (*image)->client_data);
11655              if (roi_image == (Image *) NULL)
11656                continue;
11657              /*
11658                Apply image processing technique to the region of interest.
11659              */
11660              windows->image.orphan=MagickTrue;
11661              (void) XMagickCommand(display,resource_info,windows,command_type,
11662                &roi_image,exception);
11663              progress_monitor=SetImageProgressMonitor(*image,
11664                (MagickProgressMonitor) NULL,(*image)->client_data);
11665              (void) XMagickCommand(display,resource_info,windows,
11666                SaveToUndoBufferCommand,image,exception);
11667              windows->image.orphan=MagickFalse;
11668              (void) CompositeImage(*image,roi_image,CopyCompositeOp,
11669                MagickTrue,crop_info.x,crop_info.y,exception);
11670              roi_image=DestroyImage(roi_image);
11671              (void) SetImageProgressMonitor(*image,progress_monitor,
11672                (*image)->client_data);
11673              break;
11674            }
11675          }
11676          if (command_type != InfoCommand)
11677            {
11678              XConfigureImageColormap(display,resource_info,windows,*image,
11679                exception);
11680              (void) XConfigureImage(display,resource_info,windows,*image,
11681                exception);
11682            }
11683          XCheckRefreshWindows(display,windows);
11684          XInfoWidget(display,windows,text);
11685          (void) XSetFunction(display,windows->image.highlight_context,
11686            GXinvert);
11687          state&=(~UpdateRegionState);
11688        }
11689      XHighlightRectangle(display,windows->image.id,
11690        windows->image.highlight_context,&highlight_info);
11691      XScreenEvent(display,windows,&event,exception);
11692      if (event.xany.window == windows->command.id)
11693        {
11694          /*
11695            Select a command from the Command widget.
11696          */
11697          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11698          command_type=NullCommand;
11699          id=XCommandWidget(display,windows,ApplyMenu,&event);
11700          if (id >= 0)
11701            {
11702              (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11703              command_type=ApplyCommands[id];
11704              if (id < ApplyMenus)
11705                {
11706                  /*
11707                    Select a command from a pop-up menu.
11708                  */
11709                  entry=XMenuWidget(display,windows,ApplyMenu[id],
11710                    (const char **) Menus[id],command);
11711                  if (entry >= 0)
11712                    {
11713                      (void) CopyMagickString(command,Menus[id][entry],
11714                        MaxTextExtent);
11715                      command_type=Commands[id][entry];
11716                    }
11717                }
11718            }
11719          (void) XSetFunction(display,windows->image.highlight_context,
11720            GXinvert);
11721          XHighlightRectangle(display,windows->image.id,
11722            windows->image.highlight_context,&highlight_info);
11723          if (command_type == HelpCommand)
11724            {
11725              (void) XSetFunction(display,windows->image.highlight_context,
11726                GXcopy);
11727              XTextViewWidget(display,resource_info,windows,MagickFalse,
11728                "Help Viewer - Region of Interest",ImageROIHelp);
11729              (void) XSetFunction(display,windows->image.highlight_context,
11730                GXinvert);
11731              continue;
11732            }
11733          if (command_type == QuitCommand)
11734            {
11735              /*
11736                exit.
11737              */
11738              state|=EscapeState;
11739              state|=ExitState;
11740              continue;
11741            }
11742          if (command_type != NullCommand)
11743            state|=UpdateRegionState;
11744          continue;
11745        }
11746      XHighlightRectangle(display,windows->image.id,
11747        windows->image.highlight_context,&highlight_info);
11748      switch (event.type)
11749      {
11750        case ButtonPress:
11751        {
11752          x=windows->image.x;
11753          y=windows->image.y;
11754          if (event.xbutton.button != Button1)
11755            break;
11756          if (event.xbutton.window != windows->image.id)
11757            break;
11758          x=windows->image.x+event.xbutton.x;
11759          y=windows->image.y+event.xbutton.y;
11760          if ((x < (int) (roi_info.x+RoiDelta)) &&
11761              (x > (int) (roi_info.x-RoiDelta)) &&
11762              (y < (int) (roi_info.y+RoiDelta)) &&
11763              (y > (int) (roi_info.y-RoiDelta)))
11764            {
11765              roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11766              roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11767              state|=UpdateConfigurationState;
11768              break;
11769            }
11770          if ((x < (int) (roi_info.x+RoiDelta)) &&
11771              (x > (int) (roi_info.x-RoiDelta)) &&
11772              (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11773              (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11774            {
11775              roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11776              state|=UpdateConfigurationState;
11777              break;
11778            }
11779          if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11780              (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11781              (y < (int) (roi_info.y+RoiDelta)) &&
11782              (y > (int) (roi_info.y-RoiDelta)))
11783            {
11784              roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11785              state|=UpdateConfigurationState;
11786              break;
11787            }
11788          if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11789              (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11790              (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11791              (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11792            {
11793              state|=UpdateConfigurationState;
11794              break;
11795            }
11796        }
11797        case ButtonRelease:
11798        {
11799          if (event.xbutton.window == windows->pan.id)
11800            if ((highlight_info.x != crop_info.x-windows->image.x) ||
11801                (highlight_info.y != crop_info.y-windows->image.y))
11802              XHighlightRectangle(display,windows->image.id,
11803                windows->image.highlight_context,&highlight_info);
11804          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11805            event.xbutton.time);
11806          break;
11807        }
11808        case Expose:
11809        {
11810          if (event.xexpose.window == windows->image.id)
11811            if (event.xexpose.count == 0)
11812              {
11813                event.xexpose.x=(int) highlight_info.x;
11814                event.xexpose.y=(int) highlight_info.y;
11815                event.xexpose.width=(int) highlight_info.width;
11816                event.xexpose.height=(int) highlight_info.height;
11817                XRefreshWindow(display,&windows->image,&event);
11818              }
11819          if (event.xexpose.window == windows->info.id)
11820            if (event.xexpose.count == 0)
11821              XInfoWidget(display,windows,text);
11822          break;
11823        }
11824        case KeyPress:
11825        {
11826          KeySym
11827            key_symbol;
11828
11829          if (event.xkey.window != windows->image.id)
11830            break;
11831          /*
11832            Respond to a user key press.
11833          */
11834          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11835            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11836          switch ((int) key_symbol)
11837          {
11838            case XK_Shift_L:
11839            case XK_Shift_R:
11840              break;
11841            case XK_Escape:
11842            case XK_F20:
11843              state|=EscapeState;
11844            case XK_Return:
11845            {
11846              state|=ExitState;
11847              break;
11848            }
11849            case XK_Home:
11850            case XK_KP_Home:
11851            {
11852              roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11853              roi_info.y=(ssize_t) (windows->image.height/2L-
11854                roi_info.height/2L);
11855              break;
11856            }
11857            case XK_Left:
11858            case XK_KP_Left:
11859            {
11860              roi_info.x--;
11861              break;
11862            }
11863            case XK_Up:
11864            case XK_KP_Up:
11865            case XK_Next:
11866            {
11867              roi_info.y--;
11868              break;
11869            }
11870            case XK_Right:
11871            case XK_KP_Right:
11872            {
11873              roi_info.x++;
11874              break;
11875            }
11876            case XK_Prior:
11877            case XK_Down:
11878            case XK_KP_Down:
11879            {
11880              roi_info.y++;
11881              break;
11882            }
11883            case XK_F1:
11884            case XK_Help:
11885            {
11886              (void) XSetFunction(display,windows->image.highlight_context,
11887                GXcopy);
11888              XTextViewWidget(display,resource_info,windows,MagickFalse,
11889                "Help Viewer - Region of Interest",ImageROIHelp);
11890              (void) XSetFunction(display,windows->image.highlight_context,
11891                GXinvert);
11892              break;
11893            }
11894            default:
11895            {
11896              command_type=XImageWindowCommand(display,resource_info,windows,
11897                event.xkey.state,key_symbol,image,exception);
11898              if (command_type != NullCommand)
11899                state|=UpdateRegionState;
11900              break;
11901            }
11902          }
11903          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11904            event.xkey.time);
11905          break;
11906        }
11907        case KeyRelease:
11908          break;
11909        case MotionNotify:
11910        {
11911          if (event.xbutton.window != windows->image.id)
11912            break;
11913          /*
11914            Map and unmap Info widget as text cursor crosses its boundaries.
11915          */
11916          x=event.xmotion.x;
11917          y=event.xmotion.y;
11918          if( IfMagickTrue(windows->info.mapped) )
11919            {
11920              if ((x < (int) (windows->info.x+windows->info.width)) &&
11921                  (y < (int) (windows->info.y+windows->info.height)))
11922                (void) XWithdrawWindow(display,windows->info.id,
11923                  windows->info.screen);
11924            }
11925          else
11926            if ((x > (int) (windows->info.x+windows->info.width)) ||
11927                (y > (int) (windows->info.y+windows->info.height)))
11928              (void) XMapWindow(display,windows->info.id);
11929          roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11930          roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11931          break;
11932        }
11933        case SelectionRequest:
11934        {
11935          XSelectionEvent
11936            notify;
11937
11938          XSelectionRequestEvent
11939            *request;
11940
11941          /*
11942            Set primary selection.
11943          */
11944          (void) FormatLocaleString(text,MaxTextExtent,
11945            "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11946            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11947          request=(&(event.xselectionrequest));
11948          (void) XChangeProperty(request->display,request->requestor,
11949            request->property,request->target,8,PropModeReplace,
11950            (unsigned char *) text,(int) strlen(text));
11951          notify.type=SelectionNotify;
11952          notify.display=request->display;
11953          notify.requestor=request->requestor;
11954          notify.selection=request->selection;
11955          notify.target=request->target;
11956          notify.time=request->time;
11957          if (request->property == None)
11958            notify.property=request->target;
11959          else
11960            notify.property=request->property;
11961          (void) XSendEvent(request->display,request->requestor,False,0,
11962            (XEvent *) &notify);
11963        }
11964        default:
11965          break;
11966      }
11967      if ((state & UpdateConfigurationState) != 0)
11968        {
11969          (void) XPutBackEvent(display,&event);
11970          (void) XCheckDefineCursor(display,windows->image.id,cursor);
11971          break;
11972        }
11973    } while ((state & ExitState) == 0);
11974  } while ((state & ExitState) == 0);
11975  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11976  XSetCursorState(display,windows,MagickFalse);
11977  if ((state & EscapeState) != 0)
11978    return(MagickTrue);
11979  return(MagickTrue);
11980}
11981
11982/*
11983%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11984%                                                                             %
11985%                                                                             %
11986%                                                                             %
11987+   X R o t a t e I m a g e                                                   %
11988%                                                                             %
11989%                                                                             %
11990%                                                                             %
11991%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11992%
11993%  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
11994%  rotation angle is computed from the slope of a line drawn by the user.
11995%
11996%  The format of the XRotateImage method is:
11997%
11998%      MagickBooleanType XRotateImage(Display *display,
11999%        XResourceInfo *resource_info,XWindows *windows,double degrees,
12000%        Image **image,ExceptionInfo *exception)
12001%
12002%  A description of each parameter follows:
12003%
12004%    o display: Specifies a connection to an X server; returned from
12005%      XOpenDisplay.
12006%
12007%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12008%
12009%    o windows: Specifies a pointer to a XWindows structure.
12010%
12011%    o degrees: Specifies the number of degrees to rotate the image.
12012%
12013%    o image: the image.
12014%
12015%    o exception: return any errors or warnings in this structure.
12016%
12017*/
12018static MagickBooleanType XRotateImage(Display *display,
12019  XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
12020  ExceptionInfo *exception)
12021{
12022  static const char
12023    *RotateMenu[] =
12024    {
12025      "Pixel Color",
12026      "Direction",
12027      "Help",
12028      "Dismiss",
12029      (char *) NULL
12030    };
12031
12032  static ModeType
12033    direction = HorizontalRotateCommand;
12034
12035  static const ModeType
12036    DirectionCommands[] =
12037    {
12038      HorizontalRotateCommand,
12039      VerticalRotateCommand
12040    },
12041    RotateCommands[] =
12042    {
12043      RotateColorCommand,
12044      RotateDirectionCommand,
12045      RotateHelpCommand,
12046      RotateDismissCommand
12047    };
12048
12049  static unsigned int
12050    pen_id = 0;
12051
12052  char
12053    command[MaxTextExtent],
12054    text[MaxTextExtent];
12055
12056  Image
12057    *rotate_image;
12058
12059  int
12060    id,
12061    x,
12062    y;
12063
12064  double
12065    normalized_degrees;
12066
12067  register int
12068    i;
12069
12070  unsigned int
12071    height,
12072    rotations,
12073    width;
12074
12075  if (degrees == 0.0)
12076    {
12077      unsigned int
12078        distance;
12079
12080      size_t
12081        state;
12082
12083      XEvent
12084        event;
12085
12086      XSegment
12087        rotate_info;
12088
12089      /*
12090        Map Command widget.
12091      */
12092      (void) CloneString(&windows->command.name,"Rotate");
12093      windows->command.data=2;
12094      (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12095      (void) XMapRaised(display,windows->command.id);
12096      XClientMessage(display,windows->image.id,windows->im_protocols,
12097        windows->im_update_widget,CurrentTime);
12098      /*
12099        Wait for first button press.
12100      */
12101      (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12102      XQueryPosition(display,windows->image.id,&x,&y);
12103      rotate_info.x1=x;
12104      rotate_info.y1=y;
12105      rotate_info.x2=x;
12106      rotate_info.y2=y;
12107      state=DefaultState;
12108      do
12109      {
12110        XHighlightLine(display,windows->image.id,
12111          windows->image.highlight_context,&rotate_info);
12112        /*
12113          Wait for next event.
12114        */
12115        XScreenEvent(display,windows,&event,exception);
12116        XHighlightLine(display,windows->image.id,
12117          windows->image.highlight_context,&rotate_info);
12118        if (event.xany.window == windows->command.id)
12119          {
12120            /*
12121              Select a command from the Command widget.
12122            */
12123            id=XCommandWidget(display,windows,RotateMenu,&event);
12124            if (id < 0)
12125              continue;
12126            (void) XSetFunction(display,windows->image.highlight_context,
12127              GXcopy);
12128            switch (RotateCommands[id])
12129            {
12130              case RotateColorCommand:
12131              {
12132                const char
12133                  *ColorMenu[MaxNumberPens];
12134
12135                int
12136                  pen_number;
12137
12138                XColor
12139                  color;
12140
12141                /*
12142                  Initialize menu selections.
12143                */
12144                for (i=0; i < (int) (MaxNumberPens-2); i++)
12145                  ColorMenu[i]=resource_info->pen_colors[i];
12146                ColorMenu[MaxNumberPens-2]="Browser...";
12147                ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12148                /*
12149                  Select a pen color from the pop-up menu.
12150                */
12151                pen_number=XMenuWidget(display,windows,RotateMenu[id],
12152                  (const char **) ColorMenu,command);
12153                if (pen_number < 0)
12154                  break;
12155                if (pen_number == (MaxNumberPens-2))
12156                  {
12157                    static char
12158                      color_name[MaxTextExtent] = "gray";
12159
12160                    /*
12161                      Select a pen color from a dialog.
12162                    */
12163                    resource_info->pen_colors[pen_number]=color_name;
12164                    XColorBrowserWidget(display,windows,"Select",color_name);
12165                    if (*color_name == '\0')
12166                      break;
12167                  }
12168                /*
12169                  Set pen color.
12170                */
12171                (void) XParseColor(display,windows->map_info->colormap,
12172                  resource_info->pen_colors[pen_number],&color);
12173                XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12174                  (unsigned int) MaxColors,&color);
12175                windows->pixel_info->pen_colors[pen_number]=color;
12176                pen_id=(unsigned int) pen_number;
12177                break;
12178              }
12179              case RotateDirectionCommand:
12180              {
12181                static const char
12182                  *Directions[] =
12183                  {
12184                    "horizontal",
12185                    "vertical",
12186                    (char *) NULL,
12187                  };
12188
12189                /*
12190                  Select a command from the pop-up menu.
12191                */
12192                id=XMenuWidget(display,windows,RotateMenu[id],
12193                  Directions,command);
12194                if (id >= 0)
12195                  direction=DirectionCommands[id];
12196                break;
12197              }
12198              case RotateHelpCommand:
12199              {
12200                XTextViewWidget(display,resource_info,windows,MagickFalse,
12201                  "Help Viewer - Image Rotation",ImageRotateHelp);
12202                break;
12203              }
12204              case RotateDismissCommand:
12205              {
12206                /*
12207                  Prematurely exit.
12208                */
12209                state|=EscapeState;
12210                state|=ExitState;
12211                break;
12212              }
12213              default:
12214                break;
12215            }
12216            (void) XSetFunction(display,windows->image.highlight_context,
12217              GXinvert);
12218            continue;
12219          }
12220        switch (event.type)
12221        {
12222          case ButtonPress:
12223          {
12224            if (event.xbutton.button != Button1)
12225              break;
12226            if (event.xbutton.window != windows->image.id)
12227              break;
12228            /*
12229              exit loop.
12230            */
12231            (void) XSetFunction(display,windows->image.highlight_context,
12232              GXcopy);
12233            rotate_info.x1=event.xbutton.x;
12234            rotate_info.y1=event.xbutton.y;
12235            state|=ExitState;
12236            break;
12237          }
12238          case ButtonRelease:
12239            break;
12240          case Expose:
12241            break;
12242          case KeyPress:
12243          {
12244            char
12245              command[MaxTextExtent];
12246
12247            KeySym
12248              key_symbol;
12249
12250            if (event.xkey.window != windows->image.id)
12251              break;
12252            /*
12253              Respond to a user key press.
12254            */
12255            (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12256              sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12257            switch ((int) key_symbol)
12258            {
12259              case XK_Escape:
12260              case XK_F20:
12261              {
12262                /*
12263                  Prematurely exit.
12264                */
12265                state|=EscapeState;
12266                state|=ExitState;
12267                break;
12268              }
12269              case XK_F1:
12270              case XK_Help:
12271              {
12272                (void) XSetFunction(display,windows->image.highlight_context,
12273                  GXcopy);
12274                XTextViewWidget(display,resource_info,windows,MagickFalse,
12275                  "Help Viewer - Image Rotation",ImageRotateHelp);
12276                (void) XSetFunction(display,windows->image.highlight_context,
12277                  GXinvert);
12278                break;
12279              }
12280              default:
12281              {
12282                (void) XBell(display,0);
12283                break;
12284              }
12285            }
12286            break;
12287          }
12288          case MotionNotify:
12289          {
12290            rotate_info.x1=event.xmotion.x;
12291            rotate_info.y1=event.xmotion.y;
12292          }
12293        }
12294        rotate_info.x2=rotate_info.x1;
12295        rotate_info.y2=rotate_info.y1;
12296        if (direction == HorizontalRotateCommand)
12297          rotate_info.x2+=32;
12298        else
12299          rotate_info.y2-=32;
12300      } while ((state & ExitState) == 0);
12301      (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12302      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12303      if ((state & EscapeState) != 0)
12304        return(MagickTrue);
12305      /*
12306        Draw line as pointer moves until the mouse button is released.
12307      */
12308      distance=0;
12309      (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12310      state=DefaultState;
12311      do
12312      {
12313        if (distance > 9)
12314          {
12315            /*
12316              Display info and draw rotation line.
12317            */
12318            if( IfMagickFalse(windows->info.mapped) )
12319              (void) XMapWindow(display,windows->info.id);
12320            (void) FormatLocaleString(text,MaxTextExtent," %g",
12321              direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12322            XInfoWidget(display,windows,text);
12323            XHighlightLine(display,windows->image.id,
12324              windows->image.highlight_context,&rotate_info);
12325          }
12326        else
12327          if( IfMagickTrue(windows->info.mapped) )
12328            (void) XWithdrawWindow(display,windows->info.id,
12329              windows->info.screen);
12330        /*
12331          Wait for next event.
12332        */
12333        XScreenEvent(display,windows,&event,exception);
12334        if (distance > 9)
12335          XHighlightLine(display,windows->image.id,
12336            windows->image.highlight_context,&rotate_info);
12337        switch (event.type)
12338        {
12339          case ButtonPress:
12340            break;
12341          case ButtonRelease:
12342          {
12343            /*
12344              User has committed to rotation line.
12345            */
12346            rotate_info.x2=event.xbutton.x;
12347            rotate_info.y2=event.xbutton.y;
12348            state|=ExitState;
12349            break;
12350          }
12351          case Expose:
12352            break;
12353          case MotionNotify:
12354          {
12355            rotate_info.x2=event.xmotion.x;
12356            rotate_info.y2=event.xmotion.y;
12357          }
12358          default:
12359            break;
12360        }
12361        /*
12362          Check boundary conditions.
12363        */
12364        if (rotate_info.x2 < 0)
12365          rotate_info.x2=0;
12366        else
12367          if (rotate_info.x2 > (int) windows->image.width)
12368            rotate_info.x2=(short) windows->image.width;
12369        if (rotate_info.y2 < 0)
12370          rotate_info.y2=0;
12371        else
12372          if (rotate_info.y2 > (int) windows->image.height)
12373            rotate_info.y2=(short) windows->image.height;
12374        /*
12375          Compute rotation angle from the slope of the line.
12376        */
12377        degrees=0.0;
12378        distance=(unsigned int)
12379          ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12380          ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12381        if (distance > 9)
12382          degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12383            rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12384      } while ((state & ExitState) == 0);
12385      (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12386      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12387      if (distance <= 9)
12388        return(MagickTrue);
12389    }
12390  if (direction == VerticalRotateCommand)
12391    degrees-=90.0;
12392  if (degrees == 0.0)
12393    return(MagickTrue);
12394  /*
12395    Rotate image.
12396  */
12397  normalized_degrees=degrees;
12398  while (normalized_degrees < -45.0)
12399    normalized_degrees+=360.0;
12400  for (rotations=0; normalized_degrees > 45.0; rotations++)
12401    normalized_degrees-=90.0;
12402  if (normalized_degrees != 0.0)
12403    (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12404      exception);
12405  XSetCursorState(display,windows,MagickTrue);
12406  XCheckRefreshWindows(display,windows);
12407  (*image)->background_color.red=(double) ScaleShortToQuantum(
12408    windows->pixel_info->pen_colors[pen_id].red);
12409  (*image)->background_color.green=(double) ScaleShortToQuantum(
12410    windows->pixel_info->pen_colors[pen_id].green);
12411  (*image)->background_color.blue=(double) ScaleShortToQuantum(
12412    windows->pixel_info->pen_colors[pen_id].blue);
12413  rotate_image=RotateImage(*image,degrees,exception);
12414  XSetCursorState(display,windows,MagickFalse);
12415  if (rotate_image == (Image *) NULL)
12416    return(MagickFalse);
12417  *image=DestroyImage(*image);
12418  *image=rotate_image;
12419  if (windows->image.crop_geometry != (char *) NULL)
12420    {
12421      /*
12422        Rotate crop geometry.
12423      */
12424      width=(unsigned int) (*image)->columns;
12425      height=(unsigned int) (*image)->rows;
12426      (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12427      switch (rotations % 4)
12428      {
12429        default:
12430        case 0:
12431          break;
12432        case 1:
12433        {
12434          /*
12435            Rotate 90 degrees.
12436          */
12437          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12438            "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12439            (int) height-y,x);
12440          break;
12441        }
12442        case 2:
12443        {
12444          /*
12445            Rotate 180 degrees.
12446          */
12447          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12448            "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12449          break;
12450        }
12451        case 3:
12452        {
12453          /*
12454            Rotate 270 degrees.
12455          */
12456          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12457            "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12458          break;
12459        }
12460      }
12461    }
12462  if( IfMagickTrue(windows->image.orphan) )
12463    return(MagickTrue);
12464  if (normalized_degrees != 0.0)
12465    {
12466      /*
12467        Update image colormap.
12468      */
12469      windows->image.window_changes.width=(int) (*image)->columns;
12470      windows->image.window_changes.height=(int) (*image)->rows;
12471      if (windows->image.crop_geometry != (char *) NULL)
12472        {
12473          /*
12474            Obtain dimensions of image from crop geometry.
12475          */
12476          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12477            &width,&height);
12478          windows->image.window_changes.width=(int) width;
12479          windows->image.window_changes.height=(int) height;
12480        }
12481      XConfigureImageColormap(display,resource_info,windows,*image,exception);
12482    }
12483  else
12484    if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12485      {
12486        windows->image.window_changes.width=windows->image.ximage->height;
12487        windows->image.window_changes.height=windows->image.ximage->width;
12488      }
12489  /*
12490    Update image configuration.
12491  */
12492  (void) XConfigureImage(display,resource_info,windows,*image,exception);
12493  return(MagickTrue);
12494}
12495
12496/*
12497%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12498%                                                                             %
12499%                                                                             %
12500%                                                                             %
12501+   X S a v e I m a g e                                                       %
12502%                                                                             %
12503%                                                                             %
12504%                                                                             %
12505%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12506%
12507%  XSaveImage() saves an image to a file.
12508%
12509%  The format of the XSaveImage method is:
12510%
12511%      MagickBooleanType XSaveImage(Display *display,
12512%        XResourceInfo *resource_info,XWindows *windows,Image *image,
12513%        ExceptionInfo *exception)
12514%
12515%  A description of each parameter follows:
12516%
12517%    o display: Specifies a connection to an X server; returned from
12518%      XOpenDisplay.
12519%
12520%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12521%
12522%    o windows: Specifies a pointer to a XWindows structure.
12523%
12524%    o image: the image.
12525%
12526%    o exception: return any errors or warnings in this structure.
12527%
12528*/
12529static MagickBooleanType XSaveImage(Display *display,
12530  XResourceInfo *resource_info,XWindows *windows,Image *image,
12531  ExceptionInfo *exception)
12532{
12533  char
12534    filename[MaxTextExtent],
12535    geometry[MaxTextExtent];
12536
12537  Image
12538    *save_image;
12539
12540  ImageInfo
12541    *image_info;
12542
12543  MagickStatusType
12544    status;
12545
12546  /*
12547    Request file name from user.
12548  */
12549  if (resource_info->write_filename != (char *) NULL)
12550    (void) CopyMagickString(filename,resource_info->write_filename,
12551      MaxTextExtent);
12552  else
12553    {
12554      char
12555        path[MaxTextExtent];
12556
12557      int
12558        status;
12559
12560      GetPathComponent(image->filename,HeadPath,path);
12561      GetPathComponent(image->filename,TailPath,filename);
12562      if (*path != '\0')
12563        {
12564          status=chdir(path);
12565          if (status == -1)
12566            (void) ThrowMagickException(exception,GetMagickModule(),
12567              FileOpenError,"UnableToOpenFile","%s",path);
12568        }
12569    }
12570  XFileBrowserWidget(display,windows,"Save",filename);
12571  if (*filename == '\0')
12572    return(MagickTrue);
12573  if( IfMagickTrue(IsPathAccessible(filename)) )
12574    {
12575      int
12576        status;
12577
12578      /*
12579        File exists-- seek user's permission before overwriting.
12580      */
12581      status=XConfirmWidget(display,windows,"Overwrite",filename);
12582      if (status <= 0)
12583        return(MagickTrue);
12584    }
12585  image_info=CloneImageInfo(resource_info->image_info);
12586  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12587  (void) SetImageInfo(image_info,1,exception);
12588  if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12589      (LocaleCompare(image_info->magick,"JPG") == 0))
12590    {
12591      char
12592        quality[MaxTextExtent];
12593
12594      int
12595        status;
12596
12597      /*
12598        Request JPEG quality from user.
12599      */
12600      (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
12601        image->quality);
12602      status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12603        quality);
12604      if (*quality == '\0')
12605        return(MagickTrue);
12606      image->quality=StringToUnsignedLong(quality);
12607      image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12608    }
12609  if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12610      (LocaleCompare(image_info->magick,"PDF") == 0) ||
12611      (LocaleCompare(image_info->magick,"PS") == 0) ||
12612      (LocaleCompare(image_info->magick,"PS2") == 0))
12613    {
12614      char
12615        geometry[MaxTextExtent];
12616
12617      /*
12618        Request page geometry from user.
12619      */
12620      (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12621      if (LocaleCompare(image_info->magick,"PDF") == 0)
12622        (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12623      if (image_info->page != (char *) NULL)
12624        (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12625      XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12626        "Select page geometry:",geometry);
12627      if (*geometry != '\0')
12628        image_info->page=GetPageGeometry(geometry);
12629    }
12630  /*
12631    Apply image transforms.
12632  */
12633  XSetCursorState(display,windows,MagickTrue);
12634  XCheckRefreshWindows(display,windows);
12635  save_image=CloneImage(image,0,0,MagickTrue,exception);
12636  if (save_image == (Image *) NULL)
12637    return(MagickFalse);
12638  (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
12639    windows->image.ximage->width,windows->image.ximage->height);
12640  (void) TransformImage(&save_image,windows->image.crop_geometry,geometry,
12641    exception);
12642  /*
12643    Write image.
12644  */
12645  (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12646  status=WriteImage(image_info,save_image,exception);
12647  if( IfMagickTrue(status) )
12648    image->taint=MagickFalse;
12649  save_image=DestroyImage(save_image);
12650  image_info=DestroyImageInfo(image_info);
12651  XSetCursorState(display,windows,MagickFalse);
12652  return(IsMagickTrue(status));
12653}
12654
12655/*
12656%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12657%                                                                             %
12658%                                                                             %
12659%                                                                             %
12660+   X S c r e e n E v e n t                                                   %
12661%                                                                             %
12662%                                                                             %
12663%                                                                             %
12664%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12665%
12666%  XScreenEvent() handles global events associated with the Pan and Magnify
12667%  windows.
12668%
12669%  The format of the XScreenEvent function is:
12670%
12671%      void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12672%        ExceptionInfo *exception)
12673%
12674%  A description of each parameter follows:
12675%
12676%    o display: Specifies a pointer to the Display structure;  returned from
12677%      XOpenDisplay.
12678%
12679%    o windows: Specifies a pointer to a XWindows structure.
12680%
12681%    o event: Specifies a pointer to a X11 XEvent structure.
12682%
12683%    o exception: return any errors or warnings in this structure.
12684%
12685*/
12686
12687#if defined(__cplusplus) || defined(c_plusplus)
12688extern "C" {
12689#endif
12690
12691static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12692{
12693  register XWindows
12694    *windows;
12695
12696  windows=(XWindows *) data;
12697  if ((event->type == ClientMessage) &&
12698      (event->xclient.window == windows->image.id))
12699    return(MagickFalse);
12700  return(MagickTrue);
12701}
12702
12703#if defined(__cplusplus) || defined(c_plusplus)
12704}
12705#endif
12706
12707static void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12708  ExceptionInfo *exception)
12709{
12710  register int
12711    x,
12712    y;
12713
12714  (void) XIfEvent(display,event,XPredicate,(char *) windows);
12715  if (event->xany.window == windows->command.id)
12716    return;
12717  switch (event->type)
12718  {
12719    case ButtonPress:
12720    case ButtonRelease:
12721    {
12722      if ((event->xbutton.button == Button3) &&
12723          (event->xbutton.state & Mod1Mask))
12724        {
12725          /*
12726            Convert Alt-Button3 to Button2.
12727          */
12728          event->xbutton.button=Button2;
12729          event->xbutton.state&=(~Mod1Mask);
12730        }
12731      if (event->xbutton.window == windows->backdrop.id)
12732        {
12733          (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12734            event->xbutton.time);
12735          break;
12736        }
12737      if (event->xbutton.window == windows->pan.id)
12738        {
12739          XPanImage(display,windows,event,exception);
12740          break;
12741        }
12742      if (event->xbutton.window == windows->image.id)
12743        if (event->xbutton.button == Button2)
12744          {
12745            /*
12746              Update magnified image.
12747            */
12748            x=event->xbutton.x;
12749            y=event->xbutton.y;
12750            if (x < 0)
12751              x=0;
12752            else
12753              if (x >= (int) windows->image.width)
12754                x=(int) (windows->image.width-1);
12755            windows->magnify.x=(int) windows->image.x+x;
12756            if (y < 0)
12757              y=0;
12758            else
12759             if (y >= (int) windows->image.height)
12760               y=(int) (windows->image.height-1);
12761            windows->magnify.y=windows->image.y+y;
12762            if( IfMagickFalse(windows->magnify.mapped) )
12763              (void) XMapRaised(display,windows->magnify.id);
12764            XMakeMagnifyImage(display,windows,exception);
12765            if (event->type == ButtonRelease)
12766              (void) XWithdrawWindow(display,windows->info.id,
12767                windows->info.screen);
12768            break;
12769          }
12770      break;
12771    }
12772    case ClientMessage:
12773    {
12774      /*
12775        If client window delete message, exit.
12776      */
12777      if (event->xclient.message_type != windows->wm_protocols)
12778        break;
12779      if (*event->xclient.data.l != (long) windows->wm_delete_window)
12780        break;
12781      if (event->xclient.window == windows->magnify.id)
12782        {
12783          (void) XWithdrawWindow(display,windows->magnify.id,
12784            windows->magnify.screen);
12785          break;
12786        }
12787      break;
12788    }
12789    case ConfigureNotify:
12790    {
12791      if (event->xconfigure.window == windows->magnify.id)
12792        {
12793          unsigned int
12794            magnify;
12795
12796          /*
12797            Magnify window has a new configuration.
12798          */
12799          windows->magnify.width=(unsigned int) event->xconfigure.width;
12800          windows->magnify.height=(unsigned int) event->xconfigure.height;
12801          if( IfMagickFalse(windows->magnify.mapped) )
12802            break;
12803          magnify=1;
12804          while ((int) magnify <= event->xconfigure.width)
12805            magnify<<=1;
12806          while ((int) magnify <= event->xconfigure.height)
12807            magnify<<=1;
12808          magnify>>=1;
12809          if (((int) magnify != event->xconfigure.width) ||
12810              ((int) magnify != event->xconfigure.height))
12811            {
12812              XWindowChanges
12813                window_changes;
12814
12815              window_changes.width=(int) magnify;
12816              window_changes.height=(int) magnify;
12817              (void) XReconfigureWMWindow(display,windows->magnify.id,
12818                windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12819                &window_changes);
12820              break;
12821            }
12822          XMakeMagnifyImage(display,windows,exception);
12823          break;
12824        }
12825      break;
12826    }
12827    case Expose:
12828    {
12829      if (event->xexpose.window == windows->image.id)
12830        {
12831          XRefreshWindow(display,&windows->image,event);
12832          break;
12833        }
12834      if (event->xexpose.window == windows->pan.id)
12835        if (event->xexpose.count == 0)
12836          {
12837            XDrawPanRectangle(display,windows);
12838            break;
12839          }
12840      if (event->xexpose.window == windows->magnify.id)
12841        if (event->xexpose.count == 0)
12842          {
12843            XMakeMagnifyImage(display,windows,exception);
12844            break;
12845          }
12846      break;
12847    }
12848    case KeyPress:
12849    {
12850      char
12851        command[MaxTextExtent];
12852
12853      KeySym
12854        key_symbol;
12855
12856      if (event->xkey.window != windows->magnify.id)
12857        break;
12858      /*
12859        Respond to a user key press.
12860      */
12861      (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12862        sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12863      XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol,
12864        exception);
12865      break;
12866    }
12867    case MapNotify:
12868    {
12869      if (event->xmap.window == windows->magnify.id)
12870        {
12871          windows->magnify.mapped=MagickTrue;
12872          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12873          break;
12874        }
12875      if (event->xmap.window == windows->info.id)
12876        {
12877          windows->info.mapped=MagickTrue;
12878          break;
12879        }
12880      break;
12881    }
12882    case MotionNotify:
12883    {
12884      while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12885      if (event->xmotion.window == windows->image.id)
12886        if( IfMagickTrue(windows->magnify.mapped) )
12887          {
12888            /*
12889              Update magnified image.
12890            */
12891            x=event->xmotion.x;
12892            y=event->xmotion.y;
12893            if (x < 0)
12894              x=0;
12895            else
12896              if (x >= (int) windows->image.width)
12897                x=(int) (windows->image.width-1);
12898            windows->magnify.x=(int) windows->image.x+x;
12899            if (y < 0)
12900              y=0;
12901            else
12902             if (y >= (int) windows->image.height)
12903               y=(int) (windows->image.height-1);
12904            windows->magnify.y=windows->image.y+y;
12905            XMakeMagnifyImage(display,windows,exception);
12906          }
12907      break;
12908    }
12909    case UnmapNotify:
12910    {
12911      if (event->xunmap.window == windows->magnify.id)
12912        {
12913          windows->magnify.mapped=MagickFalse;
12914          break;
12915        }
12916      if (event->xunmap.window == windows->info.id)
12917        {
12918          windows->info.mapped=MagickFalse;
12919          break;
12920        }
12921      break;
12922    }
12923    default:
12924      break;
12925  }
12926}
12927
12928/*
12929%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12930%                                                                             %
12931%                                                                             %
12932%                                                                             %
12933+   X S e t C r o p G e o m e t r y                                           %
12934%                                                                             %
12935%                                                                             %
12936%                                                                             %
12937%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12938%
12939%  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12940%  and translates it to a cropping geometry relative to the image.
12941%
12942%  The format of the XSetCropGeometry method is:
12943%
12944%      void XSetCropGeometry(Display *display,XWindows *windows,
12945%        RectangleInfo *crop_info,Image *image)
12946%
12947%  A description of each parameter follows:
12948%
12949%    o display: Specifies a connection to an X server; returned from
12950%      XOpenDisplay.
12951%
12952%    o windows: Specifies a pointer to a XWindows structure.
12953%
12954%    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12955%      Image window to crop.
12956%
12957%    o image: the image.
12958%
12959*/
12960static void XSetCropGeometry(Display *display,XWindows *windows,
12961  RectangleInfo *crop_info,Image *image)
12962{
12963  char
12964    text[MaxTextExtent];
12965
12966  int
12967    x,
12968    y;
12969
12970  double
12971    scale_factor;
12972
12973  unsigned int
12974    height,
12975    width;
12976
12977  if( IfMagickTrue(windows->info.mapped) )
12978    {
12979      /*
12980        Display info on cropping rectangle.
12981      */
12982      (void) FormatLocaleString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
12983        (double) crop_info->width,(double) crop_info->height,(double)
12984        crop_info->x,(double) crop_info->y);
12985      XInfoWidget(display,windows,text);
12986    }
12987  /*
12988    Cropping geometry is relative to any previous crop geometry.
12989  */
12990  x=0;
12991  y=0;
12992  width=(unsigned int) image->columns;
12993  height=(unsigned int) image->rows;
12994  if (windows->image.crop_geometry != (char *) NULL)
12995    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12996  else
12997    windows->image.crop_geometry=AcquireString((char *) NULL);
12998  /*
12999    Define the crop geometry string from the cropping rectangle.
13000  */
13001  scale_factor=(double) width/windows->image.ximage->width;
13002  if (crop_info->x > 0)
13003    x+=(int) (scale_factor*crop_info->x+0.5);
13004  width=(unsigned int) (scale_factor*crop_info->width+0.5);
13005  if (width == 0)
13006    width=1;
13007  scale_factor=(double) height/windows->image.ximage->height;
13008  if (crop_info->y > 0)
13009    y+=(int) (scale_factor*crop_info->y+0.5);
13010  height=(unsigned int) (scale_factor*crop_info->height+0.5);
13011  if (height == 0)
13012    height=1;
13013  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
13014    "%ux%u%+d%+d",width,height,x,y);
13015}
13016
13017/*
13018%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13019%                                                                             %
13020%                                                                             %
13021%                                                                             %
13022+   X T i l e I m a g e                                                       %
13023%                                                                             %
13024%                                                                             %
13025%                                                                             %
13026%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13027%
13028%  XTileImage() loads or deletes a selected tile from a visual image directory.
13029%  The load or delete command is chosen from a menu.
13030%
13031%  The format of the XTileImage method is:
13032%
13033%      Image *XTileImage(Display *display,XResourceInfo *resource_info,
13034%        XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13035%
13036%  A description of each parameter follows:
13037%
13038%    o tile_image:  XTileImage reads or deletes the tile image
13039%      and returns it.  A null image is returned if an error occurs.
13040%
13041%    o display: Specifies a connection to an X server;  returned from
13042%      XOpenDisplay.
13043%
13044%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13045%
13046%    o windows: Specifies a pointer to a XWindows structure.
13047%
13048%    o image: the image; returned from ReadImage.
13049%
13050%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
13051%      the entire image is refreshed.
13052%
13053%    o exception: return any errors or warnings in this structure.
13054%
13055*/
13056static Image *XTileImage(Display *display,XResourceInfo *resource_info,
13057  XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13058{
13059  static const char
13060    *VerbMenu[] =
13061    {
13062      "Load",
13063      "Next",
13064      "Former",
13065      "Delete",
13066      "Update",
13067      (char *) NULL,
13068    };
13069
13070  static const ModeType
13071    TileCommands[] =
13072    {
13073      TileLoadCommand,
13074      TileNextCommand,
13075      TileFormerCommand,
13076      TileDeleteCommand,
13077      TileUpdateCommand
13078    };
13079
13080  char
13081    command[MaxTextExtent],
13082    filename[MaxTextExtent];
13083
13084  Image
13085    *tile_image;
13086
13087  int
13088    id,
13089    status,
13090    tile,
13091    x,
13092    y;
13093
13094  double
13095    scale_factor;
13096
13097  register char
13098    *p,
13099    *q;
13100
13101  register int
13102    i;
13103
13104  unsigned int
13105    height,
13106    width;
13107
13108  /*
13109    Tile image is relative to montage image configuration.
13110  */
13111  x=0;
13112  y=0;
13113  width=(unsigned int) image->columns;
13114  height=(unsigned int) image->rows;
13115  if (windows->image.crop_geometry != (char *) NULL)
13116    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13117  scale_factor=(double) width/windows->image.ximage->width;
13118  event->xbutton.x+=windows->image.x;
13119  event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13120  scale_factor=(double) height/windows->image.ximage->height;
13121  event->xbutton.y+=windows->image.y;
13122  event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13123  /*
13124    Determine size and location of each tile in the visual image directory.
13125  */
13126  width=(unsigned int) image->columns;
13127  height=(unsigned int) image->rows;
13128  x=0;
13129  y=0;
13130  (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13131  tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13132    (event->xbutton.x-x)/width;
13133  if (tile < 0)
13134    {
13135      /*
13136        Button press is outside any tile.
13137      */
13138      (void) XBell(display,0);
13139      return((Image *) NULL);
13140    }
13141  /*
13142    Determine file name from the tile directory.
13143  */
13144  p=image->directory;
13145  for (i=tile; (i != 0) && (*p != '\0'); )
13146  {
13147    if (*p == '\n')
13148      i--;
13149    p++;
13150  }
13151  if (*p == '\0')
13152    {
13153      /*
13154        Button press is outside any tile.
13155      */
13156      (void) XBell(display,0);
13157      return((Image *) NULL);
13158    }
13159  /*
13160    Select a command from the pop-up menu.
13161  */
13162  id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13163  if (id < 0)
13164    return((Image *) NULL);
13165  q=p;
13166  while ((*q != '\n') && (*q != '\0'))
13167    q++;
13168  (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13169  /*
13170    Perform command for the selected tile.
13171  */
13172  XSetCursorState(display,windows,MagickTrue);
13173  XCheckRefreshWindows(display,windows);
13174  tile_image=NewImageList();
13175  switch (TileCommands[id])
13176  {
13177    case TileLoadCommand:
13178    {
13179      /*
13180        Load tile image.
13181      */
13182      XCheckRefreshWindows(display,windows);
13183      (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13184        MaxTextExtent);
13185      (void) CopyMagickString(resource_info->image_info->filename,filename,
13186        MaxTextExtent);
13187      tile_image=ReadImage(resource_info->image_info,exception);
13188      CatchException(exception);
13189      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13190      break;
13191    }
13192    case TileNextCommand:
13193    {
13194      /*
13195        Display next image.
13196      */
13197      XClientMessage(display,windows->image.id,windows->im_protocols,
13198        windows->im_next_image,CurrentTime);
13199      break;
13200    }
13201    case TileFormerCommand:
13202    {
13203      /*
13204        Display former image.
13205      */
13206      XClientMessage(display,windows->image.id,windows->im_protocols,
13207        windows->im_former_image,CurrentTime);
13208      break;
13209    }
13210    case TileDeleteCommand:
13211    {
13212      /*
13213        Delete tile image.
13214      */
13215      if( IfMagickFalse(IsPathAccessible(filename)) )
13216        {
13217          XNoticeWidget(display,windows,"Image file does not exist:",filename);
13218          break;
13219        }
13220      status=XConfirmWidget(display,windows,"Really delete tile",filename);
13221      if (status <= 0)
13222        break;
13223      status=IsMagickTrue(remove_utf8(filename));
13224      if( IfMagickTrue(status) )
13225        {
13226          XNoticeWidget(display,windows,"Unable to delete image file:",
13227            filename);
13228          break;
13229        }
13230    }
13231    case TileUpdateCommand:
13232    {
13233      int
13234        x_offset,
13235        y_offset;
13236
13237      PixelInfo
13238        pixel;
13239
13240      register int
13241        j;
13242
13243      register Quantum
13244        *s;
13245
13246      /*
13247        Ensure all the images exist.
13248      */
13249      tile=0;
13250      GetPixelInfo(image,&pixel);
13251      for (p=image->directory; *p != '\0'; p++)
13252      {
13253        CacheView
13254          *image_view;
13255
13256        q=p;
13257        while ((*q != '\n') && (*q != '\0'))
13258          q++;
13259        (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13260        p=q;
13261        if( IfMagickTrue(IsPathAccessible(filename)) )
13262          {
13263            tile++;
13264            continue;
13265          }
13266        /*
13267          Overwrite tile with background color.
13268        */
13269        x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13270        y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13271        image_view=AcquireAuthenticCacheView(image,exception);
13272        (void) GetOneCacheViewVirtualPixelInfo(image_view,0,0,&pixel,exception);
13273        for (i=0; i < (int) height; i++)
13274        {
13275          s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13276            y_offset+i,width,1,exception);
13277          if (s == (Quantum *) NULL)
13278            break;
13279          for (j=0; j < (int) width; j++)
13280          {
13281            SetPixelInfoPixel(image,&pixel,s);
13282            s+=GetPixelChannels(image);
13283          }
13284          if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
13285            break;
13286        }
13287        image_view=DestroyCacheView(image_view);
13288        tile++;
13289      }
13290      windows->image.window_changes.width=(int) image->columns;
13291      windows->image.window_changes.height=(int) image->rows;
13292      XConfigureImageColormap(display,resource_info,windows,image,exception);
13293      (void) XConfigureImage(display,resource_info,windows,image,exception);
13294      break;
13295    }
13296    default:
13297      break;
13298  }
13299  XSetCursorState(display,windows,MagickFalse);
13300  return(tile_image);
13301}
13302
13303/*
13304%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13305%                                                                             %
13306%                                                                             %
13307%                                                                             %
13308+   X T r a n s l a t e I m a g e                                             %
13309%                                                                             %
13310%                                                                             %
13311%                                                                             %
13312%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13313%
13314%  XTranslateImage() translates the image within an Image window by one pixel
13315%  as specified by the key symbol.  If the image has a montage string the
13316%  translation is respect to the width and height contained within the string.
13317%
13318%  The format of the XTranslateImage method is:
13319%
13320%      void XTranslateImage(Display *display,XWindows *windows,
13321%        Image *image,const KeySym key_symbol)
13322%
13323%  A description of each parameter follows:
13324%
13325%    o display: Specifies a connection to an X server; returned from
13326%      XOpenDisplay.
13327%
13328%    o windows: Specifies a pointer to a XWindows structure.
13329%
13330%    o image: the image.
13331%
13332%    o key_symbol: Specifies a KeySym which indicates which side of the image
13333%      to trim.
13334%
13335*/
13336static void XTranslateImage(Display *display,XWindows *windows,
13337  Image *image,const KeySym key_symbol)
13338{
13339  char
13340    text[MaxTextExtent];
13341
13342  int
13343    x,
13344    y;
13345
13346  unsigned int
13347    x_offset,
13348    y_offset;
13349
13350  /*
13351    User specified a pan position offset.
13352  */
13353  x_offset=windows->image.width;
13354  y_offset=windows->image.height;
13355  if (image->montage != (char *) NULL)
13356    (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13357  switch ((int) key_symbol)
13358  {
13359    case XK_Home:
13360    case XK_KP_Home:
13361    {
13362      windows->image.x=(int) windows->image.width/2;
13363      windows->image.y=(int) windows->image.height/2;
13364      break;
13365    }
13366    case XK_Left:
13367    case XK_KP_Left:
13368    {
13369      windows->image.x-=x_offset;
13370      break;
13371    }
13372    case XK_Next:
13373    case XK_Up:
13374    case XK_KP_Up:
13375    {
13376      windows->image.y-=y_offset;
13377      break;
13378    }
13379    case XK_Right:
13380    case XK_KP_Right:
13381    {
13382      windows->image.x+=x_offset;
13383      break;
13384    }
13385    case XK_Prior:
13386    case XK_Down:
13387    case XK_KP_Down:
13388    {
13389      windows->image.y+=y_offset;
13390      break;
13391    }
13392    default:
13393      return;
13394  }
13395  /*
13396    Check boundary conditions.
13397  */
13398  if (windows->image.x < 0)
13399    windows->image.x=0;
13400  else
13401    if ((int) (windows->image.x+windows->image.width) >
13402        windows->image.ximage->width)
13403      windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13404  if (windows->image.y < 0)
13405    windows->image.y=0;
13406  else
13407    if ((int) (windows->image.y+windows->image.height) >
13408        windows->image.ximage->height)
13409      windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13410  /*
13411    Refresh Image window.
13412  */
13413  (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
13414    windows->image.width,windows->image.height,windows->image.x,
13415    windows->image.y);
13416  XInfoWidget(display,windows,text);
13417  XCheckRefreshWindows(display,windows);
13418  XDrawPanRectangle(display,windows);
13419  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13420  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13421}
13422
13423/*
13424%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13425%                                                                             %
13426%                                                                             %
13427%                                                                             %
13428+   X T r i m I m a g e                                                       %
13429%                                                                             %
13430%                                                                             %
13431%                                                                             %
13432%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13433%
13434%  XTrimImage() trims the edges from the Image window.
13435%
13436%  The format of the XTrimImage method is:
13437%
13438%      MagickBooleanType XTrimImage(Display *display,
13439%        XResourceInfo *resource_info,XWindows *windows,Image *image,
13440%        ExceptionInfo *exception)
13441%
13442%  A description of each parameter follows:
13443%
13444%    o display: Specifies a connection to an X server; returned from
13445%      XOpenDisplay.
13446%
13447%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13448%
13449%    o windows: Specifies a pointer to a XWindows structure.
13450%
13451%    o image: the image.
13452%
13453%    o exception: return any errors or warnings in this structure.
13454%
13455*/
13456static MagickBooleanType XTrimImage(Display *display,
13457  XResourceInfo *resource_info,XWindows *windows,Image *image,
13458  ExceptionInfo *exception)
13459{
13460  RectangleInfo
13461    trim_info;
13462
13463  register int
13464    x,
13465    y;
13466
13467  size_t
13468    background,
13469    pixel;
13470
13471  /*
13472    Trim edges from image.
13473  */
13474  XSetCursorState(display,windows,MagickTrue);
13475  XCheckRefreshWindows(display,windows);
13476  /*
13477    Crop the left edge.
13478  */
13479  background=XGetPixel(windows->image.ximage,0,0);
13480  trim_info.width=(size_t) windows->image.ximage->width;
13481  for (x=0; x < windows->image.ximage->width; x++)
13482  {
13483    for (y=0; y < windows->image.ximage->height; y++)
13484    {
13485      pixel=XGetPixel(windows->image.ximage,x,y);
13486      if (pixel != background)
13487        break;
13488    }
13489    if (y < windows->image.ximage->height)
13490      break;
13491  }
13492  trim_info.x=(ssize_t) x;
13493  if (trim_info.x == (ssize_t) windows->image.ximage->width)
13494    {
13495      XSetCursorState(display,windows,MagickFalse);
13496      return(MagickFalse);
13497    }
13498  /*
13499    Crop the right edge.
13500  */
13501  background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13502  for (x=windows->image.ximage->width-1; x != 0; x--)
13503  {
13504    for (y=0; y < windows->image.ximage->height; y++)
13505    {
13506      pixel=XGetPixel(windows->image.ximage,x,y);
13507      if (pixel != background)
13508        break;
13509    }
13510    if (y < windows->image.ximage->height)
13511      break;
13512  }
13513  trim_info.width=(size_t) (x-trim_info.x+1);
13514  /*
13515    Crop the top edge.
13516  */
13517  background=XGetPixel(windows->image.ximage,0,0);
13518  trim_info.height=(size_t) windows->image.ximage->height;
13519  for (y=0; y < windows->image.ximage->height; y++)
13520  {
13521    for (x=0; x < windows->image.ximage->width; x++)
13522    {
13523      pixel=XGetPixel(windows->image.ximage,x,y);
13524      if (pixel != background)
13525        break;
13526    }
13527    if (x < windows->image.ximage->width)
13528      break;
13529  }
13530  trim_info.y=(ssize_t) y;
13531  /*
13532    Crop the bottom edge.
13533  */
13534  background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13535  for (y=windows->image.ximage->height-1; y != 0; y--)
13536  {
13537    for (x=0; x < windows->image.ximage->width; x++)
13538    {
13539      pixel=XGetPixel(windows->image.ximage,x,y);
13540      if (pixel != background)
13541        break;
13542    }
13543    if (x < windows->image.ximage->width)
13544      break;
13545  }
13546  trim_info.height=(size_t) y-trim_info.y+1;
13547  if (((unsigned int) trim_info.width != windows->image.width) ||
13548      ((unsigned int) trim_info.height != windows->image.height))
13549    {
13550      /*
13551        Reconfigure Image window as defined by the trimming rectangle.
13552      */
13553      XSetCropGeometry(display,windows,&trim_info,image);
13554      windows->image.window_changes.width=(int) trim_info.width;
13555      windows->image.window_changes.height=(int) trim_info.height;
13556      (void) XConfigureImage(display,resource_info,windows,image,exception);
13557    }
13558  XSetCursorState(display,windows,MagickFalse);
13559  return(MagickTrue);
13560}
13561
13562/*
13563%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13564%                                                                             %
13565%                                                                             %
13566%                                                                             %
13567+   X V i s u a l D i r e c t o r y I m a g e                                 %
13568%                                                                             %
13569%                                                                             %
13570%                                                                             %
13571%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13572%
13573%  XVisualDirectoryImage() creates a Visual Image Directory.
13574%
13575%  The format of the XVisualDirectoryImage method is:
13576%
13577%      Image *XVisualDirectoryImage(Display *display,
13578%        XResourceInfo *resource_info,XWindows *windows,
13579%        ExceptionInfo *exception)
13580%
13581%  A description of each parameter follows:
13582%
13583%    o display: Specifies a connection to an X server; returned from
13584%      XOpenDisplay.
13585%
13586%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13587%
13588%    o windows: Specifies a pointer to a XWindows structure.
13589%
13590%    o exception: return any errors or warnings in this structure.
13591%
13592*/
13593static Image *XVisualDirectoryImage(Display *display,
13594  XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
13595{
13596#define TileImageTag  "Scale/Image"
13597#define XClientName  "montage"
13598
13599  char
13600    **filelist;
13601
13602  Image
13603    *images,
13604    *montage_image,
13605    *next_image,
13606    *thumbnail_image;
13607
13608  ImageInfo
13609    *read_info;
13610
13611  int
13612    number_files;
13613
13614  MagickBooleanType
13615    backdrop;
13616
13617  MagickStatusType
13618    status;
13619
13620  MontageInfo
13621    *montage_info;
13622
13623  RectangleInfo
13624    geometry;
13625
13626  register int
13627    i;
13628
13629  static char
13630    filename[MaxTextExtent] = "\0",
13631    filenames[MaxTextExtent] = "*";
13632
13633  XResourceInfo
13634    background_resources;
13635
13636  /*
13637    Request file name from user.
13638  */
13639  XFileBrowserWidget(display,windows,"Directory",filenames);
13640  if (*filenames == '\0')
13641    return((Image *) NULL);
13642  /*
13643    Expand the filenames.
13644  */
13645  filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13646  if (filelist == (char **) NULL)
13647    {
13648      ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13649        filenames);
13650      return((Image *) NULL);
13651    }
13652  number_files=1;
13653  filelist[0]=filenames;
13654  status=ExpandFilenames(&number_files,&filelist);
13655  if( IfMagickFalse(status) || (number_files == 0))
13656    {
13657      if (number_files == 0)
13658        ThrowXWindowFatalException(ImageError,"NoImagesWereFound",filenames)
13659      else
13660        ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13661          filenames);
13662      return((Image *) NULL);
13663    }
13664  /*
13665    Set image background resources.
13666  */
13667  background_resources=(*resource_info);
13668  background_resources.window_id=AcquireString("");
13669  (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
13670    "0x%lx",windows->image.id);
13671  background_resources.backdrop=MagickTrue;
13672  /*
13673    Read each image and convert them to a tile.
13674  */
13675  backdrop=IsMagickTrue( (windows->visual_info->klass == TrueColor) ||
13676    (windows->visual_info->klass == DirectColor) );
13677  read_info=CloneImageInfo(resource_info->image_info);
13678  (void) SetImageOption(read_info,"jpeg:size","120x120");
13679  (void) CloneString(&read_info->size,DefaultTileGeometry);
13680  (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13681    (void *) NULL);
13682  images=NewImageList();
13683  XSetCursorState(display,windows,MagickTrue);
13684  XCheckRefreshWindows(display,windows);
13685  for (i=0; i < (int) number_files; i++)
13686  {
13687    (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13688    filelist[i]=DestroyString(filelist[i]);
13689    *read_info->magick='\0';
13690    next_image=ReadImage(read_info,exception);
13691    CatchException(exception);
13692    if (next_image != (Image *) NULL)
13693      {
13694        (void) DeleteImageProperty(next_image,"label");
13695        (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13696          read_info,next_image,DefaultTileLabel,exception),exception);
13697        (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13698          exception);
13699        thumbnail_image=ThumbnailImage(next_image,geometry.width,
13700          geometry.height,exception);
13701        if (thumbnail_image != (Image *) NULL)
13702          {
13703            next_image=DestroyImage(next_image);
13704            next_image=thumbnail_image;
13705          }
13706        if (backdrop)
13707          {
13708            (void) XDisplayBackgroundImage(display,&background_resources,
13709              next_image,exception);
13710            XSetCursorState(display,windows,MagickTrue);
13711          }
13712        AppendImageToList(&images,next_image);
13713        if (images->progress_monitor != (MagickProgressMonitor) NULL)
13714          {
13715            MagickBooleanType
13716              proceed;
13717
13718            proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13719              (MagickSizeType) number_files);
13720            if( IfMagickFalse(proceed) )
13721              break;
13722          }
13723      }
13724  }
13725  filelist=(char **) RelinquishMagickMemory(filelist);
13726  if (images == (Image *) NULL)
13727    {
13728      read_info=DestroyImageInfo(read_info);
13729      XSetCursorState(display,windows,MagickFalse);
13730      ThrowXWindowFatalException(ImageError,"NoImagesWereLoaded",filenames);
13731      return((Image *) NULL);
13732    }
13733  /*
13734    Create the Visual Image Directory.
13735  */
13736  montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13737  montage_info->pointsize=10;
13738  if (resource_info->font != (char *) NULL)
13739    (void) CloneString(&montage_info->font,resource_info->font);
13740  (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13741  montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13742    images),exception);
13743  images=DestroyImageList(images);
13744  montage_info=DestroyMontageInfo(montage_info);
13745  read_info=DestroyImageInfo(read_info);
13746  XSetCursorState(display,windows,MagickFalse);
13747  if (montage_image == (Image *) NULL)
13748    return(montage_image);
13749  XClientMessage(display,windows->image.id,windows->im_protocols,
13750    windows->im_next_image,CurrentTime);
13751  return(montage_image);
13752}
13753
13754/*
13755%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13756%                                                                             %
13757%                                                                             %
13758%                                                                             %
13759%   X D i s p l a y B a c k g r o u n d I m a g e                             %
13760%                                                                             %
13761%                                                                             %
13762%                                                                             %
13763%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13764%
13765%  XDisplayBackgroundImage() displays an image in the background of a window.
13766%
13767%  The format of the XDisplayBackgroundImage method is:
13768%
13769%      MagickBooleanType XDisplayBackgroundImage(Display *display,
13770%        XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13771%
13772%  A description of each parameter follows:
13773%
13774%    o display: Specifies a connection to an X server;  returned from
13775%      XOpenDisplay.
13776%
13777%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13778%
13779%    o image: the image.
13780%
13781%    o exception: return any errors or warnings in this structure.
13782%
13783*/
13784MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13785  XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13786{
13787  char
13788    geometry[MaxTextExtent],
13789    visual_type[MaxTextExtent];
13790
13791  int
13792    height,
13793    status,
13794    width;
13795
13796  RectangleInfo
13797    geometry_info;
13798
13799  static XPixelInfo
13800    pixel;
13801
13802  static XStandardColormap
13803    *map_info;
13804
13805  static XVisualInfo
13806    *visual_info = (XVisualInfo *) NULL;
13807
13808  static XWindowInfo
13809    window_info;
13810
13811  size_t
13812    delay;
13813
13814  Window
13815    root_window;
13816
13817  XGCValues
13818    context_values;
13819
13820  XResourceInfo
13821    resources;
13822
13823  XWindowAttributes
13824    window_attributes;
13825
13826  /*
13827    Determine target window.
13828  */
13829  assert(image != (Image *) NULL);
13830  assert(image->signature == MagickSignature);
13831  if( IfMagickTrue(image->debug) )
13832    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13833  resources=(*resource_info);
13834  window_info.id=(Window) NULL;
13835  root_window=XRootWindow(display,XDefaultScreen(display));
13836  if (LocaleCompare(resources.window_id,"root") == 0)
13837    window_info.id=root_window;
13838  else
13839    {
13840      if (isdigit((unsigned char) *resources.window_id) != 0)
13841        window_info.id=XWindowByID(display,root_window,
13842          (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13843      if (window_info.id == (Window) NULL)
13844        window_info.id=XWindowByName(display,root_window,resources.window_id);
13845    }
13846  if (window_info.id == (Window) NULL)
13847    {
13848      ThrowXWindowFatalException(XServerError,"NoWindowWithSpecifiedIDExists",
13849        resources.window_id);
13850      return(MagickFalse);
13851    }
13852  /*
13853    Determine window visual id.
13854  */
13855  window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13856  window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13857  (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13858  status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13859  if (status != 0)
13860    (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
13861      XVisualIDFromVisual(window_attributes.visual));
13862  if (visual_info == (XVisualInfo *) NULL)
13863    {
13864      /*
13865        Allocate standard colormap.
13866      */
13867      map_info=XAllocStandardColormap();
13868      if (map_info == (XStandardColormap *) NULL)
13869        ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13870          image->filename);
13871      map_info->colormap=(Colormap) NULL;
13872      pixel.pixels=(unsigned long *) NULL;
13873      /*
13874        Initialize visual info.
13875      */
13876      resources.map_type=(char *) NULL;
13877      resources.visual_type=visual_type;
13878      visual_info=XBestVisualInfo(display,map_info,&resources);
13879      if (visual_info == (XVisualInfo *) NULL)
13880        ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13881          resources.visual_type);
13882      /*
13883        Initialize window info.
13884      */
13885      window_info.ximage=(XImage *) NULL;
13886      window_info.matte_image=(XImage *) NULL;
13887      window_info.pixmap=(Pixmap) NULL;
13888      window_info.matte_pixmap=(Pixmap) NULL;
13889    }
13890  /*
13891    Free previous root colors.
13892  */
13893  if (window_info.id == root_window)
13894    (void) XDestroyWindowColors(display,root_window);
13895  /*
13896    Initialize Standard Colormap.
13897  */
13898  resources.colormap=SharedColormap;
13899  XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel,
13900    exception);
13901  /*
13902    Graphic context superclass.
13903  */
13904  context_values.background=pixel.background_color.pixel;
13905  context_values.foreground=pixel.foreground_color.pixel;
13906  pixel.annotate_context=XCreateGC(display,window_info.id,
13907    (size_t) (GCBackground | GCForeground),&context_values);
13908  if (pixel.annotate_context == (GC) NULL)
13909    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13910      image->filename);
13911  /*
13912    Initialize Image window attributes.
13913  */
13914  window_info.name=AcquireString("\0");
13915  window_info.icon_name=AcquireString("\0");
13916  XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13917    &resources,&window_info);
13918  /*
13919    Create the X image.
13920  */
13921  window_info.width=(unsigned int) image->columns;
13922  window_info.height=(unsigned int) image->rows;
13923  if ((image->columns != window_info.width) ||
13924      (image->rows != window_info.height))
13925    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13926      image->filename);
13927  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
13928    window_attributes.width,window_attributes.height);
13929  geometry_info.width=window_info.width;
13930  geometry_info.height=window_info.height;
13931  geometry_info.x=(ssize_t) window_info.x;
13932  geometry_info.y=(ssize_t) window_info.y;
13933  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13934    &geometry_info.width,&geometry_info.height);
13935  window_info.width=(unsigned int) geometry_info.width;
13936  window_info.height=(unsigned int) geometry_info.height;
13937  window_info.x=(int) geometry_info.x;
13938  window_info.y=(int) geometry_info.y;
13939  status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13940    window_info.height,exception);
13941  if( IfMagickFalse(status) )
13942    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13943      image->filename);
13944  window_info.x=0;
13945  window_info.y=0;
13946  if( IfMagickTrue(image->debug) )
13947    {
13948      (void) LogMagickEvent(X11Event,GetMagickModule(),
13949        "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13950        (double) image->columns,(double) image->rows);
13951      if (image->colors != 0)
13952        (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13953          image->colors);
13954      (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13955    }
13956  /*
13957    Adjust image dimensions as specified by backdrop or geometry options.
13958  */
13959  width=(int) window_info.width;
13960  height=(int) window_info.height;
13961  if( IfMagickTrue(resources.backdrop) )
13962    {
13963      /*
13964        Center image on window.
13965      */
13966      window_info.x=(window_attributes.width/2)-
13967        (window_info.ximage->width/2);
13968      window_info.y=(window_attributes.height/2)-
13969        (window_info.ximage->height/2);
13970      width=window_attributes.width;
13971      height=window_attributes.height;
13972    }
13973  if ((resources.image_geometry != (char *) NULL) &&
13974      (*resources.image_geometry != '\0'))
13975    {
13976      char
13977        default_geometry[MaxTextExtent];
13978
13979      int
13980        flags,
13981        gravity;
13982
13983      XSizeHints
13984        *size_hints;
13985
13986      /*
13987        User specified geometry.
13988      */
13989      size_hints=XAllocSizeHints();
13990      if (size_hints == (XSizeHints *) NULL)
13991        ThrowXWindowFatalException(ResourceLimitFatalError,
13992          "MemoryAllocationFailed",image->filename);
13993      size_hints->flags=0L;
13994      (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
13995        width,height);
13996      flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
13997        default_geometry,window_info.border_width,size_hints,&window_info.x,
13998        &window_info.y,&width,&height,&gravity);
13999      if (flags & (XValue | YValue))
14000        {
14001          width=window_attributes.width;
14002          height=window_attributes.height;
14003        }
14004      (void) XFree((void *) size_hints);
14005    }
14006  /*
14007    Create the X pixmap.
14008  */
14009  window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
14010    (unsigned int) height,window_info.depth);
14011  if (window_info.pixmap == (Pixmap) NULL)
14012    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
14013      image->filename);
14014  /*
14015    Display pixmap on the window.
14016  */
14017  if (((unsigned int) width > window_info.width) ||
14018      ((unsigned int) height > window_info.height))
14019    (void) XFillRectangle(display,window_info.pixmap,
14020      window_info.annotate_context,0,0,(unsigned int) width,
14021      (unsigned int) height);
14022  (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
14023    window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
14024    window_info.width,(unsigned int) window_info.height);
14025  (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
14026  (void) XClearWindow(display,window_info.id);
14027  delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
14028  XDelay(display,delay == 0UL ? 10UL : delay);
14029  (void) XSync(display,MagickFalse);
14030  return(IsMagickTrue(window_info.id == root_window));
14031}
14032
14033/*
14034%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14035%                                                                             %
14036%                                                                             %
14037%                                                                             %
14038+   X D i s p l a y I m a g e                                                 %
14039%                                                                             %
14040%                                                                             %
14041%                                                                             %
14042%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14043%
14044%  XDisplayImage() displays an image via X11.  A new image is created and
14045%  returned if the user interactively transforms the displayed image.
14046%
14047%  The format of the XDisplayImage method is:
14048%
14049%      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14050%        char **argv,int argc,Image **image,size_t *state,
14051%        ExceptionInfo *exception)
14052%
14053%  A description of each parameter follows:
14054%
14055%    o nexus:  Method XDisplayImage returns an image when the
14056%      user chooses 'Open Image' from the command menu or picks a tile
14057%      from the image directory.  Otherwise a null image is returned.
14058%
14059%    o display: Specifies a connection to an X server;  returned from
14060%      XOpenDisplay.
14061%
14062%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14063%
14064%    o argv: Specifies the application's argument list.
14065%
14066%    o argc: Specifies the number of arguments.
14067%
14068%    o image: Specifies an address to an address of an Image structure;
14069%
14070%    o exception: return any errors or warnings in this structure.
14071%
14072*/
14073MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14074  char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
14075{
14076#define MagnifySize  256  /* must be a power of 2 */
14077#define MagickMenus  10
14078#define MagickTitle  "Commands"
14079
14080  static const char
14081    *CommandMenu[] =
14082    {
14083      "File",
14084      "Edit",
14085      "View",
14086      "Transform",
14087      "Enhance",
14088      "Effects",
14089      "F/X",
14090      "Image Edit",
14091      "Miscellany",
14092      "Help",
14093      (char *) NULL
14094    },
14095    *FileMenu[] =
14096    {
14097      "Open...",
14098      "Next",
14099      "Former",
14100      "Select...",
14101      "Save...",
14102      "Print...",
14103      "Delete...",
14104      "New...",
14105      "Visual Directory...",
14106      "Quit",
14107      (char *) NULL
14108    },
14109    *EditMenu[] =
14110    {
14111      "Undo",
14112      "Redo",
14113      "Cut",
14114      "Copy",
14115      "Paste",
14116      (char *) NULL
14117    },
14118    *ViewMenu[] =
14119    {
14120      "Half Size",
14121      "Original Size",
14122      "Double Size",
14123      "Resize...",
14124      "Apply",
14125      "Refresh",
14126      "Restore",
14127      (char *) NULL
14128    },
14129    *TransformMenu[] =
14130    {
14131      "Crop",
14132      "Chop",
14133      "Flop",
14134      "Flip",
14135      "Rotate Right",
14136      "Rotate Left",
14137      "Rotate...",
14138      "Shear...",
14139      "Roll...",
14140      "Trim Edges",
14141      (char *) NULL
14142    },
14143    *EnhanceMenu[] =
14144    {
14145      "Hue...",
14146      "Saturation...",
14147      "Brightness...",
14148      "Gamma...",
14149      "Spiff",
14150      "Dull",
14151      "Contrast Stretch...",
14152      "Sigmoidal Contrast...",
14153      "Normalize",
14154      "Equalize",
14155      "Negate",
14156      "Grayscale",
14157      "Map...",
14158      "Quantize...",
14159      (char *) NULL
14160    },
14161    *EffectsMenu[] =
14162    {
14163      "Despeckle",
14164      "Emboss",
14165      "Reduce Noise",
14166      "Add Noise...",
14167      "Sharpen...",
14168      "Blur...",
14169      "Threshold...",
14170      "Edge Detect...",
14171      "Spread...",
14172      "Shade...",
14173      "Raise...",
14174      "Segment...",
14175      (char *) NULL
14176    },
14177    *FXMenu[] =
14178    {
14179      "Solarize...",
14180      "Sepia Tone...",
14181      "Swirl...",
14182      "Implode...",
14183      "Vignette...",
14184      "Wave...",
14185      "Oil Paint...",
14186      "Charcoal Draw...",
14187      (char *) NULL
14188    },
14189    *ImageEditMenu[] =
14190    {
14191      "Annotate...",
14192      "Draw...",
14193      "Color...",
14194      "Matte...",
14195      "Composite...",
14196      "Add Border...",
14197      "Add Frame...",
14198      "Comment...",
14199      "Launch...",
14200      "Region of Interest...",
14201      (char *) NULL
14202    },
14203    *MiscellanyMenu[] =
14204    {
14205      "Image Info",
14206      "Zoom Image",
14207      "Show Preview...",
14208      "Show Histogram",
14209      "Show Matte",
14210      "Background...",
14211      "Slide Show...",
14212      "Preferences...",
14213      (char *) NULL
14214    },
14215    *HelpMenu[] =
14216    {
14217      "Overview",
14218      "Browse Documentation",
14219      "About Display",
14220      (char *) NULL
14221    },
14222    *ShortCutsMenu[] =
14223    {
14224      "Next",
14225      "Former",
14226      "Open...",
14227      "Save...",
14228      "Print...",
14229      "Undo",
14230      "Restore",
14231      "Image Info",
14232      "Quit",
14233      (char *) NULL
14234    },
14235    *VirtualMenu[] =
14236    {
14237      "Image Info",
14238      "Print",
14239      "Next",
14240      "Quit",
14241      (char *) NULL
14242    };
14243
14244  static const char
14245    **Menus[MagickMenus] =
14246    {
14247      FileMenu,
14248      EditMenu,
14249      ViewMenu,
14250      TransformMenu,
14251      EnhanceMenu,
14252      EffectsMenu,
14253      FXMenu,
14254      ImageEditMenu,
14255      MiscellanyMenu,
14256      HelpMenu
14257    };
14258
14259  static CommandType
14260    CommandMenus[] =
14261    {
14262      NullCommand,
14263      NullCommand,
14264      NullCommand,
14265      NullCommand,
14266      NullCommand,
14267      NullCommand,
14268      NullCommand,
14269      NullCommand,
14270      NullCommand,
14271      NullCommand,
14272    },
14273    FileCommands[] =
14274    {
14275      OpenCommand,
14276      NextCommand,
14277      FormerCommand,
14278      SelectCommand,
14279      SaveCommand,
14280      PrintCommand,
14281      DeleteCommand,
14282      NewCommand,
14283      VisualDirectoryCommand,
14284      QuitCommand
14285    },
14286    EditCommands[] =
14287    {
14288      UndoCommand,
14289      RedoCommand,
14290      CutCommand,
14291      CopyCommand,
14292      PasteCommand
14293    },
14294    ViewCommands[] =
14295    {
14296      HalfSizeCommand,
14297      OriginalSizeCommand,
14298      DoubleSizeCommand,
14299      ResizeCommand,
14300      ApplyCommand,
14301      RefreshCommand,
14302      RestoreCommand
14303    },
14304    TransformCommands[] =
14305    {
14306      CropCommand,
14307      ChopCommand,
14308      FlopCommand,
14309      FlipCommand,
14310      RotateRightCommand,
14311      RotateLeftCommand,
14312      RotateCommand,
14313      ShearCommand,
14314      RollCommand,
14315      TrimCommand
14316    },
14317    EnhanceCommands[] =
14318    {
14319      HueCommand,
14320      SaturationCommand,
14321      BrightnessCommand,
14322      GammaCommand,
14323      SpiffCommand,
14324      DullCommand,
14325      ContrastStretchCommand,
14326      SigmoidalContrastCommand,
14327      NormalizeCommand,
14328      EqualizeCommand,
14329      NegateCommand,
14330      GrayscaleCommand,
14331      MapCommand,
14332      QuantizeCommand
14333    },
14334    EffectsCommands[] =
14335    {
14336      DespeckleCommand,
14337      EmbossCommand,
14338      ReduceNoiseCommand,
14339      AddNoiseCommand,
14340      SharpenCommand,
14341      BlurCommand,
14342      ThresholdCommand,
14343      EdgeDetectCommand,
14344      SpreadCommand,
14345      ShadeCommand,
14346      RaiseCommand,
14347      SegmentCommand
14348    },
14349    FXCommands[] =
14350    {
14351      SolarizeCommand,
14352      SepiaToneCommand,
14353      SwirlCommand,
14354      ImplodeCommand,
14355      VignetteCommand,
14356      WaveCommand,
14357      OilPaintCommand,
14358      CharcoalDrawCommand
14359    },
14360    ImageEditCommands[] =
14361    {
14362      AnnotateCommand,
14363      DrawCommand,
14364      ColorCommand,
14365      MatteCommand,
14366      CompositeCommand,
14367      AddBorderCommand,
14368      AddFrameCommand,
14369      CommentCommand,
14370      LaunchCommand,
14371      RegionofInterestCommand
14372    },
14373    MiscellanyCommands[] =
14374    {
14375      InfoCommand,
14376      ZoomCommand,
14377      ShowPreviewCommand,
14378      ShowHistogramCommand,
14379      ShowMatteCommand,
14380      BackgroundCommand,
14381      SlideShowCommand,
14382      PreferencesCommand
14383    },
14384    HelpCommands[] =
14385    {
14386      HelpCommand,
14387      BrowseDocumentationCommand,
14388      VersionCommand
14389    },
14390    ShortCutsCommands[] =
14391    {
14392      NextCommand,
14393      FormerCommand,
14394      OpenCommand,
14395      SaveCommand,
14396      PrintCommand,
14397      UndoCommand,
14398      RestoreCommand,
14399      InfoCommand,
14400      QuitCommand
14401    },
14402    VirtualCommands[] =
14403    {
14404      InfoCommand,
14405      PrintCommand,
14406      NextCommand,
14407      QuitCommand
14408    };
14409
14410  static CommandType
14411    *Commands[MagickMenus] =
14412    {
14413      FileCommands,
14414      EditCommands,
14415      ViewCommands,
14416      TransformCommands,
14417      EnhanceCommands,
14418      EffectsCommands,
14419      FXCommands,
14420      ImageEditCommands,
14421      MiscellanyCommands,
14422      HelpCommands
14423    };
14424
14425  char
14426    command[MaxTextExtent],
14427    *directory,
14428    geometry[MaxTextExtent],
14429    resource_name[MaxTextExtent];
14430
14431  CommandType
14432    command_type;
14433
14434  Image
14435    *display_image,
14436    *nexus;
14437
14438  int
14439    entry,
14440    id;
14441
14442  KeySym
14443    key_symbol;
14444
14445  MagickStatusType
14446    context_mask,
14447    status;
14448
14449  RectangleInfo
14450    geometry_info;
14451
14452  register int
14453    i;
14454
14455  static char
14456    working_directory[MaxTextExtent];
14457
14458  static XPoint
14459    vid_info;
14460
14461  static XWindowInfo
14462    *magick_windows[MaxXWindows];
14463
14464  static unsigned int
14465    number_windows;
14466
14467  struct stat
14468    attributes;
14469
14470  time_t
14471    timer,
14472    timestamp,
14473    update_time;
14474
14475  unsigned int
14476    height,
14477    width;
14478
14479  size_t
14480    delay;
14481
14482  WarningHandler
14483    warning_handler;
14484
14485  Window
14486    root_window;
14487
14488  XClassHint
14489    *class_hints;
14490
14491  XEvent
14492    event;
14493
14494  XFontStruct
14495    *font_info;
14496
14497  XGCValues
14498    context_values;
14499
14500  XPixelInfo
14501    *icon_pixel,
14502    *pixel;
14503
14504  XResourceInfo
14505    *icon_resources;
14506
14507  XStandardColormap
14508    *icon_map,
14509    *map_info;
14510
14511  XVisualInfo
14512    *icon_visual,
14513    *visual_info;
14514
14515  XWindowChanges
14516    window_changes;
14517
14518  XWindows
14519    *windows;
14520
14521  XWMHints
14522    *manager_hints;
14523
14524  assert(image != (Image **) NULL);
14525  assert((*image)->signature == MagickSignature);
14526  if( IfMagickTrue((*image)->debug) )
14527    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14528  display_image=(*image);
14529  warning_handler=(WarningHandler) NULL;
14530  windows=XSetWindows((XWindows *) ~0);
14531  if (windows != (XWindows *) NULL)
14532    {
14533      int
14534        status;
14535
14536      if (*working_directory == '\0')
14537        (void) CopyMagickString(working_directory,".",MaxTextExtent);
14538      status=chdir(working_directory);
14539      if (status == -1)
14540        (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14541          "UnableToOpenFile","%s",working_directory);
14542      warning_handler=resource_info->display_warnings ?
14543        SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14544      warning_handler=resource_info->display_warnings ?
14545        SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14546    }
14547  else
14548    {
14549      /*
14550        Allocate windows structure.
14551      */
14552      resource_info->colors=display_image->colors;
14553      windows=XSetWindows(XInitializeWindows(display,resource_info));
14554      if (windows == (XWindows *) NULL)
14555        ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14556          (*image)->filename);
14557      /*
14558        Initialize window id's.
14559      */
14560      number_windows=0;
14561      magick_windows[number_windows++]=(&windows->icon);
14562      magick_windows[number_windows++]=(&windows->backdrop);
14563      magick_windows[number_windows++]=(&windows->image);
14564      magick_windows[number_windows++]=(&windows->info);
14565      magick_windows[number_windows++]=(&windows->command);
14566      magick_windows[number_windows++]=(&windows->widget);
14567      magick_windows[number_windows++]=(&windows->popup);
14568      magick_windows[number_windows++]=(&windows->magnify);
14569      magick_windows[number_windows++]=(&windows->pan);
14570      for (i=0; i < (int) number_windows; i++)
14571        magick_windows[i]->id=(Window) NULL;
14572      vid_info.x=0;
14573      vid_info.y=0;
14574    }
14575  /*
14576    Initialize font info.
14577  */
14578  if (windows->font_info != (XFontStruct *) NULL)
14579    (void) XFreeFont(display,windows->font_info);
14580  windows->font_info=XBestFont(display,resource_info,MagickFalse);
14581  if (windows->font_info == (XFontStruct *) NULL)
14582    ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14583      resource_info->font);
14584  /*
14585    Initialize Standard Colormap.
14586  */
14587  map_info=windows->map_info;
14588  icon_map=windows->icon_map;
14589  visual_info=windows->visual_info;
14590  icon_visual=windows->icon_visual;
14591  pixel=windows->pixel_info;
14592  icon_pixel=windows->icon_pixel;
14593  font_info=windows->font_info;
14594  icon_resources=windows->icon_resources;
14595  class_hints=windows->class_hints;
14596  manager_hints=windows->manager_hints;
14597  root_window=XRootWindow(display,visual_info->screen);
14598  nexus=NewImageList();
14599  if( IfMagickTrue(display_image->debug) )
14600    {
14601      (void) LogMagickEvent(X11Event,GetMagickModule(),
14602        "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14603        (double) display_image->scene,(double) display_image->columns,
14604        (double) display_image->rows);
14605      if (display_image->colors != 0)
14606        (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14607          display_image->colors);
14608      (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14609        display_image->magick);
14610    }
14611  XMakeStandardColormap(display,visual_info,resource_info,display_image,
14612    map_info,pixel,exception);
14613  display_image->taint=MagickFalse;
14614  /*
14615    Initialize graphic context.
14616  */
14617  windows->context.id=(Window) NULL;
14618  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14619    resource_info,&windows->context);
14620  (void) CloneString(&class_hints->res_name,resource_info->client_name);
14621  (void) CloneString(&class_hints->res_class,resource_info->client_name);
14622  class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14623  manager_hints->flags=InputHint | StateHint;
14624  manager_hints->input=MagickFalse;
14625  manager_hints->initial_state=WithdrawnState;
14626  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14627    &windows->context);
14628  if( IfMagickTrue(display_image->debug) )
14629    (void) LogMagickEvent(X11Event,GetMagickModule(),
14630      "Window id: 0x%lx (context)",windows->context.id);
14631  context_values.background=pixel->background_color.pixel;
14632  context_values.font=font_info->fid;
14633  context_values.foreground=pixel->foreground_color.pixel;
14634  context_values.graphics_exposures=MagickFalse;
14635  context_mask=(MagickStatusType)
14636    (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14637  if (pixel->annotate_context != (GC) NULL)
14638    (void) XFreeGC(display,pixel->annotate_context);
14639  pixel->annotate_context=XCreateGC(display,windows->context.id,
14640    context_mask,&context_values);
14641  if (pixel->annotate_context == (GC) NULL)
14642    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14643      display_image->filename);
14644  context_values.background=pixel->depth_color.pixel;
14645  if (pixel->widget_context != (GC) NULL)
14646    (void) XFreeGC(display,pixel->widget_context);
14647  pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14648    &context_values);
14649  if (pixel->widget_context == (GC) NULL)
14650    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14651      display_image->filename);
14652  context_values.background=pixel->foreground_color.pixel;
14653  context_values.foreground=pixel->background_color.pixel;
14654  context_values.plane_mask=context_values.background ^
14655    context_values.foreground;
14656  if (pixel->highlight_context != (GC) NULL)
14657    (void) XFreeGC(display,pixel->highlight_context);
14658  pixel->highlight_context=XCreateGC(display,windows->context.id,
14659    (size_t) (context_mask | GCPlaneMask),&context_values);
14660  if (pixel->highlight_context == (GC) NULL)
14661    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14662      display_image->filename);
14663  (void) XDestroyWindow(display,windows->context.id);
14664  /*
14665    Initialize icon window.
14666  */
14667  XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14668    icon_resources,&windows->icon);
14669  windows->icon.geometry=resource_info->icon_geometry;
14670  XBestIconSize(display,&windows->icon,display_image);
14671  windows->icon.attributes.colormap=XDefaultColormap(display,
14672    icon_visual->screen);
14673  windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14674  manager_hints->flags=InputHint | StateHint;
14675  manager_hints->input=MagickFalse;
14676  manager_hints->initial_state=IconicState;
14677  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14678    &windows->icon);
14679  if( IfMagickTrue(display_image->debug) )
14680    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14681      windows->icon.id);
14682  /*
14683    Initialize graphic context for icon window.
14684  */
14685  if (icon_pixel->annotate_context != (GC) NULL)
14686    (void) XFreeGC(display,icon_pixel->annotate_context);
14687  context_values.background=icon_pixel->background_color.pixel;
14688  context_values.foreground=icon_pixel->foreground_color.pixel;
14689  icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14690    (size_t) (GCBackground | GCForeground),&context_values);
14691  if (icon_pixel->annotate_context == (GC) NULL)
14692    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14693      display_image->filename);
14694  windows->icon.annotate_context=icon_pixel->annotate_context;
14695  /*
14696    Initialize Image window.
14697  */
14698  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14699    &windows->image);
14700  windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14701  if( IfMagickFalse(resource_info->use_shared_memory) )
14702    windows->image.shared_memory=MagickFalse;
14703  if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14704    {
14705      char
14706        *title;
14707
14708      title=InterpretImageProperties(resource_info->image_info,display_image,
14709        resource_info->title,exception);
14710      (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
14711      (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
14712      title=DestroyString(title);
14713    }
14714  else
14715    {
14716      char
14717        filename[MaxTextExtent];
14718
14719      /*
14720        Window name is the base of the filename.
14721      */
14722      GetPathComponent(display_image->magick_filename,TailPath,filename);
14723      if (display_image->scene == 0)
14724        (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14725          "%s: %s",MagickPackageName,filename);
14726      else
14727        (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14728          "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14729          (double) display_image->scene,(double) GetImageListLength(
14730          display_image));
14731      (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
14732    }
14733  if (resource_info->immutable)
14734    windows->image.immutable=MagickTrue;
14735  windows->image.use_pixmap=resource_info->use_pixmap;
14736  windows->image.geometry=resource_info->image_geometry;
14737  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14738    XDisplayWidth(display,visual_info->screen),
14739    XDisplayHeight(display,visual_info->screen));
14740  geometry_info.width=display_image->columns;
14741  geometry_info.height=display_image->rows;
14742  geometry_info.x=0;
14743  geometry_info.y=0;
14744  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14745    &geometry_info.width,&geometry_info.height);
14746  windows->image.width=(unsigned int) geometry_info.width;
14747  windows->image.height=(unsigned int) geometry_info.height;
14748  windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14749    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14750    KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14751    PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14752  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14753    resource_info,&windows->backdrop);
14754  if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14755    {
14756      /*
14757        Initialize backdrop window.
14758      */
14759      windows->backdrop.x=0;
14760      windows->backdrop.y=0;
14761      (void) CloneString(&windows->backdrop.name,"Backdrop");
14762      windows->backdrop.flags=(size_t) (USSize | USPosition);
14763      windows->backdrop.width=(unsigned int)
14764        XDisplayWidth(display,visual_info->screen);
14765      windows->backdrop.height=(unsigned int)
14766        XDisplayHeight(display,visual_info->screen);
14767      windows->backdrop.border_width=0;
14768      windows->backdrop.immutable=MagickTrue;
14769      windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14770        ButtonReleaseMask;
14771      windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14772        StructureNotifyMask;
14773      manager_hints->flags=IconWindowHint | InputHint | StateHint;
14774      manager_hints->icon_window=windows->icon.id;
14775      manager_hints->input=MagickTrue;
14776      manager_hints->initial_state=resource_info->iconic ? IconicState :
14777        NormalState;
14778      XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14779        &windows->backdrop);
14780      if( IfMagickTrue(display_image->debug) )
14781        (void) LogMagickEvent(X11Event,GetMagickModule(),
14782          "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14783      (void) XMapWindow(display,windows->backdrop.id);
14784      (void) XClearWindow(display,windows->backdrop.id);
14785      if (windows->image.id != (Window) NULL)
14786        {
14787          (void) XDestroyWindow(display,windows->image.id);
14788          windows->image.id=(Window) NULL;
14789        }
14790      /*
14791        Position image in the center the backdrop.
14792      */
14793      windows->image.flags|=USPosition;
14794      windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14795        (windows->image.width/2);
14796      windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14797        (windows->image.height/2);
14798    }
14799  manager_hints->flags=IconWindowHint | InputHint | StateHint;
14800  manager_hints->icon_window=windows->icon.id;
14801  manager_hints->input=MagickTrue;
14802  manager_hints->initial_state=resource_info->iconic ? IconicState :
14803    NormalState;
14804  if (windows->group_leader.id != (Window) NULL)
14805    {
14806      /*
14807        Follow the leader.
14808      */
14809      manager_hints->flags|=WindowGroupHint;
14810      manager_hints->window_group=windows->group_leader.id;
14811      (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14812      if( IfMagickTrue(display_image->debug) )
14813        (void) LogMagickEvent(X11Event,GetMagickModule(),
14814          "Window id: 0x%lx (group leader)",windows->group_leader.id);
14815    }
14816  XMakeWindow(display,
14817    (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14818    argv,argc,class_hints,manager_hints,&windows->image);
14819  (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14820    XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14821  if (windows->group_leader.id != (Window) NULL)
14822    (void) XSetTransientForHint(display,windows->image.id,
14823      windows->group_leader.id);
14824  if( IfMagickTrue(display_image->debug) )
14825    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14826      windows->image.id);
14827  /*
14828    Initialize Info widget.
14829  */
14830  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14831    &windows->info);
14832  (void) CloneString(&windows->info.name,"Info");
14833  (void) CloneString(&windows->info.icon_name,"Info");
14834  windows->info.border_width=1;
14835  windows->info.x=2;
14836  windows->info.y=2;
14837  windows->info.flags|=PPosition;
14838  windows->info.attributes.win_gravity=UnmapGravity;
14839  windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14840    StructureNotifyMask;
14841  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14842  manager_hints->input=MagickFalse;
14843  manager_hints->initial_state=NormalState;
14844  manager_hints->window_group=windows->image.id;
14845  XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14846    &windows->info);
14847  windows->info.highlight_stipple=XCreateBitmapFromData(display,
14848    windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14849  windows->info.shadow_stipple=XCreateBitmapFromData(display,
14850    windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14851  (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14852  if( IfMagickTrue(windows->image.mapped) )
14853    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14854  if( IfMagickTrue(display_image->debug) )
14855    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14856      windows->info.id);
14857  /*
14858    Initialize Command widget.
14859  */
14860  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14861    resource_info,&windows->command);
14862  windows->command.data=MagickMenus;
14863  (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14864  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
14865    resource_info->client_name);
14866  windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14867    resource_name,"geometry",(char *) NULL);
14868  (void) CloneString(&windows->command.name,MagickTitle);
14869  windows->command.border_width=0;
14870  windows->command.flags|=PPosition;
14871  windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14872    ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14873    OwnerGrabButtonMask | StructureNotifyMask;
14874  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14875  manager_hints->input=MagickTrue;
14876  manager_hints->initial_state=NormalState;
14877  manager_hints->window_group=windows->image.id;
14878  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14879    &windows->command);
14880  windows->command.highlight_stipple=XCreateBitmapFromData(display,
14881    windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14882    HighlightHeight);
14883  windows->command.shadow_stipple=XCreateBitmapFromData(display,
14884    windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14885  (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14886  if( IfMagickTrue(windows->command.mapped) )
14887    (void) XMapRaised(display,windows->command.id);
14888  if( IfMagickTrue(display_image->debug) )
14889    (void) LogMagickEvent(X11Event,GetMagickModule(),
14890      "Window id: 0x%lx (command)",windows->command.id);
14891  /*
14892    Initialize Widget window.
14893  */
14894  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14895    resource_info,&windows->widget);
14896  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
14897    resource_info->client_name);
14898  windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14899    resource_name,"geometry",(char *) NULL);
14900  windows->widget.border_width=0;
14901  windows->widget.flags|=PPosition;
14902  windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14903    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14904    KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14905    StructureNotifyMask;
14906  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14907  manager_hints->input=MagickTrue;
14908  manager_hints->initial_state=NormalState;
14909  manager_hints->window_group=windows->image.id;
14910  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14911    &windows->widget);
14912  windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14913    windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14914  windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14915    windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14916  (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14917  if( IfMagickTrue(display_image->debug) )
14918    (void) LogMagickEvent(X11Event,GetMagickModule(),
14919      "Window id: 0x%lx (widget)",windows->widget.id);
14920  /*
14921    Initialize popup window.
14922  */
14923  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14924    resource_info,&windows->popup);
14925  windows->popup.border_width=0;
14926  windows->popup.flags|=PPosition;
14927  windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14928    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14929    KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14930  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14931  manager_hints->input=MagickTrue;
14932  manager_hints->initial_state=NormalState;
14933  manager_hints->window_group=windows->image.id;
14934  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14935    &windows->popup);
14936  windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14937    windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14938  windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14939    windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14940  (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14941  if( IfMagickTrue(display_image->debug) )
14942    (void) LogMagickEvent(X11Event,GetMagickModule(),
14943      "Window id: 0x%lx (pop up)",windows->popup.id);
14944  /*
14945    Initialize Magnify window and cursor.
14946  */
14947  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14948    resource_info,&windows->magnify);
14949  if( IfMagickFalse(resource_info->use_shared_memory) )
14950    windows->magnify.shared_memory=MagickFalse;
14951  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
14952    resource_info->client_name);
14953  windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14954    resource_name,"geometry",(char *) NULL);
14955  (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14956    resource_info->magnify);
14957  if (windows->magnify.cursor != (Cursor) NULL)
14958    (void) XFreeCursor(display,windows->magnify.cursor);
14959  windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14960    map_info->colormap,resource_info->background_color,
14961    resource_info->foreground_color);
14962  if (windows->magnify.cursor == (Cursor) NULL)
14963    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14964      display_image->filename);
14965  windows->magnify.width=MagnifySize;
14966  windows->magnify.height=MagnifySize;
14967  windows->magnify.flags|=PPosition;
14968  windows->magnify.min_width=MagnifySize;
14969  windows->magnify.min_height=MagnifySize;
14970  windows->magnify.width_inc=MagnifySize;
14971  windows->magnify.height_inc=MagnifySize;
14972  windows->magnify.data=resource_info->magnify;
14973  windows->magnify.attributes.cursor=windows->magnify.cursor;
14974  windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14975    ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14976    StructureNotifyMask;
14977  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14978  manager_hints->input=MagickTrue;
14979  manager_hints->initial_state=NormalState;
14980  manager_hints->window_group=windows->image.id;
14981  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14982    &windows->magnify);
14983  if( IfMagickTrue(display_image->debug) )
14984    (void) LogMagickEvent(X11Event,GetMagickModule(),
14985      "Window id: 0x%lx (magnify)",windows->magnify.id);
14986  (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14987  /*
14988    Initialize panning window.
14989  */
14990  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14991    resource_info,&windows->pan);
14992  (void) CloneString(&windows->pan.name,"Pan Icon");
14993  windows->pan.width=windows->icon.width;
14994  windows->pan.height=windows->icon.height;
14995  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
14996    resource_info->client_name);
14997  windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
14998    resource_name,"geometry",(char *) NULL);
14999  (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
15000    &windows->pan.width,&windows->pan.height);
15001  windows->pan.flags|=PPosition;
15002  windows->pan.immutable=MagickTrue;
15003  windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
15004    ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
15005    StructureNotifyMask;
15006  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
15007  manager_hints->input=MagickFalse;
15008  manager_hints->initial_state=NormalState;
15009  manager_hints->window_group=windows->image.id;
15010  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
15011    &windows->pan);
15012  if( IfMagickTrue(display_image->debug) )
15013    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
15014      windows->pan.id);
15015  (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
15016  if( IfMagickTrue(windows->info.mapped) )
15017    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15018  if( IfMagickFalse(windows->image.mapped) ||
15019      (windows->backdrop.id != (Window) NULL))
15020    (void) XMapWindow(display,windows->image.id);
15021  /*
15022    Set our progress monitor and warning handlers.
15023  */
15024  if (warning_handler == (WarningHandler) NULL)
15025    {
15026      warning_handler=resource_info->display_warnings ?
15027        SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
15028      warning_handler=resource_info->display_warnings ?
15029        SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
15030    }
15031  /*
15032    Initialize Image and Magnify X images.
15033  */
15034  windows->image.x=0;
15035  windows->image.y=0;
15036  windows->magnify.shape=MagickFalse;
15037  width=(unsigned int) display_image->columns;
15038  height=(unsigned int) display_image->rows;
15039  if ((display_image->columns != width) || (display_image->rows != height))
15040    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15041      display_image->filename);
15042  status=XMakeImage(display,resource_info,&windows->image,display_image,
15043    width,height,exception);
15044  if( IfMagickFalse(status) )
15045    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15046      display_image->filename);
15047  status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
15048    windows->magnify.width,windows->magnify.height,exception);
15049  if( IfMagickFalse(status) )
15050    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15051      display_image->filename);
15052  if( IfMagickTrue(windows->magnify.mapped) )
15053    (void) XMapRaised(display,windows->magnify.id);
15054  if( IfMagickTrue(windows->pan.mapped) )
15055    (void) XMapRaised(display,windows->pan.id);
15056  windows->image.window_changes.width=(int) display_image->columns;
15057  windows->image.window_changes.height=(int) display_image->rows;
15058  (void) XConfigureImage(display,resource_info,windows,display_image,exception);
15059  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15060  (void) XSync(display,MagickFalse);
15061  /*
15062    Respond to events.
15063  */
15064  delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
15065  timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15066  update_time=0;
15067  if( IfMagickTrue(resource_info->update) )
15068    {
15069      MagickBooleanType
15070        status;
15071
15072      /*
15073        Determine when file data was last modified.
15074      */
15075      status=GetPathAttributes(display_image->filename,&attributes);
15076      if( IfMagickTrue(status) )
15077        update_time=attributes.st_mtime;
15078    }
15079  *state&=(~FormerImageState);
15080  *state&=(~MontageImageState);
15081  *state&=(~NextImageState);
15082  do
15083  {
15084    /*
15085      Handle a window event.
15086    */
15087    if( IfMagickTrue(windows->image.mapped) )
15088      if ((display_image->delay != 0) || (resource_info->update != 0))
15089        {
15090          if (timer < time((time_t *) NULL))
15091            {
15092              if( IfMagickFalse(resource_info->update) )
15093                *state|=NextImageState | ExitState;
15094              else
15095                {
15096                  MagickBooleanType
15097                    status;
15098
15099                  /*
15100                    Determine if image file was modified.
15101                  */
15102                  status=GetPathAttributes(display_image->filename,&attributes);
15103                  if( IfMagickTrue(status) )
15104                    if (update_time != attributes.st_mtime)
15105                      {
15106                        /*
15107                          Redisplay image.
15108                        */
15109                        (void) FormatLocaleString(
15110                          resource_info->image_info->filename,MaxTextExtent,
15111                          "%s:%s",display_image->magick,
15112                          display_image->filename);
15113                        nexus=ReadImage(resource_info->image_info,exception);
15114                        if (nexus != (Image *) NULL)
15115                          {
15116                            nexus=DestroyImage(nexus);
15117                            *state|=NextImageState | ExitState;
15118                          }
15119                      }
15120                  delay=display_image->delay/MagickMax(
15121                    display_image->ticks_per_second,1L);
15122                  timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15123                }
15124            }
15125          if (XEventsQueued(display,QueuedAfterFlush) == 0)
15126            {
15127              /*
15128                Do not block if delay > 0.
15129              */
15130              XDelay(display,SuspendTime << 2);
15131              continue;
15132            }
15133        }
15134    timestamp=time((time_t *) NULL);
15135    (void) XNextEvent(display,&event);
15136    if( IfMagickFalse(windows->image.stasis) )
15137      windows->image.stasis=IsMagickTrue((time((time_t *) NULL)-timestamp) > 0);
15138    if( IfMagickFalse(windows->magnify.stasis) )
15139      windows->magnify.stasis=IsMagickTrue((time((time_t *) NULL)-timestamp) > 0);
15140    if (event.xany.window == windows->command.id)
15141      {
15142        /*
15143          Select a command from the Command widget.
15144        */
15145        id=XCommandWidget(display,windows,CommandMenu,&event);
15146        if (id < 0)
15147          continue;
15148        (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15149        command_type=CommandMenus[id];
15150        if (id < MagickMenus)
15151          {
15152            /*
15153              Select a command from a pop-up menu.
15154            */
15155            entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15156              command);
15157            if (entry < 0)
15158              continue;
15159            (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15160            command_type=Commands[id][entry];
15161          }
15162        if (command_type != NullCommand)
15163          nexus=XMagickCommand(display,resource_info,windows,command_type,
15164            &display_image,exception);
15165        continue;
15166      }
15167    switch (event.type)
15168    {
15169      case ButtonPress:
15170      {
15171        if( IfMagickTrue(display_image->debug) )
15172          (void) LogMagickEvent(X11Event,GetMagickModule(),
15173            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15174            event.xbutton.button,event.xbutton.x,event.xbutton.y);
15175        if ((event.xbutton.button == Button3) &&
15176            (event.xbutton.state & Mod1Mask))
15177          {
15178            /*
15179              Convert Alt-Button3 to Button2.
15180            */
15181            event.xbutton.button=Button2;
15182            event.xbutton.state&=(~Mod1Mask);
15183          }
15184        if (event.xbutton.window == windows->backdrop.id)
15185          {
15186            (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15187              event.xbutton.time);
15188            break;
15189          }
15190        if (event.xbutton.window == windows->image.id)
15191          {
15192            switch (event.xbutton.button)
15193            {
15194              case Button1:
15195              {
15196                if (resource_info->immutable)
15197                  {
15198                    /*
15199                      Select a command from the Virtual menu.
15200                    */
15201                    entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15202                      command);
15203                    if (entry >= 0)
15204                      nexus=XMagickCommand(display,resource_info,windows,
15205                        VirtualCommands[entry],&display_image,exception);
15206                    break;
15207                  }
15208                /*
15209                  Map/unmap Command widget.
15210                */
15211                if( IfMagickTrue(windows->command.mapped) )
15212                  (void) XWithdrawWindow(display,windows->command.id,
15213                    windows->command.screen);
15214                else
15215                  {
15216                    (void) XCommandWidget(display,windows,CommandMenu,
15217                      (XEvent *) NULL);
15218                    (void) XMapRaised(display,windows->command.id);
15219                  }
15220                break;
15221              }
15222              case Button2:
15223              {
15224                /*
15225                  User pressed the image magnify button.
15226                */
15227                (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15228                  &display_image,exception);
15229                XMagnifyImage(display,windows,&event,exception);
15230                break;
15231              }
15232              case Button3:
15233              {
15234                if (resource_info->immutable)
15235                  {
15236                    /*
15237                      Select a command from the Virtual menu.
15238                    */
15239                    entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15240                      command);
15241                    if (entry >= 0)
15242                      nexus=XMagickCommand(display,resource_info,windows,
15243                        VirtualCommands[entry],&display_image,exception);
15244                    break;
15245                  }
15246                if (display_image->montage != (char *) NULL)
15247                  {
15248                    /*
15249                      Open or delete a tile from a visual image directory.
15250                    */
15251                    nexus=XTileImage(display,resource_info,windows,
15252                      display_image,&event,exception);
15253                    if (nexus != (Image *) NULL)
15254                      *state|=MontageImageState | NextImageState | ExitState;
15255                    vid_info.x=(short int) windows->image.x;
15256                    vid_info.y=(short int) windows->image.y;
15257                    break;
15258                  }
15259                /*
15260                  Select a command from the Short Cuts menu.
15261                */
15262                entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15263                  command);
15264                if (entry >= 0)
15265                  nexus=XMagickCommand(display,resource_info,windows,
15266                    ShortCutsCommands[entry],&display_image,exception);
15267                break;
15268              }
15269              case Button4:
15270              {
15271                /*
15272                  Wheel up.
15273                */
15274                XTranslateImage(display,windows,*image,XK_Up);
15275                break;
15276              }
15277              case Button5:
15278              {
15279                /*
15280                  Wheel down.
15281                */
15282                XTranslateImage(display,windows,*image,XK_Down);
15283                break;
15284              }
15285              default:
15286                break;
15287            }
15288            break;
15289          }
15290        if (event.xbutton.window == windows->magnify.id)
15291          {
15292            int
15293              factor;
15294
15295            static const char
15296              *MagnifyMenu[] =
15297              {
15298                "2",
15299                "4",
15300                "5",
15301                "6",
15302                "7",
15303                "8",
15304                "9",
15305                "3",
15306                (char *) NULL,
15307              };
15308
15309            static KeySym
15310              MagnifyCommands[] =
15311              {
15312                XK_2,
15313                XK_4,
15314                XK_5,
15315                XK_6,
15316                XK_7,
15317                XK_8,
15318                XK_9,
15319                XK_3
15320              };
15321
15322            /*
15323              Select a magnify factor from the pop-up menu.
15324            */
15325            factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15326            if (factor >= 0)
15327              XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor],
15328                exception);
15329            break;
15330          }
15331        if (event.xbutton.window == windows->pan.id)
15332          {
15333            switch (event.xbutton.button)
15334            {
15335              case Button4:
15336              {
15337                /*
15338                  Wheel up.
15339                */
15340                XTranslateImage(display,windows,*image,XK_Up);
15341                break;
15342              }
15343              case Button5:
15344              {
15345                /*
15346                  Wheel down.
15347                */
15348                XTranslateImage(display,windows,*image,XK_Down);
15349                break;
15350              }
15351              default:
15352              {
15353                XPanImage(display,windows,&event,exception);
15354                break;
15355              }
15356            }
15357            break;
15358          }
15359        delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15360          1L);
15361        timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15362        break;
15363      }
15364      case ButtonRelease:
15365      {
15366        if( IfMagickTrue(display_image->debug) )
15367          (void) LogMagickEvent(X11Event,GetMagickModule(),
15368            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15369            event.xbutton.button,event.xbutton.x,event.xbutton.y);
15370        break;
15371      }
15372      case ClientMessage:
15373      {
15374        if( IfMagickTrue(display_image->debug) )
15375          (void) LogMagickEvent(X11Event,GetMagickModule(),
15376            "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15377            event.xclient.message_type,event.xclient.format,(unsigned long)
15378            event.xclient.data.l[0]);
15379        if (event.xclient.message_type == windows->im_protocols)
15380          {
15381            if (*event.xclient.data.l == (long) windows->im_update_widget)
15382              {
15383                (void) CloneString(&windows->command.name,MagickTitle);
15384                windows->command.data=MagickMenus;
15385                (void) XCommandWidget(display,windows,CommandMenu,
15386                  (XEvent *) NULL);
15387                break;
15388              }
15389            if (*event.xclient.data.l == (long) windows->im_update_colormap)
15390              {
15391                /*
15392                  Update graphic context and window colormap.
15393                */
15394                for (i=0; i < (int) number_windows; i++)
15395                {
15396                  if (magick_windows[i]->id == windows->icon.id)
15397                    continue;
15398                  context_values.background=pixel->background_color.pixel;
15399                  context_values.foreground=pixel->foreground_color.pixel;
15400                  (void) XChangeGC(display,magick_windows[i]->annotate_context,
15401                    context_mask,&context_values);
15402                  (void) XChangeGC(display,magick_windows[i]->widget_context,
15403                    context_mask,&context_values);
15404                  context_values.background=pixel->foreground_color.pixel;
15405                  context_values.foreground=pixel->background_color.pixel;
15406                  context_values.plane_mask=context_values.background ^
15407                    context_values.foreground;
15408                  (void) XChangeGC(display,magick_windows[i]->highlight_context,
15409                    (size_t) (context_mask | GCPlaneMask),
15410                    &context_values);
15411                  magick_windows[i]->attributes.background_pixel=
15412                    pixel->background_color.pixel;
15413                  magick_windows[i]->attributes.border_pixel=
15414                    pixel->border_color.pixel;
15415                  magick_windows[i]->attributes.colormap=map_info->colormap;
15416                  (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15417                    (unsigned long) magick_windows[i]->mask,
15418                    &magick_windows[i]->attributes);
15419                }
15420                if( IfMagickTrue(windows->pan.mapped) )
15421                  {
15422                    (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15423                      windows->pan.pixmap);
15424                    (void) XClearWindow(display,windows->pan.id);
15425                    XDrawPanRectangle(display,windows);
15426                  }
15427                if (windows->backdrop.id != (Window) NULL)
15428                  (void) XInstallColormap(display,map_info->colormap);
15429                break;
15430              }
15431            if (*event.xclient.data.l == (long) windows->im_former_image)
15432              {
15433                *state|=FormerImageState | ExitState;
15434                break;
15435              }
15436            if (*event.xclient.data.l == (long) windows->im_next_image)
15437              {
15438                *state|=NextImageState | ExitState;
15439                break;
15440              }
15441            if (*event.xclient.data.l == (long) windows->im_retain_colors)
15442              {
15443                *state|=RetainColorsState;
15444                break;
15445              }
15446            if (*event.xclient.data.l == (long) windows->im_exit)
15447              {
15448                *state|=ExitState;
15449                break;
15450              }
15451            break;
15452          }
15453        if (event.xclient.message_type == windows->dnd_protocols)
15454          {
15455            Atom
15456              selection,
15457              type;
15458
15459            int
15460              format,
15461              status;
15462
15463            unsigned char
15464              *data;
15465
15466            unsigned long
15467              after,
15468              length;
15469
15470            /*
15471              Display image named by the Drag-and-Drop selection.
15472            */
15473            if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15474              break;
15475            selection=XInternAtom(display,"DndSelection",MagickFalse);
15476            status=XGetWindowProperty(display,root_window,selection,0L,(long)
15477              MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15478              &length,&after,&data);
15479            if ((status != Success) || (length == 0))
15480              break;
15481            if (*event.xclient.data.l == 2)
15482              {
15483                /*
15484                  Offix DND.
15485                */
15486                (void) CopyMagickString(resource_info->image_info->filename,
15487                  (char *) data,MaxTextExtent);
15488              }
15489            else
15490              {
15491                /*
15492                  XDND.
15493                */
15494                if (strncmp((char *) data, "file:", 5) != 0)
15495                  {
15496                    (void) XFree((void *) data);
15497                    break;
15498                  }
15499                (void) CopyMagickString(resource_info->image_info->filename,
15500                  ((char *) data)+5,MaxTextExtent);
15501              }
15502            nexus=ReadImage(resource_info->image_info,exception);
15503            CatchException(exception);
15504            if (nexus != (Image *) NULL)
15505              *state|=NextImageState | ExitState;
15506            (void) XFree((void *) data);
15507            break;
15508          }
15509        /*
15510          If client window delete message, exit.
15511        */
15512        if (event.xclient.message_type != windows->wm_protocols)
15513          break;
15514        if (*event.xclient.data.l != (long) windows->wm_delete_window)
15515          break;
15516        (void) XWithdrawWindow(display,event.xclient.window,
15517          visual_info->screen);
15518        if (event.xclient.window == windows->image.id)
15519          {
15520            *state|=ExitState;
15521            break;
15522          }
15523        if (event.xclient.window == windows->pan.id)
15524          {
15525            /*
15526              Restore original image size when pan window is deleted.
15527            */
15528            windows->image.window_changes.width=windows->image.ximage->width;
15529            windows->image.window_changes.height=windows->image.ximage->height;
15530            (void) XConfigureImage(display,resource_info,windows,
15531              display_image,exception);
15532          }
15533        break;
15534      }
15535      case ConfigureNotify:
15536      {
15537        if( IfMagickTrue(display_image->debug) )
15538          (void) LogMagickEvent(X11Event,GetMagickModule(),
15539            "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15540            event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15541            event.xconfigure.y,event.xconfigure.send_event);
15542        if (event.xconfigure.window == windows->image.id)
15543          {
15544            /*
15545              Image window has a new configuration.
15546            */
15547            if (event.xconfigure.send_event != 0)
15548              {
15549                XWindowChanges
15550                  window_changes;
15551
15552                /*
15553                  Position the transient windows relative of the Image window.
15554                */
15555                if (windows->command.geometry == (char *) NULL)
15556                  if( IfMagickFalse(windows->command.mapped) )
15557                    {
15558                      windows->command.x=event.xconfigure.x-
15559                        windows->command.width-25;
15560                      windows->command.y=event.xconfigure.y;
15561                      XConstrainWindowPosition(display,&windows->command);
15562                      window_changes.x=windows->command.x;
15563                      window_changes.y=windows->command.y;
15564                      (void) XReconfigureWMWindow(display,windows->command.id,
15565                        windows->command.screen,(unsigned int) (CWX | CWY),
15566                        &window_changes);
15567                    }
15568                if (windows->widget.geometry == (char *) NULL)
15569                  if( IfMagickFalse(windows->widget.mapped) )
15570                    {
15571                      windows->widget.x=event.xconfigure.x+
15572                        event.xconfigure.width/10;
15573                      windows->widget.y=event.xconfigure.y+
15574                        event.xconfigure.height/10;
15575                      XConstrainWindowPosition(display,&windows->widget);
15576                      window_changes.x=windows->widget.x;
15577                      window_changes.y=windows->widget.y;
15578                      (void) XReconfigureWMWindow(display,windows->widget.id,
15579                        windows->widget.screen,(unsigned int) (CWX | CWY),
15580                        &window_changes);
15581                    }
15582                if (windows->magnify.geometry == (char *) NULL)
15583                  if( IfMagickFalse(windows->magnify.mapped) )
15584                    {
15585                      windows->magnify.x=event.xconfigure.x+
15586                        event.xconfigure.width+25;
15587                      windows->magnify.y=event.xconfigure.y;
15588                      XConstrainWindowPosition(display,&windows->magnify);
15589                      window_changes.x=windows->magnify.x;
15590                      window_changes.y=windows->magnify.y;
15591                      (void) XReconfigureWMWindow(display,windows->magnify.id,
15592                        windows->magnify.screen,(unsigned int) (CWX | CWY),
15593                        &window_changes);
15594                    }
15595                if (windows->pan.geometry == (char *) NULL)
15596                  if( IfMagickFalse(windows->pan.mapped) )
15597                    {
15598                      windows->pan.x=event.xconfigure.x+
15599                        event.xconfigure.width+25;
15600                      windows->pan.y=event.xconfigure.y+
15601                        windows->magnify.height+50;
15602                      XConstrainWindowPosition(display,&windows->pan);
15603                      window_changes.x=windows->pan.x;
15604                      window_changes.y=windows->pan.y;
15605                      (void) XReconfigureWMWindow(display,windows->pan.id,
15606                        windows->pan.screen,(unsigned int) (CWX | CWY),
15607                        &window_changes);
15608                    }
15609              }
15610            if ((event.xconfigure.width == (int) windows->image.width) &&
15611                (event.xconfigure.height == (int) windows->image.height))
15612              break;
15613            windows->image.width=(unsigned int) event.xconfigure.width;
15614            windows->image.height=(unsigned int) event.xconfigure.height;
15615            windows->image.x=0;
15616            windows->image.y=0;
15617            if (display_image->montage != (char *) NULL)
15618              {
15619                windows->image.x=vid_info.x;
15620                windows->image.y=vid_info.y;
15621              }
15622            if( IfMagickTrue(windows->image.mapped) &&
15623                IfMagickTrue(windows->image.stasis) )
15624              {
15625                /*
15626                  Update image window configuration.
15627                */
15628                windows->image.window_changes.width=event.xconfigure.width;
15629                windows->image.window_changes.height=event.xconfigure.height;
15630                (void) XConfigureImage(display,resource_info,windows,
15631                  display_image,exception);
15632              }
15633            /*
15634              Update pan window configuration.
15635            */
15636            if ((event.xconfigure.width < windows->image.ximage->width) ||
15637                (event.xconfigure.height < windows->image.ximage->height))
15638              {
15639                (void) XMapRaised(display,windows->pan.id);
15640                XDrawPanRectangle(display,windows);
15641              }
15642            else
15643              if( IfMagickTrue(windows->pan.mapped) )
15644                (void) XWithdrawWindow(display,windows->pan.id,
15645                  windows->pan.screen);
15646            break;
15647          }
15648        if (event.xconfigure.window == windows->magnify.id)
15649          {
15650            unsigned int
15651              magnify;
15652
15653            /*
15654              Magnify window has a new configuration.
15655            */
15656            windows->magnify.width=(unsigned int) event.xconfigure.width;
15657            windows->magnify.height=(unsigned int) event.xconfigure.height;
15658            if( IfMagickFalse(windows->magnify.mapped) )
15659              break;
15660            magnify=1;
15661            while ((int) magnify <= event.xconfigure.width)
15662              magnify<<=1;
15663            while ((int) magnify <= event.xconfigure.height)
15664              magnify<<=1;
15665            magnify>>=1;
15666            if (((int) magnify != event.xconfigure.width) ||
15667                ((int) magnify != event.xconfigure.height))
15668              {
15669                window_changes.width=(int) magnify;
15670                window_changes.height=(int) magnify;
15671                (void) XReconfigureWMWindow(display,windows->magnify.id,
15672                  windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15673                  &window_changes);
15674                break;
15675              }
15676            if( IfMagickTrue(windows->magnify.mapped) &&
15677                IfMagickTrue(windows->magnify.stasis) )
15678              {
15679                status=XMakeImage(display,resource_info,&windows->magnify,
15680                  display_image,windows->magnify.width,windows->magnify.height,
15681                  exception);
15682                XMakeMagnifyImage(display,windows,exception);
15683              }
15684            break;
15685          }
15686        if( IfMagickTrue(windows->magnify.mapped) &&
15687            (event.xconfigure.window == windows->pan.id))
15688          {
15689            /*
15690              Pan icon window has a new configuration.
15691            */
15692            if (event.xconfigure.send_event != 0)
15693              {
15694                windows->pan.x=event.xconfigure.x;
15695                windows->pan.y=event.xconfigure.y;
15696              }
15697            windows->pan.width=(unsigned int) event.xconfigure.width;
15698            windows->pan.height=(unsigned int) event.xconfigure.height;
15699            break;
15700          }
15701        if (event.xconfigure.window == windows->icon.id)
15702          {
15703            /*
15704              Icon window has a new configuration.
15705            */
15706            windows->icon.width=(unsigned int) event.xconfigure.width;
15707            windows->icon.height=(unsigned int) event.xconfigure.height;
15708            break;
15709          }
15710        break;
15711      }
15712      case DestroyNotify:
15713      {
15714        /*
15715          Group leader has exited.
15716        */
15717        if( IfMagickTrue(display_image->debug) )
15718          (void) LogMagickEvent(X11Event,GetMagickModule(),
15719            "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15720        if (event.xdestroywindow.window == windows->group_leader.id)
15721          {
15722            *state|=ExitState;
15723            break;
15724          }
15725        break;
15726      }
15727      case EnterNotify:
15728      {
15729        /*
15730          Selectively install colormap.
15731        */
15732        if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15733          if (event.xcrossing.mode != NotifyUngrab)
15734            XInstallColormap(display,map_info->colormap);
15735        break;
15736      }
15737      case Expose:
15738      {
15739        if( IfMagickTrue(display_image->debug) )
15740          (void) LogMagickEvent(X11Event,GetMagickModule(),
15741            "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15742            event.xexpose.width,event.xexpose.height,event.xexpose.x,
15743            event.xexpose.y);
15744        /*
15745          Refresh windows that are now exposed.
15746        */
15747        if ((event.xexpose.window == windows->image.id) &&
15748            IfMagickTrue(windows->image.mapped) )
15749          {
15750            XRefreshWindow(display,&windows->image,&event);
15751            delay=display_image->delay/MagickMax(
15752              display_image->ticks_per_second,1L);
15753            timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15754            break;
15755          }
15756        if ((event.xexpose.window == windows->magnify.id) &&
15757            IfMagickTrue(windows->magnify.mapped))
15758          {
15759            XMakeMagnifyImage(display,windows,exception);
15760            break;
15761          }
15762        if (event.xexpose.window == windows->pan.id)
15763          {
15764            XDrawPanRectangle(display,windows);
15765            break;
15766          }
15767        if (event.xexpose.window == windows->icon.id)
15768          {
15769            XRefreshWindow(display,&windows->icon,&event);
15770            break;
15771          }
15772        break;
15773      }
15774      case KeyPress:
15775      {
15776        int
15777          length;
15778
15779        /*
15780          Respond to a user key press.
15781        */
15782        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15783          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15784        *(command+length)='\0';
15785        if( IfMagickTrue(display_image->debug) )
15786          (void) LogMagickEvent(X11Event,GetMagickModule(),
15787            "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15788            key_symbol,command);
15789        if (event.xkey.window == windows->image.id)
15790          {
15791            command_type=XImageWindowCommand(display,resource_info,windows,
15792              event.xkey.state,key_symbol,&display_image,exception);
15793            if (command_type != NullCommand)
15794              nexus=XMagickCommand(display,resource_info,windows,command_type,
15795                &display_image,exception);
15796          }
15797        if (event.xkey.window == windows->magnify.id)
15798          XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol,
15799            exception);
15800        if (event.xkey.window == windows->pan.id)
15801          {
15802            if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15803              (void) XWithdrawWindow(display,windows->pan.id,
15804                windows->pan.screen);
15805            else
15806              if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15807                XTextViewWidget(display,resource_info,windows,MagickFalse,
15808                  "Help Viewer - Image Pan",ImagePanHelp);
15809              else
15810                XTranslateImage(display,windows,*image,key_symbol);
15811          }
15812        delay=display_image->delay/MagickMax(
15813          display_image->ticks_per_second,1L);
15814        timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15815        break;
15816      }
15817      case KeyRelease:
15818      {
15819        /*
15820          Respond to a user key release.
15821        */
15822        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15823          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15824        if( IfMagickTrue(display_image->debug) )
15825          (void) LogMagickEvent(X11Event,GetMagickModule(),
15826            "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15827        break;
15828      }
15829      case LeaveNotify:
15830      {
15831        /*
15832          Selectively uninstall colormap.
15833        */
15834        if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15835          if (event.xcrossing.mode != NotifyUngrab)
15836            XUninstallColormap(display,map_info->colormap);
15837        break;
15838      }
15839      case MapNotify:
15840      {
15841        if( IfMagickTrue(display_image->debug) )
15842          (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15843            event.xmap.window);
15844        if (event.xmap.window == windows->backdrop.id)
15845          {
15846            (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15847              CurrentTime);
15848            windows->backdrop.mapped=MagickTrue;
15849            break;
15850          }
15851        if (event.xmap.window == windows->image.id)
15852          {
15853            if (windows->backdrop.id != (Window) NULL)
15854              (void) XInstallColormap(display,map_info->colormap);
15855            if (LocaleCompare(display_image->magick,"LOGO") == 0)
15856              {
15857                if (LocaleCompare(display_image->filename,"LOGO") == 0)
15858                  nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15859              }
15860            if (((int) windows->image.width < windows->image.ximage->width) ||
15861                ((int) windows->image.height < windows->image.ximage->height))
15862              (void) XMapRaised(display,windows->pan.id);
15863            windows->image.mapped=MagickTrue;
15864            break;
15865          }
15866        if (event.xmap.window == windows->magnify.id)
15867          {
15868            XMakeMagnifyImage(display,windows,exception);
15869            windows->magnify.mapped=MagickTrue;
15870            (void) XWithdrawWindow(display,windows->info.id,
15871              windows->info.screen);
15872            break;
15873          }
15874        if (event.xmap.window == windows->pan.id)
15875          {
15876            XMakePanImage(display,resource_info,windows,display_image,
15877              exception);
15878            windows->pan.mapped=MagickTrue;
15879            break;
15880          }
15881        if (event.xmap.window == windows->info.id)
15882          {
15883            windows->info.mapped=MagickTrue;
15884            break;
15885          }
15886        if (event.xmap.window == windows->icon.id)
15887          {
15888            MagickBooleanType
15889              taint;
15890
15891            /*
15892              Create an icon image.
15893            */
15894            taint=display_image->taint;
15895            XMakeStandardColormap(display,icon_visual,icon_resources,
15896              display_image,icon_map,icon_pixel,exception);
15897            (void) XMakeImage(display,icon_resources,&windows->icon,
15898              display_image,windows->icon.width,windows->icon.height,
15899              exception);
15900            display_image->taint=taint;
15901            (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15902              windows->icon.pixmap);
15903            (void) XClearWindow(display,windows->icon.id);
15904            (void) XWithdrawWindow(display,windows->info.id,
15905              windows->info.screen);
15906            windows->icon.mapped=MagickTrue;
15907            break;
15908          }
15909        if (event.xmap.window == windows->command.id)
15910          {
15911            windows->command.mapped=MagickTrue;
15912            break;
15913          }
15914        if (event.xmap.window == windows->popup.id)
15915          {
15916            windows->popup.mapped=MagickTrue;
15917            break;
15918          }
15919        if (event.xmap.window == windows->widget.id)
15920          {
15921            windows->widget.mapped=MagickTrue;
15922            break;
15923          }
15924        break;
15925      }
15926      case MappingNotify:
15927      {
15928        (void) XRefreshKeyboardMapping(&event.xmapping);
15929        break;
15930      }
15931      case NoExpose:
15932        break;
15933      case PropertyNotify:
15934      {
15935        Atom
15936          type;
15937
15938        int
15939          format,
15940          status;
15941
15942        unsigned char
15943          *data;
15944
15945        unsigned long
15946          after,
15947          length;
15948
15949        if( IfMagickTrue(display_image->debug) )
15950          (void) LogMagickEvent(X11Event,GetMagickModule(),
15951            "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15952            event.xproperty.atom,event.xproperty.state);
15953        if (event.xproperty.atom != windows->im_remote_command)
15954          break;
15955        /*
15956          Display image named by the remote command protocol.
15957        */
15958        status=XGetWindowProperty(display,event.xproperty.window,
15959          event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15960          AnyPropertyType,&type,&format,&length,&after,&data);
15961        if ((status != Success) || (length == 0))
15962          break;
15963        if (LocaleCompare((char *) data,"-quit") == 0)
15964          {
15965            XClientMessage(display,windows->image.id,windows->im_protocols,
15966              windows->im_exit,CurrentTime);
15967            (void) XFree((void *) data);
15968            break;
15969          }
15970        (void) CopyMagickString(resource_info->image_info->filename,
15971          (char *) data,MaxTextExtent);
15972        (void) XFree((void *) data);
15973        nexus=ReadImage(resource_info->image_info,exception);
15974        CatchException(exception);
15975        if (nexus != (Image *) NULL)
15976          *state|=NextImageState | ExitState;
15977        break;
15978      }
15979      case ReparentNotify:
15980      {
15981        if( IfMagickTrue(display_image->debug) )
15982          (void) LogMagickEvent(X11Event,GetMagickModule(),
15983            "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15984            event.xreparent.window);
15985        break;
15986      }
15987      case UnmapNotify:
15988      {
15989        if( IfMagickTrue(display_image->debug) )
15990          (void) LogMagickEvent(X11Event,GetMagickModule(),
15991            "Unmap Notify: 0x%lx",event.xunmap.window);
15992        if (event.xunmap.window == windows->backdrop.id)
15993          {
15994            windows->backdrop.mapped=MagickFalse;
15995            break;
15996          }
15997        if (event.xunmap.window == windows->image.id)
15998          {
15999            windows->image.mapped=MagickFalse;
16000            break;
16001          }
16002        if (event.xunmap.window == windows->magnify.id)
16003          {
16004            windows->magnify.mapped=MagickFalse;
16005            break;
16006          }
16007        if (event.xunmap.window == windows->pan.id)
16008          {
16009            windows->pan.mapped=MagickFalse;
16010            break;
16011          }
16012        if (event.xunmap.window == windows->info.id)
16013          {
16014            windows->info.mapped=MagickFalse;
16015            break;
16016          }
16017        if (event.xunmap.window == windows->icon.id)
16018          {
16019            if (map_info->colormap == icon_map->colormap)
16020              XConfigureImageColormap(display,resource_info,windows,
16021                display_image,exception);
16022            (void) XFreeStandardColormap(display,icon_visual,icon_map,
16023              icon_pixel);
16024            windows->icon.mapped=MagickFalse;
16025            break;
16026          }
16027        if (event.xunmap.window == windows->command.id)
16028          {
16029            windows->command.mapped=MagickFalse;
16030            break;
16031          }
16032        if (event.xunmap.window == windows->popup.id)
16033          {
16034            if (windows->backdrop.id != (Window) NULL)
16035              (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16036                CurrentTime);
16037            windows->popup.mapped=MagickFalse;
16038            break;
16039          }
16040        if (event.xunmap.window == windows->widget.id)
16041          {
16042            if (windows->backdrop.id != (Window) NULL)
16043              (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16044                CurrentTime);
16045            windows->widget.mapped=MagickFalse;
16046            break;
16047          }
16048        break;
16049      }
16050      default:
16051      {
16052        if( IfMagickTrue(display_image->debug) )
16053          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
16054            event.type);
16055        break;
16056      }
16057    }
16058  } while (!(*state & ExitState));
16059  if ((*state & ExitState) == 0)
16060    (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
16061      &display_image,exception);
16062  else
16063    if( IfMagickTrue(resource_info->confirm_edit) )
16064      {
16065        /*
16066          Query user if image has changed.
16067        */
16068        if( IfMagickFalse(resource_info->immutable) &&
16069            IfMagickTrue(display_image->taint))
16070          {
16071            int
16072              status;
16073
16074            status=XConfirmWidget(display,windows,"Your image changed.",
16075              "Do you want to save it");
16076            if (status == 0)
16077              *state&=(~ExitState);
16078            else
16079              if (status > 0)
16080                (void) XMagickCommand(display,resource_info,windows,SaveCommand,
16081                  &display_image,exception);
16082          }
16083      }
16084  if ((windows->visual_info->klass == GrayScale) ||
16085      (windows->visual_info->klass == PseudoColor) ||
16086      (windows->visual_info->klass == DirectColor))
16087    {
16088      /*
16089        Withdraw pan and Magnify window.
16090      */
16091      if( IfMagickTrue(windows->info.mapped) )
16092        (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16093      if( IfMagickTrue(windows->magnify.mapped) )
16094        (void) XWithdrawWindow(display,windows->magnify.id,
16095          windows->magnify.screen);
16096      if( IfMagickTrue(windows->command.mapped) )
16097        (void) XWithdrawWindow(display,windows->command.id,
16098          windows->command.screen);
16099    }
16100  if( IfMagickTrue(windows->pan.mapped) )
16101    (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16102  if( IfMagickFalse(resource_info->backdrop) )
16103    if (windows->backdrop.mapped)
16104      {
16105        (void) XWithdrawWindow(display,windows->backdrop.id,
16106          windows->backdrop.screen);
16107        (void) XDestroyWindow(display,windows->backdrop.id);
16108        windows->backdrop.id=(Window) NULL;
16109        (void) XWithdrawWindow(display,windows->image.id,
16110          windows->image.screen);
16111        (void) XDestroyWindow(display,windows->image.id);
16112        windows->image.id=(Window) NULL;
16113      }
16114  XSetCursorState(display,windows,MagickTrue);
16115  XCheckRefreshWindows(display,windows);
16116  if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16117    *state&=(~ExitState);
16118  if (*state & ExitState)
16119    {
16120      /*
16121        Free Standard Colormap.
16122      */
16123      (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16124      if (resource_info->map_type == (char *) NULL)
16125        (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16126      /*
16127        Free X resources.
16128      */
16129      if (resource_info->copy_image != (Image *) NULL)
16130        {
16131          resource_info->copy_image=DestroyImage(resource_info->copy_image);
16132          resource_info->copy_image=NewImageList();
16133        }
16134      DestroyXResources();
16135    }
16136  (void) XSync(display,MagickFalse);
16137  /*
16138    Restore our progress monitor and warning handlers.
16139  */
16140  (void) SetErrorHandler(warning_handler);
16141  (void) SetWarningHandler(warning_handler);
16142  /*
16143    Change to home directory.
16144  */
16145  directory=getcwd(working_directory,MaxTextExtent);
16146  (void) directory;
16147  {
16148    int
16149      status;
16150
16151    if (*resource_info->home_directory == '\0')
16152      (void) CopyMagickString(resource_info->home_directory,".",MaxTextExtent);
16153    status=chdir(resource_info->home_directory);
16154    if (status == -1)
16155      (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
16156        "UnableToOpenFile","%s",resource_info->home_directory);
16157  }
16158  *image=display_image;
16159  return(nexus);
16160}
16161#else
16162
16163/*
16164%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16165%                                                                             %
16166%                                                                             %
16167%                                                                             %
16168+   D i s p l a y I m a g e s                                                 %
16169%                                                                             %
16170%                                                                             %
16171%                                                                             %
16172%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16173%
16174%  DisplayImages() displays an image sequence to any X window screen.  It
16175%  returns a value other than 0 if successful.  Check the exception member
16176%  of image to determine the reason for any failure.
16177%
16178%  The format of the DisplayImages method is:
16179%
16180%      MagickBooleanType DisplayImages(const ImageInfo *image_info,
16181%        Image *images,ExceptionInfo *exception)
16182%
16183%  A description of each parameter follows:
16184%
16185%    o image_info: the image info.
16186%
16187%    o image: the image.
16188%
16189%    o exception: return any errors or warnings in this structure.
16190%
16191*/
16192MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16193  Image *image,ExceptionInfo *exception)
16194{
16195  assert(image_info != (const ImageInfo *) NULL);
16196  assert(image_info->signature == MagickSignature);
16197  assert(image != (Image *) NULL);
16198  assert(image->signature == MagickSignature);
16199  if( IfMagickTrue(image->debug) )
16200    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16201  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16202    "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image->filename);
16203  return(MagickFalse);
16204}
16205
16206/*
16207%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16208%                                                                             %
16209%                                                                             %
16210%                                                                             %
16211+   R e m o t e D i s p l a y C o m m a n d                                   %
16212%                                                                             %
16213%                                                                             %
16214%                                                                             %
16215%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16216%
16217%  RemoteDisplayCommand() encourages a remote display program to display the
16218%  specified image filename.
16219%
16220%  The format of the RemoteDisplayCommand method is:
16221%
16222%      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16223%        const char *window,const char *filename,ExceptionInfo *exception)
16224%
16225%  A description of each parameter follows:
16226%
16227%    o image_info: the image info.
16228%
16229%    o window: Specifies the name or id of an X window.
16230%
16231%    o filename: the name of the image filename to display.
16232%
16233%    o exception: return any errors or warnings in this structure.
16234%
16235*/
16236MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16237  const char *window,const char *filename,ExceptionInfo *exception)
16238{
16239  assert(image_info != (const ImageInfo *) NULL);
16240  assert(image_info->signature == MagickSignature);
16241  assert(filename != (char *) NULL);
16242  (void) window;
16243  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16244  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16245    "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image_info->filename);
16246  return(MagickFalse);
16247}
16248#endif
16249