display.c revision 1454be7db7a897f42cd40e4165f945d77196a6f8
1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3%                                                                             %
4%                                                                             %
5%                                                                             %
6%               DDDD   IIIII  SSSSS  PPPP   L       AAA   Y   Y               %
7%               D   D    I    SS     P   P  L      A   A   Y Y                %
8%               D   D    I     SSS   PPPP   L      AAAAA    Y                 %
9%               D   D    I       SS  P      L      A   A    Y                 %
10%               DDDD   IIIII  SSSSS  P      LLLLL  A   A    Y                 %
11%                                                                             %
12%                                                                             %
13%        MagickCore Methods to Interactively Display and Edit an Image        %
14%                                                                             %
15%                             Software Design                                 %
16%                               John Cristy                                   %
17%                                July 1992                                    %
18%                                                                             %
19%                                                                             %
20%  Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization      %
21%  dedicated to making software imaging solutions freely available.           %
22%                                                                             %
23%  You may not use this file except in compliance with the License.  You may  %
24%  obtain a copy of the License at                                            %
25%                                                                             %
26%    http://www.imagemagick.org/script/license.php                            %
27%                                                                             %
28%  Unless required by applicable law or agreed to in writing, software        %
29%  distributed under the License is distributed on an "AS IS" BASIS,          %
30%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31%  See the License for the specific language governing permissions and        %
32%  limitations under the License.                                             %
33%                                                                             %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37*/
38
39/*
40  Include declarations.
41*/
42#include "MagickCore/studio.h"
43#include "MagickCore/artifact.h"
44#include "MagickCore/blob.h"
45#include "MagickCore/cache.h"
46#include "MagickCore/cache-private.h"
47#include "MagickCore/client.h"
48#include "MagickCore/color.h"
49#include "MagickCore/colorspace.h"
50#include "MagickCore/composite.h"
51#include "MagickCore/constitute.h"
52#include "MagickCore/decorate.h"
53#include "MagickCore/delegate.h"
54#include "MagickCore/display.h"
55#include "MagickCore/display-private.h"
56#include "MagickCore/distort.h"
57#include "MagickCore/draw.h"
58#include "MagickCore/effect.h"
59#include "MagickCore/enhance.h"
60#include "MagickCore/exception.h"
61#include "MagickCore/exception-private.h"
62#include "MagickCore/fx.h"
63#include "MagickCore/geometry.h"
64#include "MagickCore/image.h"
65#include "MagickCore/image-private.h"
66#include "MagickCore/list.h"
67#include "MagickCore/log.h"
68#include "MagickCore/magick.h"
69#include "MagickCore/memory_.h"
70#include "MagickCore/monitor.h"
71#include "MagickCore/monitor-private.h"
72#include "MagickCore/montage.h"
73#include "MagickCore/option.h"
74#include "MagickCore/paint.h"
75#include "MagickCore/pixel.h"
76#include "MagickCore/pixel-accessor.h"
77#include "MagickCore/PreRvIcccm.h"
78#include "MagickCore/property.h"
79#include "MagickCore/quantum.h"
80#include "MagickCore/quantum-private.h"
81#include "MagickCore/resize.h"
82#include "MagickCore/resource_.h"
83#include "MagickCore/shear.h"
84#include "MagickCore/segment.h"
85#include "MagickCore/statistic.h"
86#include "MagickCore/string_.h"
87#include "MagickCore/string-private.h"
88#include "MagickCore/transform.h"
89#include "MagickCore/threshold.h"
90#include "MagickCore/utility.h"
91#include "MagickCore/utility-private.h"
92#include "MagickCore/version.h"
93#include "MagickCore/widget.h"
94#include "MagickCore/widget-private.h"
95#include "MagickCore/xwindow.h"
96#include "MagickCore/xwindow-private.h"
97
98#if defined(MAGICKCORE_X11_DELEGATE)
99/*
100  Define declarations.
101*/
102#define MaxColors  MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
103
104/*
105  Constant declarations.
106*/
107static const unsigned char
108  HighlightBitmap[8] =
109  {
110    0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
111  },
112  OpaqueBitmap[8] =
113  {
114    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
115  },
116  ShadowBitmap[8] =
117  {
118    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
119  };
120
121static const char
122  *PageSizes[] =
123  {
124    "Letter",
125    "Tabloid",
126    "Ledger",
127    "Legal",
128    "Statement",
129    "Executive",
130    "A3",
131    "A4",
132    "A5",
133    "B4",
134    "B5",
135    "Folio",
136    "Quarto",
137    "10x14",
138    (char *) NULL
139  };
140
141/*
142  Help widget declarations.
143*/
144static const char
145  *ImageAnnotateHelp[] =
146  {
147    "In annotate mode, the Command widget has these options:",
148    "",
149    "    Font Name",
150    "      fixed",
151    "      variable",
152    "      5x8",
153    "      6x10",
154    "      7x13bold",
155    "      8x13bold",
156    "      9x15bold",
157    "      10x20",
158    "      12x24",
159    "      Browser...",
160    "    Font Color",
161    "      black",
162    "      blue",
163    "      cyan",
164    "      green",
165    "      gray",
166    "      red",
167    "      magenta",
168    "      yellow",
169    "      white",
170    "      transparent",
171    "      Browser...",
172    "    Font Color",
173    "      black",
174    "      blue",
175    "      cyan",
176    "      green",
177    "      gray",
178    "      red",
179    "      magenta",
180    "      yellow",
181    "      white",
182    "      transparent",
183    "      Browser...",
184    "    Rotate Text",
185    "      -90",
186    "      -45",
187    "      -30",
188    "      0",
189    "      30",
190    "      45",
191    "      90",
192    "      180",
193    "      Dialog...",
194    "    Help",
195    "    Dismiss",
196    "",
197    "Choose a font name from the Font Name sub-menu.  Additional",
198    "font names can be specified with the font browser.  You can",
199    "change the menu names by setting the X resources font1",
200    "through font9.",
201    "",
202    "Choose a font color from the Font Color sub-menu.",
203    "Additional font colors can be specified with the color",
204    "browser.  You can change the menu colors by setting the X",
205    "resources pen1 through pen9.",
206    "",
207    "If you select the color browser and press Grab, you can",
208    "choose the font color by moving the pointer to the desired",
209    "color on the screen and press any button.",
210    "",
211    "If you choose to rotate the text, choose Rotate Text from the",
212    "menu and select an angle.  Typically you will only want to",
213    "rotate one line of text at a time.  Depending on the angle you",
214    "choose, subsequent lines may end up overwriting each other.",
215    "",
216    "Choosing a font and its color is optional.  The default font",
217    "is fixed and the default color is black.  However, you must",
218    "choose a location to begin entering text and press button 1.",
219    "An underscore character will appear at the location of the",
220    "pointer.  The cursor changes to a pencil to indicate you are",
221    "in text mode.  To exit immediately, press Dismiss.",
222    "",
223    "In text mode, any key presses will display the character at",
224    "the location of the underscore and advance the underscore",
225    "cursor.  Enter your text and once completed press Apply to",
226    "finish your image annotation.  To correct errors press BACK",
227    "SPACE.  To delete an entire line of text, press DELETE.  Any",
228    "text that exceeds the boundaries of the image window is",
229    "automagically continued onto the next line.",
230    "",
231    "The actual color you request for the font is saved in the",
232    "image.  However, the color that appears in your image window",
233    "may be different.  For example, on a monochrome screen the",
234    "text will appear black or white even if you choose the color",
235    "red as the font color.  However, the image saved to a file",
236    "with -write is written with red lettering.  To assure the",
237    "correct color text in the final image, any PseudoClass image",
238    "is promoted to DirectClass (see miff(5)).  To force a",
239    "PseudoClass image to remain PseudoClass, use -colors.",
240    (char *) NULL,
241  },
242  *ImageChopHelp[] =
243  {
244    "In chop mode, the Command widget has these options:",
245    "",
246    "    Direction",
247    "      horizontal",
248    "      vertical",
249    "    Help",
250    "    Dismiss",
251    "",
252    "If the you choose the horizontal direction (this the",
253    "default), the area of the image between the two horizontal",
254    "endpoints of the chop line is removed.  Otherwise, the area",
255    "of the image between the two vertical endpoints of the chop",
256    "line is removed.",
257    "",
258    "Select a location within the image window to begin your chop,",
259    "press and hold any button.  Next, move the pointer to",
260    "another location in the image.  As you move a line will",
261    "connect the initial location and the pointer.  When you",
262    "release the button, the area within the image to chop is",
263    "determined by which direction you choose from the Command",
264    "widget.",
265    "",
266    "To cancel the image chopping, move the pointer back to the",
267    "starting point of the line and release the button.",
268    (char *) NULL,
269  },
270  *ImageColorEditHelp[] =
271  {
272    "In color edit mode, the Command widget has these options:",
273    "",
274    "    Method",
275    "      point",
276    "      replace",
277    "      floodfill",
278    "      filltoborder",
279    "      reset",
280    "    Pixel Color",
281    "      black",
282    "      blue",
283    "      cyan",
284    "      green",
285    "      gray",
286    "      red",
287    "      magenta",
288    "      yellow",
289    "      white",
290    "      Browser...",
291    "    Border Color",
292    "      black",
293    "      blue",
294    "      cyan",
295    "      green",
296    "      gray",
297    "      red",
298    "      magenta",
299    "      yellow",
300    "      white",
301    "      Browser...",
302    "    Fuzz",
303    "      0%",
304    "      2%",
305    "      5%",
306    "      10%",
307    "      15%",
308    "      Dialog...",
309    "    Undo",
310    "    Help",
311    "    Dismiss",
312    "",
313    "Choose a color editing method from the Method sub-menu",
314    "of the Command widget.  The point method recolors any pixel",
315    "selected with the pointer until the button is released.  The",
316    "replace method recolors any pixel that matches the color of",
317    "the pixel you select with a button press.  Floodfill recolors",
318    "any pixel that matches the color of the pixel you select with",
319    "a button press and is a neighbor.  Whereas filltoborder recolors",
320    "any neighbor pixel that is not the border color.  Finally reset",
321    "changes the entire image to the designated color.",
322    "",
323    "Next, choose a pixel color from the Pixel Color sub-menu.",
324    "Additional pixel colors can be specified with the color",
325    "browser.  You can change the menu colors by setting the X",
326    "resources pen1 through pen9.",
327    "",
328    "Now press button 1 to select a pixel within the image window",
329    "to change its color.  Additional pixels may be recolored as",
330    "prescribed by the method you choose.",
331    "",
332    "If the Magnify widget is mapped, it can be helpful in positioning",
333    "your pointer within the image (refer to button 2).",
334    "",
335    "The actual color you request for the pixels is saved in the",
336    "image.  However, the color that appears in your image window",
337    "may be different.  For example, on a monochrome screen the",
338    "pixel will appear black or white even if you choose the",
339    "color red as the pixel color.  However, the image saved to a",
340    "file with -write is written with red pixels.  To assure the",
341    "correct color text in the final image, any PseudoClass image",
342    "is promoted to DirectClass (see miff(5)).  To force a",
343    "PseudoClass image to remain PseudoClass, use -colors.",
344    (char *) NULL,
345  },
346  *ImageCompositeHelp[] =
347  {
348    "First a widget window is displayed requesting you to enter an",
349    "image name. Press Composite, Grab or type a file name.",
350    "Press Cancel if you choose not to create a composite image.",
351    "When you choose Grab, move the pointer to the desired window",
352    "and press any button.",
353    "",
354    "If the Composite image does not have any matte information,",
355    "you are informed and the file browser is displayed again.",
356    "Enter the name of a mask image.  The image is typically",
357    "grayscale and the same size as the composite image.  If the",
358    "image is not grayscale, it is converted to grayscale and the",
359    "resulting intensities are used as matte information.",
360    "",
361    "A small window appears showing the location of the cursor in",
362    "the image window. You are now in composite mode.  To exit",
363    "immediately, press Dismiss.  In composite mode, the Command",
364    "widget has these options:",
365    "",
366    "    Operators",
367    "      Over",
368    "      In",
369    "      Out",
370    "      Atop",
371    "      Xor",
372    "      Plus",
373    "      Minus",
374    "      Add",
375    "      Subtract",
376    "      Difference",
377    "      Multiply",
378    "      Bumpmap",
379    "      Copy",
380    "      CopyRed",
381    "      CopyGreen",
382    "      CopyBlue",
383    "      CopyOpacity",
384    "      Clear",
385    "    Dissolve",
386    "    Displace",
387    "    Help",
388    "    Dismiss",
389    "",
390    "Choose a composite operation from the Operators sub-menu of",
391    "the Command widget.  How each operator behaves is described",
392    "below.  Image window is the image currently displayed on",
393    "your X server and image is the image obtained with the File",
394    "Browser widget.",
395    "",
396    "Over     The result is the union of the two image shapes,",
397    "         with image obscuring image window in the region of",
398    "         overlap.",
399    "",
400    "In       The result is simply image cut by the shape of",
401    "         image window.  None of the image data of image",
402    "         window is in the result.",
403    "",
404    "Out      The resulting image is image with the shape of",
405    "         image window cut out.",
406    "",
407    "Atop     The result is the same shape as image image window,",
408    "         with image obscuring image window where the image",
409    "         shapes overlap.  Note this differs from over",
410    "         because the portion of image outside image window's",
411    "         shape does not appear in the result.",
412    "",
413    "Xor      The result is the image data from both image and",
414    "         image window that is outside the overlap region.",
415    "         The overlap region is blank.",
416    "",
417    "Plus     The result is just the sum of the image data.",
418    "         Output values are cropped to QuantumRange (no overflow).",
419    "",
420    "Minus    The result of image - image window, with underflow",
421    "         cropped to zero.",
422    "",
423    "Add      The result of image + image window, with overflow",
424    "         wrapping around (mod 256).",
425    "",
426    "Subtract The result of image - image window, with underflow",
427    "         wrapping around (mod 256).  The add and subtract",
428    "         operators can be used to perform reversible",
429    "         transformations.",
430    "",
431    "Difference",
432    "         The result of abs(image - image window).  This",
433    "         useful for comparing two very similar images.",
434    "",
435    "Multiply",
436    "         The result of image * image window.  This",
437    "         useful for the creation of drop-shadows.",
438    "",
439    "Bumpmap  The result of surface normals from image * image",
440    "         window.",
441    "",
442    "Copy     The resulting image is image window replaced with",
443    "         image.  Here the matte information is ignored.",
444    "",
445    "CopyRed  The red layer of the image window is replace with",
446    "         the red layer of the image.  The other layers are",
447    "         untouched.",
448    "",
449    "CopyGreen",
450    "         The green layer of the image window is replace with",
451    "         the green layer of the image.  The other layers are",
452    "         untouched.",
453    "",
454    "CopyBlue The blue layer of the image window is replace with",
455    "         the blue layer of the image.  The other layers are",
456    "         untouched.",
457    "",
458    "CopyOpacity",
459    "         The matte layer of the image window is replace with",
460    "         the matte layer of the image.  The other layers are",
461    "         untouched.",
462    "",
463    "The image compositor requires a matte, or alpha channel in",
464    "the image for some operations.  This extra channel usually",
465    "defines a mask which represents a sort of a cookie-cutter",
466    "for the image.  This the case when matte is opaque (full",
467    "coverage) for pixels inside the shape, zero outside, and",
468    "between 0 and QuantumRange on the boundary.  If image does not",
469    "have a matte channel, it is initialized with 0 for any pixel",
470    "matching in color to pixel location (0,0), otherwise QuantumRange.",
471    "",
472    "If you choose Dissolve, the composite operator becomes Over.  The",
473    "image matte channel percent transparency is initialized to factor.",
474    "The image window is initialized to (100-factor). Where factor is the",
475    "value you specify in the Dialog widget.",
476    "",
477    "Displace shifts the image pixels as defined by a displacement",
478    "map.  With this option, image is used as a displacement map.",
479    "Black, within the displacement map, is a maximum positive",
480    "displacement.  White is a maximum negative displacement and",
481    "middle gray is neutral.  The displacement is scaled to determine",
482    "the pixel shift.  By default, the displacement applies in both the",
483    "horizontal and vertical directions.  However, if you specify a mask,",
484    "image is the horizontal X displacement and mask the vertical Y",
485    "displacement.",
486    "",
487    "Note that matte information for image window is not retained",
488    "for colormapped X server visuals (e.g. StaticColor,",
489    "StaticColor, GrayScale, PseudoColor).  Correct compositing",
490    "behavior may require a TrueColor or DirectColor visual or a",
491    "Standard Colormap.",
492    "",
493    "Choosing a composite operator is optional.  The default",
494    "operator is replace.  However, you must choose a location to",
495    "composite your image and press button 1.  Press and hold the",
496    "button before releasing and an outline of the image will",
497    "appear to help you identify your location.",
498    "",
499    "The actual colors of the composite image is saved.  However,",
500    "the color that appears in image window may be different.",
501    "For example, on a monochrome screen image window will appear",
502    "black or white even though your composited image may have",
503    "many colors.  If the image is saved to a file it is written",
504    "with the correct colors.  To assure the correct colors are",
505    "saved in the final image, any PseudoClass image is promoted",
506    "to DirectClass (see miff(5)).  To force a PseudoClass image",
507    "to remain PseudoClass, use -colors.",
508    (char *) NULL,
509  },
510  *ImageCutHelp[] =
511  {
512    "In cut mode, the Command widget has these options:",
513    "",
514    "    Help",
515    "    Dismiss",
516    "",
517    "To define a cut region, press button 1 and drag.  The",
518    "cut region is defined by a highlighted rectangle that",
519    "expands or contracts as it follows the pointer.  Once you",
520    "are satisfied with the cut region, release the button.",
521    "You are now in rectify mode.  In rectify mode, the Command",
522    "widget has these options:",
523    "",
524    "    Cut",
525    "    Help",
526    "    Dismiss",
527    "",
528    "You can make adjustments by moving the pointer to one of the",
529    "cut rectangle corners, pressing a button, and dragging.",
530    "Finally, press Cut to commit your copy region.  To",
531    "exit without cutting the image, press Dismiss.",
532    (char *) NULL,
533  },
534  *ImageCopyHelp[] =
535  {
536    "In copy mode, the Command widget has these options:",
537    "",
538    "    Help",
539    "    Dismiss",
540    "",
541    "To define a copy region, press button 1 and drag.  The",
542    "copy region is defined by a highlighted rectangle that",
543    "expands or contracts as it follows the pointer.  Once you",
544    "are satisfied with the copy region, release the button.",
545    "You are now in rectify mode.  In rectify mode, the Command",
546    "widget has these options:",
547    "",
548    "    Copy",
549    "    Help",
550    "    Dismiss",
551    "",
552    "You can make adjustments by moving the pointer to one of the",
553    "copy rectangle corners, pressing a button, and dragging.",
554    "Finally, press Copy to commit your copy region.  To",
555    "exit without copying the image, press Dismiss.",
556    (char *) NULL,
557  },
558  *ImageCropHelp[] =
559  {
560    "In crop mode, the Command widget has these options:",
561    "",
562    "    Help",
563    "    Dismiss",
564    "",
565    "To define a cropping region, press button 1 and drag.  The",
566    "cropping region is defined by a highlighted rectangle that",
567    "expands or contracts as it follows the pointer.  Once you",
568    "are satisfied with the cropping region, release the button.",
569    "You are now in rectify mode.  In rectify mode, the Command",
570    "widget has these options:",
571    "",
572    "    Crop",
573    "    Help",
574    "    Dismiss",
575    "",
576    "You can make adjustments by moving the pointer to one of the",
577    "cropping rectangle corners, pressing a button, and dragging.",
578    "Finally, press Crop to commit your cropping region.  To",
579    "exit without cropping the image, press Dismiss.",
580    (char *) NULL,
581  },
582  *ImageDrawHelp[] =
583  {
584    "The cursor changes to a crosshair to indicate you are in",
585    "draw mode.  To exit immediately, press Dismiss.  In draw mode,",
586    "the Command widget has these options:",
587    "",
588    "    Element",
589    "      point",
590    "      line",
591    "      rectangle",
592    "      fill rectangle",
593    "      circle",
594    "      fill circle",
595    "      ellipse",
596    "      fill ellipse",
597    "      polygon",
598    "      fill polygon",
599    "    Color",
600    "      black",
601    "      blue",
602    "      cyan",
603    "      green",
604    "      gray",
605    "      red",
606    "      magenta",
607    "      yellow",
608    "      white",
609    "      transparent",
610    "      Browser...",
611    "    Stipple",
612    "      Brick",
613    "      Diagonal",
614    "      Scales",
615    "      Vertical",
616    "      Wavy",
617    "      Translucent",
618    "      Opaque",
619    "      Open...",
620    "    Width",
621    "      1",
622    "      2",
623    "      4",
624    "      8",
625    "      16",
626    "      Dialog...",
627    "    Undo",
628    "    Help",
629    "    Dismiss",
630    "",
631    "Choose a drawing primitive from the Element sub-menu.",
632    "",
633    "Choose a color from the Color sub-menu.  Additional",
634    "colors can be specified with the color browser.",
635    "",
636    "If you choose the color browser and press Grab, you can",
637    "select the color by moving the pointer to the desired",
638    "color on the screen and press any button.  The transparent",
639    "color updates the image matte channel and is useful for",
640    "image compositing.",
641    "",
642    "Choose a stipple, if appropriate, from the Stipple sub-menu.",
643    "Additional stipples can be specified with the file browser.",
644    "Stipples obtained from the file browser must be on disk in the",
645    "X11 bitmap format.",
646    "",
647    "Choose a width, if appropriate, from the Width sub-menu.  To",
648    "choose a specific width select the Dialog widget.",
649    "",
650    "Choose a point in the Image window and press button 1 and",
651    "hold.  Next, move the pointer to another location in the",
652    "image.  As you move, a line connects the initial location and",
653    "the pointer.  When you release the button, the image is",
654    "updated with the primitive you just drew.  For polygons, the",
655    "image is updated when you press and release the button without",
656    "moving the pointer.",
657    "",
658    "To cancel image drawing, move the pointer back to the",
659    "starting point of the line and release the button.",
660    (char *) NULL,
661  },
662  *DisplayHelp[] =
663  {
664    "BUTTONS",
665    "  The effects of each button press is described below.  Three",
666    "  buttons are required.  If you have a two button mouse,",
667    "  button 1 and 3 are returned.  Press ALT and button 3 to",
668    "  simulate button 2.",
669    "",
670    "  1    Press this button to map or unmap the Command widget.",
671    "",
672    "  2    Press and drag to define a region of the image to",
673    "       magnify.",
674    "",
675    "  3    Press and drag to choose from a select set of commands.",
676    "       This button behaves differently if the image being",
677    "       displayed is a visual image directory.  Here, choose a",
678    "       particular tile of the directory and press this button and",
679    "       drag to select a command from a pop-up menu.  Choose from",
680    "       these menu items:",
681    "",
682    "           Open",
683    "           Next",
684    "           Former",
685    "           Delete",
686    "           Update",
687    "",
688    "       If you choose Open, the image represented by the tile is",
689    "       displayed.  To return to the visual image directory, choose",
690    "       Next from the Command widget.  Next and Former moves to the",
691    "       next or former image respectively.  Choose Delete to delete",
692    "       a particular image tile.  Finally, choose Update to",
693    "       synchronize all the image tiles with their respective",
694    "       images.",
695    "",
696    "COMMAND WIDGET",
697    "  The Command widget lists a number of sub-menus and commands.",
698    "  They are",
699    "",
700    "      File",
701    "        Open...",
702    "        Next",
703    "        Former",
704    "        Select...",
705    "        Save...",
706    "        Print...",
707    "        Delete...",
708    "        New...",
709    "        Visual Directory...",
710    "        Quit",
711    "      Edit",
712    "        Undo",
713    "        Redo",
714    "        Cut",
715    "        Copy",
716    "        Paste",
717    "      View",
718    "        Half Size",
719    "        Original Size",
720    "        Double Size",
721    "        Resize...",
722    "        Apply",
723    "        Refresh",
724    "        Restore",
725    "      Transform",
726    "        Crop",
727    "        Chop",
728    "        Flop",
729    "        Flip",
730    "        Rotate Right",
731    "        Rotate Left",
732    "        Rotate...",
733    "        Shear...",
734    "        Roll...",
735    "        Trim Edges",
736    "      Enhance",
737    "        Brightness...",
738    "        Saturation...",
739    "        Hue...",
740    "        Gamma...",
741    "        Sharpen...",
742    "        Dull",
743    "        Contrast Stretch...",
744    "        Sigmoidal Contrast...",
745    "        Normalize",
746    "        Equalize",
747    "        Negate",
748    "        Grayscale",
749    "        Map...",
750    "        Quantize...",
751    "      Effects",
752    "        Despeckle",
753    "        Emboss",
754    "        Reduce Noise",
755    "        Add Noise",
756    "        Sharpen...",
757    "        Blur...",
758    "        Threshold...",
759    "        Edge Detect...",
760    "        Spread...",
761    "        Shade...",
762    "        Painting...",
763    "        Segment...",
764    "      F/X",
765    "        Solarize...",
766    "        Sepia Tone...",
767    "        Swirl...",
768    "        Implode...",
769    "        Vignette...",
770    "        Wave...",
771    "        Oil Painting...",
772    "        Charcoal Drawing...",
773    "      Image Edit",
774    "        Annotate...",
775    "        Draw...",
776    "        Color...",
777    "        Matte...",
778    "        Composite...",
779    "        Add Border...",
780    "        Add Frame...",
781    "        Comment...",
782    "        Launch...",
783    "        Region of Interest...",
784    "      Miscellany",
785    "        Image Info",
786    "        Zoom Image",
787    "        Show Preview...",
788    "        Show Histogram",
789    "        Show Matte",
790    "        Background...",
791    "        Slide Show",
792    "        Preferences...",
793    "      Help",
794    "        Overview",
795    "        Browse Documentation",
796    "        About Display",
797    "",
798    "  Menu items with a indented triangle have a sub-menu.  They",
799    "  are represented above as the indented items.  To access a",
800    "  sub-menu item, move the pointer to the appropriate menu and",
801    "  press a button and drag.  When you find the desired sub-menu",
802    "  item, release the button and the command is executed.  Move",
803    "  the pointer away from the sub-menu if you decide not to",
804    "  execute a particular command.",
805    "",
806    "KEYBOARD ACCELERATORS",
807    "  Accelerators are one or two key presses that effect a",
808    "  particular command.  The keyboard accelerators that",
809    "  display(1) understands is:",
810    "",
811    "  Ctl+O     Press to open an image from a file.",
812    "",
813    "  space     Press to display the next image.",
814    "",
815    "            If the image is a multi-paged document such as a Postscript",
816    "            document, you can skip ahead several pages by preceding",
817    "            this command with a number.  For example to display the",
818    "            third page beyond the current page, press 3<space>.",
819    "",
820    "  backspace Press to display the former image.",
821    "",
822    "            If the image is a multi-paged document such as a Postscript",
823    "            document, you can skip behind several pages by preceding",
824    "            this command with a number.  For example to display the",
825    "            third page preceding the current page, press 3<backspace>.",
826    "",
827    "  Ctl+S     Press to write the image to a file.",
828    "",
829    "  Ctl+P     Press to print the image to a Postscript printer.",
830    "",
831    "  Ctl+D     Press to delete an image file.",
832    "",
833    "  Ctl+N     Press to create a blank canvas.",
834    "",
835    "  Ctl+Q     Press to discard all images and exit program.",
836    "",
837    "  Ctl+Z     Press to undo last image transformation.",
838    "",
839    "  Ctl+R     Press to redo last image transformation.",
840    "",
841    "  Ctl+X     Press to cut a region of the image.",
842    "",
843    "  Ctl+C     Press to copy a region of the image.",
844    "",
845    "  Ctl+V     Press to paste a region to the image.",
846    "",
847    "  <         Press to half the image size.",
848    "",
849    "  -         Press to return to the original image size.",
850    "",
851    "  >         Press to double the image size.",
852    "",
853    "  %         Press to resize the image to a width and height you",
854    "            specify.",
855    "",
856    "Cmd-A       Press to make any image transformations permanent."
857    "",
858    "            By default, any image size transformations are applied",
859    "            to the original image to create the image displayed on",
860    "            the X server.  However, the transformations are not",
861    "            permanent (i.e. the original image does not change",
862    "            size only the X image does).  For example, if you",
863    "            press > the X image will appear to double in size,",
864    "            but the original image will in fact remain the same size.",
865    "            To force the original image to double in size, press >",
866    "            followed by Cmd-A.",
867    "",
868    "  @         Press to refresh the image window.",
869    "",
870    "  C         Press to cut out a rectangular region of the image.",
871    "",
872    "  [         Press to chop the image.",
873    "",
874    "  H         Press to flop image in the horizontal direction.",
875    "",
876    "  V         Press to flip image in the vertical direction.",
877    "",
878    "  /         Press to rotate the image 90 degrees clockwise.",
879    "",
880    " \\         Press to rotate the image 90 degrees counter-clockwise.",
881    "",
882    "  *         Press to rotate the image the number of degrees you",
883    "            specify.",
884    "",
885    "  S         Press to shear the image the number of degrees you",
886    "            specify.",
887    "",
888    "  R         Press to roll the image.",
889    "",
890    "  T         Press to trim the image edges.",
891    "",
892    "  Shft-H    Press to vary the image hue.",
893    "",
894    "  Shft-S    Press to vary the color saturation.",
895    "",
896    "  Shft-L    Press to vary the color brightness.",
897    "",
898    "  Shft-G    Press to gamma correct the image.",
899    "",
900    "  Shft-C    Press to sharpen the image contrast.",
901    "",
902    "  Shft-Z    Press to dull the image contrast.",
903    "",
904    "  =         Press to perform histogram equalization on the image.",
905    "",
906    "  Shft-N    Press to perform histogram normalization on the image.",
907    "",
908    "  Shft-~    Press to negate the colors of the image.",
909    "",
910    "  .         Press to convert the image colors to gray.",
911    "",
912    "  Shft-#    Press to set the maximum number of unique colors in the",
913    "            image.",
914    "",
915    "  F2        Press to reduce the speckles in an image.",
916    "",
917    "  F3        Press to eliminate peak noise from an image.",
918    "",
919    "  F4        Press to add noise to an image.",
920    "",
921    "  F5        Press to sharpen an image.",
922    "",
923    "  F6        Press to delete an image file.",
924    "",
925    "  F7        Press to threshold the image.",
926    "",
927    "  F8        Press to detect edges within an image.",
928    "",
929    "  F9        Press to emboss an image.",
930    "",
931    "  F10       Press to displace pixels by a random amount.",
932    "",
933    "  F11       Press to negate all pixels above the threshold level.",
934    "",
935    "  F12       Press to shade the image using a distant light source.",
936    "",
937    "  F13       Press to lighten or darken image edges to create a 3-D effect.",
938    "",
939    "  F14       Press to segment the image by color.",
940    "",
941    "  Meta-S    Press to swirl image pixels about the center.",
942    "",
943    "  Meta-I    Press to implode image pixels about the center.",
944    "",
945    "  Meta-W    Press to alter an image along a sine wave.",
946    "",
947    "  Meta-P    Press to simulate an oil painting.",
948    "",
949    "  Meta-C    Press to simulate a charcoal drawing.",
950    "",
951    "  Alt-A     Press to annotate the image with text.",
952    "",
953    "  Alt-D     Press to draw on an image.",
954    "",
955    "  Alt-P     Press to edit an image pixel color.",
956    "",
957    "  Alt-M     Press to edit the image matte information.",
958    "",
959    "  Alt-V     Press to composite the image with another.",
960    "",
961    "  Alt-B     Press to add a border to the image.",
962    "",
963    "  Alt-F     Press to add an ornamental border to the image.",
964    "",
965    "  Alt-Shft-!",
966    "            Press to add an image comment.",
967    "",
968    "  Ctl-A     Press to apply image processing techniques to a region",
969    "            of interest.",
970    "",
971    "  Shft-?    Press to display information about the image.",
972    "",
973    "  Shft-+    Press to map the zoom image window.",
974    "",
975    "  Shft-P    Press to preview an image enhancement, effect, or f/x.",
976    "",
977    "  F1        Press to display helpful information about display(1).",
978    "",
979    "  Find      Press to browse documentation about ImageMagick.",
980    "",
981    "  1-9       Press to change the level of magnification.",
982    "",
983    "  Use the arrow keys to move the image one pixel up, down,",
984    "  left, or right within the magnify window.  Be sure to first",
985    "  map the magnify window by pressing button 2.",
986    "",
987    "  Press ALT and one of the arrow keys to trim off one pixel",
988    "  from any side of the image.",
989    (char *) NULL,
990  },
991  *ImageMatteEditHelp[] =
992  {
993    "Matte information within an image is useful for some",
994    "operations such as image compositing (See IMAGE",
995    "COMPOSITING).  This extra channel usually defines a mask",
996    "which represents a sort of a cookie-cutter for the image.",
997    "This the case when matte is opaque (full coverage) for",
998    "pixels inside the shape, zero outside, and between 0 and",
999    "QuantumRange on the boundary.",
1000    "",
1001    "A small window appears showing the location of the cursor in",
1002    "the image window. You are now in matte edit mode.  To exit",
1003    "immediately, press Dismiss.  In matte edit mode, the Command",
1004    "widget has these options:",
1005    "",
1006    "    Method",
1007    "      point",
1008    "      replace",
1009    "      floodfill",
1010    "      filltoborder",
1011    "      reset",
1012    "    Border Color",
1013    "      black",
1014    "      blue",
1015    "      cyan",
1016    "      green",
1017    "      gray",
1018    "      red",
1019    "      magenta",
1020    "      yellow",
1021    "      white",
1022    "      Browser...",
1023    "    Fuzz",
1024    "      0%",
1025    "      2%",
1026    "      5%",
1027    "      10%",
1028    "      15%",
1029    "      Dialog...",
1030    "    Matte",
1031    "      Opaque",
1032    "      Transparent",
1033    "      Dialog...",
1034    "    Undo",
1035    "    Help",
1036    "    Dismiss",
1037    "",
1038    "Choose a matte editing method from the Method sub-menu of",
1039    "the Command widget.  The point method changes the matte value",
1040    "of any pixel selected with the pointer until the button is",
1041    "is released.  The replace method changes the matte value of",
1042    "any pixel that matches the color of the pixel you select with",
1043    "a button press.  Floodfill changes the matte value of any pixel",
1044    "that matches the color of the pixel you select with a button",
1045    "press and is a neighbor.  Whereas filltoborder changes the matte",
1046    "value any neighbor pixel that is not the border color.  Finally",
1047    "reset changes the entire image to the designated matte value.",
1048    "",
1049    "Choose Matte Value and pick Opaque or Transarent.  For other values",
1050    "select the Dialog entry.  Here a dialog appears requesting a matte",
1051    "value.  The value you select is assigned as the opacity value of the",
1052    "selected pixel or pixels.",
1053    "",
1054    "Now, press any button to select a pixel within the image",
1055    "window to change its matte value.",
1056    "",
1057    "If the Magnify widget is mapped, it can be helpful in positioning",
1058    "your pointer within the image (refer to button 2).",
1059    "",
1060    "Matte information is only valid in a DirectClass image.",
1061    "Therefore, any PseudoClass image is promoted to DirectClass",
1062    "(see miff(5)).  Note that matte information for PseudoClass",
1063    "is not retained for colormapped X server visuals (e.g.",
1064    "StaticColor, StaticColor, GrayScale, PseudoColor) unless you",
1065    "immediately save your image to a file (refer to Write).",
1066    "Correct matte editing behavior may require a TrueColor or",
1067    "DirectColor visual or a Standard Colormap.",
1068    (char *) NULL,
1069  },
1070  *ImagePanHelp[] =
1071  {
1072    "When an image exceeds the width or height of the X server",
1073    "screen, display maps a small panning icon.  The rectangle",
1074    "within the panning icon shows the area that is currently",
1075    "displayed in the image window.  To pan about the image,",
1076    "press any button and drag the pointer within the panning",
1077    "icon.  The pan rectangle moves with the pointer and the",
1078    "image window is updated to reflect the location of the",
1079    "rectangle within the panning icon.  When you have selected",
1080    "the area of the image you wish to view, release the button.",
1081    "",
1082    "Use the arrow keys to pan the image one pixel up, down,",
1083    "left, or right within the image window.",
1084    "",
1085    "The panning icon is withdrawn if the image becomes smaller",
1086    "than the dimensions of the X server screen.",
1087    (char *) NULL,
1088  },
1089  *ImagePasteHelp[] =
1090  {
1091    "A small window appears showing the location of the cursor in",
1092    "the image window. You are now in paste mode.  To exit",
1093    "immediately, press Dismiss.  In paste mode, the Command",
1094    "widget has these options:",
1095    "",
1096    "    Operators",
1097    "      over",
1098    "      in",
1099    "      out",
1100    "      atop",
1101    "      xor",
1102    "      plus",
1103    "      minus",
1104    "      add",
1105    "      subtract",
1106    "      difference",
1107    "      replace",
1108    "    Help",
1109    "    Dismiss",
1110    "",
1111    "Choose a composite operation from the Operators sub-menu of",
1112    "the Command widget.  How each operator behaves is described",
1113    "below.  Image window is the image currently displayed on",
1114    "your X server and image is the image obtained with the File",
1115    "Browser widget.",
1116    "",
1117    "Over     The result is the union of the two image shapes,",
1118    "         with image obscuring image window in the region of",
1119    "         overlap.",
1120    "",
1121    "In       The result is simply image cut by the shape of",
1122    "         image window.  None of the image data of image",
1123    "         window is in the result.",
1124    "",
1125    "Out      The resulting image is image with the shape of",
1126    "         image window cut out.",
1127    "",
1128    "Atop     The result is the same shape as image image window,",
1129    "         with image obscuring image window where the image",
1130    "         shapes overlap.  Note this differs from over",
1131    "         because the portion of image outside image window's",
1132    "         shape does not appear in the result.",
1133    "",
1134    "Xor      The result is the image data from both image and",
1135    "         image window that is outside the overlap region.",
1136    "         The overlap region is blank.",
1137    "",
1138    "Plus     The result is just the sum of the image data.",
1139    "         Output values are cropped to QuantumRange (no overflow).",
1140    "         This operation is independent of the matte",
1141    "         channels.",
1142    "",
1143    "Minus    The result of image - image window, with underflow",
1144    "         cropped to zero.",
1145    "",
1146    "Add      The result of image + image window, with overflow",
1147    "         wrapping around (mod 256).",
1148    "",
1149    "Subtract The result of image - image window, with underflow",
1150    "         wrapping around (mod 256).  The add and subtract",
1151    "         operators can be used to perform reversible",
1152    "         transformations.",
1153    "",
1154    "Difference",
1155    "         The result of abs(image - image window).  This",
1156    "         useful for comparing two very similar images.",
1157    "",
1158    "Copy     The resulting image is image window replaced with",
1159    "         image.  Here the matte information is ignored.",
1160    "",
1161    "CopyRed  The red layer of the image window is replace with",
1162    "         the red layer of the image.  The other layers are",
1163    "         untouched.",
1164    "",
1165    "CopyGreen",
1166    "         The green layer of the image window is replace with",
1167    "         the green layer of the image.  The other layers are",
1168    "         untouched.",
1169    "",
1170    "CopyBlue The blue layer of the image window is replace with",
1171    "         the blue layer of the image.  The other layers are",
1172    "         untouched.",
1173    "",
1174    "CopyOpacity",
1175    "         The matte layer of the image window is replace with",
1176    "         the matte layer of the image.  The other layers are",
1177    "         untouched.",
1178    "",
1179    "The image compositor requires a matte, or alpha channel in",
1180    "the image for some operations.  This extra channel usually",
1181    "defines a mask which represents a sort of a cookie-cutter",
1182    "for the image.  This the case when matte is opaque (full",
1183    "coverage) for pixels inside the shape, zero outside, and",
1184    "between 0 and QuantumRange on the boundary.  If image does not",
1185    "have a matte channel, it is initialized with 0 for any pixel",
1186    "matching in color to pixel location (0,0), otherwise QuantumRange.",
1187    "",
1188    "Note that matte information for image window is not retained",
1189    "for colormapped X server visuals (e.g. StaticColor,",
1190    "StaticColor, GrayScale, PseudoColor).  Correct compositing",
1191    "behavior may require a TrueColor or DirectColor visual or a",
1192    "Standard Colormap.",
1193    "",
1194    "Choosing a composite operator is optional.  The default",
1195    "operator is replace.  However, you must choose a location to",
1196    "paste your image and press button 1.  Press and hold the",
1197    "button before releasing and an outline of the image will",
1198    "appear to help you identify your location.",
1199    "",
1200    "The actual colors of the pasted image is saved.  However,",
1201    "the color that appears in image window may be different.",
1202    "For example, on a monochrome screen image window will appear",
1203    "black or white even though your pasted image may have",
1204    "many colors.  If the image is saved to a file it is written",
1205    "with the correct colors.  To assure the correct colors are",
1206    "saved in the final image, any PseudoClass image is promoted",
1207    "to DirectClass (see miff(5)).  To force a PseudoClass image",
1208    "to remain PseudoClass, use -colors.",
1209    (char *) NULL,
1210  },
1211  *ImageROIHelp[] =
1212  {
1213    "In region of interest mode, the Command widget has these",
1214    "options:",
1215    "",
1216    "    Help",
1217    "    Dismiss",
1218    "",
1219    "To define a region of interest, press button 1 and drag.",
1220    "The region of interest is defined by a highlighted rectangle",
1221    "that expands or contracts as it follows the pointer.  Once",
1222    "you are satisfied with the region of interest, release the",
1223    "button.  You are now in apply mode.  In apply mode the",
1224    "Command widget has these options:",
1225    "",
1226    "      File",
1227    "        Save...",
1228    "        Print...",
1229    "      Edit",
1230    "        Undo",
1231    "        Redo",
1232    "      Transform",
1233    "        Flop",
1234    "        Flip",
1235    "        Rotate Right",
1236    "        Rotate Left",
1237    "      Enhance",
1238    "        Hue...",
1239    "        Saturation...",
1240    "        Brightness...",
1241    "        Gamma...",
1242    "        Spiff",
1243    "        Dull",
1244    "        Contrast Stretch",
1245    "        Sigmoidal Contrast...",
1246    "        Normalize",
1247    "        Equalize",
1248    "        Negate",
1249    "        Grayscale",
1250    "        Map...",
1251    "        Quantize...",
1252    "      Effects",
1253    "        Despeckle",
1254    "        Emboss",
1255    "        Reduce Noise",
1256    "        Sharpen...",
1257    "        Blur...",
1258    "        Threshold...",
1259    "        Edge Detect...",
1260    "        Spread...",
1261    "        Shade...",
1262    "        Raise...",
1263    "        Segment...",
1264    "      F/X",
1265    "        Solarize...",
1266    "        Sepia Tone...",
1267    "        Swirl...",
1268    "        Implode...",
1269    "        Vignette...",
1270    "        Wave...",
1271    "        Oil Painting...",
1272    "        Charcoal Drawing...",
1273    "      Miscellany",
1274    "        Image Info",
1275    "        Zoom Image",
1276    "        Show Preview...",
1277    "        Show Histogram",
1278    "        Show Matte",
1279    "      Help",
1280    "      Dismiss",
1281    "",
1282    "You can make adjustments to the region of interest by moving",
1283    "the pointer to one of the rectangle corners, pressing a",
1284    "button, and dragging.  Finally, choose an image processing",
1285    "technique from the Command widget.  You can choose more than",
1286    "one image processing technique to apply to an area.",
1287    "Alternatively, you can move the region of interest before",
1288    "applying another image processing technique.  To exit, press",
1289    "Dismiss.",
1290    (char *) NULL,
1291  },
1292  *ImageRotateHelp[] =
1293  {
1294    "In rotate mode, the Command widget has these options:",
1295    "",
1296    "    Pixel Color",
1297    "      black",
1298    "      blue",
1299    "      cyan",
1300    "      green",
1301    "      gray",
1302    "      red",
1303    "      magenta",
1304    "      yellow",
1305    "      white",
1306    "      Browser...",
1307    "    Direction",
1308    "      horizontal",
1309    "      vertical",
1310    "    Help",
1311    "    Dismiss",
1312    "",
1313    "Choose a background color from the Pixel Color sub-menu.",
1314    "Additional background colors can be specified with the color",
1315    "browser.  You can change the menu colors by setting the X",
1316    "resources pen1 through pen9.",
1317    "",
1318    "If you choose the color browser and press Grab, you can",
1319    "select the background color by moving the pointer to the",
1320    "desired color on the screen and press any button.",
1321    "",
1322    "Choose a point in the image window and press this button and",
1323    "hold.  Next, move the pointer to another location in the",
1324    "image.  As you move a line connects the initial location and",
1325    "the pointer.  When you release the button, the degree of",
1326    "image rotation is determined by the slope of the line you",
1327    "just drew.  The slope is relative to the direction you",
1328    "choose from the Direction sub-menu of the Command widget.",
1329    "",
1330    "To cancel the image rotation, move the pointer back to the",
1331    "starting point of the line and release the button.",
1332    (char *) NULL,
1333  };
1334
1335/*
1336  Enumeration declarations.
1337*/
1338typedef enum
1339{
1340  CopyMode,
1341  CropMode,
1342  CutMode
1343} ClipboardMode;
1344
1345typedef enum
1346{
1347  OpenCommand,
1348  NextCommand,
1349  FormerCommand,
1350  SelectCommand,
1351  SaveCommand,
1352  PrintCommand,
1353  DeleteCommand,
1354  NewCommand,
1355  VisualDirectoryCommand,
1356  QuitCommand,
1357  UndoCommand,
1358  RedoCommand,
1359  CutCommand,
1360  CopyCommand,
1361  PasteCommand,
1362  HalfSizeCommand,
1363  OriginalSizeCommand,
1364  DoubleSizeCommand,
1365  ResizeCommand,
1366  ApplyCommand,
1367  RefreshCommand,
1368  RestoreCommand,
1369  CropCommand,
1370  ChopCommand,
1371  FlopCommand,
1372  FlipCommand,
1373  RotateRightCommand,
1374  RotateLeftCommand,
1375  RotateCommand,
1376  ShearCommand,
1377  RollCommand,
1378  TrimCommand,
1379  HueCommand,
1380  SaturationCommand,
1381  BrightnessCommand,
1382  GammaCommand,
1383  SpiffCommand,
1384  DullCommand,
1385  ContrastStretchCommand,
1386  SigmoidalContrastCommand,
1387  NormalizeCommand,
1388  EqualizeCommand,
1389  NegateCommand,
1390  GrayscaleCommand,
1391  MapCommand,
1392  QuantizeCommand,
1393  DespeckleCommand,
1394  EmbossCommand,
1395  ReduceNoiseCommand,
1396  AddNoiseCommand,
1397  SharpenCommand,
1398  BlurCommand,
1399  ThresholdCommand,
1400  EdgeDetectCommand,
1401  SpreadCommand,
1402  ShadeCommand,
1403  RaiseCommand,
1404  SegmentCommand,
1405  SolarizeCommand,
1406  SepiaToneCommand,
1407  SwirlCommand,
1408  ImplodeCommand,
1409  VignetteCommand,
1410  WaveCommand,
1411  OilPaintCommand,
1412  CharcoalDrawCommand,
1413  AnnotateCommand,
1414  DrawCommand,
1415  ColorCommand,
1416  MatteCommand,
1417  CompositeCommand,
1418  AddBorderCommand,
1419  AddFrameCommand,
1420  CommentCommand,
1421  LaunchCommand,
1422  RegionofInterestCommand,
1423  ROIHelpCommand,
1424  ROIDismissCommand,
1425  InfoCommand,
1426  ZoomCommand,
1427  ShowPreviewCommand,
1428  ShowHistogramCommand,
1429  ShowMatteCommand,
1430  BackgroundCommand,
1431  SlideShowCommand,
1432  PreferencesCommand,
1433  HelpCommand,
1434  BrowseDocumentationCommand,
1435  VersionCommand,
1436  SaveToUndoBufferCommand,
1437  FreeBuffersCommand,
1438  NullCommand
1439} CommandType;
1440
1441typedef enum
1442{
1443  AnnotateNameCommand,
1444  AnnotateFontColorCommand,
1445  AnnotateBackgroundColorCommand,
1446  AnnotateRotateCommand,
1447  AnnotateHelpCommand,
1448  AnnotateDismissCommand,
1449  TextHelpCommand,
1450  TextApplyCommand,
1451  ChopDirectionCommand,
1452  ChopHelpCommand,
1453  ChopDismissCommand,
1454  HorizontalChopCommand,
1455  VerticalChopCommand,
1456  ColorEditMethodCommand,
1457  ColorEditColorCommand,
1458  ColorEditBorderCommand,
1459  ColorEditFuzzCommand,
1460  ColorEditUndoCommand,
1461  ColorEditHelpCommand,
1462  ColorEditDismissCommand,
1463  CompositeOperatorsCommand,
1464  CompositeDissolveCommand,
1465  CompositeDisplaceCommand,
1466  CompositeHelpCommand,
1467  CompositeDismissCommand,
1468  CropHelpCommand,
1469  CropDismissCommand,
1470  RectifyCopyCommand,
1471  RectifyHelpCommand,
1472  RectifyDismissCommand,
1473  DrawElementCommand,
1474  DrawColorCommand,
1475  DrawStippleCommand,
1476  DrawWidthCommand,
1477  DrawUndoCommand,
1478  DrawHelpCommand,
1479  DrawDismissCommand,
1480  MatteEditMethod,
1481  MatteEditBorderCommand,
1482  MatteEditFuzzCommand,
1483  MatteEditValueCommand,
1484  MatteEditUndoCommand,
1485  MatteEditHelpCommand,
1486  MatteEditDismissCommand,
1487  PasteOperatorsCommand,
1488  PasteHelpCommand,
1489  PasteDismissCommand,
1490  RotateColorCommand,
1491  RotateDirectionCommand,
1492  RotateCropCommand,
1493  RotateSharpenCommand,
1494  RotateHelpCommand,
1495  RotateDismissCommand,
1496  HorizontalRotateCommand,
1497  VerticalRotateCommand,
1498  TileLoadCommand,
1499  TileNextCommand,
1500  TileFormerCommand,
1501  TileDeleteCommand,
1502  TileUpdateCommand
1503} ModeType;
1504
1505/*
1506  Stipples.
1507*/
1508#define BricksWidth  20
1509#define BricksHeight  20
1510#define DiagonalWidth  16
1511#define DiagonalHeight  16
1512#define HighlightWidth  8
1513#define HighlightHeight  8
1514#define OpaqueWidth  8
1515#define OpaqueHeight  8
1516#define ScalesWidth  16
1517#define ScalesHeight  16
1518#define ShadowWidth  8
1519#define ShadowHeight  8
1520#define VerticalWidth  16
1521#define VerticalHeight  16
1522#define WavyWidth  16
1523#define WavyHeight  16
1524
1525/*
1526  Constant declaration.
1527*/
1528static const int
1529  RoiDelta = 8;
1530
1531static const unsigned char
1532  BricksBitmap[] =
1533  {
1534    0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1535    0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1536    0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1537    0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1538    0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1539  },
1540  DiagonalBitmap[] =
1541  {
1542    0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1543    0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1544    0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1545  },
1546  ScalesBitmap[] =
1547  {
1548    0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1549    0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1550    0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1551  },
1552  VerticalBitmap[] =
1553  {
1554    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1555    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1556    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1557  },
1558  WavyBitmap[] =
1559  {
1560    0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1561    0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1562    0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1563  };
1564
1565/*
1566  Function prototypes.
1567*/
1568static CommandType
1569  XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1570    const MagickStatusType,KeySym,Image **,ExceptionInfo *);
1571
1572static Image
1573  *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
1574    Image **,ExceptionInfo *),
1575  *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1576  *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *,
1577    ExceptionInfo *),
1578  *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *,
1579    ExceptionInfo *);
1580
1581static MagickBooleanType
1582  XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *,
1583    ExceptionInfo *),
1584  XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **,
1585    ExceptionInfo *),
1586  XChopImage(Display *,XResourceInfo *,XWindows *,Image **,
1587    ExceptionInfo *),
1588  XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode,
1589    ExceptionInfo *),
1590  XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1591    ExceptionInfo *),
1592  XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *,
1593    ExceptionInfo *),
1594  XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1595  XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1596    ExceptionInfo *),
1597  XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1598    ExceptionInfo *),
1599  XPasteImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1600  XPrintImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1601  XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **,
1602    ExceptionInfo *),
1603  XROIImage(Display *,XResourceInfo *,XWindows *,Image **,ExceptionInfo *),
1604  XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1605  XTrimImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
1606
1607static void
1608  XDrawPanRectangle(Display *,XWindows *),
1609  XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **,
1610    ExceptionInfo *),
1611  XMagnifyImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1612  XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1613  XPanImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1614  XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1615    const KeySym,ExceptionInfo *),
1616  XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1617  XScreenEvent(Display *,XWindows *,XEvent *,ExceptionInfo *),
1618  XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1619
1620/*
1621%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1622%                                                                             %
1623%                                                                             %
1624%                                                                             %
1625%   D i s p l a y I m a g e s                                                 %
1626%                                                                             %
1627%                                                                             %
1628%                                                                             %
1629%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1630%
1631%  DisplayImages() displays an image sequence to any X window screen.  It
1632%  returns a value other than 0 if successful.  Check the exception member
1633%  of image to determine the reason for any failure.
1634%
1635%  The format of the DisplayImages method is:
1636%
1637%      MagickBooleanType DisplayImages(const ImageInfo *image_info,
1638%        Image *images,ExceptionInfo *exception)
1639%
1640%  A description of each parameter follows:
1641%
1642%    o image_info: the image info.
1643%
1644%    o image: the image.
1645%
1646%    o exception: return any errors or warnings in this structure.
1647%
1648*/
1649MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1650  Image *images,ExceptionInfo *exception)
1651{
1652  char
1653    *argv[1];
1654
1655  Display
1656    *display;
1657
1658  Image
1659    *image;
1660
1661  register ssize_t
1662    i;
1663
1664  size_t
1665    state;
1666
1667  XrmDatabase
1668    resource_database;
1669
1670  XResourceInfo
1671    resource_info;
1672
1673  assert(image_info != (const ImageInfo *) NULL);
1674  assert(image_info->signature == MagickSignature);
1675  assert(images != (Image *) NULL);
1676  assert(images->signature == MagickSignature);
1677  if (images->debug != MagickFalse)
1678    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1679  display=XOpenDisplay(image_info->server_name);
1680  if (display == (Display *) NULL)
1681    {
1682      (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1683        "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1684      return(MagickFalse);
1685    }
1686  if (exception->severity != UndefinedException)
1687    CatchException(exception);
1688  (void) XSetErrorHandler(XError);
1689  resource_database=XGetResourceDatabase(display,GetClientName());
1690  (void) ResetMagickMemory(&resource_info,0,sizeof(resource_info));
1691  XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1692  if (image_info->page != (char *) NULL)
1693    resource_info.image_geometry=AcquireString(image_info->page);
1694  resource_info.immutable=MagickTrue;
1695  argv[0]=AcquireString(GetClientName());
1696  state=DefaultState;
1697  for (i=0; (state & ExitState) == 0; i++)
1698  {
1699    if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1700      break;
1701    image=GetImageFromList(images,i % GetImageListLength(images));
1702    (void) XDisplayImage(display,&resource_info,argv,1,&image,&state,exception);
1703  }
1704  SetErrorHandler((ErrorHandler) NULL);
1705  SetWarningHandler((WarningHandler) NULL);
1706  argv[0]=DestroyString(argv[0]);
1707  (void) XCloseDisplay(display);
1708  XDestroyResourceInfo(&resource_info);
1709  if (exception->severity != UndefinedException)
1710    return(MagickFalse);
1711  return(MagickTrue);
1712}
1713
1714/*
1715%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1716%                                                                             %
1717%                                                                             %
1718%                                                                             %
1719%   R e m o t e D i s p l a y C o m m a n d                                   %
1720%                                                                             %
1721%                                                                             %
1722%                                                                             %
1723%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1724%
1725%  RemoteDisplayCommand() encourages a remote display program to display the
1726%  specified image filename.
1727%
1728%  The format of the RemoteDisplayCommand method is:
1729%
1730%      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1731%        const char *window,const char *filename,ExceptionInfo *exception)
1732%
1733%  A description of each parameter follows:
1734%
1735%    o image_info: the image info.
1736%
1737%    o window: Specifies the name or id of an X window.
1738%
1739%    o filename: the name of the image filename to display.
1740%
1741%    o exception: return any errors or warnings in this structure.
1742%
1743*/
1744MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1745  const char *window,const char *filename,ExceptionInfo *exception)
1746{
1747  Display
1748    *display;
1749
1750  MagickStatusType
1751    status;
1752
1753  assert(image_info != (const ImageInfo *) NULL);
1754  assert(image_info->signature == MagickSignature);
1755  assert(filename != (char *) NULL);
1756  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1757  display=XOpenDisplay(image_info->server_name);
1758  if (display == (Display *) NULL)
1759    {
1760      (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1761        "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1762      return(MagickFalse);
1763    }
1764  (void) XSetErrorHandler(XError);
1765  status=XRemoteCommand(display,window,filename);
1766  (void) XCloseDisplay(display);
1767  return(status != 0 ? MagickTrue : MagickFalse);
1768}
1769
1770/*
1771%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1772%                                                                             %
1773%                                                                             %
1774%                                                                             %
1775+   X A n n o t a t e E d i t I m a g e                                       %
1776%                                                                             %
1777%                                                                             %
1778%                                                                             %
1779%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1780%
1781%  XAnnotateEditImage() annotates the image with text.
1782%
1783%  The format of the XAnnotateEditImage method is:
1784%
1785%      MagickBooleanType XAnnotateEditImage(Display *display,
1786%        XResourceInfo *resource_info,XWindows *windows,Image *image,
1787%        ExceptionInfo *exception)
1788%
1789%  A description of each parameter follows:
1790%
1791%    o display: Specifies a connection to an X server;  returned from
1792%      XOpenDisplay.
1793%
1794%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1795%
1796%    o windows: Specifies a pointer to a XWindows structure.
1797%
1798%    o image: the image; returned from ReadImage.
1799%
1800*/
1801
1802static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
1803{
1804  if (x > y)
1805    return(x);
1806  return(y);
1807}
1808
1809static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
1810{
1811  if (x < y)
1812    return(x);
1813  return(y);
1814}
1815
1816static MagickBooleanType XAnnotateEditImage(Display *display,
1817  XResourceInfo *resource_info,XWindows *windows,Image *image,
1818  ExceptionInfo *exception)
1819{
1820  static const char
1821    *AnnotateMenu[] =
1822    {
1823      "Font Name",
1824      "Font Color",
1825      "Box Color",
1826      "Rotate Text",
1827      "Help",
1828      "Dismiss",
1829      (char *) NULL
1830    },
1831    *TextMenu[] =
1832    {
1833      "Help",
1834      "Apply",
1835      (char *) NULL
1836    };
1837
1838  static const ModeType
1839    AnnotateCommands[] =
1840    {
1841      AnnotateNameCommand,
1842      AnnotateFontColorCommand,
1843      AnnotateBackgroundColorCommand,
1844      AnnotateRotateCommand,
1845      AnnotateHelpCommand,
1846      AnnotateDismissCommand
1847    },
1848    TextCommands[] =
1849    {
1850      TextHelpCommand,
1851      TextApplyCommand
1852    };
1853
1854  static MagickBooleanType
1855    transparent_box = MagickTrue,
1856    transparent_pen = MagickFalse;
1857
1858  static MagickRealType
1859    degrees = 0.0;
1860
1861  static unsigned int
1862    box_id = MaxNumberPens-2,
1863    font_id = 0,
1864    pen_id = 0;
1865
1866  char
1867    command[MaxTextExtent],
1868    text[MaxTextExtent];
1869
1870  const char
1871    *ColorMenu[MaxNumberPens+1];
1872
1873  Cursor
1874    cursor;
1875
1876  GC
1877    annotate_context;
1878
1879  int
1880    id,
1881    pen_number,
1882    status,
1883    x,
1884    y;
1885
1886  KeySym
1887    key_symbol;
1888
1889  register char
1890    *p;
1891
1892  register ssize_t
1893    i;
1894
1895  unsigned int
1896    height,
1897    width;
1898
1899  size_t
1900    state;
1901
1902  XAnnotateInfo
1903    *annotate_info,
1904    *previous_info;
1905
1906  XColor
1907    color;
1908
1909  XFontStruct
1910    *font_info;
1911
1912  XEvent
1913    event,
1914    text_event;
1915
1916  /*
1917    Map Command widget.
1918  */
1919  (void) CloneString(&windows->command.name,"Annotate");
1920  windows->command.data=4;
1921  (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1922  (void) XMapRaised(display,windows->command.id);
1923  XClientMessage(display,windows->image.id,windows->im_protocols,
1924    windows->im_update_widget,CurrentTime);
1925  /*
1926    Track pointer until button 1 is pressed.
1927  */
1928  XQueryPosition(display,windows->image.id,&x,&y);
1929  (void) XSelectInput(display,windows->image.id,
1930    windows->image.attributes.event_mask | PointerMotionMask);
1931  cursor=XCreateFontCursor(display,XC_left_side);
1932  (void) XCheckDefineCursor(display,windows->image.id,cursor);
1933  state=DefaultState;
1934  do
1935  {
1936    if (windows->info.mapped != MagickFalse)
1937      {
1938        /*
1939          Display pointer position.
1940        */
1941        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
1942          x+windows->image.x,y+windows->image.y);
1943        XInfoWidget(display,windows,text);
1944      }
1945    /*
1946      Wait for next event.
1947    */
1948    XScreenEvent(display,windows,&event,exception);
1949    if (event.xany.window == windows->command.id)
1950      {
1951        /*
1952          Select a command from the Command widget.
1953        */
1954        id=XCommandWidget(display,windows,AnnotateMenu,&event);
1955        (void) XCheckDefineCursor(display,windows->image.id,cursor);
1956        if (id < 0)
1957          continue;
1958        switch (AnnotateCommands[id])
1959        {
1960          case AnnotateNameCommand:
1961          {
1962            const char
1963              *FontMenu[MaxNumberFonts];
1964
1965            int
1966              font_number;
1967
1968            /*
1969              Initialize menu selections.
1970            */
1971            for (i=0; i < MaxNumberFonts; i++)
1972              FontMenu[i]=resource_info->font_name[i];
1973            FontMenu[MaxNumberFonts-2]="Browser...";
1974            FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1975            /*
1976              Select a font name from the pop-up menu.
1977            */
1978            font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1979              (const char **) FontMenu,command);
1980            if (font_number < 0)
1981              break;
1982            if (font_number == (MaxNumberFonts-2))
1983              {
1984                static char
1985                  font_name[MaxTextExtent] = "fixed";
1986
1987                /*
1988                  Select a font name from a browser.
1989                */
1990                resource_info->font_name[font_number]=font_name;
1991                XFontBrowserWidget(display,windows,"Select",font_name);
1992                if (*font_name == '\0')
1993                  break;
1994              }
1995            /*
1996              Initialize font info.
1997            */
1998            font_info=XLoadQueryFont(display,resource_info->font_name[
1999              font_number]);
2000            if (font_info == (XFontStruct *) NULL)
2001              {
2002                XNoticeWidget(display,windows,"Unable to load font:",
2003                  resource_info->font_name[font_number]);
2004                break;
2005              }
2006            font_id=(unsigned int) font_number;
2007            (void) XFreeFont(display,font_info);
2008            break;
2009          }
2010          case AnnotateFontColorCommand:
2011          {
2012            /*
2013              Initialize menu selections.
2014            */
2015            for (i=0; i < (int) (MaxNumberPens-2); i++)
2016              ColorMenu[i]=resource_info->pen_colors[i];
2017            ColorMenu[MaxNumberPens-2]="transparent";
2018            ColorMenu[MaxNumberPens-1]="Browser...";
2019            ColorMenu[MaxNumberPens]=(const char *) NULL;
2020            /*
2021              Select a pen color from the pop-up menu.
2022            */
2023            pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2024              (const char **) ColorMenu,command);
2025            if (pen_number < 0)
2026              break;
2027            transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
2028              MagickFalse;
2029            if (transparent_pen != MagickFalse)
2030              break;
2031            if (pen_number == (MaxNumberPens-1))
2032              {
2033                static char
2034                  color_name[MaxTextExtent] = "gray";
2035
2036                /*
2037                  Select a pen color from a dialog.
2038                */
2039                resource_info->pen_colors[pen_number]=color_name;
2040                XColorBrowserWidget(display,windows,"Select",color_name);
2041                if (*color_name == '\0')
2042                  break;
2043              }
2044            /*
2045              Set pen color.
2046            */
2047            (void) XParseColor(display,windows->map_info->colormap,
2048              resource_info->pen_colors[pen_number],&color);
2049            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2050              (unsigned int) MaxColors,&color);
2051            windows->pixel_info->pen_colors[pen_number]=color;
2052            pen_id=(unsigned int) pen_number;
2053            break;
2054          }
2055          case AnnotateBackgroundColorCommand:
2056          {
2057            /*
2058              Initialize menu selections.
2059            */
2060            for (i=0; i < (int) (MaxNumberPens-2); i++)
2061              ColorMenu[i]=resource_info->pen_colors[i];
2062            ColorMenu[MaxNumberPens-2]="transparent";
2063            ColorMenu[MaxNumberPens-1]="Browser...";
2064            ColorMenu[MaxNumberPens]=(const char *) NULL;
2065            /*
2066              Select a pen color from the pop-up menu.
2067            */
2068            pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2069              (const char **) ColorMenu,command);
2070            if (pen_number < 0)
2071              break;
2072            transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2073              MagickFalse;
2074            if (transparent_box != MagickFalse)
2075              break;
2076            if (pen_number == (MaxNumberPens-1))
2077              {
2078                static char
2079                  color_name[MaxTextExtent] = "gray";
2080
2081                /*
2082                  Select a pen color from a dialog.
2083                */
2084                resource_info->pen_colors[pen_number]=color_name;
2085                XColorBrowserWidget(display,windows,"Select",color_name);
2086                if (*color_name == '\0')
2087                  break;
2088              }
2089            /*
2090              Set pen color.
2091            */
2092            (void) XParseColor(display,windows->map_info->colormap,
2093              resource_info->pen_colors[pen_number],&color);
2094            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2095              (unsigned int) MaxColors,&color);
2096            windows->pixel_info->pen_colors[pen_number]=color;
2097            box_id=(unsigned int) pen_number;
2098            break;
2099          }
2100          case AnnotateRotateCommand:
2101          {
2102            int
2103              entry;
2104
2105            static char
2106              angle[MaxTextExtent] = "30.0";
2107
2108            static const char
2109              *RotateMenu[] =
2110              {
2111                "-90",
2112                "-45",
2113                "-30",
2114                "0",
2115                "30",
2116                "45",
2117                "90",
2118                "180",
2119                "Dialog...",
2120                (char *) NULL,
2121              };
2122
2123            /*
2124              Select a command from the pop-up menu.
2125            */
2126            entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2127              command);
2128            if (entry < 0)
2129              break;
2130            if (entry != 8)
2131              {
2132                degrees=StringToDouble(RotateMenu[entry],(char **) NULL);
2133                break;
2134              }
2135            (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2136              angle);
2137            if (*angle == '\0')
2138              break;
2139            degrees=StringToDouble(angle,(char **) NULL);
2140            break;
2141          }
2142          case AnnotateHelpCommand:
2143          {
2144            XTextViewWidget(display,resource_info,windows,MagickFalse,
2145              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2146            break;
2147          }
2148          case AnnotateDismissCommand:
2149          {
2150            /*
2151              Prematurely exit.
2152            */
2153            state|=EscapeState;
2154            state|=ExitState;
2155            break;
2156          }
2157          default:
2158            break;
2159        }
2160        continue;
2161      }
2162    switch (event.type)
2163    {
2164      case ButtonPress:
2165      {
2166        if (event.xbutton.button != Button1)
2167          break;
2168        if (event.xbutton.window != windows->image.id)
2169          break;
2170        /*
2171          Change to text entering mode.
2172        */
2173        x=event.xbutton.x;
2174        y=event.xbutton.y;
2175        state|=ExitState;
2176        break;
2177      }
2178      case ButtonRelease:
2179        break;
2180      case Expose:
2181        break;
2182      case KeyPress:
2183      {
2184        if (event.xkey.window != windows->image.id)
2185          break;
2186        /*
2187          Respond to a user key press.
2188        */
2189        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2190          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2191        switch ((int) key_symbol)
2192        {
2193          case XK_Escape:
2194          case XK_F20:
2195          {
2196            /*
2197              Prematurely exit.
2198            */
2199            state|=EscapeState;
2200            state|=ExitState;
2201            break;
2202          }
2203          case XK_F1:
2204          case XK_Help:
2205          {
2206            XTextViewWidget(display,resource_info,windows,MagickFalse,
2207              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2208            break;
2209          }
2210          default:
2211          {
2212            (void) XBell(display,0);
2213            break;
2214          }
2215        }
2216        break;
2217      }
2218      case MotionNotify:
2219      {
2220        /*
2221          Map and unmap Info widget as cursor crosses its boundaries.
2222        */
2223        x=event.xmotion.x;
2224        y=event.xmotion.y;
2225        if (windows->info.mapped != MagickFalse)
2226          {
2227            if ((x < (int) (windows->info.x+windows->info.width)) &&
2228                (y < (int) (windows->info.y+windows->info.height)))
2229              (void) XWithdrawWindow(display,windows->info.id,
2230                windows->info.screen);
2231          }
2232        else
2233          if ((x > (int) (windows->info.x+windows->info.width)) ||
2234              (y > (int) (windows->info.y+windows->info.height)))
2235            (void) XMapWindow(display,windows->info.id);
2236        break;
2237      }
2238      default:
2239        break;
2240    }
2241  } while ((state & ExitState) == 0);
2242  (void) XSelectInput(display,windows->image.id,
2243    windows->image.attributes.event_mask);
2244  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2245  if ((state & EscapeState) != 0)
2246    return(MagickTrue);
2247  /*
2248    Set font info and check boundary conditions.
2249  */
2250  font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2251  if (font_info == (XFontStruct *) NULL)
2252    {
2253      XNoticeWidget(display,windows,"Unable to load font:",
2254        resource_info->font_name[font_id]);
2255      font_info=windows->font_info;
2256    }
2257  if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2258    x=(int) windows->image.width-font_info->max_bounds.width;
2259  if (y < (int) (font_info->ascent+font_info->descent))
2260    y=(int) font_info->ascent+font_info->descent;
2261  if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2262      ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2263    return(MagickFalse);
2264  /*
2265    Initialize annotate structure.
2266  */
2267  annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2268  if (annotate_info == (XAnnotateInfo *) NULL)
2269    return(MagickFalse);
2270  XGetAnnotateInfo(annotate_info);
2271  annotate_info->x=x;
2272  annotate_info->y=y;
2273  if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
2274    annotate_info->stencil=OpaqueStencil;
2275  else
2276    if (transparent_box == MagickFalse)
2277      annotate_info->stencil=BackgroundStencil;
2278    else
2279      annotate_info->stencil=ForegroundStencil;
2280  annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2281  annotate_info->degrees=degrees;
2282  annotate_info->font_info=font_info;
2283  annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2284    windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
2285    sizeof(*annotate_info->text));
2286  if (annotate_info->text == (char *) NULL)
2287    return(MagickFalse);
2288  /*
2289    Create cursor and set graphic context.
2290  */
2291  cursor=XCreateFontCursor(display,XC_pencil);
2292  (void) XCheckDefineCursor(display,windows->image.id,cursor);
2293  annotate_context=windows->image.annotate_context;
2294  (void) XSetFont(display,annotate_context,font_info->fid);
2295  (void) XSetBackground(display,annotate_context,
2296    windows->pixel_info->pen_colors[box_id].pixel);
2297  (void) XSetForeground(display,annotate_context,
2298    windows->pixel_info->pen_colors[pen_id].pixel);
2299  /*
2300    Begin annotating the image with text.
2301  */
2302  (void) CloneString(&windows->command.name,"Text");
2303  windows->command.data=0;
2304  (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2305  state=DefaultState;
2306  (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2307  text_event.xexpose.width=(int) font_info->max_bounds.width;
2308  text_event.xexpose.height=font_info->max_bounds.ascent+
2309    font_info->max_bounds.descent;
2310  p=annotate_info->text;
2311  do
2312  {
2313    /*
2314      Display text cursor.
2315    */
2316    *p='\0';
2317    (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2318    /*
2319      Wait for next event.
2320    */
2321    XScreenEvent(display,windows,&event,exception);
2322    if (event.xany.window == windows->command.id)
2323      {
2324        /*
2325          Select a command from the Command widget.
2326        */
2327        (void) XSetBackground(display,annotate_context,
2328          windows->pixel_info->background_color.pixel);
2329        (void) XSetForeground(display,annotate_context,
2330          windows->pixel_info->foreground_color.pixel);
2331        id=XCommandWidget(display,windows,AnnotateMenu,&event);
2332        (void) XSetBackground(display,annotate_context,
2333          windows->pixel_info->pen_colors[box_id].pixel);
2334        (void) XSetForeground(display,annotate_context,
2335          windows->pixel_info->pen_colors[pen_id].pixel);
2336        if (id < 0)
2337          continue;
2338        switch (TextCommands[id])
2339        {
2340          case TextHelpCommand:
2341          {
2342            XTextViewWidget(display,resource_info,windows,MagickFalse,
2343              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2344            (void) XCheckDefineCursor(display,windows->image.id,cursor);
2345            break;
2346          }
2347          case TextApplyCommand:
2348          {
2349            /*
2350              Finished annotating.
2351            */
2352            annotate_info->width=(unsigned int) XTextWidth(font_info,
2353              annotate_info->text,(int) strlen(annotate_info->text));
2354            XRefreshWindow(display,&windows->image,&text_event);
2355            state|=ExitState;
2356            break;
2357          }
2358          default:
2359            break;
2360        }
2361        continue;
2362      }
2363    /*
2364      Erase text cursor.
2365    */
2366    text_event.xexpose.x=x;
2367    text_event.xexpose.y=y-font_info->max_bounds.ascent;
2368    (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2369      (unsigned int) text_event.xexpose.width,(unsigned int)
2370      text_event.xexpose.height,MagickFalse);
2371    XRefreshWindow(display,&windows->image,&text_event);
2372    switch (event.type)
2373    {
2374      case ButtonPress:
2375      {
2376        if (event.xbutton.window != windows->image.id)
2377          break;
2378        if (event.xbutton.button == Button2)
2379          {
2380            /*
2381              Request primary selection.
2382            */
2383            (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2384              windows->image.id,CurrentTime);
2385            break;
2386          }
2387        break;
2388      }
2389      case Expose:
2390      {
2391        if (event.xexpose.count == 0)
2392          {
2393            XAnnotateInfo
2394              *text_info;
2395
2396            /*
2397              Refresh Image window.
2398            */
2399            XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2400            text_info=annotate_info;
2401            while (text_info != (XAnnotateInfo *) NULL)
2402            {
2403              if (annotate_info->stencil == ForegroundStencil)
2404                (void) XDrawString(display,windows->image.id,annotate_context,
2405                  text_info->x,text_info->y,text_info->text,
2406                  (int) strlen(text_info->text));
2407              else
2408                (void) XDrawImageString(display,windows->image.id,
2409                  annotate_context,text_info->x,text_info->y,text_info->text,
2410                  (int) strlen(text_info->text));
2411              text_info=text_info->previous;
2412            }
2413            (void) XDrawString(display,windows->image.id,annotate_context,
2414              x,y,"_",1);
2415          }
2416        break;
2417      }
2418      case KeyPress:
2419      {
2420        int
2421          length;
2422
2423        if (event.xkey.window != windows->image.id)
2424          break;
2425        /*
2426          Respond to a user key press.
2427        */
2428        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2429          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2430        *(command+length)='\0';
2431        if (((event.xkey.state & ControlMask) != 0) ||
2432            ((event.xkey.state & Mod1Mask) != 0))
2433          state|=ModifierState;
2434        if ((state & ModifierState) != 0)
2435          switch ((int) key_symbol)
2436          {
2437            case XK_u:
2438            case XK_U:
2439            {
2440              key_symbol=DeleteCommand;
2441              break;
2442            }
2443            default:
2444              break;
2445          }
2446        switch ((int) key_symbol)
2447        {
2448          case XK_BackSpace:
2449          {
2450            /*
2451              Erase one character.
2452            */
2453            if (p == annotate_info->text)
2454              {
2455                if (annotate_info->previous == (XAnnotateInfo *) NULL)
2456                  break;
2457                else
2458                  {
2459                    /*
2460                      Go to end of the previous line of text.
2461                    */
2462                    annotate_info=annotate_info->previous;
2463                    p=annotate_info->text;
2464                    x=annotate_info->x+annotate_info->width;
2465                    y=annotate_info->y;
2466                    if (annotate_info->width != 0)
2467                      p+=strlen(annotate_info->text);
2468                    break;
2469                  }
2470              }
2471            p--;
2472            x-=XTextWidth(font_info,p,1);
2473            text_event.xexpose.x=x;
2474            text_event.xexpose.y=y-font_info->max_bounds.ascent;
2475            XRefreshWindow(display,&windows->image,&text_event);
2476            break;
2477          }
2478          case XK_bracketleft:
2479          {
2480            key_symbol=XK_Escape;
2481            break;
2482          }
2483          case DeleteCommand:
2484          {
2485            /*
2486              Erase the entire line of text.
2487            */
2488            while (p != annotate_info->text)
2489            {
2490              p--;
2491              x-=XTextWidth(font_info,p,1);
2492              text_event.xexpose.x=x;
2493              XRefreshWindow(display,&windows->image,&text_event);
2494            }
2495            break;
2496          }
2497          case XK_Escape:
2498          case XK_F20:
2499          {
2500            /*
2501              Finished annotating.
2502            */
2503            annotate_info->width=(unsigned int) XTextWidth(font_info,
2504              annotate_info->text,(int) strlen(annotate_info->text));
2505            XRefreshWindow(display,&windows->image,&text_event);
2506            state|=ExitState;
2507            break;
2508          }
2509          default:
2510          {
2511            /*
2512              Draw a single character on the Image window.
2513            */
2514            if ((state & ModifierState) != 0)
2515              break;
2516            if (*command == '\0')
2517              break;
2518            *p=(*command);
2519            if (annotate_info->stencil == ForegroundStencil)
2520              (void) XDrawString(display,windows->image.id,annotate_context,
2521                x,y,p,1);
2522            else
2523              (void) XDrawImageString(display,windows->image.id,
2524                annotate_context,x,y,p,1);
2525            x+=XTextWidth(font_info,p,1);
2526            p++;
2527            if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2528              break;
2529          }
2530          case XK_Return:
2531          case XK_KP_Enter:
2532          {
2533            /*
2534              Advance to the next line of text.
2535            */
2536            *p='\0';
2537            annotate_info->width=(unsigned int) XTextWidth(font_info,
2538              annotate_info->text,(int) strlen(annotate_info->text));
2539            if (annotate_info->next != (XAnnotateInfo *) NULL)
2540              {
2541                /*
2542                  Line of text already exists.
2543                */
2544                annotate_info=annotate_info->next;
2545                x=annotate_info->x;
2546                y=annotate_info->y;
2547                p=annotate_info->text;
2548                break;
2549              }
2550            annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2551              sizeof(*annotate_info->next));
2552            if (annotate_info->next == (XAnnotateInfo *) NULL)
2553              return(MagickFalse);
2554            *annotate_info->next=(*annotate_info);
2555            annotate_info->next->previous=annotate_info;
2556            annotate_info=annotate_info->next;
2557            annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2558              windows->image.width/MagickMax((ssize_t)
2559              font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2560            if (annotate_info->text == (char *) NULL)
2561              return(MagickFalse);
2562            annotate_info->y+=annotate_info->height;
2563            if (annotate_info->y > (int) windows->image.height)
2564              annotate_info->y=(int) annotate_info->height;
2565            annotate_info->next=(XAnnotateInfo *) NULL;
2566            x=annotate_info->x;
2567            y=annotate_info->y;
2568            p=annotate_info->text;
2569            break;
2570          }
2571        }
2572        break;
2573      }
2574      case KeyRelease:
2575      {
2576        /*
2577          Respond to a user key release.
2578        */
2579        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2580          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2581        state&=(~ModifierState);
2582        break;
2583      }
2584      case SelectionNotify:
2585      {
2586        Atom
2587          type;
2588
2589        int
2590          format;
2591
2592        unsigned char
2593          *data;
2594
2595        unsigned long
2596          after,
2597          length;
2598
2599        /*
2600          Obtain response from primary selection.
2601        */
2602        if (event.xselection.property == (Atom) None)
2603          break;
2604        status=XGetWindowProperty(display,event.xselection.requestor,
2605          event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
2606          &type,&format,&length,&after,&data);
2607        if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2608            (length == 0))
2609          break;
2610        /*
2611          Annotate Image window with primary selection.
2612        */
2613        for (i=0; i < (ssize_t) length; i++)
2614        {
2615          if ((char) data[i] != '\n')
2616            {
2617              /*
2618                Draw a single character on the Image window.
2619              */
2620              *p=(char) data[i];
2621              (void) XDrawString(display,windows->image.id,annotate_context,
2622                x,y,p,1);
2623              x+=XTextWidth(font_info,p,1);
2624              p++;
2625              if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2626                continue;
2627            }
2628          /*
2629            Advance to the next line of text.
2630          */
2631          *p='\0';
2632          annotate_info->width=(unsigned int) XTextWidth(font_info,
2633            annotate_info->text,(int) strlen(annotate_info->text));
2634          if (annotate_info->next != (XAnnotateInfo *) NULL)
2635            {
2636              /*
2637                Line of text already exists.
2638              */
2639              annotate_info=annotate_info->next;
2640              x=annotate_info->x;
2641              y=annotate_info->y;
2642              p=annotate_info->text;
2643              continue;
2644            }
2645          annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2646            sizeof(*annotate_info->next));
2647          if (annotate_info->next == (XAnnotateInfo *) NULL)
2648            return(MagickFalse);
2649          *annotate_info->next=(*annotate_info);
2650          annotate_info->next->previous=annotate_info;
2651          annotate_info=annotate_info->next;
2652          annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2653            windows->image.width/MagickMax((ssize_t)
2654            font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2655          if (annotate_info->text == (char *) NULL)
2656            return(MagickFalse);
2657          annotate_info->y+=annotate_info->height;
2658          if (annotate_info->y > (int) windows->image.height)
2659            annotate_info->y=(int) annotate_info->height;
2660          annotate_info->next=(XAnnotateInfo *) NULL;
2661          x=annotate_info->x;
2662          y=annotate_info->y;
2663          p=annotate_info->text;
2664        }
2665        (void) XFree((void *) data);
2666        break;
2667      }
2668      default:
2669        break;
2670    }
2671  } while ((state & ExitState) == 0);
2672  (void) XFreeCursor(display,cursor);
2673  /*
2674    Annotation is relative to image configuration.
2675  */
2676  width=(unsigned int) image->columns;
2677  height=(unsigned int) image->rows;
2678  x=0;
2679  y=0;
2680  if (windows->image.crop_geometry != (char *) NULL)
2681    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2682  /*
2683    Initialize annotated image.
2684  */
2685  XSetCursorState(display,windows,MagickTrue);
2686  XCheckRefreshWindows(display,windows);
2687  while (annotate_info != (XAnnotateInfo *) NULL)
2688  {
2689    if (annotate_info->width == 0)
2690      {
2691        /*
2692          No text on this line--  go to the next line of text.
2693        */
2694        previous_info=annotate_info->previous;
2695        annotate_info->text=(char *)
2696          RelinquishMagickMemory(annotate_info->text);
2697        annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2698        annotate_info=previous_info;
2699        continue;
2700      }
2701    /*
2702      Determine pixel index for box and pen color.
2703    */
2704    windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2705    if (windows->pixel_info->colors != 0)
2706      for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2707        if (windows->pixel_info->pixels[i] ==
2708            windows->pixel_info->pen_colors[box_id].pixel)
2709          {
2710            windows->pixel_info->box_index=(unsigned short) i;
2711            break;
2712          }
2713    windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2714    if (windows->pixel_info->colors != 0)
2715      for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2716        if (windows->pixel_info->pixels[i] ==
2717            windows->pixel_info->pen_colors[pen_id].pixel)
2718          {
2719            windows->pixel_info->pen_index=(unsigned short) i;
2720            break;
2721          }
2722    /*
2723      Define the annotate geometry string.
2724    */
2725    annotate_info->x=(int)
2726      width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2727    annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2728      windows->image.y)/windows->image.ximage->height;
2729    (void) FormatLocaleString(annotate_info->geometry,MaxTextExtent,
2730      "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2731      height*annotate_info->height/windows->image.ximage->height,
2732      annotate_info->x+x,annotate_info->y+y);
2733    /*
2734      Annotate image with text.
2735    */
2736    status=XAnnotateImage(display,windows->pixel_info,annotate_info,image,
2737      exception);
2738    if (status == 0)
2739      return(MagickFalse);
2740    /*
2741      Free up memory.
2742    */
2743    previous_info=annotate_info->previous;
2744    annotate_info->text=DestroyString(annotate_info->text);
2745    annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2746    annotate_info=previous_info;
2747  }
2748  (void) XSetForeground(display,annotate_context,
2749    windows->pixel_info->foreground_color.pixel);
2750  (void) XSetBackground(display,annotate_context,
2751    windows->pixel_info->background_color.pixel);
2752  (void) XSetFont(display,annotate_context,windows->font_info->fid);
2753  XSetCursorState(display,windows,MagickFalse);
2754  (void) XFreeFont(display,font_info);
2755  /*
2756    Update image configuration.
2757  */
2758  XConfigureImageColormap(display,resource_info,windows,image,exception);
2759  (void) XConfigureImage(display,resource_info,windows,image,exception);
2760  return(MagickTrue);
2761}
2762
2763/*
2764%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2765%                                                                             %
2766%                                                                             %
2767%                                                                             %
2768+   X B a c k g r o u n d I m a g e                                           %
2769%                                                                             %
2770%                                                                             %
2771%                                                                             %
2772%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2773%
2774%  XBackgroundImage() displays the image in the background of a window.
2775%
2776%  The format of the XBackgroundImage method is:
2777%
2778%      MagickBooleanType XBackgroundImage(Display *display,
2779%        XResourceInfo *resource_info,XWindows *windows,Image **image,
2780%        ExceptionInfo *exception)
2781%
2782%  A description of each parameter follows:
2783%
2784%    o display: Specifies a connection to an X server; returned from
2785%      XOpenDisplay.
2786%
2787%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2788%
2789%    o windows: Specifies a pointer to a XWindows structure.
2790%
2791%    o image: the image.
2792%
2793%    o exception: return any errors or warnings in this structure.
2794%
2795*/
2796static MagickBooleanType XBackgroundImage(Display *display,
2797  XResourceInfo *resource_info,XWindows *windows,Image **image,
2798  ExceptionInfo *exception)
2799{
2800#define BackgroundImageTag  "Background/Image"
2801
2802  int
2803    status;
2804
2805  static char
2806    window_id[MaxTextExtent] = "root";
2807
2808  XResourceInfo
2809    background_resources;
2810
2811  /*
2812    Put image in background.
2813  */
2814  status=XDialogWidget(display,windows,"Background",
2815    "Enter window id (id 0x00 selects window with pointer):",window_id);
2816  if (*window_id == '\0')
2817    return(MagickFalse);
2818  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
2819    exception);
2820  XInfoWidget(display,windows,BackgroundImageTag);
2821  XSetCursorState(display,windows,MagickTrue);
2822  XCheckRefreshWindows(display,windows);
2823  background_resources=(*resource_info);
2824  background_resources.window_id=window_id;
2825  background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2826  status=XDisplayBackgroundImage(display,&background_resources,*image,
2827    exception);
2828  if (status != MagickFalse)
2829    XClientMessage(display,windows->image.id,windows->im_protocols,
2830      windows->im_retain_colors,CurrentTime);
2831  XSetCursorState(display,windows,MagickFalse);
2832  (void) XMagickCommand(display,resource_info,windows,UndoCommand,image,
2833    exception);
2834  return(MagickTrue);
2835}
2836
2837/*
2838%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2839%                                                                             %
2840%                                                                             %
2841%                                                                             %
2842+   X C h o p I m a g e                                                       %
2843%                                                                             %
2844%                                                                             %
2845%                                                                             %
2846%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2847%
2848%  XChopImage() chops the X image.
2849%
2850%  The format of the XChopImage method is:
2851%
2852%    MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2853%      XWindows *windows,Image **image,ExceptionInfo *exception)
2854%
2855%  A description of each parameter follows:
2856%
2857%    o display: Specifies a connection to an X server; returned from
2858%      XOpenDisplay.
2859%
2860%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2861%
2862%    o windows: Specifies a pointer to a XWindows structure.
2863%
2864%    o image: the image.
2865%
2866%    o exception: return any errors or warnings in this structure.
2867%
2868*/
2869static MagickBooleanType XChopImage(Display *display,
2870  XResourceInfo *resource_info,XWindows *windows,Image **image,
2871  ExceptionInfo *exception)
2872{
2873  static const char
2874    *ChopMenu[] =
2875    {
2876      "Direction",
2877      "Help",
2878      "Dismiss",
2879      (char *) NULL
2880    };
2881
2882  static ModeType
2883    direction = HorizontalChopCommand;
2884
2885  static const ModeType
2886    ChopCommands[] =
2887    {
2888      ChopDirectionCommand,
2889      ChopHelpCommand,
2890      ChopDismissCommand
2891    },
2892    DirectionCommands[] =
2893    {
2894      HorizontalChopCommand,
2895      VerticalChopCommand
2896    };
2897
2898  char
2899    text[MaxTextExtent];
2900
2901  Image
2902    *chop_image;
2903
2904  int
2905    id,
2906    x,
2907    y;
2908
2909  MagickRealType
2910    scale_factor;
2911
2912  RectangleInfo
2913    chop_info;
2914
2915  unsigned int
2916    distance,
2917    height,
2918    width;
2919
2920  size_t
2921    state;
2922
2923  XEvent
2924    event;
2925
2926  XSegment
2927    segment_info;
2928
2929  /*
2930    Map Command widget.
2931  */
2932  (void) CloneString(&windows->command.name,"Chop");
2933  windows->command.data=1;
2934  (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2935  (void) XMapRaised(display,windows->command.id);
2936  XClientMessage(display,windows->image.id,windows->im_protocols,
2937    windows->im_update_widget,CurrentTime);
2938  /*
2939    Track pointer until button 1 is pressed.
2940  */
2941  XQueryPosition(display,windows->image.id,&x,&y);
2942  (void) XSelectInput(display,windows->image.id,
2943    windows->image.attributes.event_mask | PointerMotionMask);
2944  state=DefaultState;
2945  do
2946  {
2947    if (windows->info.mapped != MagickFalse)
2948      {
2949        /*
2950          Display pointer position.
2951        */
2952        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
2953          x+windows->image.x,y+windows->image.y);
2954        XInfoWidget(display,windows,text);
2955      }
2956    /*
2957      Wait for next event.
2958    */
2959    XScreenEvent(display,windows,&event,exception);
2960    if (event.xany.window == windows->command.id)
2961      {
2962        /*
2963          Select a command from the Command widget.
2964        */
2965        id=XCommandWidget(display,windows,ChopMenu,&event);
2966        if (id < 0)
2967          continue;
2968        switch (ChopCommands[id])
2969        {
2970          case ChopDirectionCommand:
2971          {
2972            char
2973              command[MaxTextExtent];
2974
2975            static const char
2976              *Directions[] =
2977              {
2978                "horizontal",
2979                "vertical",
2980                (char *) NULL,
2981              };
2982
2983            /*
2984              Select a command from the pop-up menu.
2985            */
2986            id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2987            if (id >= 0)
2988              direction=DirectionCommands[id];
2989            break;
2990          }
2991          case ChopHelpCommand:
2992          {
2993            XTextViewWidget(display,resource_info,windows,MagickFalse,
2994              "Help Viewer - Image Chop",ImageChopHelp);
2995            break;
2996          }
2997          case ChopDismissCommand:
2998          {
2999            /*
3000              Prematurely exit.
3001            */
3002            state|=EscapeState;
3003            state|=ExitState;
3004            break;
3005          }
3006          default:
3007            break;
3008        }
3009        continue;
3010      }
3011    switch (event.type)
3012    {
3013      case ButtonPress:
3014      {
3015        if (event.xbutton.button != Button1)
3016          break;
3017        if (event.xbutton.window != windows->image.id)
3018          break;
3019        /*
3020          User has committed to start point of chopping line.
3021        */
3022        segment_info.x1=(short int) event.xbutton.x;
3023        segment_info.x2=(short int) event.xbutton.x;
3024        segment_info.y1=(short int) event.xbutton.y;
3025        segment_info.y2=(short int) event.xbutton.y;
3026        state|=ExitState;
3027        break;
3028      }
3029      case ButtonRelease:
3030        break;
3031      case Expose:
3032        break;
3033      case KeyPress:
3034      {
3035        char
3036          command[MaxTextExtent];
3037
3038        KeySym
3039          key_symbol;
3040
3041        if (event.xkey.window != windows->image.id)
3042          break;
3043        /*
3044          Respond to a user key press.
3045        */
3046        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3047          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3048        switch ((int) key_symbol)
3049        {
3050          case XK_Escape:
3051          case XK_F20:
3052          {
3053            /*
3054              Prematurely exit.
3055            */
3056            state|=EscapeState;
3057            state|=ExitState;
3058            break;
3059          }
3060          case XK_F1:
3061          case XK_Help:
3062          {
3063            (void) XSetFunction(display,windows->image.highlight_context,
3064              GXcopy);
3065            XTextViewWidget(display,resource_info,windows,MagickFalse,
3066              "Help Viewer - Image Chop",ImageChopHelp);
3067            (void) XSetFunction(display,windows->image.highlight_context,
3068              GXinvert);
3069            break;
3070          }
3071          default:
3072          {
3073            (void) XBell(display,0);
3074            break;
3075          }
3076        }
3077        break;
3078      }
3079      case MotionNotify:
3080      {
3081        /*
3082          Map and unmap Info widget as text cursor crosses its boundaries.
3083        */
3084        x=event.xmotion.x;
3085        y=event.xmotion.y;
3086        if (windows->info.mapped != MagickFalse)
3087          {
3088            if ((x < (int) (windows->info.x+windows->info.width)) &&
3089                (y < (int) (windows->info.y+windows->info.height)))
3090              (void) XWithdrawWindow(display,windows->info.id,
3091                windows->info.screen);
3092          }
3093        else
3094          if ((x > (int) (windows->info.x+windows->info.width)) ||
3095              (y > (int) (windows->info.y+windows->info.height)))
3096            (void) XMapWindow(display,windows->info.id);
3097      }
3098    }
3099  } while ((state & ExitState) == 0);
3100  (void) XSelectInput(display,windows->image.id,
3101    windows->image.attributes.event_mask);
3102  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3103  if ((state & EscapeState) != 0)
3104    return(MagickTrue);
3105  /*
3106    Draw line as pointer moves until the mouse button is released.
3107  */
3108  chop_info.width=0;
3109  chop_info.height=0;
3110  chop_info.x=0;
3111  chop_info.y=0;
3112  distance=0;
3113  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3114  state=DefaultState;
3115  do
3116  {
3117    if (distance > 9)
3118      {
3119        /*
3120          Display info and draw chopping line.
3121        */
3122        if (windows->info.mapped == MagickFalse)
3123          (void) XMapWindow(display,windows->info.id);
3124        (void) FormatLocaleString(text,MaxTextExtent,
3125          " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3126          chop_info.height,(double) chop_info.x,(double) chop_info.y);
3127        XInfoWidget(display,windows,text);
3128        XHighlightLine(display,windows->image.id,
3129          windows->image.highlight_context,&segment_info);
3130      }
3131    else
3132      if (windows->info.mapped != MagickFalse)
3133        (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3134    /*
3135      Wait for next event.
3136    */
3137    XScreenEvent(display,windows,&event,exception);
3138    if (distance > 9)
3139      XHighlightLine(display,windows->image.id,
3140        windows->image.highlight_context,&segment_info);
3141    switch (event.type)
3142    {
3143      case ButtonPress:
3144      {
3145        segment_info.x2=(short int) event.xmotion.x;
3146        segment_info.y2=(short int) event.xmotion.y;
3147        break;
3148      }
3149      case ButtonRelease:
3150      {
3151        /*
3152          User has committed to chopping line.
3153        */
3154        segment_info.x2=(short int) event.xbutton.x;
3155        segment_info.y2=(short int) event.xbutton.y;
3156        state|=ExitState;
3157        break;
3158      }
3159      case Expose:
3160        break;
3161      case MotionNotify:
3162      {
3163        segment_info.x2=(short int) event.xmotion.x;
3164        segment_info.y2=(short int) event.xmotion.y;
3165      }
3166      default:
3167        break;
3168    }
3169    /*
3170      Check boundary conditions.
3171    */
3172    if (segment_info.x2 < 0)
3173      segment_info.x2=0;
3174    else
3175      if (segment_info.x2 > windows->image.ximage->width)
3176        segment_info.x2=windows->image.ximage->width;
3177    if (segment_info.y2 < 0)
3178      segment_info.y2=0;
3179    else
3180      if (segment_info.y2 > windows->image.ximage->height)
3181        segment_info.y2=windows->image.ximage->height;
3182    distance=(unsigned int)
3183      (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3184       ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3185    /*
3186      Compute chopping geometry.
3187    */
3188    if (direction == HorizontalChopCommand)
3189      {
3190        chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3191        chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3192        chop_info.height=0;
3193        chop_info.y=0;
3194        if (segment_info.x1 > (int) segment_info.x2)
3195          {
3196            chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3197            chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3198          }
3199      }
3200    else
3201      {
3202        chop_info.width=0;
3203        chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3204        chop_info.x=0;
3205        chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3206        if (segment_info.y1 > segment_info.y2)
3207          {
3208            chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3209            chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3210          }
3211      }
3212  } while ((state & ExitState) == 0);
3213  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3214  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3215  if (distance <= 9)
3216    return(MagickTrue);
3217  /*
3218    Image chopping is relative to image configuration.
3219  */
3220  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
3221    exception);
3222  XSetCursorState(display,windows,MagickTrue);
3223  XCheckRefreshWindows(display,windows);
3224  windows->image.window_changes.width=windows->image.ximage->width-
3225    (unsigned int) chop_info.width;
3226  windows->image.window_changes.height=windows->image.ximage->height-
3227    (unsigned int) chop_info.height;
3228  width=(unsigned int) (*image)->columns;
3229  height=(unsigned int) (*image)->rows;
3230  x=0;
3231  y=0;
3232  if (windows->image.crop_geometry != (char *) NULL)
3233    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3234  scale_factor=(MagickRealType) width/windows->image.ximage->width;
3235  chop_info.x+=x;
3236  chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3237  chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3238  scale_factor=(MagickRealType) height/windows->image.ximage->height;
3239  chop_info.y+=y;
3240  chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3241  chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3242  /*
3243    Chop image.
3244  */
3245  chop_image=ChopImage(*image,&chop_info,exception);
3246  XSetCursorState(display,windows,MagickFalse);
3247  if (chop_image == (Image *) NULL)
3248    return(MagickFalse);
3249  *image=DestroyImage(*image);
3250  *image=chop_image;
3251  /*
3252    Update image configuration.
3253  */
3254  XConfigureImageColormap(display,resource_info,windows,*image,exception);
3255  (void) XConfigureImage(display,resource_info,windows,*image,exception);
3256  return(MagickTrue);
3257}
3258
3259/*
3260%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3261%                                                                             %
3262%                                                                             %
3263%                                                                             %
3264+   X C o l o r E d i t I m a g e                                             %
3265%                                                                             %
3266%                                                                             %
3267%                                                                             %
3268%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3269%
3270%  XColorEditImage() allows the user to interactively change the color of one
3271%  pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3272%
3273%  The format of the XColorEditImage method is:
3274%
3275%      MagickBooleanType XColorEditImage(Display *display,
3276%        XResourceInfo *resource_info,XWindows *windows,Image **image,
3277%          ExceptionInfo *exception)
3278%
3279%  A description of each parameter follows:
3280%
3281%    o display: Specifies a connection to an X server;  returned from
3282%      XOpenDisplay.
3283%
3284%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3285%
3286%    o windows: Specifies a pointer to a XWindows structure.
3287%
3288%    o image: the image; returned from ReadImage.
3289%
3290%    o exception: return any errors or warnings in this structure.
3291%
3292*/
3293static MagickBooleanType XColorEditImage(Display *display,
3294  XResourceInfo *resource_info,XWindows *windows,Image **image,
3295  ExceptionInfo *exception)
3296{
3297  static const char
3298    *ColorEditMenu[] =
3299    {
3300      "Method",
3301      "Pixel Color",
3302      "Border Color",
3303      "Fuzz",
3304      "Undo",
3305      "Help",
3306      "Dismiss",
3307      (char *) NULL
3308    };
3309
3310  static const ModeType
3311    ColorEditCommands[] =
3312    {
3313      ColorEditMethodCommand,
3314      ColorEditColorCommand,
3315      ColorEditBorderCommand,
3316      ColorEditFuzzCommand,
3317      ColorEditUndoCommand,
3318      ColorEditHelpCommand,
3319      ColorEditDismissCommand
3320    };
3321
3322  static PaintMethod
3323    method = PointMethod;
3324
3325  static unsigned int
3326    pen_id = 0;
3327
3328  static XColor
3329    border_color = { 0, 0, 0, 0, 0, 0 };
3330
3331  char
3332    command[MaxTextExtent],
3333    text[MaxTextExtent];
3334
3335  Cursor
3336    cursor;
3337
3338  int
3339    entry,
3340    id,
3341    x,
3342    x_offset,
3343    y,
3344    y_offset;
3345
3346  register Quantum
3347    *q;
3348
3349  register ssize_t
3350    i;
3351
3352  unsigned int
3353    height,
3354    width;
3355
3356  size_t
3357    state;
3358
3359  XColor
3360    color;
3361
3362  XEvent
3363    event;
3364
3365  /*
3366    Map Command widget.
3367  */
3368  (void) CloneString(&windows->command.name,"Color Edit");
3369  windows->command.data=4;
3370  (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3371  (void) XMapRaised(display,windows->command.id);
3372  XClientMessage(display,windows->image.id,windows->im_protocols,
3373    windows->im_update_widget,CurrentTime);
3374  /*
3375    Make cursor.
3376  */
3377  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3378    resource_info->background_color,resource_info->foreground_color);
3379  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3380  /*
3381    Track pointer until button 1 is pressed.
3382  */
3383  XQueryPosition(display,windows->image.id,&x,&y);
3384  (void) XSelectInput(display,windows->image.id,
3385    windows->image.attributes.event_mask | PointerMotionMask);
3386  state=DefaultState;
3387  do
3388  {
3389    if (windows->info.mapped != MagickFalse)
3390      {
3391        /*
3392          Display pointer position.
3393        */
3394        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
3395          x+windows->image.x,y+windows->image.y);
3396        XInfoWidget(display,windows,text);
3397      }
3398    /*
3399      Wait for next event.
3400    */
3401    XScreenEvent(display,windows,&event,exception);
3402    if (event.xany.window == windows->command.id)
3403      {
3404        /*
3405          Select a command from the Command widget.
3406        */
3407        id=XCommandWidget(display,windows,ColorEditMenu,&event);
3408        if (id < 0)
3409          {
3410            (void) XCheckDefineCursor(display,windows->image.id,cursor);
3411            continue;
3412          }
3413        switch (ColorEditCommands[id])
3414        {
3415          case ColorEditMethodCommand:
3416          {
3417            char
3418              **methods;
3419
3420            /*
3421              Select a method from the pop-up menu.
3422            */
3423            methods=(char **) GetCommandOptions(MagickMethodOptions);
3424            if (methods == (char **) NULL)
3425              break;
3426            entry=XMenuWidget(display,windows,ColorEditMenu[id],
3427              (const char **) methods,command);
3428            if (entry >= 0)
3429              method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3430                MagickFalse,methods[entry]);
3431            methods=DestroyStringList(methods);
3432            break;
3433          }
3434          case ColorEditColorCommand:
3435          {
3436            const char
3437              *ColorMenu[MaxNumberPens];
3438
3439            int
3440              pen_number;
3441
3442            /*
3443              Initialize menu selections.
3444            */
3445            for (i=0; i < (int) (MaxNumberPens-2); i++)
3446              ColorMenu[i]=resource_info->pen_colors[i];
3447            ColorMenu[MaxNumberPens-2]="Browser...";
3448            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3449            /*
3450              Select a pen color from the pop-up menu.
3451            */
3452            pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3453              (const char **) ColorMenu,command);
3454            if (pen_number < 0)
3455              break;
3456            if (pen_number == (MaxNumberPens-2))
3457              {
3458                static char
3459                  color_name[MaxTextExtent] = "gray";
3460
3461                /*
3462                  Select a pen color from a dialog.
3463                */
3464                resource_info->pen_colors[pen_number]=color_name;
3465                XColorBrowserWidget(display,windows,"Select",color_name);
3466                if (*color_name == '\0')
3467                  break;
3468              }
3469            /*
3470              Set pen color.
3471            */
3472            (void) XParseColor(display,windows->map_info->colormap,
3473              resource_info->pen_colors[pen_number],&color);
3474            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3475              (unsigned int) MaxColors,&color);
3476            windows->pixel_info->pen_colors[pen_number]=color;
3477            pen_id=(unsigned int) pen_number;
3478            break;
3479          }
3480          case ColorEditBorderCommand:
3481          {
3482            const char
3483              *ColorMenu[MaxNumberPens];
3484
3485            int
3486              pen_number;
3487
3488            /*
3489              Initialize menu selections.
3490            */
3491            for (i=0; i < (int) (MaxNumberPens-2); i++)
3492              ColorMenu[i]=resource_info->pen_colors[i];
3493            ColorMenu[MaxNumberPens-2]="Browser...";
3494            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3495            /*
3496              Select a pen color from the pop-up menu.
3497            */
3498            pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3499              (const char **) ColorMenu,command);
3500            if (pen_number < 0)
3501              break;
3502            if (pen_number == (MaxNumberPens-2))
3503              {
3504                static char
3505                  color_name[MaxTextExtent] = "gray";
3506
3507                /*
3508                  Select a pen color from a dialog.
3509                */
3510                resource_info->pen_colors[pen_number]=color_name;
3511                XColorBrowserWidget(display,windows,"Select",color_name);
3512                if (*color_name == '\0')
3513                  break;
3514              }
3515            /*
3516              Set border color.
3517            */
3518            (void) XParseColor(display,windows->map_info->colormap,
3519              resource_info->pen_colors[pen_number],&border_color);
3520            break;
3521          }
3522          case ColorEditFuzzCommand:
3523          {
3524            static char
3525              fuzz[MaxTextExtent];
3526
3527            static const char
3528              *FuzzMenu[] =
3529              {
3530                "0%",
3531                "2%",
3532                "5%",
3533                "10%",
3534                "15%",
3535                "Dialog...",
3536                (char *) NULL,
3537              };
3538
3539            /*
3540              Select a command from the pop-up menu.
3541            */
3542            entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3543              command);
3544            if (entry < 0)
3545              break;
3546            if (entry != 5)
3547              {
3548                (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
3549                  QuantumRange+1.0);
3550                break;
3551              }
3552            (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
3553            (void) XDialogWidget(display,windows,"Ok",
3554              "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3555            if (*fuzz == '\0')
3556              break;
3557            (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
3558            (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
3559              1.0);
3560            break;
3561          }
3562          case ColorEditUndoCommand:
3563          {
3564            (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3565              image,exception);
3566            break;
3567          }
3568          case ColorEditHelpCommand:
3569          default:
3570          {
3571            XTextViewWidget(display,resource_info,windows,MagickFalse,
3572              "Help Viewer - Image Annotation",ImageColorEditHelp);
3573            break;
3574          }
3575          case ColorEditDismissCommand:
3576          {
3577            /*
3578              Prematurely exit.
3579            */
3580            state|=EscapeState;
3581            state|=ExitState;
3582            break;
3583          }
3584        }
3585        (void) XCheckDefineCursor(display,windows->image.id,cursor);
3586        continue;
3587      }
3588    switch (event.type)
3589    {
3590      case ButtonPress:
3591      {
3592        if (event.xbutton.button != Button1)
3593          break;
3594        if ((event.xbutton.window != windows->image.id) &&
3595            (event.xbutton.window != windows->magnify.id))
3596          break;
3597        /*
3598          exit loop.
3599        */
3600        x=event.xbutton.x;
3601        y=event.xbutton.y;
3602        (void) XMagickCommand(display,resource_info,windows,
3603          SaveToUndoBufferCommand,image,exception);
3604        state|=UpdateConfigurationState;
3605        break;
3606      }
3607      case ButtonRelease:
3608      {
3609        if (event.xbutton.button != Button1)
3610          break;
3611        if ((event.xbutton.window != windows->image.id) &&
3612            (event.xbutton.window != windows->magnify.id))
3613          break;
3614        /*
3615          Update colormap information.
3616        */
3617        x=event.xbutton.x;
3618        y=event.xbutton.y;
3619        XConfigureImageColormap(display,resource_info,windows,*image,exception);
3620        (void) XConfigureImage(display,resource_info,windows,*image,exception);
3621        XInfoWidget(display,windows,text);
3622        (void) XCheckDefineCursor(display,windows->image.id,cursor);
3623        state&=(~UpdateConfigurationState);
3624        break;
3625      }
3626      case Expose:
3627        break;
3628      case KeyPress:
3629      {
3630        KeySym
3631          key_symbol;
3632
3633        if (event.xkey.window == windows->magnify.id)
3634          {
3635            Window
3636              window;
3637
3638            window=windows->magnify.id;
3639            while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3640          }
3641        if (event.xkey.window != windows->image.id)
3642          break;
3643        /*
3644          Respond to a user key press.
3645        */
3646        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3647          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3648        switch ((int) key_symbol)
3649        {
3650          case XK_Escape:
3651          case XK_F20:
3652          {
3653            /*
3654              Prematurely exit.
3655            */
3656            state|=ExitState;
3657            break;
3658          }
3659          case XK_F1:
3660          case XK_Help:
3661          {
3662            XTextViewWidget(display,resource_info,windows,MagickFalse,
3663              "Help Viewer - Image Annotation",ImageColorEditHelp);
3664            break;
3665          }
3666          default:
3667          {
3668            (void) XBell(display,0);
3669            break;
3670          }
3671        }
3672        break;
3673      }
3674      case MotionNotify:
3675      {
3676        /*
3677          Map and unmap Info widget as cursor crosses its boundaries.
3678        */
3679        x=event.xmotion.x;
3680        y=event.xmotion.y;
3681        if (windows->info.mapped != MagickFalse)
3682          {
3683            if ((x < (int) (windows->info.x+windows->info.width)) &&
3684                (y < (int) (windows->info.y+windows->info.height)))
3685              (void) XWithdrawWindow(display,windows->info.id,
3686                windows->info.screen);
3687          }
3688        else
3689          if ((x > (int) (windows->info.x+windows->info.width)) ||
3690              (y > (int) (windows->info.y+windows->info.height)))
3691            (void) XMapWindow(display,windows->info.id);
3692        break;
3693      }
3694      default:
3695        break;
3696    }
3697    if (event.xany.window == windows->magnify.id)
3698      {
3699        x=windows->magnify.x-windows->image.x;
3700        y=windows->magnify.y-windows->image.y;
3701      }
3702    x_offset=x;
3703    y_offset=y;
3704    if ((state & UpdateConfigurationState) != 0)
3705      {
3706        CacheView
3707          *image_view;
3708
3709        int
3710          x,
3711          y;
3712
3713        /*
3714          Pixel edit is relative to image configuration.
3715        */
3716        (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3717          MagickTrue);
3718        color=windows->pixel_info->pen_colors[pen_id];
3719        XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3720        width=(unsigned int) (*image)->columns;
3721        height=(unsigned int) (*image)->rows;
3722        x=0;
3723        y=0;
3724        if (windows->image.crop_geometry != (char *) NULL)
3725          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3726            &width,&height);
3727        x_offset=(int)
3728          (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3729        y_offset=(int)
3730          (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3731        if ((x_offset < 0) || (y_offset < 0))
3732          continue;
3733        if ((x_offset >= (int) (*image)->columns) ||
3734            (y_offset >= (int) (*image)->rows))
3735          continue;
3736        image_view=AcquireCacheView(*image);
3737        switch (method)
3738        {
3739          case PointMethod:
3740          default:
3741          {
3742            /*
3743              Update color information using point algorithm.
3744            */
3745            if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3746              return(MagickFalse);
3747            q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3748              (ssize_t) y_offset,1,1,exception);
3749            if (q == (Quantum *) NULL)
3750              break;
3751            SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3752            SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3753            SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3754            (void) SyncCacheViewAuthenticPixels(image_view,exception);
3755            break;
3756          }
3757          case ReplaceMethod:
3758          {
3759            PixelInfo
3760              pixel,
3761              target;
3762
3763            Quantum
3764              virtual_pixel[CompositePixelChannel];
3765
3766            /*
3767              Update color information using replace algorithm.
3768            */
3769            (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
3770              (ssize_t) y_offset,virtual_pixel,exception);
3771            target.red=virtual_pixel[RedPixelChannel];
3772            target.green=virtual_pixel[GreenPixelChannel];
3773            target.blue=virtual_pixel[BluePixelChannel];
3774            target.alpha=virtual_pixel[AlphaPixelChannel];
3775            if ((*image)->storage_class == DirectClass)
3776              {
3777                for (y=0; y < (int) (*image)->rows; y++)
3778                {
3779                  q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3780                    (*image)->columns,1,exception);
3781                  if (q == (Quantum *) NULL)
3782                    break;
3783                  for (x=0; x < (int) (*image)->columns; x++)
3784                  {
3785                    GetPixelInfoPixel(*image,q,&pixel);
3786                    if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
3787                      {
3788                        SetPixelRed(*image,ScaleShortToQuantum(
3789                          color.red),q);
3790                        SetPixelGreen(*image,ScaleShortToQuantum(
3791                          color.green),q);
3792                        SetPixelBlue(*image,ScaleShortToQuantum(
3793                          color.blue),q);
3794                      }
3795                    q+=GetPixelChannels(*image);
3796                  }
3797                  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3798                    break;
3799                }
3800              }
3801            else
3802              {
3803                for (i=0; i < (ssize_t) (*image)->colors; i++)
3804                  if (IsFuzzyEquivalencePixelInfo((*image)->colormap+i,&target))
3805                    {
3806                      (*image)->colormap[i].red=ScaleShortToQuantum(
3807                        color.red);
3808                      (*image)->colormap[i].green=ScaleShortToQuantum(
3809                        color.green);
3810                      (*image)->colormap[i].blue=ScaleShortToQuantum(
3811                        color.blue);
3812                    }
3813                (void) SyncImage(*image,exception);
3814              }
3815            break;
3816          }
3817          case FloodfillMethod:
3818          case FillToBorderMethod:
3819          {
3820            DrawInfo
3821              *draw_info;
3822
3823            PixelInfo
3824              target;
3825
3826            /*
3827              Update color information using floodfill algorithm.
3828            */
3829            (void) GetOneVirtualPixelInfo(*image,
3830              GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
3831              y_offset,&target,exception);
3832            if (method == FillToBorderMethod)
3833              {
3834                target.red=(MagickRealType)
3835                  ScaleShortToQuantum(border_color.red);
3836                target.green=(MagickRealType)
3837                  ScaleShortToQuantum(border_color.green);
3838                target.blue=(MagickRealType)
3839                  ScaleShortToQuantum(border_color.blue);
3840              }
3841            draw_info=CloneDrawInfo(resource_info->image_info,
3842              (DrawInfo *) NULL);
3843            (void) QueryColorCompliance(resource_info->pen_colors[pen_id],
3844              AllCompliance,&draw_info->fill,exception);
3845            (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
3846              x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
3847              MagickFalse : MagickTrue,exception);
3848            draw_info=DestroyDrawInfo(draw_info);
3849            break;
3850          }
3851          case ResetMethod:
3852          {
3853            /*
3854              Update color information using reset algorithm.
3855            */
3856            if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3857              return(MagickFalse);
3858            for (y=0; y < (int) (*image)->rows; y++)
3859            {
3860              q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3861                (*image)->columns,1,exception);
3862              if (q == (Quantum *) NULL)
3863                break;
3864              for (x=0; x < (int) (*image)->columns; x++)
3865              {
3866                SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3867                SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3868                SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3869                q+=GetPixelChannels(*image);
3870              }
3871              if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3872                break;
3873            }
3874            break;
3875          }
3876        }
3877        image_view=DestroyCacheView(image_view);
3878        state&=(~UpdateConfigurationState);
3879      }
3880  } while ((state & ExitState) == 0);
3881  (void) XSelectInput(display,windows->image.id,
3882    windows->image.attributes.event_mask);
3883  XSetCursorState(display,windows,MagickFalse);
3884  (void) XFreeCursor(display,cursor);
3885  return(MagickTrue);
3886}
3887
3888/*
3889%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3890%                                                                             %
3891%                                                                             %
3892%                                                                             %
3893+   X C o m p o s i t e I m a g e                                             %
3894%                                                                             %
3895%                                                                             %
3896%                                                                             %
3897%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3898%
3899%  XCompositeImage() requests an image name from the user, reads the image and
3900%  composites it with the X window image at a location the user chooses with
3901%  the pointer.
3902%
3903%  The format of the XCompositeImage method is:
3904%
3905%      MagickBooleanType XCompositeImage(Display *display,
3906%        XResourceInfo *resource_info,XWindows *windows,Image *image,
3907%        ExceptionInfo *exception)
3908%
3909%  A description of each parameter follows:
3910%
3911%    o display: Specifies a connection to an X server;  returned from
3912%      XOpenDisplay.
3913%
3914%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3915%
3916%    o windows: Specifies a pointer to a XWindows structure.
3917%
3918%    o image: the image; returned from ReadImage.
3919%
3920%    o exception: return any errors or warnings in this structure.
3921%
3922*/
3923static MagickBooleanType XCompositeImage(Display *display,
3924  XResourceInfo *resource_info,XWindows *windows,Image *image,
3925  ExceptionInfo *exception)
3926{
3927  static char
3928    displacement_geometry[MaxTextExtent] = "30x30",
3929    filename[MaxTextExtent] = "\0";
3930
3931  static const char
3932    *CompositeMenu[] =
3933    {
3934      "Operators",
3935      "Dissolve",
3936      "Displace",
3937      "Help",
3938      "Dismiss",
3939      (char *) NULL
3940    };
3941
3942  static CompositeOperator
3943    compose = CopyCompositeOp;
3944
3945  static const ModeType
3946    CompositeCommands[] =
3947    {
3948      CompositeOperatorsCommand,
3949      CompositeDissolveCommand,
3950      CompositeDisplaceCommand,
3951      CompositeHelpCommand,
3952      CompositeDismissCommand
3953    };
3954
3955  char
3956    text[MaxTextExtent];
3957
3958  Cursor
3959    cursor;
3960
3961  Image
3962    *composite_image;
3963
3964  int
3965    entry,
3966    id,
3967    x,
3968    y;
3969
3970  MagickRealType
3971    blend,
3972    scale_factor;
3973
3974  RectangleInfo
3975    highlight_info,
3976    composite_info;
3977
3978  unsigned int
3979    height,
3980    width;
3981
3982  size_t
3983    state;
3984
3985  XEvent
3986    event;
3987
3988  /*
3989    Request image file name from user.
3990  */
3991  XFileBrowserWidget(display,windows,"Composite",filename);
3992  if (*filename == '\0')
3993    return(MagickTrue);
3994  /*
3995    Read image.
3996  */
3997  XSetCursorState(display,windows,MagickTrue);
3998  XCheckRefreshWindows(display,windows);
3999  (void) CopyMagickString(resource_info->image_info->filename,filename,
4000    MaxTextExtent);
4001  composite_image=ReadImage(resource_info->image_info,exception);
4002  CatchException(exception);
4003  XSetCursorState(display,windows,MagickFalse);
4004  if (composite_image == (Image *) NULL)
4005    return(MagickFalse);
4006  /*
4007    Map Command widget.
4008  */
4009  (void) CloneString(&windows->command.name,"Composite");
4010  windows->command.data=1;
4011  (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
4012  (void) XMapRaised(display,windows->command.id);
4013  XClientMessage(display,windows->image.id,windows->im_protocols,
4014    windows->im_update_widget,CurrentTime);
4015  /*
4016    Track pointer until button 1 is pressed.
4017  */
4018  XQueryPosition(display,windows->image.id,&x,&y);
4019  (void) XSelectInput(display,windows->image.id,
4020    windows->image.attributes.event_mask | PointerMotionMask);
4021  composite_info.x=(ssize_t) windows->image.x+x;
4022  composite_info.y=(ssize_t) windows->image.y+y;
4023  composite_info.width=0;
4024  composite_info.height=0;
4025  cursor=XCreateFontCursor(display,XC_ul_angle);
4026  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4027  blend=0.0;
4028  state=DefaultState;
4029  do
4030  {
4031    if (windows->info.mapped != MagickFalse)
4032      {
4033        /*
4034          Display pointer position.
4035        */
4036        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4037          (long) composite_info.x,(long) composite_info.y);
4038        XInfoWidget(display,windows,text);
4039      }
4040    highlight_info=composite_info;
4041    highlight_info.x=composite_info.x-windows->image.x;
4042    highlight_info.y=composite_info.y-windows->image.y;
4043    XHighlightRectangle(display,windows->image.id,
4044      windows->image.highlight_context,&highlight_info);
4045    /*
4046      Wait for next event.
4047    */
4048    XScreenEvent(display,windows,&event,exception);
4049    XHighlightRectangle(display,windows->image.id,
4050      windows->image.highlight_context,&highlight_info);
4051    if (event.xany.window == windows->command.id)
4052      {
4053        /*
4054          Select a command from the Command widget.
4055        */
4056        id=XCommandWidget(display,windows,CompositeMenu,&event);
4057        if (id < 0)
4058          continue;
4059        switch (CompositeCommands[id])
4060        {
4061          case CompositeOperatorsCommand:
4062          {
4063            char
4064              command[MaxTextExtent],
4065              **operators;
4066
4067            /*
4068              Select a command from the pop-up menu.
4069            */
4070            operators=GetCommandOptions(MagickComposeOptions);
4071            if (operators == (char **) NULL)
4072              break;
4073            entry=XMenuWidget(display,windows,CompositeMenu[id],
4074              (const char **) operators,command);
4075            if (entry >= 0)
4076              compose=(CompositeOperator) ParseCommandOption(
4077                MagickComposeOptions,MagickFalse,operators[entry]);
4078            operators=DestroyStringList(operators);
4079            break;
4080          }
4081          case CompositeDissolveCommand:
4082          {
4083            static char
4084              factor[MaxTextExtent] = "20.0";
4085
4086            /*
4087              Dissolve the two images a given percent.
4088            */
4089            (void) XSetFunction(display,windows->image.highlight_context,
4090              GXcopy);
4091            (void) XDialogWidget(display,windows,"Dissolve",
4092              "Enter the blend factor (0.0 - 99.9%):",factor);
4093            (void) XSetFunction(display,windows->image.highlight_context,
4094              GXinvert);
4095            if (*factor == '\0')
4096              break;
4097            blend=StringToDouble(factor,(char **) NULL);
4098            compose=DissolveCompositeOp;
4099            break;
4100          }
4101          case CompositeDisplaceCommand:
4102          {
4103            /*
4104              Get horizontal and vertical scale displacement geometry.
4105            */
4106            (void) XSetFunction(display,windows->image.highlight_context,
4107              GXcopy);
4108            (void) XDialogWidget(display,windows,"Displace",
4109              "Enter the horizontal and vertical scale:",displacement_geometry);
4110            (void) XSetFunction(display,windows->image.highlight_context,
4111              GXinvert);
4112            if (*displacement_geometry == '\0')
4113              break;
4114            compose=DisplaceCompositeOp;
4115            break;
4116          }
4117          case CompositeHelpCommand:
4118          {
4119            (void) XSetFunction(display,windows->image.highlight_context,
4120              GXcopy);
4121            XTextViewWidget(display,resource_info,windows,MagickFalse,
4122              "Help Viewer - Image Composite",ImageCompositeHelp);
4123            (void) XSetFunction(display,windows->image.highlight_context,
4124              GXinvert);
4125            break;
4126          }
4127          case CompositeDismissCommand:
4128          {
4129            /*
4130              Prematurely exit.
4131            */
4132            state|=EscapeState;
4133            state|=ExitState;
4134            break;
4135          }
4136          default:
4137            break;
4138        }
4139        continue;
4140      }
4141    switch (event.type)
4142    {
4143      case ButtonPress:
4144      {
4145        if (image->debug != MagickFalse)
4146          (void) LogMagickEvent(X11Event,GetMagickModule(),
4147            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4148            event.xbutton.button,event.xbutton.x,event.xbutton.y);
4149        if (event.xbutton.button != Button1)
4150          break;
4151        if (event.xbutton.window != windows->image.id)
4152          break;
4153        /*
4154          Change cursor.
4155        */
4156        composite_info.width=composite_image->columns;
4157        composite_info.height=composite_image->rows;
4158        (void) XCheckDefineCursor(display,windows->image.id,cursor);
4159        composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4160        composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4161        break;
4162      }
4163      case ButtonRelease:
4164      {
4165        if (image->debug != MagickFalse)
4166          (void) LogMagickEvent(X11Event,GetMagickModule(),
4167            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4168            event.xbutton.button,event.xbutton.x,event.xbutton.y);
4169        if (event.xbutton.button != Button1)
4170          break;
4171        if (event.xbutton.window != windows->image.id)
4172          break;
4173        if ((composite_info.width != 0) && (composite_info.height != 0))
4174          {
4175            /*
4176              User has selected the location of the composite image.
4177            */
4178            composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4179            composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4180            state|=ExitState;
4181          }
4182        break;
4183      }
4184      case Expose:
4185        break;
4186      case KeyPress:
4187      {
4188        char
4189          command[MaxTextExtent];
4190
4191        KeySym
4192          key_symbol;
4193
4194        int
4195          length;
4196
4197        if (event.xkey.window != windows->image.id)
4198          break;
4199        /*
4200          Respond to a user key press.
4201        */
4202        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4203          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4204        *(command+length)='\0';
4205        if (image->debug != MagickFalse)
4206          (void) LogMagickEvent(X11Event,GetMagickModule(),
4207            "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4208        switch ((int) key_symbol)
4209        {
4210          case XK_Escape:
4211          case XK_F20:
4212          {
4213            /*
4214              Prematurely exit.
4215            */
4216            composite_image=DestroyImage(composite_image);
4217            state|=EscapeState;
4218            state|=ExitState;
4219            break;
4220          }
4221          case XK_F1:
4222          case XK_Help:
4223          {
4224            (void) XSetFunction(display,windows->image.highlight_context,
4225              GXcopy);
4226            XTextViewWidget(display,resource_info,windows,MagickFalse,
4227              "Help Viewer - Image Composite",ImageCompositeHelp);
4228            (void) XSetFunction(display,windows->image.highlight_context,
4229              GXinvert);
4230            break;
4231          }
4232          default:
4233          {
4234            (void) XBell(display,0);
4235            break;
4236          }
4237        }
4238        break;
4239      }
4240      case MotionNotify:
4241      {
4242        /*
4243          Map and unmap Info widget as text cursor crosses its boundaries.
4244        */
4245        x=event.xmotion.x;
4246        y=event.xmotion.y;
4247        if (windows->info.mapped != MagickFalse)
4248          {
4249            if ((x < (int) (windows->info.x+windows->info.width)) &&
4250                (y < (int) (windows->info.y+windows->info.height)))
4251              (void) XWithdrawWindow(display,windows->info.id,
4252                windows->info.screen);
4253          }
4254        else
4255          if ((x > (int) (windows->info.x+windows->info.width)) ||
4256              (y > (int) (windows->info.y+windows->info.height)))
4257            (void) XMapWindow(display,windows->info.id);
4258        composite_info.x=(ssize_t) windows->image.x+x;
4259        composite_info.y=(ssize_t) windows->image.y+y;
4260        break;
4261      }
4262      default:
4263      {
4264        if (image->debug != MagickFalse)
4265          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4266            event.type);
4267        break;
4268      }
4269    }
4270  } while ((state & ExitState) == 0);
4271  (void) XSelectInput(display,windows->image.id,
4272    windows->image.attributes.event_mask);
4273  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4274  XSetCursorState(display,windows,MagickFalse);
4275  (void) XFreeCursor(display,cursor);
4276  if ((state & EscapeState) != 0)
4277    return(MagickTrue);
4278  /*
4279    Image compositing is relative to image configuration.
4280  */
4281  XSetCursorState(display,windows,MagickTrue);
4282  XCheckRefreshWindows(display,windows);
4283  width=(unsigned int) image->columns;
4284  height=(unsigned int) image->rows;
4285  x=0;
4286  y=0;
4287  if (windows->image.crop_geometry != (char *) NULL)
4288    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4289  scale_factor=(MagickRealType) width/windows->image.ximage->width;
4290  composite_info.x+=x;
4291  composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4292  composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4293  scale_factor=(MagickRealType) height/windows->image.ximage->height;
4294  composite_info.y+=y;
4295  composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4296  composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4297  if ((composite_info.width != composite_image->columns) ||
4298      (composite_info.height != composite_image->rows))
4299    {
4300      Image
4301        *resize_image;
4302
4303      /*
4304        Scale composite image.
4305      */
4306      resize_image=ResizeImage(composite_image,composite_info.width,
4307        composite_info.height,composite_image->filter,composite_image->blur,
4308        exception);
4309      composite_image=DestroyImage(composite_image);
4310      if (resize_image == (Image *) NULL)
4311        {
4312          XSetCursorState(display,windows,MagickFalse);
4313          return(MagickFalse);
4314        }
4315      composite_image=resize_image;
4316    }
4317  if (compose == DisplaceCompositeOp)
4318    (void) SetImageArtifact(composite_image,"compose:args",
4319      displacement_geometry);
4320  if (blend != 0.0)
4321    {
4322      CacheView
4323        *image_view;
4324
4325      int
4326        y;
4327
4328      Quantum
4329        opacity;
4330
4331      register int
4332        x;
4333
4334      register Quantum
4335        *q;
4336
4337      /*
4338        Create mattes for blending.
4339      */
4340      (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,exception);
4341      opacity=(Quantum) (ScaleQuantumToChar((Quantum) QuantumRange)-
4342        ((ssize_t) ScaleQuantumToChar((Quantum) QuantumRange)*blend)/100);
4343      if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
4344        return(MagickFalse);
4345      image->matte=MagickTrue;
4346      image_view=AcquireCacheView(image);
4347      for (y=0; y < (int) image->rows; y++)
4348      {
4349        q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4350          exception);
4351        if (q == (Quantum *) NULL)
4352          break;
4353        for (x=0; x < (int) image->columns; x++)
4354        {
4355          SetPixelAlpha(image,opacity,q);
4356          q+=GetPixelChannels(image);
4357        }
4358        if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4359          break;
4360      }
4361      image_view=DestroyCacheView(image_view);
4362    }
4363  /*
4364    Composite image with X Image window.
4365  */
4366  (void) CompositeImage(image,compose,composite_image,composite_info.x,
4367    composite_info.y,exception);
4368  composite_image=DestroyImage(composite_image);
4369  XSetCursorState(display,windows,MagickFalse);
4370  /*
4371    Update image configuration.
4372  */
4373  XConfigureImageColormap(display,resource_info,windows,image,exception);
4374  (void) XConfigureImage(display,resource_info,windows,image,exception);
4375  return(MagickTrue);
4376}
4377
4378/*
4379%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4380%                                                                             %
4381%                                                                             %
4382%                                                                             %
4383+   X C o n f i g u r e I m a g e                                             %
4384%                                                                             %
4385%                                                                             %
4386%                                                                             %
4387%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4388%
4389%  XConfigureImage() creates a new X image.  It also notifies the window
4390%  manager of the new image size and configures the transient widows.
4391%
4392%  The format of the XConfigureImage method is:
4393%
4394%      MagickBooleanType XConfigureImage(Display *display,
4395%        XResourceInfo *resource_info,XWindows *windows,Image *image,
4396%        ExceptionInfo *exception)
4397%
4398%  A description of each parameter follows:
4399%
4400%    o display: Specifies a connection to an X server; returned from
4401%      XOpenDisplay.
4402%
4403%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4404%
4405%    o windows: Specifies a pointer to a XWindows structure.
4406%
4407%    o image: the image.
4408%
4409%    o exception: return any errors or warnings in this structure.
4410%
4411%    o exception: return any errors or warnings in this structure.
4412%
4413*/
4414static MagickBooleanType XConfigureImage(Display *display,
4415  XResourceInfo *resource_info,XWindows *windows,Image *image,
4416  ExceptionInfo *exception)
4417{
4418  char
4419    geometry[MaxTextExtent];
4420
4421  MagickStatusType
4422    status;
4423
4424  size_t
4425    mask,
4426    height,
4427    width;
4428
4429  ssize_t
4430    x,
4431    y;
4432
4433  XSizeHints
4434    *size_hints;
4435
4436  XWindowChanges
4437    window_changes;
4438
4439  /*
4440    Dismiss if window dimensions are zero.
4441  */
4442  width=(unsigned int) windows->image.window_changes.width;
4443  height=(unsigned int) windows->image.window_changes.height;
4444  if (image->debug != MagickFalse)
4445    (void) LogMagickEvent(X11Event,GetMagickModule(),
4446      "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4447      windows->image.ximage->height,(double) width,(double) height);
4448  if ((width*height) == 0)
4449    return(MagickTrue);
4450  x=0;
4451  y=0;
4452  /*
4453    Resize image to fit Image window dimensions.
4454  */
4455  XSetCursorState(display,windows,MagickTrue);
4456  (void) XFlush(display);
4457  if (((int) width != windows->image.ximage->width) ||
4458      ((int) height != windows->image.ximage->height))
4459    image->taint=MagickTrue;
4460  windows->magnify.x=(int)
4461    width*windows->magnify.x/windows->image.ximage->width;
4462  windows->magnify.y=(int)
4463    height*windows->magnify.y/windows->image.ximage->height;
4464  windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4465  windows->image.y=(int)
4466    (height*windows->image.y/windows->image.ximage->height);
4467  status=XMakeImage(display,resource_info,&windows->image,image,
4468    (unsigned int) width,(unsigned int) height,exception);
4469  if (status == MagickFalse)
4470    XNoticeWidget(display,windows,"Unable to configure X image:",
4471      windows->image.name);
4472  /*
4473    Notify window manager of the new configuration.
4474  */
4475  if (resource_info->image_geometry != (char *) NULL)
4476    (void) FormatLocaleString(geometry,MaxTextExtent,"%s>!",
4477      resource_info->image_geometry);
4478  else
4479    (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
4480      XDisplayWidth(display,windows->image.screen),
4481      XDisplayHeight(display,windows->image.screen));
4482  (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4483  window_changes.width=(int) width;
4484  if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4485    window_changes.width=XDisplayWidth(display,windows->image.screen);
4486  window_changes.height=(int) height;
4487  if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4488    window_changes.height=XDisplayHeight(display,windows->image.screen);
4489  mask=(size_t) (CWWidth | CWHeight);
4490  if (resource_info->backdrop)
4491    {
4492      mask|=CWX | CWY;
4493      window_changes.x=(int)
4494        ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4495      window_changes.y=(int)
4496        ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4497    }
4498  (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4499    (unsigned int) mask,&window_changes);
4500  (void) XClearWindow(display,windows->image.id);
4501  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4502  /*
4503    Update Magnify window configuration.
4504  */
4505  if (windows->magnify.mapped != MagickFalse)
4506    XMakeMagnifyImage(display,windows,exception);
4507  windows->pan.crop_geometry=windows->image.crop_geometry;
4508  XBestIconSize(display,&windows->pan,image);
4509  while (((windows->pan.width << 1) < MaxIconSize) &&
4510         ((windows->pan.height << 1) < MaxIconSize))
4511  {
4512    windows->pan.width<<=1;
4513    windows->pan.height<<=1;
4514  }
4515  if (windows->pan.geometry != (char *) NULL)
4516    (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4517      &windows->pan.width,&windows->pan.height);
4518  window_changes.width=(int) windows->pan.width;
4519  window_changes.height=(int) windows->pan.height;
4520  size_hints=XAllocSizeHints();
4521  if (size_hints != (XSizeHints *) NULL)
4522    {
4523      /*
4524        Set new size hints.
4525      */
4526      size_hints->flags=PSize | PMinSize | PMaxSize;
4527      size_hints->width=window_changes.width;
4528      size_hints->height=window_changes.height;
4529      size_hints->min_width=size_hints->width;
4530      size_hints->min_height=size_hints->height;
4531      size_hints->max_width=size_hints->width;
4532      size_hints->max_height=size_hints->height;
4533      (void) XSetNormalHints(display,windows->pan.id,size_hints);
4534      (void) XFree((void *) size_hints);
4535    }
4536  (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4537    (unsigned int) (CWWidth | CWHeight),&window_changes);
4538  /*
4539    Update icon window configuration.
4540  */
4541  windows->icon.crop_geometry=windows->image.crop_geometry;
4542  XBestIconSize(display,&windows->icon,image);
4543  window_changes.width=(int) windows->icon.width;
4544  window_changes.height=(int) windows->icon.height;
4545  (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4546    (unsigned int) (CWWidth | CWHeight),&window_changes);
4547  XSetCursorState(display,windows,MagickFalse);
4548  return(status != 0 ? MagickTrue : MagickFalse);
4549}
4550
4551/*
4552%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4553%                                                                             %
4554%                                                                             %
4555%                                                                             %
4556+   X C r o p I m a g e                                                       %
4557%                                                                             %
4558%                                                                             %
4559%                                                                             %
4560%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4561%
4562%  XCropImage() allows the user to select a region of the image and crop, copy,
4563%  or cut it.  For copy or cut, the image can subsequently be composited onto
4564%  the image with XPasteImage.
4565%
4566%  The format of the XCropImage method is:
4567%
4568%      MagickBooleanType XCropImage(Display *display,
4569%        XResourceInfo *resource_info,XWindows *windows,Image *image,
4570%        const ClipboardMode mode,ExceptionInfo *exception)
4571%
4572%  A description of each parameter follows:
4573%
4574%    o display: Specifies a connection to an X server; returned from
4575%      XOpenDisplay.
4576%
4577%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4578%
4579%    o windows: Specifies a pointer to a XWindows structure.
4580%
4581%    o image: the image; returned from ReadImage.
4582%
4583%    o mode: This unsigned value specified whether the image should be
4584%      cropped, copied, or cut.
4585%
4586%    o exception: return any errors or warnings in this structure.
4587%
4588*/
4589static MagickBooleanType XCropImage(Display *display,
4590  XResourceInfo *resource_info,XWindows *windows,Image *image,
4591  const ClipboardMode mode,ExceptionInfo *exception)
4592{
4593  static const char
4594    *CropModeMenu[] =
4595    {
4596      "Help",
4597      "Dismiss",
4598      (char *) NULL
4599    },
4600    *RectifyModeMenu[] =
4601    {
4602      "Crop",
4603      "Help",
4604      "Dismiss",
4605      (char *) NULL
4606    };
4607
4608  static const ModeType
4609    CropCommands[] =
4610    {
4611      CropHelpCommand,
4612      CropDismissCommand
4613    },
4614    RectifyCommands[] =
4615    {
4616      RectifyCopyCommand,
4617      RectifyHelpCommand,
4618      RectifyDismissCommand
4619    };
4620
4621  CacheView
4622    *image_view;
4623
4624  char
4625    command[MaxTextExtent],
4626    text[MaxTextExtent];
4627
4628  Cursor
4629    cursor;
4630
4631  int
4632    id,
4633    x,
4634    y;
4635
4636  KeySym
4637    key_symbol;
4638
4639  Image
4640    *crop_image;
4641
4642  MagickRealType
4643    scale_factor;
4644
4645  RectangleInfo
4646    crop_info,
4647    highlight_info;
4648
4649  register Quantum
4650    *q;
4651
4652  unsigned int
4653    height,
4654    width;
4655
4656  size_t
4657    state;
4658
4659  XEvent
4660    event;
4661
4662  /*
4663    Map Command widget.
4664  */
4665  switch (mode)
4666  {
4667    case CopyMode:
4668    {
4669      (void) CloneString(&windows->command.name,"Copy");
4670      break;
4671    }
4672    case CropMode:
4673    {
4674      (void) CloneString(&windows->command.name,"Crop");
4675      break;
4676    }
4677    case CutMode:
4678    {
4679      (void) CloneString(&windows->command.name,"Cut");
4680      break;
4681    }
4682  }
4683  RectifyModeMenu[0]=windows->command.name;
4684  windows->command.data=0;
4685  (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4686  (void) XMapRaised(display,windows->command.id);
4687  XClientMessage(display,windows->image.id,windows->im_protocols,
4688    windows->im_update_widget,CurrentTime);
4689  /*
4690    Track pointer until button 1 is pressed.
4691  */
4692  XQueryPosition(display,windows->image.id,&x,&y);
4693  (void) XSelectInput(display,windows->image.id,
4694    windows->image.attributes.event_mask | PointerMotionMask);
4695  crop_info.x=(ssize_t) windows->image.x+x;
4696  crop_info.y=(ssize_t) windows->image.y+y;
4697  crop_info.width=0;
4698  crop_info.height=0;
4699  cursor=XCreateFontCursor(display,XC_fleur);
4700  state=DefaultState;
4701  do
4702  {
4703    if (windows->info.mapped != MagickFalse)
4704      {
4705        /*
4706          Display pointer position.
4707        */
4708        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4709          (long) crop_info.x,(long) crop_info.y);
4710        XInfoWidget(display,windows,text);
4711      }
4712    /*
4713      Wait for next event.
4714    */
4715    XScreenEvent(display,windows,&event,exception);
4716    if (event.xany.window == windows->command.id)
4717      {
4718        /*
4719          Select a command from the Command widget.
4720        */
4721        id=XCommandWidget(display,windows,CropModeMenu,&event);
4722        if (id < 0)
4723          continue;
4724        switch (CropCommands[id])
4725        {
4726          case CropHelpCommand:
4727          {
4728            switch (mode)
4729            {
4730              case CopyMode:
4731              {
4732                XTextViewWidget(display,resource_info,windows,MagickFalse,
4733                  "Help Viewer - Image Copy",ImageCopyHelp);
4734                break;
4735              }
4736              case CropMode:
4737              {
4738                XTextViewWidget(display,resource_info,windows,MagickFalse,
4739                  "Help Viewer - Image Crop",ImageCropHelp);
4740                break;
4741              }
4742              case CutMode:
4743              {
4744                XTextViewWidget(display,resource_info,windows,MagickFalse,
4745                  "Help Viewer - Image Cut",ImageCutHelp);
4746                break;
4747              }
4748            }
4749            break;
4750          }
4751          case CropDismissCommand:
4752          {
4753            /*
4754              Prematurely exit.
4755            */
4756            state|=EscapeState;
4757            state|=ExitState;
4758            break;
4759          }
4760          default:
4761            break;
4762        }
4763        continue;
4764      }
4765    switch (event.type)
4766    {
4767      case ButtonPress:
4768      {
4769        if (event.xbutton.button != Button1)
4770          break;
4771        if (event.xbutton.window != windows->image.id)
4772          break;
4773        /*
4774          Note first corner of cropping rectangle-- exit loop.
4775        */
4776        (void) XCheckDefineCursor(display,windows->image.id,cursor);
4777        crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4778        crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4779        state|=ExitState;
4780        break;
4781      }
4782      case ButtonRelease:
4783        break;
4784      case Expose:
4785        break;
4786      case KeyPress:
4787      {
4788        if (event.xkey.window != windows->image.id)
4789          break;
4790        /*
4791          Respond to a user key press.
4792        */
4793        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4794          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4795        switch ((int) key_symbol)
4796        {
4797          case XK_Escape:
4798          case XK_F20:
4799          {
4800            /*
4801              Prematurely exit.
4802            */
4803            state|=EscapeState;
4804            state|=ExitState;
4805            break;
4806          }
4807          case XK_F1:
4808          case XK_Help:
4809          {
4810            switch (mode)
4811            {
4812              case CopyMode:
4813              {
4814                XTextViewWidget(display,resource_info,windows,MagickFalse,
4815                  "Help Viewer - Image Copy",ImageCopyHelp);
4816                break;
4817              }
4818              case CropMode:
4819              {
4820                XTextViewWidget(display,resource_info,windows,MagickFalse,
4821                  "Help Viewer - Image Crop",ImageCropHelp);
4822                break;
4823              }
4824              case CutMode:
4825              {
4826                XTextViewWidget(display,resource_info,windows,MagickFalse,
4827                  "Help Viewer - Image Cut",ImageCutHelp);
4828                break;
4829              }
4830            }
4831            break;
4832          }
4833          default:
4834          {
4835            (void) XBell(display,0);
4836            break;
4837          }
4838        }
4839        break;
4840      }
4841      case MotionNotify:
4842      {
4843        if (event.xmotion.window != windows->image.id)
4844          break;
4845        /*
4846          Map and unmap Info widget as text cursor crosses its boundaries.
4847        */
4848        x=event.xmotion.x;
4849        y=event.xmotion.y;
4850        if (windows->info.mapped != MagickFalse)
4851          {
4852            if ((x < (int) (windows->info.x+windows->info.width)) &&
4853                (y < (int) (windows->info.y+windows->info.height)))
4854              (void) XWithdrawWindow(display,windows->info.id,
4855                windows->info.screen);
4856          }
4857        else
4858          if ((x > (int) (windows->info.x+windows->info.width)) ||
4859              (y > (int) (windows->info.y+windows->info.height)))
4860            (void) XMapWindow(display,windows->info.id);
4861        crop_info.x=(ssize_t) windows->image.x+x;
4862        crop_info.y=(ssize_t) windows->image.y+y;
4863        break;
4864      }
4865      default:
4866        break;
4867    }
4868  } while ((state & ExitState) == 0);
4869  (void) XSelectInput(display,windows->image.id,
4870    windows->image.attributes.event_mask);
4871  if ((state & EscapeState) != 0)
4872    {
4873      /*
4874        User want to exit without cropping.
4875      */
4876      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4877      (void) XFreeCursor(display,cursor);
4878      return(MagickTrue);
4879    }
4880  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4881  do
4882  {
4883    /*
4884      Size rectangle as pointer moves until the mouse button is released.
4885    */
4886    x=(int) crop_info.x;
4887    y=(int) crop_info.y;
4888    crop_info.width=0;
4889    crop_info.height=0;
4890    state=DefaultState;
4891    do
4892    {
4893      highlight_info=crop_info;
4894      highlight_info.x=crop_info.x-windows->image.x;
4895      highlight_info.y=crop_info.y-windows->image.y;
4896      if ((highlight_info.width > 3) && (highlight_info.height > 3))
4897        {
4898          /*
4899            Display info and draw cropping rectangle.
4900          */
4901          if (windows->info.mapped == MagickFalse)
4902            (void) XMapWindow(display,windows->info.id);
4903          (void) FormatLocaleString(text,MaxTextExtent,
4904            " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4905            crop_info.height,(double) crop_info.x,(double) crop_info.y);
4906          XInfoWidget(display,windows,text);
4907          XHighlightRectangle(display,windows->image.id,
4908            windows->image.highlight_context,&highlight_info);
4909        }
4910      else
4911        if (windows->info.mapped != MagickFalse)
4912          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4913      /*
4914        Wait for next event.
4915      */
4916      XScreenEvent(display,windows,&event,exception);
4917      if ((highlight_info.width > 3) && (highlight_info.height > 3))
4918        XHighlightRectangle(display,windows->image.id,
4919          windows->image.highlight_context,&highlight_info);
4920      switch (event.type)
4921      {
4922        case ButtonPress:
4923        {
4924          crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4925          crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4926          break;
4927        }
4928        case ButtonRelease:
4929        {
4930          /*
4931            User has committed to cropping rectangle.
4932          */
4933          crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4934          crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4935          XSetCursorState(display,windows,MagickFalse);
4936          state|=ExitState;
4937          windows->command.data=0;
4938          (void) XCommandWidget(display,windows,RectifyModeMenu,
4939            (XEvent *) NULL);
4940          break;
4941        }
4942        case Expose:
4943          break;
4944        case MotionNotify:
4945        {
4946          crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4947          crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4948        }
4949        default:
4950          break;
4951      }
4952      if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4953          ((state & ExitState) != 0))
4954        {
4955          /*
4956            Check boundary conditions.
4957          */
4958          if (crop_info.x < 0)
4959            crop_info.x=0;
4960          else
4961            if (crop_info.x > (ssize_t) windows->image.ximage->width)
4962              crop_info.x=(ssize_t) windows->image.ximage->width;
4963          if ((int) crop_info.x < x)
4964            crop_info.width=(unsigned int) (x-crop_info.x);
4965          else
4966            {
4967              crop_info.width=(unsigned int) (crop_info.x-x);
4968              crop_info.x=(ssize_t) x;
4969            }
4970          if (crop_info.y < 0)
4971            crop_info.y=0;
4972          else
4973            if (crop_info.y > (ssize_t) windows->image.ximage->height)
4974              crop_info.y=(ssize_t) windows->image.ximage->height;
4975          if ((int) crop_info.y < y)
4976            crop_info.height=(unsigned int) (y-crop_info.y);
4977          else
4978            {
4979              crop_info.height=(unsigned int) (crop_info.y-y);
4980              crop_info.y=(ssize_t) y;
4981            }
4982        }
4983    } while ((state & ExitState) == 0);
4984    /*
4985      Wait for user to grab a corner of the rectangle or press return.
4986    */
4987    state=DefaultState;
4988    (void) XMapWindow(display,windows->info.id);
4989    do
4990    {
4991      if (windows->info.mapped != MagickFalse)
4992        {
4993          /*
4994            Display pointer position.
4995          */
4996          (void) FormatLocaleString(text,MaxTextExtent,
4997            " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4998            crop_info.height,(double) crop_info.x,(double) crop_info.y);
4999          XInfoWidget(display,windows,text);
5000        }
5001      highlight_info=crop_info;
5002      highlight_info.x=crop_info.x-windows->image.x;
5003      highlight_info.y=crop_info.y-windows->image.y;
5004      if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
5005        {
5006          state|=EscapeState;
5007          state|=ExitState;
5008          break;
5009        }
5010      XHighlightRectangle(display,windows->image.id,
5011        windows->image.highlight_context,&highlight_info);
5012      XScreenEvent(display,windows,&event,exception);
5013      if (event.xany.window == windows->command.id)
5014        {
5015          /*
5016            Select a command from the Command widget.
5017          */
5018          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5019          id=XCommandWidget(display,windows,RectifyModeMenu,&event);
5020          (void) XSetFunction(display,windows->image.highlight_context,
5021            GXinvert);
5022          XHighlightRectangle(display,windows->image.id,
5023            windows->image.highlight_context,&highlight_info);
5024          if (id >= 0)
5025            switch (RectifyCommands[id])
5026            {
5027              case RectifyCopyCommand:
5028              {
5029                state|=ExitState;
5030                break;
5031              }
5032              case RectifyHelpCommand:
5033              {
5034                (void) XSetFunction(display,windows->image.highlight_context,
5035                  GXcopy);
5036                switch (mode)
5037                {
5038                  case CopyMode:
5039                  {
5040                    XTextViewWidget(display,resource_info,windows,MagickFalse,
5041                      "Help Viewer - Image Copy",ImageCopyHelp);
5042                    break;
5043                  }
5044                  case CropMode:
5045                  {
5046                    XTextViewWidget(display,resource_info,windows,MagickFalse,
5047                      "Help Viewer - Image Crop",ImageCropHelp);
5048                    break;
5049                  }
5050                  case CutMode:
5051                  {
5052                    XTextViewWidget(display,resource_info,windows,MagickFalse,
5053                      "Help Viewer - Image Cut",ImageCutHelp);
5054                    break;
5055                  }
5056                }
5057                (void) XSetFunction(display,windows->image.highlight_context,
5058                  GXinvert);
5059                break;
5060              }
5061              case RectifyDismissCommand:
5062              {
5063                /*
5064                  Prematurely exit.
5065                */
5066                state|=EscapeState;
5067                state|=ExitState;
5068                break;
5069              }
5070              default:
5071                break;
5072            }
5073          continue;
5074        }
5075      XHighlightRectangle(display,windows->image.id,
5076        windows->image.highlight_context,&highlight_info);
5077      switch (event.type)
5078      {
5079        case ButtonPress:
5080        {
5081          if (event.xbutton.button != Button1)
5082            break;
5083          if (event.xbutton.window != windows->image.id)
5084            break;
5085          x=windows->image.x+event.xbutton.x;
5086          y=windows->image.y+event.xbutton.y;
5087          if ((x < (int) (crop_info.x+RoiDelta)) &&
5088              (x > (int) (crop_info.x-RoiDelta)) &&
5089              (y < (int) (crop_info.y+RoiDelta)) &&
5090              (y > (int) (crop_info.y-RoiDelta)))
5091            {
5092              crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5093              crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5094              state|=UpdateConfigurationState;
5095              break;
5096            }
5097          if ((x < (int) (crop_info.x+RoiDelta)) &&
5098              (x > (int) (crop_info.x-RoiDelta)) &&
5099              (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5100              (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5101            {
5102              crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5103              state|=UpdateConfigurationState;
5104              break;
5105            }
5106          if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5107              (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5108              (y < (int) (crop_info.y+RoiDelta)) &&
5109              (y > (int) (crop_info.y-RoiDelta)))
5110            {
5111              crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5112              state|=UpdateConfigurationState;
5113              break;
5114            }
5115          if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5116              (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5117              (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5118              (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5119            {
5120              state|=UpdateConfigurationState;
5121              break;
5122            }
5123        }
5124        case ButtonRelease:
5125        {
5126          if (event.xbutton.window == windows->pan.id)
5127            if ((highlight_info.x != crop_info.x-windows->image.x) ||
5128                (highlight_info.y != crop_info.y-windows->image.y))
5129              XHighlightRectangle(display,windows->image.id,
5130                windows->image.highlight_context,&highlight_info);
5131          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5132            event.xbutton.time);
5133          break;
5134        }
5135        case Expose:
5136        {
5137          if (event.xexpose.window == windows->image.id)
5138            if (event.xexpose.count == 0)
5139              {
5140                event.xexpose.x=(int) highlight_info.x;
5141                event.xexpose.y=(int) highlight_info.y;
5142                event.xexpose.width=(int) highlight_info.width;
5143                event.xexpose.height=(int) highlight_info.height;
5144                XRefreshWindow(display,&windows->image,&event);
5145              }
5146          if (event.xexpose.window == windows->info.id)
5147            if (event.xexpose.count == 0)
5148              XInfoWidget(display,windows,text);
5149          break;
5150        }
5151        case KeyPress:
5152        {
5153          if (event.xkey.window != windows->image.id)
5154            break;
5155          /*
5156            Respond to a user key press.
5157          */
5158          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5159            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5160          switch ((int) key_symbol)
5161          {
5162            case XK_Escape:
5163            case XK_F20:
5164              state|=EscapeState;
5165            case XK_Return:
5166            {
5167              state|=ExitState;
5168              break;
5169            }
5170            case XK_Home:
5171            case XK_KP_Home:
5172            {
5173              crop_info.x=(ssize_t) (windows->image.width/2L-
5174                crop_info.width/2L);
5175              crop_info.y=(ssize_t) (windows->image.height/2L-
5176                crop_info.height/2L);
5177              break;
5178            }
5179            case XK_Left:
5180            case XK_KP_Left:
5181            {
5182              crop_info.x--;
5183              break;
5184            }
5185            case XK_Up:
5186            case XK_KP_Up:
5187            case XK_Next:
5188            {
5189              crop_info.y--;
5190              break;
5191            }
5192            case XK_Right:
5193            case XK_KP_Right:
5194            {
5195              crop_info.x++;
5196              break;
5197            }
5198            case XK_Prior:
5199            case XK_Down:
5200            case XK_KP_Down:
5201            {
5202              crop_info.y++;
5203              break;
5204            }
5205            case XK_F1:
5206            case XK_Help:
5207            {
5208              (void) XSetFunction(display,windows->image.highlight_context,
5209                GXcopy);
5210              switch (mode)
5211              {
5212                case CopyMode:
5213                {
5214                  XTextViewWidget(display,resource_info,windows,MagickFalse,
5215                    "Help Viewer - Image Copy",ImageCopyHelp);
5216                  break;
5217                }
5218                case CropMode:
5219                {
5220                  XTextViewWidget(display,resource_info,windows,MagickFalse,
5221                    "Help Viewer - Image Cropg",ImageCropHelp);
5222                  break;
5223                }
5224                case CutMode:
5225                {
5226                  XTextViewWidget(display,resource_info,windows,MagickFalse,
5227                    "Help Viewer - Image Cutg",ImageCutHelp);
5228                  break;
5229                }
5230              }
5231              (void) XSetFunction(display,windows->image.highlight_context,
5232                GXinvert);
5233              break;
5234            }
5235            default:
5236            {
5237              (void) XBell(display,0);
5238              break;
5239            }
5240          }
5241          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5242            event.xkey.time);
5243          break;
5244        }
5245        case KeyRelease:
5246          break;
5247        case MotionNotify:
5248        {
5249          if (event.xmotion.window != windows->image.id)
5250            break;
5251          /*
5252            Map and unmap Info widget as text cursor crosses its boundaries.
5253          */
5254          x=event.xmotion.x;
5255          y=event.xmotion.y;
5256          if (windows->info.mapped != MagickFalse)
5257            {
5258              if ((x < (int) (windows->info.x+windows->info.width)) &&
5259                  (y < (int) (windows->info.y+windows->info.height)))
5260                (void) XWithdrawWindow(display,windows->info.id,
5261                  windows->info.screen);
5262            }
5263          else
5264            if ((x > (int) (windows->info.x+windows->info.width)) ||
5265                (y > (int) (windows->info.y+windows->info.height)))
5266              (void) XMapWindow(display,windows->info.id);
5267          crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5268          crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5269          break;
5270        }
5271        case SelectionRequest:
5272        {
5273          XSelectionEvent
5274            notify;
5275
5276          XSelectionRequestEvent
5277            *request;
5278
5279          /*
5280            Set primary selection.
5281          */
5282          (void) FormatLocaleString(text,MaxTextExtent,
5283            "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5284            crop_info.height,(double) crop_info.x,(double) crop_info.y);
5285          request=(&(event.xselectionrequest));
5286          (void) XChangeProperty(request->display,request->requestor,
5287            request->property,request->target,8,PropModeReplace,
5288            (unsigned char *) text,(int) strlen(text));
5289          notify.type=SelectionNotify;
5290          notify.display=request->display;
5291          notify.requestor=request->requestor;
5292          notify.selection=request->selection;
5293          notify.target=request->target;
5294          notify.time=request->time;
5295          if (request->property == None)
5296            notify.property=request->target;
5297          else
5298            notify.property=request->property;
5299          (void) XSendEvent(request->display,request->requestor,False,0,
5300            (XEvent *) &notify);
5301        }
5302        default:
5303          break;
5304      }
5305      if ((state & UpdateConfigurationState) != 0)
5306        {
5307          (void) XPutBackEvent(display,&event);
5308          (void) XCheckDefineCursor(display,windows->image.id,cursor);
5309          break;
5310        }
5311    } while ((state & ExitState) == 0);
5312  } while ((state & ExitState) == 0);
5313  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5314  XSetCursorState(display,windows,MagickFalse);
5315  if ((state & EscapeState) != 0)
5316    return(MagickTrue);
5317  if (mode == CropMode)
5318    if (((int) crop_info.width != windows->image.ximage->width) ||
5319        ((int) crop_info.height != windows->image.ximage->height))
5320      {
5321        /*
5322          Reconfigure Image window as defined by cropping rectangle.
5323        */
5324        XSetCropGeometry(display,windows,&crop_info,image);
5325        windows->image.window_changes.width=(int) crop_info.width;
5326        windows->image.window_changes.height=(int) crop_info.height;
5327        (void) XConfigureImage(display,resource_info,windows,image,exception);
5328        return(MagickTrue);
5329      }
5330  /*
5331    Copy image before applying image transforms.
5332  */
5333  XSetCursorState(display,windows,MagickTrue);
5334  XCheckRefreshWindows(display,windows);
5335  width=(unsigned int) image->columns;
5336  height=(unsigned int) image->rows;
5337  x=0;
5338  y=0;
5339  if (windows->image.crop_geometry != (char *) NULL)
5340    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5341  scale_factor=(MagickRealType) width/windows->image.ximage->width;
5342  crop_info.x+=x;
5343  crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5344  crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5345  scale_factor=(MagickRealType) height/windows->image.ximage->height;
5346  crop_info.y+=y;
5347  crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5348  crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5349  crop_image=CropImage(image,&crop_info,exception);
5350  XSetCursorState(display,windows,MagickFalse);
5351  if (crop_image == (Image *) NULL)
5352    return(MagickFalse);
5353  if (resource_info->copy_image != (Image *) NULL)
5354    resource_info->copy_image=DestroyImage(resource_info->copy_image);
5355  resource_info->copy_image=crop_image;
5356  if (mode == CopyMode)
5357    {
5358      (void) XConfigureImage(display,resource_info,windows,image,exception);
5359      return(MagickTrue);
5360    }
5361  /*
5362    Cut image.
5363  */
5364  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
5365    return(MagickFalse);
5366  image->matte=MagickTrue;
5367  image_view=AcquireCacheView(image);
5368  for (y=0; y < (int) crop_info.height; y++)
5369  {
5370    q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5371      crop_info.width,1,exception);
5372    if (q == (Quantum *) NULL)
5373      break;
5374    for (x=0; x < (int) crop_info.width; x++)
5375    {
5376      SetPixelAlpha(image,TransparentAlpha,q);
5377      q+=GetPixelChannels(image);
5378    }
5379    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5380      break;
5381  }
5382  image_view=DestroyCacheView(image_view);
5383  /*
5384    Update image configuration.
5385  */
5386  XConfigureImageColormap(display,resource_info,windows,image,exception);
5387  (void) XConfigureImage(display,resource_info,windows,image,exception);
5388  return(MagickTrue);
5389}
5390
5391/*
5392%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5393%                                                                             %
5394%                                                                             %
5395%                                                                             %
5396+   X D r a w I m a g e                                                       %
5397%                                                                             %
5398%                                                                             %
5399%                                                                             %
5400%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5401%
5402%  XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5403%  the image.
5404%
5405%  The format of the XDrawEditImage method is:
5406%
5407%      MagickBooleanType XDrawEditImage(Display *display,
5408%        XResourceInfo *resource_info,XWindows *windows,Image **image,
5409%        ExceptionInfo *exception)
5410%
5411%  A description of each parameter follows:
5412%
5413%    o display: Specifies a connection to an X server; returned from
5414%      XOpenDisplay.
5415%
5416%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5417%
5418%    o windows: Specifies a pointer to a XWindows structure.
5419%
5420%    o image: the image.
5421%
5422%    o exception: return any errors or warnings in this structure.
5423%
5424*/
5425static MagickBooleanType XDrawEditImage(Display *display,
5426  XResourceInfo *resource_info,XWindows *windows,Image **image,
5427  ExceptionInfo *exception)
5428{
5429  static const char
5430    *DrawMenu[] =
5431    {
5432      "Element",
5433      "Color",
5434      "Stipple",
5435      "Width",
5436      "Undo",
5437      "Help",
5438      "Dismiss",
5439      (char *) NULL
5440    };
5441
5442  static ElementType
5443    element = PointElement;
5444
5445  static const ModeType
5446    DrawCommands[] =
5447    {
5448      DrawElementCommand,
5449      DrawColorCommand,
5450      DrawStippleCommand,
5451      DrawWidthCommand,
5452      DrawUndoCommand,
5453      DrawHelpCommand,
5454      DrawDismissCommand
5455    };
5456
5457  static Pixmap
5458    stipple = (Pixmap) NULL;
5459
5460  static unsigned int
5461    pen_id = 0,
5462    line_width = 1;
5463
5464  char
5465    command[MaxTextExtent],
5466    text[MaxTextExtent];
5467
5468  Cursor
5469    cursor;
5470
5471  int
5472    entry,
5473    id,
5474    number_coordinates,
5475    x,
5476    y;
5477
5478  MagickRealType
5479    degrees;
5480
5481  MagickStatusType
5482    status;
5483
5484  RectangleInfo
5485    rectangle_info;
5486
5487  register int
5488    i;
5489
5490  unsigned int
5491    distance,
5492    height,
5493    max_coordinates,
5494    width;
5495
5496  size_t
5497    state;
5498
5499  Window
5500    root_window;
5501
5502  XDrawInfo
5503    draw_info;
5504
5505  XEvent
5506    event;
5507
5508  XPoint
5509    *coordinate_info;
5510
5511  XSegment
5512    line_info;
5513
5514  /*
5515    Allocate polygon info.
5516  */
5517  max_coordinates=2048;
5518  coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5519    sizeof(*coordinate_info));
5520  if (coordinate_info == (XPoint *) NULL)
5521    {
5522      (void) ThrowMagickException(exception,GetMagickModule(),
5523        ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5524      return(MagickFalse);
5525    }
5526  /*
5527    Map Command widget.
5528  */
5529  (void) CloneString(&windows->command.name,"Draw");
5530  windows->command.data=4;
5531  (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5532  (void) XMapRaised(display,windows->command.id);
5533  XClientMessage(display,windows->image.id,windows->im_protocols,
5534    windows->im_update_widget,CurrentTime);
5535  /*
5536    Wait for first button press.
5537  */
5538  root_window=XRootWindow(display,XDefaultScreen(display));
5539  draw_info.stencil=OpaqueStencil;
5540  status=MagickTrue;
5541  cursor=XCreateFontCursor(display,XC_tcross);
5542  for ( ; ; )
5543  {
5544    XQueryPosition(display,windows->image.id,&x,&y);
5545    (void) XSelectInput(display,windows->image.id,
5546      windows->image.attributes.event_mask | PointerMotionMask);
5547    (void) XCheckDefineCursor(display,windows->image.id,cursor);
5548    state=DefaultState;
5549    do
5550    {
5551      if (windows->info.mapped != MagickFalse)
5552        {
5553          /*
5554            Display pointer position.
5555          */
5556          (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
5557            x+windows->image.x,y+windows->image.y);
5558          XInfoWidget(display,windows,text);
5559        }
5560      /*
5561        Wait for next event.
5562      */
5563      XScreenEvent(display,windows,&event,exception);
5564      if (event.xany.window == windows->command.id)
5565        {
5566          /*
5567            Select a command from the Command widget.
5568          */
5569          id=XCommandWidget(display,windows,DrawMenu,&event);
5570          if (id < 0)
5571            continue;
5572          switch (DrawCommands[id])
5573          {
5574            case DrawElementCommand:
5575            {
5576              static const char
5577                *Elements[] =
5578                {
5579                  "point",
5580                  "line",
5581                  "rectangle",
5582                  "fill rectangle",
5583                  "circle",
5584                  "fill circle",
5585                  "ellipse",
5586                  "fill ellipse",
5587                  "polygon",
5588                  "fill polygon",
5589                  (char *) NULL,
5590                };
5591
5592              /*
5593                Select a command from the pop-up menu.
5594              */
5595              element=(ElementType) (XMenuWidget(display,windows,
5596                DrawMenu[id],Elements,command)+1);
5597              break;
5598            }
5599            case DrawColorCommand:
5600            {
5601              const char
5602                *ColorMenu[MaxNumberPens+1];
5603
5604              int
5605                pen_number;
5606
5607              MagickBooleanType
5608                transparent;
5609
5610              XColor
5611                color;
5612
5613              /*
5614                Initialize menu selections.
5615              */
5616              for (i=0; i < (int) (MaxNumberPens-2); i++)
5617                ColorMenu[i]=resource_info->pen_colors[i];
5618              ColorMenu[MaxNumberPens-2]="transparent";
5619              ColorMenu[MaxNumberPens-1]="Browser...";
5620              ColorMenu[MaxNumberPens]=(char *) NULL;
5621              /*
5622                Select a pen color from the pop-up menu.
5623              */
5624              pen_number=XMenuWidget(display,windows,DrawMenu[id],
5625                (const char **) ColorMenu,command);
5626              if (pen_number < 0)
5627                break;
5628              transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5629                MagickFalse;
5630              if (transparent != MagickFalse)
5631                {
5632                  draw_info.stencil=TransparentStencil;
5633                  break;
5634                }
5635              if (pen_number == (MaxNumberPens-1))
5636                {
5637                  static char
5638                    color_name[MaxTextExtent] = "gray";
5639
5640                  /*
5641                    Select a pen color from a dialog.
5642                  */
5643                  resource_info->pen_colors[pen_number]=color_name;
5644                  XColorBrowserWidget(display,windows,"Select",color_name);
5645                  if (*color_name == '\0')
5646                    break;
5647                }
5648              /*
5649                Set pen color.
5650              */
5651              (void) XParseColor(display,windows->map_info->colormap,
5652                resource_info->pen_colors[pen_number],&color);
5653              XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5654                (unsigned int) MaxColors,&color);
5655              windows->pixel_info->pen_colors[pen_number]=color;
5656              pen_id=(unsigned int) pen_number;
5657              draw_info.stencil=OpaqueStencil;
5658              break;
5659            }
5660            case DrawStippleCommand:
5661            {
5662              Image
5663                *stipple_image;
5664
5665              ImageInfo
5666                *image_info;
5667
5668              int
5669                status;
5670
5671              static char
5672                filename[MaxTextExtent] = "\0";
5673
5674              static const char
5675                *StipplesMenu[] =
5676                {
5677                  "Brick",
5678                  "Diagonal",
5679                  "Scales",
5680                  "Vertical",
5681                  "Wavy",
5682                  "Translucent",
5683                  "Opaque",
5684                  (char *) NULL,
5685                  (char *) NULL,
5686                };
5687
5688              /*
5689                Select a command from the pop-up menu.
5690              */
5691              StipplesMenu[7]="Open...";
5692              entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5693                command);
5694              if (entry < 0)
5695                break;
5696              if (stipple != (Pixmap) NULL)
5697                (void) XFreePixmap(display,stipple);
5698              stipple=(Pixmap) NULL;
5699              if (entry != 7)
5700                {
5701                  switch (entry)
5702                  {
5703                    case 0:
5704                    {
5705                      stipple=XCreateBitmapFromData(display,root_window,
5706                        (char *) BricksBitmap,BricksWidth,BricksHeight);
5707                      break;
5708                    }
5709                    case 1:
5710                    {
5711                      stipple=XCreateBitmapFromData(display,root_window,
5712                        (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5713                      break;
5714                    }
5715                    case 2:
5716                    {
5717                      stipple=XCreateBitmapFromData(display,root_window,
5718                        (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5719                      break;
5720                    }
5721                    case 3:
5722                    {
5723                      stipple=XCreateBitmapFromData(display,root_window,
5724                        (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5725                      break;
5726                    }
5727                    case 4:
5728                    {
5729                      stipple=XCreateBitmapFromData(display,root_window,
5730                        (char *) WavyBitmap,WavyWidth,WavyHeight);
5731                      break;
5732                    }
5733                    case 5:
5734                    {
5735                      stipple=XCreateBitmapFromData(display,root_window,
5736                        (char *) HighlightBitmap,HighlightWidth,
5737                        HighlightHeight);
5738                      break;
5739                    }
5740                    case 6:
5741                    default:
5742                    {
5743                      stipple=XCreateBitmapFromData(display,root_window,
5744                        (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5745                      break;
5746                    }
5747                  }
5748                  break;
5749                }
5750              XFileBrowserWidget(display,windows,"Stipple",filename);
5751              if (*filename == '\0')
5752                break;
5753              /*
5754                Read image.
5755              */
5756              XSetCursorState(display,windows,MagickTrue);
5757              XCheckRefreshWindows(display,windows);
5758              image_info=AcquireImageInfo();
5759              (void) CopyMagickString(image_info->filename,filename,
5760                MaxTextExtent);
5761              stipple_image=ReadImage(image_info,exception);
5762              CatchException(exception);
5763              XSetCursorState(display,windows,MagickFalse);
5764              if (stipple_image == (Image *) NULL)
5765                break;
5766              (void) AcquireUniqueFileResource(filename);
5767              (void) FormatLocaleString(stipple_image->filename,MaxTextExtent,
5768                "xbm:%s",filename);
5769              (void) WriteImage(image_info,stipple_image,exception);
5770              stipple_image=DestroyImage(stipple_image);
5771              image_info=DestroyImageInfo(image_info);
5772              status=XReadBitmapFile(display,root_window,filename,&width,
5773                &height,&stipple,&x,&y);
5774              (void) RelinquishUniqueFileResource(filename);
5775              if ((status != BitmapSuccess) != 0)
5776                XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5777                  filename);
5778              break;
5779            }
5780            case DrawWidthCommand:
5781            {
5782              static char
5783                width[MaxTextExtent] = "0";
5784
5785              static const char
5786                *WidthsMenu[] =
5787                {
5788                  "1",
5789                  "2",
5790                  "4",
5791                  "8",
5792                  "16",
5793                  "Dialog...",
5794                  (char *) NULL,
5795                };
5796
5797              /*
5798                Select a command from the pop-up menu.
5799              */
5800              entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5801                command);
5802              if (entry < 0)
5803                break;
5804              if (entry != 5)
5805                {
5806                  line_width=(unsigned int) StringToUnsignedLong(
5807                    WidthsMenu[entry]);
5808                  break;
5809                }
5810              (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5811                width);
5812              if (*width == '\0')
5813                break;
5814              line_width=(unsigned int) StringToUnsignedLong(width);
5815              break;
5816            }
5817            case DrawUndoCommand:
5818            {
5819              (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5820                image,exception);
5821              break;
5822            }
5823            case DrawHelpCommand:
5824            {
5825              XTextViewWidget(display,resource_info,windows,MagickFalse,
5826                "Help Viewer - Image Rotation",ImageDrawHelp);
5827              (void) XCheckDefineCursor(display,windows->image.id,cursor);
5828              break;
5829            }
5830            case DrawDismissCommand:
5831            {
5832              /*
5833                Prematurely exit.
5834              */
5835              state|=EscapeState;
5836              state|=ExitState;
5837              break;
5838            }
5839            default:
5840              break;
5841          }
5842          (void) XCheckDefineCursor(display,windows->image.id,cursor);
5843          continue;
5844        }
5845      switch (event.type)
5846      {
5847        case ButtonPress:
5848        {
5849          if (event.xbutton.button != Button1)
5850            break;
5851          if (event.xbutton.window != windows->image.id)
5852            break;
5853          /*
5854            exit loop.
5855          */
5856          x=event.xbutton.x;
5857          y=event.xbutton.y;
5858          state|=ExitState;
5859          break;
5860        }
5861        case ButtonRelease:
5862          break;
5863        case Expose:
5864          break;
5865        case KeyPress:
5866        {
5867          KeySym
5868            key_symbol;
5869
5870          if (event.xkey.window != windows->image.id)
5871            break;
5872          /*
5873            Respond to a user key press.
5874          */
5875          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5876            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5877          switch ((int) key_symbol)
5878          {
5879            case XK_Escape:
5880            case XK_F20:
5881            {
5882              /*
5883                Prematurely exit.
5884              */
5885              state|=EscapeState;
5886              state|=ExitState;
5887              break;
5888            }
5889            case XK_F1:
5890            case XK_Help:
5891            {
5892              XTextViewWidget(display,resource_info,windows,MagickFalse,
5893                "Help Viewer - Image Rotation",ImageDrawHelp);
5894              break;
5895            }
5896            default:
5897            {
5898              (void) XBell(display,0);
5899              break;
5900            }
5901          }
5902          break;
5903        }
5904        case MotionNotify:
5905        {
5906          /*
5907            Map and unmap Info widget as text cursor crosses its boundaries.
5908          */
5909          x=event.xmotion.x;
5910          y=event.xmotion.y;
5911          if (windows->info.mapped != MagickFalse)
5912            {
5913              if ((x < (int) (windows->info.x+windows->info.width)) &&
5914                  (y < (int) (windows->info.y+windows->info.height)))
5915                (void) XWithdrawWindow(display,windows->info.id,
5916                  windows->info.screen);
5917            }
5918          else
5919            if ((x > (int) (windows->info.x+windows->info.width)) ||
5920                (y > (int) (windows->info.y+windows->info.height)))
5921              (void) XMapWindow(display,windows->info.id);
5922          break;
5923        }
5924      }
5925    } while ((state & ExitState) == 0);
5926    (void) XSelectInput(display,windows->image.id,
5927      windows->image.attributes.event_mask);
5928    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5929    if ((state & EscapeState) != 0)
5930      break;
5931    /*
5932      Draw element as pointer moves until the button is released.
5933    */
5934    distance=0;
5935    degrees=0.0;
5936    line_info.x1=x;
5937    line_info.y1=y;
5938    line_info.x2=x;
5939    line_info.y2=y;
5940    rectangle_info.x=(ssize_t) x;
5941    rectangle_info.y=(ssize_t) y;
5942    rectangle_info.width=0;
5943    rectangle_info.height=0;
5944    number_coordinates=1;
5945    coordinate_info->x=x;
5946    coordinate_info->y=y;
5947    (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5948    state=DefaultState;
5949    do
5950    {
5951      switch (element)
5952      {
5953        case PointElement:
5954        default:
5955        {
5956          if (number_coordinates > 1)
5957            {
5958              (void) XDrawLines(display,windows->image.id,
5959                windows->image.highlight_context,coordinate_info,
5960                number_coordinates,CoordModeOrigin);
5961              (void) FormatLocaleString(text,MaxTextExtent," %+d%+d",
5962                coordinate_info[number_coordinates-1].x,
5963                coordinate_info[number_coordinates-1].y);
5964              XInfoWidget(display,windows,text);
5965            }
5966          break;
5967        }
5968        case LineElement:
5969        {
5970          if (distance > 9)
5971            {
5972              /*
5973                Display angle of the line.
5974              */
5975              degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5976                line_info.y1),(double) (line_info.x2-line_info.x1)));
5977              (void) FormatLocaleString(text,MaxTextExtent," %g",
5978                (double) degrees);
5979              XInfoWidget(display,windows,text);
5980              XHighlightLine(display,windows->image.id,
5981                windows->image.highlight_context,&line_info);
5982            }
5983          else
5984            if (windows->info.mapped != MagickFalse)
5985              (void) XWithdrawWindow(display,windows->info.id,
5986                windows->info.screen);
5987          break;
5988        }
5989        case RectangleElement:
5990        case FillRectangleElement:
5991        {
5992          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5993            {
5994              /*
5995                Display info and draw drawing rectangle.
5996              */
5997              (void) FormatLocaleString(text,MaxTextExtent,
5998                " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5999                (double) rectangle_info.height,(double) rectangle_info.x,
6000                (double) rectangle_info.y);
6001              XInfoWidget(display,windows,text);
6002              XHighlightRectangle(display,windows->image.id,
6003                windows->image.highlight_context,&rectangle_info);
6004            }
6005          else
6006            if (windows->info.mapped != MagickFalse)
6007              (void) XWithdrawWindow(display,windows->info.id,
6008                windows->info.screen);
6009          break;
6010        }
6011        case CircleElement:
6012        case FillCircleElement:
6013        case EllipseElement:
6014        case FillEllipseElement:
6015        {
6016          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6017            {
6018              /*
6019                Display info and draw drawing rectangle.
6020              */
6021              (void) FormatLocaleString(text,MaxTextExtent,
6022                " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
6023                (double) rectangle_info.height,(double) rectangle_info.x,
6024                (double) rectangle_info.y);
6025              XInfoWidget(display,windows,text);
6026              XHighlightEllipse(display,windows->image.id,
6027                windows->image.highlight_context,&rectangle_info);
6028            }
6029          else
6030            if (windows->info.mapped != MagickFalse)
6031              (void) XWithdrawWindow(display,windows->info.id,
6032                windows->info.screen);
6033          break;
6034        }
6035        case PolygonElement:
6036        case FillPolygonElement:
6037        {
6038          if (number_coordinates > 1)
6039            (void) XDrawLines(display,windows->image.id,
6040              windows->image.highlight_context,coordinate_info,
6041              number_coordinates,CoordModeOrigin);
6042          if (distance > 9)
6043            {
6044              /*
6045                Display angle of the line.
6046              */
6047              degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
6048                line_info.y1),(double) (line_info.x2-line_info.x1)));
6049              (void) FormatLocaleString(text,MaxTextExtent," %g",
6050                (double) degrees);
6051              XInfoWidget(display,windows,text);
6052              XHighlightLine(display,windows->image.id,
6053                windows->image.highlight_context,&line_info);
6054            }
6055          else
6056            if (windows->info.mapped != MagickFalse)
6057              (void) XWithdrawWindow(display,windows->info.id,
6058                windows->info.screen);
6059          break;
6060        }
6061      }
6062      /*
6063        Wait for next event.
6064      */
6065      XScreenEvent(display,windows,&event,exception);
6066      switch (element)
6067      {
6068        case PointElement:
6069        default:
6070        {
6071          if (number_coordinates > 1)
6072            (void) XDrawLines(display,windows->image.id,
6073              windows->image.highlight_context,coordinate_info,
6074              number_coordinates,CoordModeOrigin);
6075          break;
6076        }
6077        case LineElement:
6078        {
6079          if (distance > 9)
6080            XHighlightLine(display,windows->image.id,
6081              windows->image.highlight_context,&line_info);
6082          break;
6083        }
6084        case RectangleElement:
6085        case FillRectangleElement:
6086        {
6087          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6088            XHighlightRectangle(display,windows->image.id,
6089              windows->image.highlight_context,&rectangle_info);
6090          break;
6091        }
6092        case CircleElement:
6093        case FillCircleElement:
6094        case EllipseElement:
6095        case FillEllipseElement:
6096        {
6097          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6098            XHighlightEllipse(display,windows->image.id,
6099              windows->image.highlight_context,&rectangle_info);
6100          break;
6101        }
6102        case PolygonElement:
6103        case FillPolygonElement:
6104        {
6105          if (number_coordinates > 1)
6106            (void) XDrawLines(display,windows->image.id,
6107              windows->image.highlight_context,coordinate_info,
6108              number_coordinates,CoordModeOrigin);
6109          if (distance > 9)
6110            XHighlightLine(display,windows->image.id,
6111              windows->image.highlight_context,&line_info);
6112          break;
6113        }
6114      }
6115      switch (event.type)
6116      {
6117        case ButtonPress:
6118          break;
6119        case ButtonRelease:
6120        {
6121          /*
6122            User has committed to element.
6123          */
6124          line_info.x2=event.xbutton.x;
6125          line_info.y2=event.xbutton.y;
6126          rectangle_info.x=(ssize_t) event.xbutton.x;
6127          rectangle_info.y=(ssize_t) event.xbutton.y;
6128          coordinate_info[number_coordinates].x=event.xbutton.x;
6129          coordinate_info[number_coordinates].y=event.xbutton.y;
6130          if (((element != PolygonElement) &&
6131               (element != FillPolygonElement)) || (distance <= 9))
6132            {
6133              state|=ExitState;
6134              break;
6135            }
6136          number_coordinates++;
6137          if (number_coordinates < (int) max_coordinates)
6138            {
6139              line_info.x1=event.xbutton.x;
6140              line_info.y1=event.xbutton.y;
6141              break;
6142            }
6143          max_coordinates<<=1;
6144          coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6145            max_coordinates,sizeof(*coordinate_info));
6146          if (coordinate_info == (XPoint *) NULL)
6147            (void) ThrowMagickException(exception,GetMagickModule(),
6148              ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6149          break;
6150        }
6151        case Expose:
6152          break;
6153        case MotionNotify:
6154        {
6155          if (event.xmotion.window != windows->image.id)
6156            break;
6157          if (element != PointElement)
6158            {
6159              line_info.x2=event.xmotion.x;
6160              line_info.y2=event.xmotion.y;
6161              rectangle_info.x=(ssize_t) event.xmotion.x;
6162              rectangle_info.y=(ssize_t) event.xmotion.y;
6163              break;
6164            }
6165          coordinate_info[number_coordinates].x=event.xbutton.x;
6166          coordinate_info[number_coordinates].y=event.xbutton.y;
6167          number_coordinates++;
6168          if (number_coordinates < (int) max_coordinates)
6169            break;
6170          max_coordinates<<=1;
6171          coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6172            max_coordinates,sizeof(*coordinate_info));
6173          if (coordinate_info == (XPoint *) NULL)
6174            (void) ThrowMagickException(exception,GetMagickModule(),
6175              ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6176          break;
6177        }
6178        default:
6179          break;
6180      }
6181      /*
6182        Check boundary conditions.
6183      */
6184      if (line_info.x2 < 0)
6185        line_info.x2=0;
6186      else
6187        if (line_info.x2 > (int) windows->image.width)
6188          line_info.x2=(short) windows->image.width;
6189      if (line_info.y2 < 0)
6190        line_info.y2=0;
6191      else
6192        if (line_info.y2 > (int) windows->image.height)
6193          line_info.y2=(short) windows->image.height;
6194      distance=(unsigned int)
6195        (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6196         ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6197      if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6198          ((state & ExitState) != 0))
6199        {
6200          if (rectangle_info.x < 0)
6201            rectangle_info.x=0;
6202          else
6203            if (rectangle_info.x > (ssize_t) windows->image.width)
6204              rectangle_info.x=(ssize_t) windows->image.width;
6205          if ((int) rectangle_info.x < x)
6206            rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6207          else
6208            {
6209              rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6210              rectangle_info.x=(ssize_t) x;
6211            }
6212          if (rectangle_info.y < 0)
6213            rectangle_info.y=0;
6214          else
6215            if (rectangle_info.y > (ssize_t) windows->image.height)
6216              rectangle_info.y=(ssize_t) windows->image.height;
6217          if ((int) rectangle_info.y < y)
6218            rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6219          else
6220            {
6221              rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6222              rectangle_info.y=(ssize_t) y;
6223            }
6224        }
6225    } while ((state & ExitState) == 0);
6226    (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6227    if ((element == PointElement) || (element == PolygonElement) ||
6228        (element == FillPolygonElement))
6229      {
6230        /*
6231          Determine polygon bounding box.
6232        */
6233        rectangle_info.x=(ssize_t) coordinate_info->x;
6234        rectangle_info.y=(ssize_t) coordinate_info->y;
6235        x=coordinate_info->x;
6236        y=coordinate_info->y;
6237        for (i=1; i < number_coordinates; i++)
6238        {
6239          if (coordinate_info[i].x > x)
6240            x=coordinate_info[i].x;
6241          if (coordinate_info[i].y > y)
6242            y=coordinate_info[i].y;
6243          if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6244            rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6245          if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6246            rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6247        }
6248        rectangle_info.width=(size_t) (x-rectangle_info.x);
6249        rectangle_info.height=(size_t) (y-rectangle_info.y);
6250        for (i=0; i < number_coordinates; i++)
6251        {
6252          coordinate_info[i].x-=rectangle_info.x;
6253          coordinate_info[i].y-=rectangle_info.y;
6254        }
6255      }
6256    else
6257      if (distance <= 9)
6258        continue;
6259      else
6260        if ((element == RectangleElement) ||
6261            (element == CircleElement) || (element == EllipseElement))
6262          {
6263            rectangle_info.width--;
6264            rectangle_info.height--;
6265          }
6266    /*
6267      Drawing is relative to image configuration.
6268    */
6269    draw_info.x=(int) rectangle_info.x;
6270    draw_info.y=(int) rectangle_info.y;
6271    (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6272      image,exception);
6273    width=(unsigned int) (*image)->columns;
6274    height=(unsigned int) (*image)->rows;
6275    x=0;
6276    y=0;
6277    if (windows->image.crop_geometry != (char *) NULL)
6278      (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6279    draw_info.x+=windows->image.x-(line_width/2);
6280    if (draw_info.x < 0)
6281      draw_info.x=0;
6282    draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6283    draw_info.y+=windows->image.y-(line_width/2);
6284    if (draw_info.y < 0)
6285      draw_info.y=0;
6286    draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6287    draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6288    if (draw_info.width > (unsigned int) (*image)->columns)
6289      draw_info.width=(unsigned int) (*image)->columns;
6290    draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6291    if (draw_info.height > (unsigned int) (*image)->rows)
6292      draw_info.height=(unsigned int) (*image)->rows;
6293    (void) FormatLocaleString(draw_info.geometry,MaxTextExtent,"%ux%u%+d%+d",
6294      width*draw_info.width/windows->image.ximage->width,
6295      height*draw_info.height/windows->image.ximage->height,
6296      draw_info.x+x,draw_info.y+y);
6297    /*
6298      Initialize drawing attributes.
6299    */
6300    draw_info.degrees=0.0;
6301    draw_info.element=element;
6302    draw_info.stipple=stipple;
6303    draw_info.line_width=line_width;
6304    draw_info.line_info=line_info;
6305    if (line_info.x1 > (int) (line_width/2))
6306      draw_info.line_info.x1=(short) line_width/2;
6307    if (line_info.y1 > (int) (line_width/2))
6308      draw_info.line_info.y1=(short) line_width/2;
6309    draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6310    draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6311    if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6312      {
6313        draw_info.line_info.x2=(-draw_info.line_info.x2);
6314        draw_info.line_info.y2=(-draw_info.line_info.y2);
6315      }
6316    if (draw_info.line_info.x2 < 0)
6317      {
6318        draw_info.line_info.x2=(-draw_info.line_info.x2);
6319        Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6320      }
6321    if (draw_info.line_info.y2 < 0)
6322      {
6323        draw_info.line_info.y2=(-draw_info.line_info.y2);
6324        Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6325      }
6326    draw_info.rectangle_info=rectangle_info;
6327    if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6328      draw_info.rectangle_info.x=(ssize_t) line_width/2;
6329    if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6330      draw_info.rectangle_info.y=(ssize_t) line_width/2;
6331    draw_info.number_coordinates=(unsigned int) number_coordinates;
6332    draw_info.coordinate_info=coordinate_info;
6333    windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6334    /*
6335      Draw element on image.
6336    */
6337    XSetCursorState(display,windows,MagickTrue);
6338    XCheckRefreshWindows(display,windows);
6339    status=XDrawImage(display,windows->pixel_info,&draw_info,*image,exception);
6340    XSetCursorState(display,windows,MagickFalse);
6341    /*
6342      Update image colormap and return to image drawing.
6343    */
6344    XConfigureImageColormap(display,resource_info,windows,*image,exception);
6345    (void) XConfigureImage(display,resource_info,windows,*image,exception);
6346  }
6347  XSetCursorState(display,windows,MagickFalse);
6348  coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6349  return(status != 0 ? MagickTrue : MagickFalse);
6350}
6351
6352/*
6353%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6354%                                                                             %
6355%                                                                             %
6356%                                                                             %
6357+   X D r a w P a n R e c t a n g l e                                         %
6358%                                                                             %
6359%                                                                             %
6360%                                                                             %
6361%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6362%
6363%  XDrawPanRectangle() draws a rectangle in the pan window.  The pan window
6364%  displays a zoom image and the rectangle shows which portion of the image is
6365%  displayed in the Image window.
6366%
6367%  The format of the XDrawPanRectangle method is:
6368%
6369%      XDrawPanRectangle(Display *display,XWindows *windows)
6370%
6371%  A description of each parameter follows:
6372%
6373%    o display: Specifies a connection to an X server;  returned from
6374%      XOpenDisplay.
6375%
6376%    o windows: Specifies a pointer to a XWindows structure.
6377%
6378*/
6379static void XDrawPanRectangle(Display *display,XWindows *windows)
6380{
6381  MagickRealType
6382    scale_factor;
6383
6384  RectangleInfo
6385    highlight_info;
6386
6387  /*
6388    Determine dimensions of the panning rectangle.
6389  */
6390  scale_factor=(MagickRealType) windows->pan.width/windows->image.ximage->width;
6391  highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6392  highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6393  scale_factor=(MagickRealType)
6394    windows->pan.height/windows->image.ximage->height;
6395  highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6396  highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6397  /*
6398    Display the panning rectangle.
6399  */
6400  (void) XClearWindow(display,windows->pan.id);
6401  XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6402    &highlight_info);
6403}
6404
6405/*
6406%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6407%                                                                             %
6408%                                                                             %
6409%                                                                             %
6410+   X I m a g e C a c h e                                                     %
6411%                                                                             %
6412%                                                                             %
6413%                                                                             %
6414%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6415%
6416%  XImageCache() handles the creation, manipulation, and destruction of the
6417%  image cache (undo and redo buffers).
6418%
6419%  The format of the XImageCache method is:
6420%
6421%      void XImageCache(Display *display,XResourceInfo *resource_info,
6422%        XWindows *windows,const CommandType command,Image **image,
6423%        ExceptionInfo *exception)
6424%
6425%  A description of each parameter follows:
6426%
6427%    o display: Specifies a connection to an X server; returned from
6428%      XOpenDisplay.
6429%
6430%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6431%
6432%    o windows: Specifies a pointer to a XWindows structure.
6433%
6434%    o command: Specifies a command to perform.
6435%
6436%    o image: the image;  XImageCache may transform the image and return a new
6437%      image pointer.
6438%
6439%    o exception: return any errors or warnings in this structure.
6440%
6441*/
6442static void XImageCache(Display *display,XResourceInfo *resource_info,
6443  XWindows *windows,const CommandType command,Image **image,
6444  ExceptionInfo *exception)
6445{
6446  Image
6447    *cache_image;
6448
6449  static Image
6450    *redo_image = (Image *) NULL,
6451    *undo_image = (Image *) NULL;
6452
6453  switch (command)
6454  {
6455    case FreeBuffersCommand:
6456    {
6457      /*
6458        Free memory from the undo and redo cache.
6459      */
6460      while (undo_image != (Image *) NULL)
6461      {
6462        cache_image=undo_image;
6463        undo_image=GetPreviousImageInList(undo_image);
6464        cache_image->list=DestroyImage(cache_image->list);
6465        cache_image=DestroyImage(cache_image);
6466      }
6467      undo_image=NewImageList();
6468      if (redo_image != (Image *) NULL)
6469        redo_image=DestroyImage(redo_image);
6470      redo_image=NewImageList();
6471      return;
6472    }
6473    case UndoCommand:
6474    {
6475      char
6476        image_geometry[MaxTextExtent];
6477
6478      /*
6479        Undo the last image transformation.
6480      */
6481      if (undo_image == (Image *) NULL)
6482        {
6483          (void) XBell(display,0);
6484          return;
6485        }
6486      cache_image=undo_image;
6487      undo_image=GetPreviousImageInList(undo_image);
6488      windows->image.window_changes.width=(int) cache_image->columns;
6489      windows->image.window_changes.height=(int) cache_image->rows;
6490      (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
6491        windows->image.ximage->width,windows->image.ximage->height);
6492      (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
6493        exception);
6494      if (windows->image.crop_geometry != (char *) NULL)
6495        windows->image.crop_geometry=(char *) RelinquishMagickMemory(
6496          windows->image.crop_geometry);
6497      windows->image.crop_geometry=cache_image->geometry;
6498      if (redo_image != (Image *) NULL)
6499        redo_image=DestroyImage(redo_image);
6500      redo_image=(*image);
6501      *image=cache_image->list;
6502      cache_image=DestroyImage(cache_image);
6503      if (windows->image.orphan != MagickFalse)
6504        return;
6505      XConfigureImageColormap(display,resource_info,windows,*image,exception);
6506      (void) XConfigureImage(display,resource_info,windows,*image,exception);
6507      return;
6508    }
6509    case CutCommand:
6510    case PasteCommand:
6511    case ApplyCommand:
6512    case HalfSizeCommand:
6513    case OriginalSizeCommand:
6514    case DoubleSizeCommand:
6515    case ResizeCommand:
6516    case TrimCommand:
6517    case CropCommand:
6518    case ChopCommand:
6519    case FlipCommand:
6520    case FlopCommand:
6521    case RotateRightCommand:
6522    case RotateLeftCommand:
6523    case RotateCommand:
6524    case ShearCommand:
6525    case RollCommand:
6526    case NegateCommand:
6527    case ContrastStretchCommand:
6528    case SigmoidalContrastCommand:
6529    case NormalizeCommand:
6530    case EqualizeCommand:
6531    case HueCommand:
6532    case SaturationCommand:
6533    case BrightnessCommand:
6534    case GammaCommand:
6535    case SpiffCommand:
6536    case DullCommand:
6537    case GrayscaleCommand:
6538    case MapCommand:
6539    case QuantizeCommand:
6540    case DespeckleCommand:
6541    case EmbossCommand:
6542    case ReduceNoiseCommand:
6543    case AddNoiseCommand:
6544    case SharpenCommand:
6545    case BlurCommand:
6546    case ThresholdCommand:
6547    case EdgeDetectCommand:
6548    case SpreadCommand:
6549    case ShadeCommand:
6550    case RaiseCommand:
6551    case SegmentCommand:
6552    case SolarizeCommand:
6553    case SepiaToneCommand:
6554    case SwirlCommand:
6555    case ImplodeCommand:
6556    case VignetteCommand:
6557    case WaveCommand:
6558    case OilPaintCommand:
6559    case CharcoalDrawCommand:
6560    case AnnotateCommand:
6561    case AddBorderCommand:
6562    case AddFrameCommand:
6563    case CompositeCommand:
6564    case CommentCommand:
6565    case LaunchCommand:
6566    case RegionofInterestCommand:
6567    case SaveToUndoBufferCommand:
6568    case RedoCommand:
6569    {
6570      Image
6571        *previous_image;
6572
6573      ssize_t
6574        bytes;
6575
6576      bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelInfo));
6577      if (undo_image != (Image *) NULL)
6578        {
6579          /*
6580            Ensure the undo cache has enough memory available.
6581          */
6582          previous_image=undo_image;
6583          while (previous_image != (Image *) NULL)
6584          {
6585            bytes+=previous_image->list->columns*previous_image->list->rows*
6586              sizeof(PixelInfo);
6587            if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6588              {
6589                previous_image=GetPreviousImageInList(previous_image);
6590                continue;
6591              }
6592            bytes-=previous_image->list->columns*previous_image->list->rows*
6593              sizeof(PixelInfo);
6594            if (previous_image == undo_image)
6595              undo_image=NewImageList();
6596            else
6597              previous_image->next->previous=NewImageList();
6598            break;
6599          }
6600          while (previous_image != (Image *) NULL)
6601          {
6602            /*
6603              Delete any excess memory from undo cache.
6604            */
6605            cache_image=previous_image;
6606            previous_image=GetPreviousImageInList(previous_image);
6607            cache_image->list=DestroyImage(cache_image->list);
6608            cache_image=DestroyImage(cache_image);
6609          }
6610        }
6611      if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6612        break;
6613      /*
6614        Save image before transformations are applied.
6615      */
6616      cache_image=AcquireImage((ImageInfo *) NULL,exception);
6617      if (cache_image == (Image *) NULL)
6618        break;
6619      XSetCursorState(display,windows,MagickTrue);
6620      XCheckRefreshWindows(display,windows);
6621      cache_image->list=CloneImage(*image,0,0,MagickTrue,exception);
6622      XSetCursorState(display,windows,MagickFalse);
6623      if (cache_image->list == (Image *) NULL)
6624        {
6625          cache_image=DestroyImage(cache_image);
6626          break;
6627        }
6628      cache_image->columns=(size_t) windows->image.ximage->width;
6629      cache_image->rows=(size_t) windows->image.ximage->height;
6630      cache_image->geometry=windows->image.crop_geometry;
6631      if (windows->image.crop_geometry != (char *) NULL)
6632        {
6633          cache_image->geometry=AcquireString((char *) NULL);
6634          (void) CopyMagickString(cache_image->geometry,
6635            windows->image.crop_geometry,MaxTextExtent);
6636        }
6637      if (undo_image == (Image *) NULL)
6638        {
6639          undo_image=cache_image;
6640          break;
6641        }
6642      undo_image->next=cache_image;
6643      undo_image->next->previous=undo_image;
6644      undo_image=undo_image->next;
6645      break;
6646    }
6647    default:
6648      break;
6649  }
6650  if (command == RedoCommand)
6651    {
6652      /*
6653        Redo the last image transformation.
6654      */
6655      if (redo_image == (Image *) NULL)
6656        {
6657          (void) XBell(display,0);
6658          return;
6659        }
6660      windows->image.window_changes.width=(int) redo_image->columns;
6661      windows->image.window_changes.height=(int) redo_image->rows;
6662      if (windows->image.crop_geometry != (char *) NULL)
6663        windows->image.crop_geometry=(char *)
6664          RelinquishMagickMemory(windows->image.crop_geometry);
6665      windows->image.crop_geometry=redo_image->geometry;
6666      *image=DestroyImage(*image);
6667      *image=redo_image;
6668      redo_image=NewImageList();
6669      if (windows->image.orphan != MagickFalse)
6670        return;
6671      XConfigureImageColormap(display,resource_info,windows,*image,exception);
6672      (void) XConfigureImage(display,resource_info,windows,*image,exception);
6673      return;
6674    }
6675  if (command != InfoCommand)
6676    return;
6677  /*
6678    Display image info.
6679  */
6680  XSetCursorState(display,windows,MagickTrue);
6681  XCheckRefreshWindows(display,windows);
6682  XDisplayImageInfo(display,resource_info,windows,undo_image,*image,exception);
6683  XSetCursorState(display,windows,MagickFalse);
6684}
6685
6686/*
6687%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6688%                                                                             %
6689%                                                                             %
6690%                                                                             %
6691+   X I m a g e W i n d o w C o m m a n d                                     %
6692%                                                                             %
6693%                                                                             %
6694%                                                                             %
6695%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6696%
6697%  XImageWindowCommand() makes a transform to the image or Image window as
6698%  specified by a user menu button or keyboard command.
6699%
6700%  The format of the XImageWindowCommand method is:
6701%
6702%      CommandType XImageWindowCommand(Display *display,
6703%        XResourceInfo *resource_info,XWindows *windows,
6704%        const MagickStatusType state,KeySym key_symbol,Image **image,
6705%        ExceptionInfo *exception)
6706%
6707%  A description of each parameter follows:
6708%
6709%    o nexus:  Method XImageWindowCommand returns an image when the
6710%      user chooses 'Open Image' from the command menu.  Otherwise a null
6711%      image is returned.
6712%
6713%    o display: Specifies a connection to an X server; returned from
6714%      XOpenDisplay.
6715%
6716%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6717%
6718%    o windows: Specifies a pointer to a XWindows structure.
6719%
6720%    o state: key mask.
6721%
6722%    o key_symbol: Specifies a command to perform.
6723%
6724%    o image: the image;  XImageWIndowCommand may transform the image and
6725%      return a new image pointer.
6726%
6727%    o exception: return any errors or warnings in this structure.
6728%
6729*/
6730static CommandType XImageWindowCommand(Display *display,
6731  XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6732  KeySym key_symbol,Image **image,ExceptionInfo *exception)
6733{
6734  static char
6735    delta[MaxTextExtent] = "";
6736
6737  static const char
6738    Digits[] = "01234567890";
6739
6740  static KeySym
6741    last_symbol = XK_0;
6742
6743  if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6744    {
6745      if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6746        {
6747          *delta='\0';
6748          resource_info->quantum=1;
6749        }
6750      last_symbol=key_symbol;
6751      delta[strlen(delta)+1]='\0';
6752      delta[strlen(delta)]=Digits[key_symbol-XK_0];
6753      resource_info->quantum=StringToLong(delta);
6754      return(NullCommand);
6755    }
6756  last_symbol=key_symbol;
6757  if (resource_info->immutable)
6758    {
6759      /*
6760        Virtual image window has a restricted command set.
6761      */
6762      switch (key_symbol)
6763      {
6764        case XK_question:
6765          return(InfoCommand);
6766        case XK_p:
6767        case XK_Print:
6768          return(PrintCommand);
6769        case XK_space:
6770          return(NextCommand);
6771        case XK_q:
6772        case XK_Escape:
6773          return(QuitCommand);
6774        default:
6775          break;
6776      }
6777      return(NullCommand);
6778    }
6779  switch ((int) key_symbol)
6780  {
6781    case XK_o:
6782    {
6783      if ((state & ControlMask) == 0)
6784        break;
6785      return(OpenCommand);
6786    }
6787    case XK_space:
6788      return(NextCommand);
6789    case XK_BackSpace:
6790      return(FormerCommand);
6791    case XK_s:
6792    {
6793      if ((state & Mod1Mask) != 0)
6794        return(SwirlCommand);
6795      if ((state & ControlMask) == 0)
6796        return(ShearCommand);
6797      return(SaveCommand);
6798    }
6799    case XK_p:
6800    case XK_Print:
6801    {
6802      if ((state & Mod1Mask) != 0)
6803        return(OilPaintCommand);
6804      if ((state & Mod4Mask) != 0)
6805        return(ColorCommand);
6806      if ((state & ControlMask) == 0)
6807        return(NullCommand);
6808      return(PrintCommand);
6809    }
6810    case XK_d:
6811    {
6812      if ((state & Mod4Mask) != 0)
6813        return(DrawCommand);
6814      if ((state & ControlMask) == 0)
6815        return(NullCommand);
6816      return(DeleteCommand);
6817    }
6818    case XK_Select:
6819    {
6820      if ((state & ControlMask) == 0)
6821        return(NullCommand);
6822      return(SelectCommand);
6823    }
6824    case XK_n:
6825    {
6826      if ((state & ControlMask) == 0)
6827        return(NullCommand);
6828      return(NewCommand);
6829    }
6830    case XK_q:
6831    case XK_Escape:
6832      return(QuitCommand);
6833    case XK_z:
6834    case XK_Undo:
6835    {
6836      if ((state & ControlMask) == 0)
6837        return(NullCommand);
6838      return(UndoCommand);
6839    }
6840    case XK_r:
6841    case XK_Redo:
6842    {
6843      if ((state & ControlMask) == 0)
6844        return(RollCommand);
6845      return(RedoCommand);
6846    }
6847    case XK_x:
6848    {
6849      if ((state & ControlMask) == 0)
6850        return(NullCommand);
6851      return(CutCommand);
6852    }
6853    case XK_c:
6854    {
6855      if ((state & Mod1Mask) != 0)
6856        return(CharcoalDrawCommand);
6857      if ((state & ControlMask) == 0)
6858        return(CropCommand);
6859      return(CopyCommand);
6860    }
6861    case XK_v:
6862    case XK_Insert:
6863    {
6864      if ((state & Mod4Mask) != 0)
6865        return(CompositeCommand);
6866      if ((state & ControlMask) == 0)
6867        return(FlipCommand);
6868      return(PasteCommand);
6869    }
6870    case XK_less:
6871      return(HalfSizeCommand);
6872    case XK_minus:
6873      return(OriginalSizeCommand);
6874    case XK_greater:
6875      return(DoubleSizeCommand);
6876    case XK_percent:
6877      return(ResizeCommand);
6878    case XK_at:
6879      return(RefreshCommand);
6880    case XK_bracketleft:
6881      return(ChopCommand);
6882    case XK_h:
6883      return(FlopCommand);
6884    case XK_slash:
6885      return(RotateRightCommand);
6886    case XK_backslash:
6887      return(RotateLeftCommand);
6888    case XK_asterisk:
6889      return(RotateCommand);
6890    case XK_t:
6891      return(TrimCommand);
6892    case XK_H:
6893      return(HueCommand);
6894    case XK_S:
6895      return(SaturationCommand);
6896    case XK_L:
6897      return(BrightnessCommand);
6898    case XK_G:
6899      return(GammaCommand);
6900    case XK_C:
6901      return(SpiffCommand);
6902    case XK_Z:
6903      return(DullCommand);
6904    case XK_N:
6905      return(NormalizeCommand);
6906    case XK_equal:
6907      return(EqualizeCommand);
6908    case XK_asciitilde:
6909      return(NegateCommand);
6910    case XK_period:
6911      return(GrayscaleCommand);
6912    case XK_numbersign:
6913      return(QuantizeCommand);
6914    case XK_F2:
6915      return(DespeckleCommand);
6916    case XK_F3:
6917      return(EmbossCommand);
6918    case XK_F4:
6919      return(ReduceNoiseCommand);
6920    case XK_F5:
6921      return(AddNoiseCommand);
6922    case XK_F6:
6923      return(SharpenCommand);
6924    case XK_F7:
6925      return(BlurCommand);
6926    case XK_F8:
6927      return(ThresholdCommand);
6928    case XK_F9:
6929      return(EdgeDetectCommand);
6930    case XK_F10:
6931      return(SpreadCommand);
6932    case XK_F11:
6933      return(ShadeCommand);
6934    case XK_F12:
6935      return(RaiseCommand);
6936    case XK_F13:
6937      return(SegmentCommand);
6938    case XK_i:
6939    {
6940      if ((state & Mod1Mask) == 0)
6941        return(NullCommand);
6942      return(ImplodeCommand);
6943    }
6944    case XK_w:
6945    {
6946      if ((state & Mod1Mask) == 0)
6947        return(NullCommand);
6948      return(WaveCommand);
6949    }
6950    case XK_m:
6951    {
6952      if ((state & Mod4Mask) == 0)
6953        return(NullCommand);
6954      return(MatteCommand);
6955    }
6956    case XK_b:
6957    {
6958      if ((state & Mod4Mask) == 0)
6959        return(NullCommand);
6960      return(AddBorderCommand);
6961    }
6962    case XK_f:
6963    {
6964      if ((state & Mod4Mask) == 0)
6965        return(NullCommand);
6966      return(AddFrameCommand);
6967    }
6968    case XK_exclam:
6969    {
6970      if ((state & Mod4Mask) == 0)
6971        return(NullCommand);
6972      return(CommentCommand);
6973    }
6974    case XK_a:
6975    {
6976      if ((state & Mod1Mask) != 0)
6977        return(ApplyCommand);
6978      if ((state & Mod4Mask) != 0)
6979        return(AnnotateCommand);
6980      if ((state & ControlMask) == 0)
6981        return(NullCommand);
6982      return(RegionofInterestCommand);
6983    }
6984    case XK_question:
6985      return(InfoCommand);
6986    case XK_plus:
6987      return(ZoomCommand);
6988    case XK_P:
6989    {
6990      if ((state & ShiftMask) == 0)
6991        return(NullCommand);
6992      return(ShowPreviewCommand);
6993    }
6994    case XK_Execute:
6995      return(LaunchCommand);
6996    case XK_F1:
6997      return(HelpCommand);
6998    case XK_Find:
6999      return(BrowseDocumentationCommand);
7000    case XK_Menu:
7001    {
7002      (void) XMapRaised(display,windows->command.id);
7003      return(NullCommand);
7004    }
7005    case XK_Next:
7006    case XK_Prior:
7007    case XK_Home:
7008    case XK_KP_Home:
7009    {
7010      XTranslateImage(display,windows,*image,key_symbol);
7011      return(NullCommand);
7012    }
7013    case XK_Up:
7014    case XK_KP_Up:
7015    case XK_Down:
7016    case XK_KP_Down:
7017    case XK_Left:
7018    case XK_KP_Left:
7019    case XK_Right:
7020    case XK_KP_Right:
7021    {
7022      if ((state & Mod1Mask) != 0)
7023        {
7024          RectangleInfo
7025            crop_info;
7026
7027          /*
7028            Trim one pixel from edge of image.
7029          */
7030          crop_info.x=0;
7031          crop_info.y=0;
7032          crop_info.width=(size_t) windows->image.ximage->width;
7033          crop_info.height=(size_t) windows->image.ximage->height;
7034          if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
7035            {
7036              if (resource_info->quantum >= (int) crop_info.height)
7037                resource_info->quantum=(int) crop_info.height-1;
7038              crop_info.height-=resource_info->quantum;
7039            }
7040          if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
7041            {
7042              if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
7043                resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
7044              crop_info.y+=resource_info->quantum;
7045              crop_info.height-=resource_info->quantum;
7046            }
7047          if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
7048            {
7049              if (resource_info->quantum >= (int) crop_info.width)
7050                resource_info->quantum=(int) crop_info.width-1;
7051              crop_info.width-=resource_info->quantum;
7052            }
7053          if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
7054            {
7055              if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
7056                resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
7057              crop_info.x+=resource_info->quantum;
7058              crop_info.width-=resource_info->quantum;
7059            }
7060          if ((int) (windows->image.x+windows->image.width) >
7061              (int) crop_info.width)
7062            windows->image.x=(int) (crop_info.width-windows->image.width);
7063          if ((int) (windows->image.y+windows->image.height) >
7064              (int) crop_info.height)
7065            windows->image.y=(int) (crop_info.height-windows->image.height);
7066          XSetCropGeometry(display,windows,&crop_info,*image);
7067          windows->image.window_changes.width=(int) crop_info.width;
7068          windows->image.window_changes.height=(int) crop_info.height;
7069          (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
7070          (void) XConfigureImage(display,resource_info,windows,*image,
7071            exception);
7072          return(NullCommand);
7073        }
7074      XTranslateImage(display,windows,*image,key_symbol);
7075      return(NullCommand);
7076    }
7077    default:
7078      return(NullCommand);
7079  }
7080  return(NullCommand);
7081}
7082
7083/*
7084%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7085%                                                                             %
7086%                                                                             %
7087%                                                                             %
7088+   X M a g i c k C o m m a n d                                               %
7089%                                                                             %
7090%                                                                             %
7091%                                                                             %
7092%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7093%
7094%  XMagickCommand() makes a transform to the image or Image window as
7095%  specified by a user menu button or keyboard command.
7096%
7097%  The format of the XMagickCommand method is:
7098%
7099%      Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7100%        XWindows *windows,const CommandType command,Image **image,
7101%        ExceptionInfo *exception)
7102%
7103%  A description of each parameter follows:
7104%
7105%    o display: Specifies a connection to an X server; returned from
7106%      XOpenDisplay.
7107%
7108%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7109%
7110%    o windows: Specifies a pointer to a XWindows structure.
7111%
7112%    o command: Specifies a command to perform.
7113%
7114%    o image: the image;  XMagickCommand may transform the image and return a
7115%      new image pointer.
7116%
7117%    o exception: return any errors or warnings in this structure.
7118%
7119*/
7120static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7121  XWindows *windows,const CommandType command,Image **image,
7122  ExceptionInfo *exception)
7123{
7124  char
7125    filename[MaxTextExtent],
7126    geometry[MaxTextExtent],
7127    modulate_factors[MaxTextExtent];
7128
7129  GeometryInfo
7130    geometry_info;
7131
7132  Image
7133    *nexus;
7134
7135  ImageInfo
7136    *image_info;
7137
7138  int
7139    x,
7140    y;
7141
7142  MagickStatusType
7143    flags,
7144    status;
7145
7146  QuantizeInfo
7147    quantize_info;
7148
7149  RectangleInfo
7150    page_geometry;
7151
7152  register int
7153    i;
7154
7155  static char
7156    color[MaxTextExtent] = "gray";
7157
7158  unsigned int
7159    height,
7160    width;
7161
7162  /*
7163    Process user command.
7164  */
7165  XCheckRefreshWindows(display,windows);
7166  XImageCache(display,resource_info,windows,command,image,exception);
7167  nexus=NewImageList();
7168  windows->image.window_changes.width=windows->image.ximage->width;
7169  windows->image.window_changes.height=windows->image.ximage->height;
7170  image_info=CloneImageInfo(resource_info->image_info);
7171  SetGeometryInfo(&geometry_info);
7172  GetQuantizeInfo(&quantize_info);
7173  switch (command)
7174  {
7175    case OpenCommand:
7176    {
7177      /*
7178        Load image.
7179      */
7180      nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7181      break;
7182    }
7183    case NextCommand:
7184    {
7185      /*
7186        Display next image.
7187      */
7188      for (i=0; i < resource_info->quantum; i++)
7189        XClientMessage(display,windows->image.id,windows->im_protocols,
7190          windows->im_next_image,CurrentTime);
7191      break;
7192    }
7193    case FormerCommand:
7194    {
7195      /*
7196        Display former image.
7197      */
7198      for (i=0; i < resource_info->quantum; i++)
7199        XClientMessage(display,windows->image.id,windows->im_protocols,
7200          windows->im_former_image,CurrentTime);
7201      break;
7202    }
7203    case SelectCommand:
7204    {
7205      int
7206        status;
7207
7208      /*
7209        Select image.
7210      */
7211      status=chdir(resource_info->home_directory);
7212      if (status == -1)
7213        (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
7214          "UnableToOpenFile","%s",resource_info->home_directory);
7215      nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7216      break;
7217    }
7218    case SaveCommand:
7219    {
7220      /*
7221        Save image.
7222      */
7223      status=XSaveImage(display,resource_info,windows,*image,exception);
7224      if (status == MagickFalse)
7225        {
7226          char
7227            message[MaxTextExtent];
7228
7229          (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7230            exception->reason != (char *) NULL ? exception->reason : "",
7231            exception->description != (char *) NULL ? exception->description :
7232            "");
7233          XNoticeWidget(display,windows,"Unable to save file:",message);
7234          break;
7235        }
7236      break;
7237    }
7238    case PrintCommand:
7239    {
7240      /*
7241        Print image.
7242      */
7243      status=XPrintImage(display,resource_info,windows,*image,exception);
7244      if (status == MagickFalse)
7245        {
7246          char
7247            message[MaxTextExtent];
7248
7249          (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7250            exception->reason != (char *) NULL ? exception->reason : "",
7251            exception->description != (char *) NULL ? exception->description :
7252            "");
7253          XNoticeWidget(display,windows,"Unable to print file:",message);
7254          break;
7255        }
7256      break;
7257    }
7258    case DeleteCommand:
7259    {
7260      static char
7261        filename[MaxTextExtent] = "\0";
7262
7263      /*
7264        Delete image file.
7265      */
7266      XFileBrowserWidget(display,windows,"Delete",filename);
7267      if (*filename == '\0')
7268        break;
7269      status=remove_utf8(filename) != 0 ? MagickTrue : MagickFalse;
7270      if (status != MagickFalse)
7271        XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7272      break;
7273    }
7274    case NewCommand:
7275    {
7276      int
7277        status;
7278
7279      static char
7280        color[MaxTextExtent] = "gray",
7281        geometry[MaxTextExtent] = "640x480";
7282
7283      static const char
7284        *format = "gradient";
7285
7286      /*
7287        Query user for canvas geometry.
7288      */
7289      status=XDialogWidget(display,windows,"New","Enter image geometry:",
7290        geometry);
7291      if (*geometry == '\0')
7292        break;
7293      if (status == 0)
7294        format="xc";
7295      XColorBrowserWidget(display,windows,"Select",color);
7296      if (*color == '\0')
7297        break;
7298      /*
7299        Create canvas.
7300      */
7301      (void) FormatLocaleString(image_info->filename,MaxTextExtent,
7302        "%s:%s",format,color);
7303      (void) CloneString(&image_info->size,geometry);
7304      nexus=ReadImage(image_info,exception);
7305      CatchException(exception);
7306      XClientMessage(display,windows->image.id,windows->im_protocols,
7307        windows->im_next_image,CurrentTime);
7308      break;
7309    }
7310    case VisualDirectoryCommand:
7311    {
7312      /*
7313        Visual Image directory.
7314      */
7315      nexus=XVisualDirectoryImage(display,resource_info,windows,exception);
7316      break;
7317    }
7318    case QuitCommand:
7319    {
7320      /*
7321        exit program.
7322      */
7323      if (resource_info->confirm_exit == MagickFalse)
7324        XClientMessage(display,windows->image.id,windows->im_protocols,
7325          windows->im_exit,CurrentTime);
7326      else
7327        {
7328          int
7329            status;
7330
7331          /*
7332            Confirm program exit.
7333          */
7334          status=XConfirmWidget(display,windows,"Do you really want to exit",
7335            resource_info->client_name);
7336          if (status > 0)
7337            XClientMessage(display,windows->image.id,windows->im_protocols,
7338              windows->im_exit,CurrentTime);
7339        }
7340      break;
7341    }
7342    case CutCommand:
7343    {
7344      /*
7345        Cut image.
7346      */
7347      (void) XCropImage(display,resource_info,windows,*image,CutMode,exception);
7348      break;
7349    }
7350    case CopyCommand:
7351    {
7352      /*
7353        Copy image.
7354      */
7355      (void) XCropImage(display,resource_info,windows,*image,CopyMode,
7356        exception);
7357      break;
7358    }
7359    case PasteCommand:
7360    {
7361      /*
7362        Paste image.
7363      */
7364      status=XPasteImage(display,resource_info,windows,*image,exception);
7365      if (status == MagickFalse)
7366        {
7367          XNoticeWidget(display,windows,"Unable to paste X image",
7368            (*image)->filename);
7369          break;
7370        }
7371      break;
7372    }
7373    case HalfSizeCommand:
7374    {
7375      /*
7376        Half image size.
7377      */
7378      windows->image.window_changes.width=windows->image.ximage->width/2;
7379      windows->image.window_changes.height=windows->image.ximage->height/2;
7380      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7381      break;
7382    }
7383    case OriginalSizeCommand:
7384    {
7385      /*
7386        Original image size.
7387      */
7388      windows->image.window_changes.width=(int) (*image)->columns;
7389      windows->image.window_changes.height=(int) (*image)->rows;
7390      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7391      break;
7392    }
7393    case DoubleSizeCommand:
7394    {
7395      /*
7396        Double the image size.
7397      */
7398      windows->image.window_changes.width=windows->image.ximage->width << 1;
7399      windows->image.window_changes.height=windows->image.ximage->height << 1;
7400      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7401      break;
7402    }
7403    case ResizeCommand:
7404    {
7405      int
7406        status;
7407
7408      size_t
7409        height,
7410        width;
7411
7412      ssize_t
7413        x,
7414        y;
7415
7416      /*
7417        Resize image.
7418      */
7419      width=(size_t) windows->image.ximage->width;
7420      height=(size_t) windows->image.ximage->height;
7421      x=0;
7422      y=0;
7423      (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g+0+0",
7424        (double) width,(double) height);
7425      status=XDialogWidget(display,windows,"Resize",
7426        "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7427      if (*geometry == '\0')
7428        break;
7429      if (status == 0)
7430        (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7431      (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7432      windows->image.window_changes.width=(int) width;
7433      windows->image.window_changes.height=(int) height;
7434      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7435      break;
7436    }
7437    case ApplyCommand:
7438    {
7439      char
7440        image_geometry[MaxTextExtent];
7441
7442      if ((windows->image.crop_geometry == (char *) NULL) &&
7443          ((int) (*image)->columns == windows->image.ximage->width) &&
7444          ((int) (*image)->rows == windows->image.ximage->height))
7445        break;
7446      /*
7447        Apply size transforms to image.
7448      */
7449      XSetCursorState(display,windows,MagickTrue);
7450      XCheckRefreshWindows(display,windows);
7451      /*
7452        Crop and/or scale displayed image.
7453      */
7454      (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
7455        windows->image.ximage->width,windows->image.ximage->height);
7456      (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
7457        exception);
7458      if (windows->image.crop_geometry != (char *) NULL)
7459        windows->image.crop_geometry=(char *) RelinquishMagickMemory(
7460          windows->image.crop_geometry);
7461      windows->image.x=0;
7462      windows->image.y=0;
7463      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7464      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7465      break;
7466    }
7467    case RefreshCommand:
7468    {
7469      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7470      break;
7471    }
7472    case RestoreCommand:
7473    {
7474      /*
7475        Restore Image window to its original size.
7476      */
7477      if ((windows->image.width == (unsigned int) (*image)->columns) &&
7478          (windows->image.height == (unsigned int) (*image)->rows) &&
7479          (windows->image.crop_geometry == (char *) NULL))
7480        {
7481          (void) XBell(display,0);
7482          break;
7483        }
7484      windows->image.window_changes.width=(int) (*image)->columns;
7485      windows->image.window_changes.height=(int) (*image)->rows;
7486      if (windows->image.crop_geometry != (char *) NULL)
7487        {
7488          windows->image.crop_geometry=(char *)
7489            RelinquishMagickMemory(windows->image.crop_geometry);
7490          windows->image.crop_geometry=(char *) NULL;
7491          windows->image.x=0;
7492          windows->image.y=0;
7493        }
7494      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7495      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7496      break;
7497    }
7498    case CropCommand:
7499    {
7500      /*
7501        Crop image.
7502      */
7503      (void) XCropImage(display,resource_info,windows,*image,CropMode,
7504        exception);
7505      break;
7506    }
7507    case ChopCommand:
7508    {
7509      /*
7510        Chop image.
7511      */
7512      status=XChopImage(display,resource_info,windows,image,exception);
7513      if (status == MagickFalse)
7514        {
7515          XNoticeWidget(display,windows,"Unable to cut X image",
7516            (*image)->filename);
7517          break;
7518        }
7519      break;
7520    }
7521    case FlopCommand:
7522    {
7523      Image
7524        *flop_image;
7525
7526      /*
7527        Flop image scanlines.
7528      */
7529      XSetCursorState(display,windows,MagickTrue);
7530      XCheckRefreshWindows(display,windows);
7531      flop_image=FlopImage(*image,exception);
7532      if (flop_image != (Image *) NULL)
7533        {
7534          *image=DestroyImage(*image);
7535          *image=flop_image;
7536        }
7537      CatchException(exception);
7538      XSetCursorState(display,windows,MagickFalse);
7539      if (windows->image.crop_geometry != (char *) NULL)
7540        {
7541          /*
7542            Flop crop geometry.
7543          */
7544          width=(unsigned int) (*image)->columns;
7545          height=(unsigned int) (*image)->rows;
7546          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7547            &width,&height);
7548          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7549            "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7550        }
7551      if (windows->image.orphan != MagickFalse)
7552        break;
7553      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7554      break;
7555    }
7556    case FlipCommand:
7557    {
7558      Image
7559        *flip_image;
7560
7561      /*
7562        Flip image scanlines.
7563      */
7564      XSetCursorState(display,windows,MagickTrue);
7565      XCheckRefreshWindows(display,windows);
7566      flip_image=FlipImage(*image,exception);
7567      if (flip_image != (Image *) NULL)
7568        {
7569          *image=DestroyImage(*image);
7570          *image=flip_image;
7571        }
7572      CatchException(exception);
7573      XSetCursorState(display,windows,MagickFalse);
7574      if (windows->image.crop_geometry != (char *) NULL)
7575        {
7576          /*
7577            Flip crop geometry.
7578          */
7579          width=(unsigned int) (*image)->columns;
7580          height=(unsigned int) (*image)->rows;
7581          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7582            &width,&height);
7583          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7584            "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7585        }
7586      if (windows->image.orphan != MagickFalse)
7587        break;
7588      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7589      break;
7590    }
7591    case RotateRightCommand:
7592    {
7593      /*
7594        Rotate image 90 degrees clockwise.
7595      */
7596      status=XRotateImage(display,resource_info,windows,90.0,image,exception);
7597      if (status == MagickFalse)
7598        {
7599          XNoticeWidget(display,windows,"Unable to rotate X image",
7600            (*image)->filename);
7601          break;
7602        }
7603      break;
7604    }
7605    case RotateLeftCommand:
7606    {
7607      /*
7608        Rotate image 90 degrees counter-clockwise.
7609      */
7610      status=XRotateImage(display,resource_info,windows,-90.0,image,exception);
7611      if (status == MagickFalse)
7612        {
7613          XNoticeWidget(display,windows,"Unable to rotate X image",
7614            (*image)->filename);
7615          break;
7616        }
7617      break;
7618    }
7619    case RotateCommand:
7620    {
7621      /*
7622        Rotate image.
7623      */
7624      status=XRotateImage(display,resource_info,windows,0.0,image,exception);
7625      if (status == MagickFalse)
7626        {
7627          XNoticeWidget(display,windows,"Unable to rotate X image",
7628            (*image)->filename);
7629          break;
7630        }
7631      break;
7632    }
7633    case ShearCommand:
7634    {
7635      Image
7636        *shear_image;
7637
7638      static char
7639        geometry[MaxTextExtent] = "45.0x45.0";
7640
7641      /*
7642        Query user for shear color and geometry.
7643      */
7644      XColorBrowserWidget(display,windows,"Select",color);
7645      if (*color == '\0')
7646        break;
7647      (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7648        geometry);
7649      if (*geometry == '\0')
7650        break;
7651      /*
7652        Shear image.
7653      */
7654      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7655        exception);
7656      XSetCursorState(display,windows,MagickTrue);
7657      XCheckRefreshWindows(display,windows);
7658      (void) QueryColorCompliance(color,AllCompliance,
7659        &(*image)->background_color,exception);
7660      flags=ParseGeometry(geometry,&geometry_info);
7661      if ((flags & SigmaValue) == 0)
7662        geometry_info.sigma=geometry_info.rho;
7663      shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7664        exception);
7665      if (shear_image != (Image *) NULL)
7666        {
7667          *image=DestroyImage(*image);
7668          *image=shear_image;
7669        }
7670      CatchException(exception);
7671      XSetCursorState(display,windows,MagickFalse);
7672      if (windows->image.orphan != MagickFalse)
7673        break;
7674      windows->image.window_changes.width=(int) (*image)->columns;
7675      windows->image.window_changes.height=(int) (*image)->rows;
7676      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7677      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7678      break;
7679    }
7680    case RollCommand:
7681    {
7682      Image
7683        *roll_image;
7684
7685      static char
7686        geometry[MaxTextExtent] = "+2+2";
7687
7688      /*
7689        Query user for the roll geometry.
7690      */
7691      (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7692        geometry);
7693      if (*geometry == '\0')
7694        break;
7695      /*
7696        Roll image.
7697      */
7698      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7699        exception);
7700      XSetCursorState(display,windows,MagickTrue);
7701      XCheckRefreshWindows(display,windows);
7702      (void) ParsePageGeometry(*image,geometry,&page_geometry,
7703        exception);
7704      roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7705        exception);
7706      if (roll_image != (Image *) NULL)
7707        {
7708          *image=DestroyImage(*image);
7709          *image=roll_image;
7710        }
7711      CatchException(exception);
7712      XSetCursorState(display,windows,MagickFalse);
7713      if (windows->image.orphan != MagickFalse)
7714        break;
7715      windows->image.window_changes.width=(int) (*image)->columns;
7716      windows->image.window_changes.height=(int) (*image)->rows;
7717      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7718      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7719      break;
7720    }
7721    case TrimCommand:
7722    {
7723      static char
7724        fuzz[MaxTextExtent];
7725
7726      /*
7727        Query user for the fuzz factor.
7728      */
7729      (void) FormatLocaleString(fuzz,MaxTextExtent,"%g%%",100.0*
7730        (*image)->fuzz/(QuantumRange+1.0));
7731      (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7732      if (*fuzz == '\0')
7733        break;
7734      (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+1.0);
7735      /*
7736        Trim image.
7737      */
7738      status=XTrimImage(display,resource_info,windows,*image,exception);
7739      if (status == MagickFalse)
7740        {
7741          XNoticeWidget(display,windows,"Unable to trim X image",
7742            (*image)->filename);
7743          break;
7744        }
7745      break;
7746    }
7747    case HueCommand:
7748    {
7749      static char
7750        hue_percent[MaxTextExtent] = "110";
7751
7752      /*
7753        Query user for percent hue change.
7754      */
7755      (void) XDialogWidget(display,windows,"Apply",
7756        "Enter percent change in image hue (0-200):",hue_percent);
7757      if (*hue_percent == '\0')
7758        break;
7759      /*
7760        Vary the image hue.
7761      */
7762      XSetCursorState(display,windows,MagickTrue);
7763      XCheckRefreshWindows(display,windows);
7764      (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7765      (void) ConcatenateMagickString(modulate_factors,hue_percent,
7766        MaxTextExtent);
7767      (void) ModulateImage(*image,modulate_factors,exception);
7768      XSetCursorState(display,windows,MagickFalse);
7769      if (windows->image.orphan != MagickFalse)
7770        break;
7771      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7772      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7773      break;
7774    }
7775    case SaturationCommand:
7776    {
7777      static char
7778        saturation_percent[MaxTextExtent] = "110";
7779
7780      /*
7781        Query user for percent saturation change.
7782      */
7783      (void) XDialogWidget(display,windows,"Apply",
7784        "Enter percent change in color saturation (0-200):",saturation_percent);
7785      if (*saturation_percent == '\0')
7786        break;
7787      /*
7788        Vary color saturation.
7789      */
7790      XSetCursorState(display,windows,MagickTrue);
7791      XCheckRefreshWindows(display,windows);
7792      (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7793      (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7794        MaxTextExtent);
7795      (void) ModulateImage(*image,modulate_factors,exception);
7796      XSetCursorState(display,windows,MagickFalse);
7797      if (windows->image.orphan != MagickFalse)
7798        break;
7799      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7800      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7801      break;
7802    }
7803    case BrightnessCommand:
7804    {
7805      static char
7806        brightness_percent[MaxTextExtent] = "110";
7807
7808      /*
7809        Query user for percent brightness change.
7810      */
7811      (void) XDialogWidget(display,windows,"Apply",
7812        "Enter percent change in color brightness (0-200):",brightness_percent);
7813      if (*brightness_percent == '\0')
7814        break;
7815      /*
7816        Vary the color brightness.
7817      */
7818      XSetCursorState(display,windows,MagickTrue);
7819      XCheckRefreshWindows(display,windows);
7820      (void) CopyMagickString(modulate_factors,brightness_percent,
7821        MaxTextExtent);
7822      (void) ModulateImage(*image,modulate_factors,exception);
7823      XSetCursorState(display,windows,MagickFalse);
7824      if (windows->image.orphan != MagickFalse)
7825        break;
7826      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7827      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7828      break;
7829    }
7830    case GammaCommand:
7831    {
7832      static char
7833        factor[MaxTextExtent] = "1.6";
7834
7835      /*
7836        Query user for gamma value.
7837      */
7838      (void) XDialogWidget(display,windows,"Gamma",
7839        "Enter gamma value (e.g. 1.2):",factor);
7840      if (*factor == '\0')
7841        break;
7842      /*
7843        Gamma correct image.
7844      */
7845      XSetCursorState(display,windows,MagickTrue);
7846      XCheckRefreshWindows(display,windows);
7847      (void) GammaImage(*image,atof(factor),exception);
7848      XSetCursorState(display,windows,MagickFalse);
7849      if (windows->image.orphan != MagickFalse)
7850        break;
7851      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7852      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7853      break;
7854    }
7855    case SpiffCommand:
7856    {
7857      /*
7858        Sharpen the image contrast.
7859      */
7860      XSetCursorState(display,windows,MagickTrue);
7861      XCheckRefreshWindows(display,windows);
7862      (void) ContrastImage(*image,MagickTrue,exception);
7863      XSetCursorState(display,windows,MagickFalse);
7864      if (windows->image.orphan != MagickFalse)
7865        break;
7866      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7867      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7868      break;
7869    }
7870    case DullCommand:
7871    {
7872      /*
7873        Dull the image contrast.
7874      */
7875      XSetCursorState(display,windows,MagickTrue);
7876      XCheckRefreshWindows(display,windows);
7877      (void) ContrastImage(*image,MagickFalse,exception);
7878      XSetCursorState(display,windows,MagickFalse);
7879      if (windows->image.orphan != MagickFalse)
7880        break;
7881      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7882      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7883      break;
7884    }
7885    case ContrastStretchCommand:
7886    {
7887      double
7888        black_point,
7889        white_point;
7890
7891      static char
7892        levels[MaxTextExtent] = "1%";
7893
7894      /*
7895        Query user for gamma value.
7896      */
7897      (void) XDialogWidget(display,windows,"Contrast Stretch",
7898        "Enter black and white points:",levels);
7899      if (*levels == '\0')
7900        break;
7901      /*
7902        Contrast stretch image.
7903      */
7904      XSetCursorState(display,windows,MagickTrue);
7905      XCheckRefreshWindows(display,windows);
7906      flags=ParseGeometry(levels,&geometry_info);
7907      black_point=geometry_info.rho;
7908      white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7909      if ((flags & PercentValue) != 0)
7910        {
7911          black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7912          white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7913        }
7914      white_point=(MagickRealType) (*image)->columns*(*image)->rows-white_point;
7915      (void) ContrastStretchImage(*image,black_point,white_point,
7916        exception);
7917      XSetCursorState(display,windows,MagickFalse);
7918      if (windows->image.orphan != MagickFalse)
7919        break;
7920      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7921      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7922      break;
7923    }
7924    case SigmoidalContrastCommand:
7925    {
7926      GeometryInfo
7927        geometry_info;
7928
7929      MagickStatusType
7930        flags;
7931
7932      static char
7933        levels[MaxTextExtent] = "3x50%";
7934
7935      /*
7936        Query user for gamma value.
7937      */
7938      (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7939        "Enter contrast and midpoint:",levels);
7940      if (*levels == '\0')
7941        break;
7942      /*
7943        Contrast stretch image.
7944      */
7945      XSetCursorState(display,windows,MagickTrue);
7946      XCheckRefreshWindows(display,windows);
7947      flags=ParseGeometry(levels,&geometry_info);
7948      if ((flags & SigmaValue) == 0)
7949        geometry_info.sigma=1.0*QuantumRange/2.0;
7950      if ((flags & PercentValue) != 0)
7951        geometry_info.sigma=1.0*QuantumRange*geometry_info.sigma/100.0;
7952      (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
7953        geometry_info.sigma,exception);
7954      XSetCursorState(display,windows,MagickFalse);
7955      if (windows->image.orphan != MagickFalse)
7956        break;
7957      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7958      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7959      break;
7960    }
7961    case NormalizeCommand:
7962    {
7963      /*
7964        Perform histogram normalization on the image.
7965      */
7966      XSetCursorState(display,windows,MagickTrue);
7967      XCheckRefreshWindows(display,windows);
7968      (void) NormalizeImage(*image,exception);
7969      XSetCursorState(display,windows,MagickFalse);
7970      if (windows->image.orphan != MagickFalse)
7971        break;
7972      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7973      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7974      break;
7975    }
7976    case EqualizeCommand:
7977    {
7978      /*
7979        Perform histogram equalization on the image.
7980      */
7981      XSetCursorState(display,windows,MagickTrue);
7982      XCheckRefreshWindows(display,windows);
7983      (void) EqualizeImage(*image,exception);
7984      XSetCursorState(display,windows,MagickFalse);
7985      if (windows->image.orphan != MagickFalse)
7986        break;
7987      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7988      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7989      break;
7990    }
7991    case NegateCommand:
7992    {
7993      /*
7994        Negate colors in image.
7995      */
7996      XSetCursorState(display,windows,MagickTrue);
7997      XCheckRefreshWindows(display,windows);
7998      (void) NegateImage(*image,MagickFalse,exception);
7999      XSetCursorState(display,windows,MagickFalse);
8000      if (windows->image.orphan != MagickFalse)
8001        break;
8002      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8003      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8004      break;
8005    }
8006    case GrayscaleCommand:
8007    {
8008      /*
8009        Convert image to grayscale.
8010      */
8011      XSetCursorState(display,windows,MagickTrue);
8012      XCheckRefreshWindows(display,windows);
8013      (void) SetImageType(*image,(*image)->matte == MagickFalse ?
8014        GrayscaleType : GrayscaleMatteType,exception);
8015      XSetCursorState(display,windows,MagickFalse);
8016      if (windows->image.orphan != MagickFalse)
8017        break;
8018      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8019      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8020      break;
8021    }
8022    case MapCommand:
8023    {
8024      Image
8025        *affinity_image;
8026
8027      static char
8028        filename[MaxTextExtent] = "\0";
8029
8030      /*
8031        Request image file name from user.
8032      */
8033      XFileBrowserWidget(display,windows,"Map",filename);
8034      if (*filename == '\0')
8035        break;
8036      /*
8037        Map image.
8038      */
8039      XSetCursorState(display,windows,MagickTrue);
8040      XCheckRefreshWindows(display,windows);
8041      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
8042      affinity_image=ReadImage(image_info,exception);
8043      if (affinity_image != (Image *) NULL)
8044        {
8045          (void) RemapImage(&quantize_info,*image,affinity_image,exception);
8046          affinity_image=DestroyImage(affinity_image);
8047        }
8048      CatchException(exception);
8049      XSetCursorState(display,windows,MagickFalse);
8050      if (windows->image.orphan != MagickFalse)
8051        break;
8052      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8053      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8054      break;
8055    }
8056    case QuantizeCommand:
8057    {
8058      int
8059        status;
8060
8061      static char
8062        colors[MaxTextExtent] = "256";
8063
8064      /*
8065        Query user for maximum number of colors.
8066      */
8067      status=XDialogWidget(display,windows,"Quantize",
8068        "Maximum number of colors:",colors);
8069      if (*colors == '\0')
8070        break;
8071      /*
8072        Color reduce the image.
8073      */
8074      XSetCursorState(display,windows,MagickTrue);
8075      XCheckRefreshWindows(display,windows);
8076      quantize_info.number_colors=StringToUnsignedLong(colors);
8077      quantize_info.dither=status != 0 ? MagickTrue : MagickFalse;
8078      (void) QuantizeImage(&quantize_info,*image,exception);
8079      XSetCursorState(display,windows,MagickFalse);
8080      if (windows->image.orphan != MagickFalse)
8081        break;
8082      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8083      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8084      break;
8085    }
8086    case DespeckleCommand:
8087    {
8088      Image
8089        *despeckle_image;
8090
8091      /*
8092        Despeckle image.
8093      */
8094      XSetCursorState(display,windows,MagickTrue);
8095      XCheckRefreshWindows(display,windows);
8096      despeckle_image=DespeckleImage(*image,exception);
8097      if (despeckle_image != (Image *) NULL)
8098        {
8099          *image=DestroyImage(*image);
8100          *image=despeckle_image;
8101        }
8102      CatchException(exception);
8103      XSetCursorState(display,windows,MagickFalse);
8104      if (windows->image.orphan != MagickFalse)
8105        break;
8106      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8107      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8108      break;
8109    }
8110    case EmbossCommand:
8111    {
8112      Image
8113        *emboss_image;
8114
8115      static char
8116        radius[MaxTextExtent] = "0.0x1.0";
8117
8118      /*
8119        Query user for emboss radius.
8120      */
8121      (void) XDialogWidget(display,windows,"Emboss",
8122        "Enter the emboss radius and standard deviation:",radius);
8123      if (*radius == '\0')
8124        break;
8125      /*
8126        Reduce noise in the image.
8127      */
8128      XSetCursorState(display,windows,MagickTrue);
8129      XCheckRefreshWindows(display,windows);
8130      flags=ParseGeometry(radius,&geometry_info);
8131      if ((flags & SigmaValue) == 0)
8132        geometry_info.sigma=1.0;
8133      emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8134        exception);
8135      if (emboss_image != (Image *) NULL)
8136        {
8137          *image=DestroyImage(*image);
8138          *image=emboss_image;
8139        }
8140      CatchException(exception);
8141      XSetCursorState(display,windows,MagickFalse);
8142      if (windows->image.orphan != MagickFalse)
8143        break;
8144      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8145      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8146      break;
8147    }
8148    case ReduceNoiseCommand:
8149    {
8150      Image
8151        *noise_image;
8152
8153      static char
8154        radius[MaxTextExtent] = "0";
8155
8156      /*
8157        Query user for noise radius.
8158      */
8159      (void) XDialogWidget(display,windows,"Reduce Noise",
8160        "Enter the noise radius:",radius);
8161      if (*radius == '\0')
8162        break;
8163      /*
8164        Reduce noise in the image.
8165      */
8166      XSetCursorState(display,windows,MagickTrue);
8167      XCheckRefreshWindows(display,windows);
8168      flags=ParseGeometry(radius,&geometry_info);
8169      noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8170        geometry_info.rho,(size_t) geometry_info.rho,exception);
8171      if (noise_image != (Image *) NULL)
8172        {
8173          *image=DestroyImage(*image);
8174          *image=noise_image;
8175        }
8176      CatchException(exception);
8177      XSetCursorState(display,windows,MagickFalse);
8178      if (windows->image.orphan != MagickFalse)
8179        break;
8180      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8181      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8182      break;
8183    }
8184    case AddNoiseCommand:
8185    {
8186      char
8187        **noises;
8188
8189      Image
8190        *noise_image;
8191
8192      static char
8193        noise_type[MaxTextExtent] = "Gaussian";
8194
8195      /*
8196        Add noise to the image.
8197      */
8198      noises=GetCommandOptions(MagickNoiseOptions);
8199      if (noises == (char **) NULL)
8200        break;
8201      XListBrowserWidget(display,windows,&windows->widget,
8202        (const char **) noises,"Add Noise",
8203        "Select a type of noise to add to your image:",noise_type);
8204      noises=DestroyStringList(noises);
8205      if (*noise_type == '\0')
8206        break;
8207      XSetCursorState(display,windows,MagickTrue);
8208      XCheckRefreshWindows(display,windows);
8209      noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8210        MagickNoiseOptions,MagickFalse,noise_type),1.0,exception);
8211      if (noise_image != (Image *) NULL)
8212        {
8213          *image=DestroyImage(*image);
8214          *image=noise_image;
8215        }
8216      CatchException(exception);
8217      XSetCursorState(display,windows,MagickFalse);
8218      if (windows->image.orphan != MagickFalse)
8219        break;
8220      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8221      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8222      break;
8223    }
8224    case SharpenCommand:
8225    {
8226      Image
8227        *sharp_image;
8228
8229      static char
8230        radius[MaxTextExtent] = "0.0x1.0";
8231
8232      /*
8233        Query user for sharpen radius.
8234      */
8235      (void) XDialogWidget(display,windows,"Sharpen",
8236        "Enter the sharpen radius and standard deviation:",radius);
8237      if (*radius == '\0')
8238        break;
8239      /*
8240        Sharpen image scanlines.
8241      */
8242      XSetCursorState(display,windows,MagickTrue);
8243      XCheckRefreshWindows(display,windows);
8244      flags=ParseGeometry(radius,&geometry_info);
8245      sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8246        geometry_info.xi,exception);
8247      if (sharp_image != (Image *) NULL)
8248        {
8249          *image=DestroyImage(*image);
8250          *image=sharp_image;
8251        }
8252      CatchException(exception);
8253      XSetCursorState(display,windows,MagickFalse);
8254      if (windows->image.orphan != MagickFalse)
8255        break;
8256      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8257      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8258      break;
8259    }
8260    case BlurCommand:
8261    {
8262      Image
8263        *blur_image;
8264
8265      static char
8266        radius[MaxTextExtent] = "0.0x1.0";
8267
8268      /*
8269        Query user for blur radius.
8270      */
8271      (void) XDialogWidget(display,windows,"Blur",
8272        "Enter the blur radius and standard deviation:",radius);
8273      if (*radius == '\0')
8274        break;
8275      /*
8276        Blur an image.
8277      */
8278      XSetCursorState(display,windows,MagickTrue);
8279      XCheckRefreshWindows(display,windows);
8280      flags=ParseGeometry(radius,&geometry_info);
8281      blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8282        geometry_info.xi,exception);
8283      if (blur_image != (Image *) NULL)
8284        {
8285          *image=DestroyImage(*image);
8286          *image=blur_image;
8287        }
8288      CatchException(exception);
8289      XSetCursorState(display,windows,MagickFalse);
8290      if (windows->image.orphan != MagickFalse)
8291        break;
8292      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8293      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8294      break;
8295    }
8296    case ThresholdCommand:
8297    {
8298      double
8299        threshold;
8300
8301      static char
8302        factor[MaxTextExtent] = "128";
8303
8304      /*
8305        Query user for threshold value.
8306      */
8307      (void) XDialogWidget(display,windows,"Threshold",
8308        "Enter threshold value:",factor);
8309      if (*factor == '\0')
8310        break;
8311      /*
8312        Gamma correct image.
8313      */
8314      XSetCursorState(display,windows,MagickTrue);
8315      XCheckRefreshWindows(display,windows);
8316      threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8317      (void) BilevelImage(*image,threshold,exception);
8318      XSetCursorState(display,windows,MagickFalse);
8319      if (windows->image.orphan != MagickFalse)
8320        break;
8321      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8322      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8323      break;
8324    }
8325    case EdgeDetectCommand:
8326    {
8327      Image
8328        *edge_image;
8329
8330      static char
8331        radius[MaxTextExtent] = "0";
8332
8333      /*
8334        Query user for edge factor.
8335      */
8336      (void) XDialogWidget(display,windows,"Detect Edges",
8337        "Enter the edge detect radius:",radius);
8338      if (*radius == '\0')
8339        break;
8340      /*
8341        Detect edge in image.
8342      */
8343      XSetCursorState(display,windows,MagickTrue);
8344      XCheckRefreshWindows(display,windows);
8345      flags=ParseGeometry(radius,&geometry_info);
8346      edge_image=EdgeImage(*image,geometry_info.rho,geometry_info.sigma,
8347        exception);
8348      if (edge_image != (Image *) NULL)
8349        {
8350          *image=DestroyImage(*image);
8351          *image=edge_image;
8352        }
8353      CatchException(exception);
8354      XSetCursorState(display,windows,MagickFalse);
8355      if (windows->image.orphan != MagickFalse)
8356        break;
8357      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8358      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8359      break;
8360    }
8361    case SpreadCommand:
8362    {
8363      Image
8364        *spread_image;
8365
8366      static char
8367        amount[MaxTextExtent] = "2";
8368
8369      /*
8370        Query user for spread amount.
8371      */
8372      (void) XDialogWidget(display,windows,"Spread",
8373        "Enter the displacement amount:",amount);
8374      if (*amount == '\0')
8375        break;
8376      /*
8377        Displace image pixels by a random amount.
8378      */
8379      XSetCursorState(display,windows,MagickTrue);
8380      XCheckRefreshWindows(display,windows);
8381      flags=ParseGeometry(amount,&geometry_info);
8382      spread_image=EdgeImage(*image,geometry_info.rho,geometry_info.sigma,
8383        exception);
8384      if (spread_image != (Image *) NULL)
8385        {
8386          *image=DestroyImage(*image);
8387          *image=spread_image;
8388        }
8389      CatchException(exception);
8390      XSetCursorState(display,windows,MagickFalse);
8391      if (windows->image.orphan != MagickFalse)
8392        break;
8393      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8394      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8395      break;
8396    }
8397    case ShadeCommand:
8398    {
8399      Image
8400        *shade_image;
8401
8402      int
8403        status;
8404
8405      static char
8406        geometry[MaxTextExtent] = "30x30";
8407
8408      /*
8409        Query user for the shade geometry.
8410      */
8411      status=XDialogWidget(display,windows,"Shade",
8412        "Enter the azimuth and elevation of the light source:",geometry);
8413      if (*geometry == '\0')
8414        break;
8415      /*
8416        Shade image pixels.
8417      */
8418      XSetCursorState(display,windows,MagickTrue);
8419      XCheckRefreshWindows(display,windows);
8420      flags=ParseGeometry(geometry,&geometry_info);
8421      if ((flags & SigmaValue) == 0)
8422        geometry_info.sigma=1.0;
8423      shade_image=ShadeImage(*image,status != 0 ? MagickFalse : MagickTrue,
8424        geometry_info.rho,geometry_info.sigma,exception);
8425      if (shade_image != (Image *) NULL)
8426        {
8427          *image=DestroyImage(*image);
8428          *image=shade_image;
8429        }
8430      CatchException(exception);
8431      XSetCursorState(display,windows,MagickFalse);
8432      if (windows->image.orphan != MagickFalse)
8433        break;
8434      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8435      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8436      break;
8437    }
8438    case RaiseCommand:
8439    {
8440      static char
8441        bevel_width[MaxTextExtent] = "10";
8442
8443      /*
8444        Query user for bevel width.
8445      */
8446      (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8447      if (*bevel_width == '\0')
8448        break;
8449      /*
8450        Raise an image.
8451      */
8452      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8453        exception);
8454      XSetCursorState(display,windows,MagickTrue);
8455      XCheckRefreshWindows(display,windows);
8456      (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8457        exception);
8458      (void) RaiseImage(*image,&page_geometry,MagickTrue,exception);
8459      XSetCursorState(display,windows,MagickFalse);
8460      if (windows->image.orphan != MagickFalse)
8461        break;
8462      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8463      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8464      break;
8465    }
8466    case SegmentCommand:
8467    {
8468      static char
8469        threshold[MaxTextExtent] = "1.0x1.5";
8470
8471      /*
8472        Query user for smoothing threshold.
8473      */
8474      (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8475        threshold);
8476      if (*threshold == '\0')
8477        break;
8478      /*
8479        Segment an image.
8480      */
8481      XSetCursorState(display,windows,MagickTrue);
8482      XCheckRefreshWindows(display,windows);
8483      flags=ParseGeometry(threshold,&geometry_info);
8484      if ((flags & SigmaValue) == 0)
8485        geometry_info.sigma=1.0;
8486      (void) SegmentImage(*image,RGBColorspace,MagickFalse,geometry_info.rho,
8487        geometry_info.sigma,exception);
8488      XSetCursorState(display,windows,MagickFalse);
8489      if (windows->image.orphan != MagickFalse)
8490        break;
8491      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8492      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8493      break;
8494    }
8495    case SepiaToneCommand:
8496    {
8497      double
8498        threshold;
8499
8500      Image
8501        *sepia_image;
8502
8503      static char
8504        factor[MaxTextExtent] = "80%";
8505
8506      /*
8507        Query user for sepia-tone factor.
8508      */
8509      (void) XDialogWidget(display,windows,"Sepia Tone",
8510        "Enter the sepia tone factor (0 - 99.9%):",factor);
8511      if (*factor == '\0')
8512        break;
8513      /*
8514        Sepia tone image pixels.
8515      */
8516      XSetCursorState(display,windows,MagickTrue);
8517      XCheckRefreshWindows(display,windows);
8518      threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8519      sepia_image=SepiaToneImage(*image,threshold,exception);
8520      if (sepia_image != (Image *) NULL)
8521        {
8522          *image=DestroyImage(*image);
8523          *image=sepia_image;
8524        }
8525      CatchException(exception);
8526      XSetCursorState(display,windows,MagickFalse);
8527      if (windows->image.orphan != MagickFalse)
8528        break;
8529      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8530      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8531      break;
8532    }
8533    case SolarizeCommand:
8534    {
8535      double
8536        threshold;
8537
8538      static char
8539        factor[MaxTextExtent] = "60%";
8540
8541      /*
8542        Query user for solarize factor.
8543      */
8544      (void) XDialogWidget(display,windows,"Solarize",
8545        "Enter the solarize factor (0 - 99.9%):",factor);
8546      if (*factor == '\0')
8547        break;
8548      /*
8549        Solarize image pixels.
8550      */
8551      XSetCursorState(display,windows,MagickTrue);
8552      XCheckRefreshWindows(display,windows);
8553      threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8554      (void) SolarizeImage(*image,threshold,exception);
8555      XSetCursorState(display,windows,MagickFalse);
8556      if (windows->image.orphan != MagickFalse)
8557        break;
8558      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8559      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8560      break;
8561    }
8562    case SwirlCommand:
8563    {
8564      Image
8565        *swirl_image;
8566
8567      static char
8568        degrees[MaxTextExtent] = "60";
8569
8570      /*
8571        Query user for swirl angle.
8572      */
8573      (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8574        degrees);
8575      if (*degrees == '\0')
8576        break;
8577      /*
8578        Swirl image pixels about the center.
8579      */
8580      XSetCursorState(display,windows,MagickTrue);
8581      XCheckRefreshWindows(display,windows);
8582      flags=ParseGeometry(degrees,&geometry_info);
8583      swirl_image=SwirlImage(*image,geometry_info.rho,(*image)->interpolate,
8584        exception);
8585      if (swirl_image != (Image *) NULL)
8586        {
8587          *image=DestroyImage(*image);
8588          *image=swirl_image;
8589        }
8590      CatchException(exception);
8591      XSetCursorState(display,windows,MagickFalse);
8592      if (windows->image.orphan != MagickFalse)
8593        break;
8594      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8595      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8596      break;
8597    }
8598    case ImplodeCommand:
8599    {
8600      Image
8601        *implode_image;
8602
8603      static char
8604        factor[MaxTextExtent] = "0.3";
8605
8606      /*
8607        Query user for implode factor.
8608      */
8609      (void) XDialogWidget(display,windows,"Implode",
8610        "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8611      if (*factor == '\0')
8612        break;
8613      /*
8614        Implode image pixels about the center.
8615      */
8616      XSetCursorState(display,windows,MagickTrue);
8617      XCheckRefreshWindows(display,windows);
8618      flags=ParseGeometry(factor,&geometry_info);
8619      implode_image=ImplodeImage(*image,geometry_info.rho,(*image)->interpolate,
8620        exception);
8621      if (implode_image != (Image *) NULL)
8622        {
8623          *image=DestroyImage(*image);
8624          *image=implode_image;
8625        }
8626      CatchException(exception);
8627      XSetCursorState(display,windows,MagickFalse);
8628      if (windows->image.orphan != MagickFalse)
8629        break;
8630      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8631      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8632      break;
8633    }
8634    case VignetteCommand:
8635    {
8636      Image
8637        *vignette_image;
8638
8639      static char
8640        geometry[MaxTextExtent] = "0x20";
8641
8642      /*
8643        Query user for the vignette geometry.
8644      */
8645      (void) XDialogWidget(display,windows,"Vignette",
8646        "Enter the radius, sigma, and x and y offsets:",geometry);
8647      if (*geometry == '\0')
8648        break;
8649      /*
8650        Soften the edges of the image in vignette style
8651      */
8652      XSetCursorState(display,windows,MagickTrue);
8653      XCheckRefreshWindows(display,windows);
8654      flags=ParseGeometry(geometry,&geometry_info);
8655      if ((flags & SigmaValue) == 0)
8656        geometry_info.sigma=1.0;
8657      if ((flags & XiValue) == 0)
8658        geometry_info.xi=0.1*(*image)->columns;
8659      if ((flags & PsiValue) == 0)
8660        geometry_info.psi=0.1*(*image)->rows;
8661      vignette_image=VignetteImage(*image,geometry_info.rho,geometry_info.sigma,
8662        0.0,(ssize_t) ceil(geometry_info.xi-0.5),(ssize_t)
8663        ceil(geometry_info.psi-0.5),exception);
8664      if (vignette_image != (Image *) NULL)
8665        {
8666          *image=DestroyImage(*image);
8667          *image=vignette_image;
8668        }
8669      CatchException(exception);
8670      XSetCursorState(display,windows,MagickFalse);
8671      if (windows->image.orphan != MagickFalse)
8672        break;
8673      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8674      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8675      break;
8676    }
8677    case WaveCommand:
8678    {
8679      Image
8680        *wave_image;
8681
8682      static char
8683        geometry[MaxTextExtent] = "25x150";
8684
8685      /*
8686        Query user for the wave geometry.
8687      */
8688      (void) XDialogWidget(display,windows,"Wave",
8689        "Enter the amplitude and length of the wave:",geometry);
8690      if (*geometry == '\0')
8691        break;
8692      /*
8693        Alter an image along a sine wave.
8694      */
8695      XSetCursorState(display,windows,MagickTrue);
8696      XCheckRefreshWindows(display,windows);
8697      flags=ParseGeometry(geometry,&geometry_info);
8698      if ((flags & SigmaValue) == 0)
8699        geometry_info.sigma=1.0;
8700      wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8701        (*image)->interpolate,exception);
8702      if (wave_image != (Image *) NULL)
8703        {
8704          *image=DestroyImage(*image);
8705          *image=wave_image;
8706        }
8707      CatchException(exception);
8708      XSetCursorState(display,windows,MagickFalse);
8709      if (windows->image.orphan != MagickFalse)
8710        break;
8711      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8712      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8713      break;
8714    }
8715    case OilPaintCommand:
8716    {
8717      Image
8718        *paint_image;
8719
8720      static char
8721        radius[MaxTextExtent] = "0";
8722
8723      /*
8724        Query user for circular neighborhood radius.
8725      */
8726      (void) XDialogWidget(display,windows,"Oil Paint",
8727        "Enter the mask radius:",radius);
8728      if (*radius == '\0')
8729        break;
8730      /*
8731        OilPaint image scanlines.
8732      */
8733      XSetCursorState(display,windows,MagickTrue);
8734      XCheckRefreshWindows(display,windows);
8735      flags=ParseGeometry(radius,&geometry_info);
8736      paint_image=OilPaintImage(*image,geometry_info.rho,geometry_info.sigma,
8737        exception);
8738      if (paint_image != (Image *) NULL)
8739        {
8740          *image=DestroyImage(*image);
8741          *image=paint_image;
8742        }
8743      CatchException(exception);
8744      XSetCursorState(display,windows,MagickFalse);
8745      if (windows->image.orphan != MagickFalse)
8746        break;
8747      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8748      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8749      break;
8750    }
8751    case CharcoalDrawCommand:
8752    {
8753      Image
8754        *charcoal_image;
8755
8756      static char
8757        radius[MaxTextExtent] = "0x1";
8758
8759      /*
8760        Query user for charcoal radius.
8761      */
8762      (void) XDialogWidget(display,windows,"Charcoal Draw",
8763        "Enter the charcoal radius and sigma:",radius);
8764      if (*radius == '\0')
8765        break;
8766      /*
8767        Charcoal the image.
8768      */
8769      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8770        exception);
8771      XSetCursorState(display,windows,MagickTrue);
8772      XCheckRefreshWindows(display,windows);
8773      flags=ParseGeometry(radius,&geometry_info);
8774      if ((flags & SigmaValue) == 0)
8775        geometry_info.sigma=geometry_info.rho;
8776      charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8777        geometry_info.xi,exception);
8778      if (charcoal_image != (Image *) NULL)
8779        {
8780          *image=DestroyImage(*image);
8781          *image=charcoal_image;
8782        }
8783      CatchException(exception);
8784      XSetCursorState(display,windows,MagickFalse);
8785      if (windows->image.orphan != MagickFalse)
8786        break;
8787      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8788      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8789      break;
8790    }
8791    case AnnotateCommand:
8792    {
8793      /*
8794        Annotate the image with text.
8795      */
8796      status=XAnnotateEditImage(display,resource_info,windows,*image,exception);
8797      if (status == MagickFalse)
8798        {
8799          XNoticeWidget(display,windows,"Unable to annotate X image",
8800            (*image)->filename);
8801          break;
8802        }
8803      break;
8804    }
8805    case DrawCommand:
8806    {
8807      /*
8808        Draw image.
8809      */
8810      status=XDrawEditImage(display,resource_info,windows,image,exception);
8811      if (status == MagickFalse)
8812        {
8813          XNoticeWidget(display,windows,"Unable to draw on the X image",
8814            (*image)->filename);
8815          break;
8816        }
8817      break;
8818    }
8819    case ColorCommand:
8820    {
8821      /*
8822        Color edit.
8823      */
8824      status=XColorEditImage(display,resource_info,windows,image,exception);
8825      if (status == MagickFalse)
8826        {
8827          XNoticeWidget(display,windows,"Unable to pixel edit X image",
8828            (*image)->filename);
8829          break;
8830        }
8831      break;
8832    }
8833    case MatteCommand:
8834    {
8835      /*
8836        Matte edit.
8837      */
8838      status=XMatteEditImage(display,resource_info,windows,image,exception);
8839      if (status == MagickFalse)
8840        {
8841          XNoticeWidget(display,windows,"Unable to matte edit X image",
8842            (*image)->filename);
8843          break;
8844        }
8845      break;
8846    }
8847    case CompositeCommand:
8848    {
8849      /*
8850        Composite image.
8851      */
8852      status=XCompositeImage(display,resource_info,windows,*image,
8853        exception);
8854      if (status == MagickFalse)
8855        {
8856          XNoticeWidget(display,windows,"Unable to composite X image",
8857            (*image)->filename);
8858          break;
8859        }
8860      break;
8861    }
8862    case AddBorderCommand:
8863    {
8864      Image
8865        *border_image;
8866
8867      static char
8868        geometry[MaxTextExtent] = "6x6";
8869
8870      /*
8871        Query user for border color and geometry.
8872      */
8873      XColorBrowserWidget(display,windows,"Select",color);
8874      if (*color == '\0')
8875        break;
8876      (void) XDialogWidget(display,windows,"Add Border",
8877        "Enter border geometry:",geometry);
8878      if (*geometry == '\0')
8879        break;
8880      /*
8881        Add a border to the image.
8882      */
8883      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8884        exception);
8885      XSetCursorState(display,windows,MagickTrue);
8886      XCheckRefreshWindows(display,windows);
8887      (void) QueryColorCompliance(color,AllCompliance,&(*image)->border_color,
8888        exception);
8889      (void) ParsePageGeometry(*image,geometry,&page_geometry,
8890        exception);
8891      border_image=BorderImage(*image,&page_geometry,(*image)->compose,
8892        exception);
8893      if (border_image != (Image *) NULL)
8894        {
8895          *image=DestroyImage(*image);
8896          *image=border_image;
8897        }
8898      CatchException(exception);
8899      XSetCursorState(display,windows,MagickFalse);
8900      if (windows->image.orphan != MagickFalse)
8901        break;
8902      windows->image.window_changes.width=(int) (*image)->columns;
8903      windows->image.window_changes.height=(int) (*image)->rows;
8904      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8905      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8906      break;
8907    }
8908    case AddFrameCommand:
8909    {
8910      FrameInfo
8911        frame_info;
8912
8913      Image
8914        *frame_image;
8915
8916      static char
8917        geometry[MaxTextExtent] = "6x6";
8918
8919      /*
8920        Query user for frame color and geometry.
8921      */
8922      XColorBrowserWidget(display,windows,"Select",color);
8923      if (*color == '\0')
8924        break;
8925      (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8926        geometry);
8927      if (*geometry == '\0')
8928        break;
8929      /*
8930        Surround image with an ornamental border.
8931      */
8932      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8933        exception);
8934      XSetCursorState(display,windows,MagickTrue);
8935      XCheckRefreshWindows(display,windows);
8936      (void) QueryColorCompliance(color,AllCompliance,&(*image)->matte_color,
8937        exception);
8938      (void) ParsePageGeometry(*image,geometry,&page_geometry,
8939        exception);
8940      frame_info.width=page_geometry.width;
8941      frame_info.height=page_geometry.height;
8942      frame_info.outer_bevel=page_geometry.x;
8943      frame_info.inner_bevel=page_geometry.y;
8944      frame_info.x=(ssize_t) frame_info.width;
8945      frame_info.y=(ssize_t) frame_info.height;
8946      frame_info.width=(*image)->columns+2*frame_info.width;
8947      frame_info.height=(*image)->rows+2*frame_info.height;
8948      frame_image=FrameImage(*image,&frame_info,(*image)->compose,exception);
8949      if (frame_image != (Image *) NULL)
8950        {
8951          *image=DestroyImage(*image);
8952          *image=frame_image;
8953        }
8954      CatchException(exception);
8955      XSetCursorState(display,windows,MagickFalse);
8956      if (windows->image.orphan != MagickFalse)
8957        break;
8958      windows->image.window_changes.width=(int) (*image)->columns;
8959      windows->image.window_changes.height=(int) (*image)->rows;
8960      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8961      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8962      break;
8963    }
8964    case CommentCommand:
8965    {
8966      const char
8967        *value;
8968
8969      FILE
8970        *file;
8971
8972      int
8973        unique_file;
8974
8975      /*
8976        Edit image comment.
8977      */
8978      unique_file=AcquireUniqueFileResource(image_info->filename);
8979      if (unique_file == -1)
8980        XNoticeWidget(display,windows,"Unable to edit image comment",
8981          image_info->filename);
8982      value=GetImageProperty(*image,"comment",exception);
8983      if (value == (char *) NULL)
8984        unique_file=close(unique_file)-1;
8985      else
8986        {
8987          register const char
8988            *p;
8989
8990          file=fdopen(unique_file,"w");
8991          if (file == (FILE *) NULL)
8992            {
8993              XNoticeWidget(display,windows,"Unable to edit image comment",
8994                image_info->filename);
8995              break;
8996            }
8997          for (p=value; *p != '\0'; p++)
8998            (void) fputc((int) *p,file);
8999          (void) fputc('\n',file);
9000          (void) fclose(file);
9001        }
9002      XSetCursorState(display,windows,MagickTrue);
9003      XCheckRefreshWindows(display,windows);
9004      status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
9005        exception);
9006      if (status == MagickFalse)
9007        XNoticeWidget(display,windows,"Unable to edit image comment",
9008          (char *) NULL);
9009      else
9010        {
9011          char
9012            *comment;
9013
9014          comment=FileToString(image_info->filename,~0UL,exception);
9015          if (comment != (char *) NULL)
9016            {
9017              (void) SetImageProperty(*image,"comment",comment,exception);
9018              (*image)->taint=MagickTrue;
9019            }
9020        }
9021      (void) RelinquishUniqueFileResource(image_info->filename);
9022      XSetCursorState(display,windows,MagickFalse);
9023      break;
9024    }
9025    case LaunchCommand:
9026    {
9027      /*
9028        Launch program.
9029      */
9030      XSetCursorState(display,windows,MagickTrue);
9031      XCheckRefreshWindows(display,windows);
9032      (void) AcquireUniqueFilename(filename);
9033      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"launch:%s",
9034        filename);
9035      status=WriteImage(image_info,*image,exception);
9036      if (status == MagickFalse)
9037        XNoticeWidget(display,windows,"Unable to launch image editor",
9038          (char *) NULL);
9039      else
9040        {
9041          nexus=ReadImage(resource_info->image_info,exception);
9042          CatchException(exception);
9043          XClientMessage(display,windows->image.id,windows->im_protocols,
9044            windows->im_next_image,CurrentTime);
9045        }
9046      (void) RelinquishUniqueFileResource(filename);
9047      XSetCursorState(display,windows,MagickFalse);
9048      break;
9049    }
9050    case RegionofInterestCommand:
9051    {
9052      /*
9053        Apply an image processing technique to a region of interest.
9054      */
9055      (void) XROIImage(display,resource_info,windows,image,exception);
9056      break;
9057    }
9058    case InfoCommand:
9059      break;
9060    case ZoomCommand:
9061    {
9062      /*
9063        Zoom image.
9064      */
9065      if (windows->magnify.mapped != MagickFalse)
9066        (void) XRaiseWindow(display,windows->magnify.id);
9067      else
9068        {
9069          /*
9070            Make magnify image.
9071          */
9072          XSetCursorState(display,windows,MagickTrue);
9073          (void) XMapRaised(display,windows->magnify.id);
9074          XSetCursorState(display,windows,MagickFalse);
9075        }
9076      break;
9077    }
9078    case ShowPreviewCommand:
9079    {
9080      char
9081        **previews;
9082
9083      Image
9084        *preview_image;
9085
9086      static char
9087        preview_type[MaxTextExtent] = "Gamma";
9088
9089      /*
9090        Select preview type from menu.
9091      */
9092      previews=GetCommandOptions(MagickPreviewOptions);
9093      if (previews == (char **) NULL)
9094        break;
9095      XListBrowserWidget(display,windows,&windows->widget,
9096        (const char **) previews,"Preview",
9097        "Select an enhancement, effect, or F/X:",preview_type);
9098      previews=DestroyStringList(previews);
9099      if (*preview_type == '\0')
9100        break;
9101      /*
9102        Show image preview.
9103      */
9104      XSetCursorState(display,windows,MagickTrue);
9105      XCheckRefreshWindows(display,windows);
9106      image_info->preview_type=(PreviewType)
9107        ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
9108      image_info->group=(ssize_t) windows->image.id;
9109      (void) DeleteImageProperty(*image,"label");
9110      (void) SetImageProperty(*image,"label","Preview",exception);
9111      (void) AcquireUniqueFilename(filename);
9112      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"preview:%s",
9113        filename);
9114      status=WriteImage(image_info,*image,exception);
9115      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9116      preview_image=ReadImage(image_info,exception);
9117      (void) RelinquishUniqueFileResource(filename);
9118      if (preview_image == (Image *) NULL)
9119        break;
9120      (void) FormatLocaleString(preview_image->filename,MaxTextExtent,"show:%s",
9121        filename);
9122      status=WriteImage(image_info,preview_image,exception);
9123      preview_image=DestroyImage(preview_image);
9124      if (status == MagickFalse)
9125        XNoticeWidget(display,windows,"Unable to show image preview",
9126          (*image)->filename);
9127      XDelay(display,1500);
9128      XSetCursorState(display,windows,MagickFalse);
9129      break;
9130    }
9131    case ShowHistogramCommand:
9132    {
9133      Image
9134        *histogram_image;
9135
9136      /*
9137        Show image histogram.
9138      */
9139      XSetCursorState(display,windows,MagickTrue);
9140      XCheckRefreshWindows(display,windows);
9141      image_info->group=(ssize_t) windows->image.id;
9142      (void) DeleteImageProperty(*image,"label");
9143      (void) SetImageProperty(*image,"label","Histogram",exception);
9144      (void) AcquireUniqueFilename(filename);
9145      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"histogram:%s",
9146        filename);
9147      status=WriteImage(image_info,*image,exception);
9148      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9149      histogram_image=ReadImage(image_info,exception);
9150      (void) RelinquishUniqueFileResource(filename);
9151      if (histogram_image == (Image *) NULL)
9152        break;
9153      (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
9154        "show:%s",filename);
9155      status=WriteImage(image_info,histogram_image,exception);
9156      histogram_image=DestroyImage(histogram_image);
9157      if (status == MagickFalse)
9158        XNoticeWidget(display,windows,"Unable to show histogram",
9159          (*image)->filename);
9160      XDelay(display,1500);
9161      XSetCursorState(display,windows,MagickFalse);
9162      break;
9163    }
9164    case ShowMatteCommand:
9165    {
9166      Image
9167        *matte_image;
9168
9169      if ((*image)->matte == MagickFalse)
9170        {
9171          XNoticeWidget(display,windows,
9172            "Image does not have any matte information",(*image)->filename);
9173          break;
9174        }
9175      /*
9176        Show image matte.
9177      */
9178      XSetCursorState(display,windows,MagickTrue);
9179      XCheckRefreshWindows(display,windows);
9180      image_info->group=(ssize_t) windows->image.id;
9181      (void) DeleteImageProperty(*image,"label");
9182      (void) SetImageProperty(*image,"label","Matte",exception);
9183      (void) AcquireUniqueFilename(filename);
9184      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"matte:%s",
9185        filename);
9186      status=WriteImage(image_info,*image,exception);
9187      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9188      matte_image=ReadImage(image_info,exception);
9189      (void) RelinquishUniqueFileResource(filename);
9190      if (matte_image == (Image *) NULL)
9191        break;
9192      (void) FormatLocaleString(matte_image->filename,MaxTextExtent,"show:%s",
9193        filename);
9194      status=WriteImage(image_info,matte_image,exception);
9195      matte_image=DestroyImage(matte_image);
9196      if (status == MagickFalse)
9197        XNoticeWidget(display,windows,"Unable to show matte",
9198          (*image)->filename);
9199      XDelay(display,1500);
9200      XSetCursorState(display,windows,MagickFalse);
9201      break;
9202    }
9203    case BackgroundCommand:
9204    {
9205      /*
9206        Background image.
9207      */
9208      status=XBackgroundImage(display,resource_info,windows,image,exception);
9209      if (status == MagickFalse)
9210        break;
9211      nexus=CloneImage(*image,0,0,MagickTrue,exception);
9212      if (nexus != (Image *) NULL)
9213        XClientMessage(display,windows->image.id,windows->im_protocols,
9214          windows->im_next_image,CurrentTime);
9215      break;
9216    }
9217    case SlideShowCommand:
9218    {
9219      static char
9220        delay[MaxTextExtent] = "5";
9221
9222      /*
9223        Display next image after pausing.
9224      */
9225      (void) XDialogWidget(display,windows,"Slide Show",
9226        "Pause how many 1/100ths of a second between images:",delay);
9227      if (*delay == '\0')
9228        break;
9229      resource_info->delay=StringToUnsignedLong(delay);
9230      XClientMessage(display,windows->image.id,windows->im_protocols,
9231        windows->im_next_image,CurrentTime);
9232      break;
9233    }
9234    case PreferencesCommand:
9235    {
9236      /*
9237        Set user preferences.
9238      */
9239      status=XPreferencesWidget(display,resource_info,windows);
9240      if (status == MagickFalse)
9241        break;
9242      nexus=CloneImage(*image,0,0,MagickTrue,exception);
9243      if (nexus != (Image *) NULL)
9244        XClientMessage(display,windows->image.id,windows->im_protocols,
9245          windows->im_next_image,CurrentTime);
9246      break;
9247    }
9248    case HelpCommand:
9249    {
9250      /*
9251        User requested help.
9252      */
9253      XTextViewWidget(display,resource_info,windows,MagickFalse,
9254        "Help Viewer - Display",DisplayHelp);
9255      break;
9256    }
9257    case BrowseDocumentationCommand:
9258    {
9259      Atom
9260        mozilla_atom;
9261
9262      Window
9263        mozilla_window,
9264        root_window;
9265
9266      /*
9267        Browse the ImageMagick documentation.
9268      */
9269      root_window=XRootWindow(display,XDefaultScreen(display));
9270      mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9271      mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9272      if (mozilla_window != (Window) NULL)
9273        {
9274          char
9275            command[MaxTextExtent],
9276            *url;
9277
9278          /*
9279            Display documentation using Netscape remote control.
9280          */
9281          url=GetMagickHomeURL();
9282          (void) FormatLocaleString(command,MaxTextExtent,
9283            "openurl(%s,new-tab)",url);
9284          url=DestroyString(url);
9285          mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9286          (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9287            8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9288          XSetCursorState(display,windows,MagickFalse);
9289          break;
9290        }
9291      XSetCursorState(display,windows,MagickTrue);
9292      XCheckRefreshWindows(display,windows);
9293      status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9294        exception);
9295      if (status == MagickFalse)
9296        XNoticeWidget(display,windows,"Unable to browse documentation",
9297          (char *) NULL);
9298      XDelay(display,1500);
9299      XSetCursorState(display,windows,MagickFalse);
9300      break;
9301    }
9302    case VersionCommand:
9303    {
9304      XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9305        GetMagickCopyright());
9306      break;
9307    }
9308    case SaveToUndoBufferCommand:
9309      break;
9310    default:
9311    {
9312      (void) XBell(display,0);
9313      break;
9314    }
9315  }
9316  image_info=DestroyImageInfo(image_info);
9317  return(nexus);
9318}
9319
9320/*
9321%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9322%                                                                             %
9323%                                                                             %
9324%                                                                             %
9325+   X M a g n i f y I m a g e                                                 %
9326%                                                                             %
9327%                                                                             %
9328%                                                                             %
9329%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9330%
9331%  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9332%  The magnified portion is displayed in a separate window.
9333%
9334%  The format of the XMagnifyImage method is:
9335%
9336%      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9337%        ExceptionInfo *exception)
9338%
9339%  A description of each parameter follows:
9340%
9341%    o display: Specifies a connection to an X server;  returned from
9342%      XOpenDisplay.
9343%
9344%    o windows: Specifies a pointer to a XWindows structure.
9345%
9346%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
9347%      the entire image is refreshed.
9348%
9349%    o exception: return any errors or warnings in this structure.
9350%
9351*/
9352static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9353  ExceptionInfo *exception)
9354{
9355  char
9356    text[MaxTextExtent];
9357
9358  register int
9359    x,
9360    y;
9361
9362  size_t
9363    state;
9364
9365  /*
9366    Update magnified image until the mouse button is released.
9367  */
9368  (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9369  state=DefaultState;
9370  x=event->xbutton.x;
9371  y=event->xbutton.y;
9372  windows->magnify.x=(int) windows->image.x+x;
9373  windows->magnify.y=(int) windows->image.y+y;
9374  do
9375  {
9376    /*
9377      Map and unmap Info widget as text cursor crosses its boundaries.
9378    */
9379    if (windows->info.mapped != MagickFalse)
9380      {
9381        if ((x < (int) (windows->info.x+windows->info.width)) &&
9382            (y < (int) (windows->info.y+windows->info.height)))
9383          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9384      }
9385    else
9386      if ((x > (int) (windows->info.x+windows->info.width)) ||
9387          (y > (int) (windows->info.y+windows->info.height)))
9388        (void) XMapWindow(display,windows->info.id);
9389    if (windows->info.mapped != MagickFalse)
9390      {
9391        /*
9392          Display pointer position.
9393        */
9394        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9395          windows->magnify.x,windows->magnify.y);
9396        XInfoWidget(display,windows,text);
9397      }
9398    /*
9399      Wait for next event.
9400    */
9401    XScreenEvent(display,windows,event,exception);
9402    switch (event->type)
9403    {
9404      case ButtonPress:
9405        break;
9406      case ButtonRelease:
9407      {
9408        /*
9409          User has finished magnifying image.
9410        */
9411        x=event->xbutton.x;
9412        y=event->xbutton.y;
9413        state|=ExitState;
9414        break;
9415      }
9416      case Expose:
9417        break;
9418      case MotionNotify:
9419      {
9420        x=event->xmotion.x;
9421        y=event->xmotion.y;
9422        break;
9423      }
9424      default:
9425        break;
9426    }
9427    /*
9428      Check boundary conditions.
9429    */
9430    if (x < 0)
9431      x=0;
9432    else
9433      if (x >= (int) windows->image.width)
9434        x=(int) windows->image.width-1;
9435    if (y < 0)
9436      y=0;
9437    else
9438     if (y >= (int) windows->image.height)
9439       y=(int) windows->image.height-1;
9440  } while ((state & ExitState) == 0);
9441  /*
9442    Display magnified image.
9443  */
9444  XSetCursorState(display,windows,MagickFalse);
9445}
9446
9447/*
9448%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9449%                                                                             %
9450%                                                                             %
9451%                                                                             %
9452+   X M a g n i f y W i n d o w C o m m a n d                                 %
9453%                                                                             %
9454%                                                                             %
9455%                                                                             %
9456%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9457%
9458%  XMagnifyWindowCommand() moves the image within an Magnify window by one
9459%  pixel as specified by the key symbol.
9460%
9461%  The format of the XMagnifyWindowCommand method is:
9462%
9463%      void XMagnifyWindowCommand(Display *display,XWindows *windows,
9464%        const MagickStatusType state,const KeySym key_symbol,
9465%        ExceptionInfo *exception)
9466%
9467%  A description of each parameter follows:
9468%
9469%    o display: Specifies a connection to an X server; returned from
9470%      XOpenDisplay.
9471%
9472%    o windows: Specifies a pointer to a XWindows structure.
9473%
9474%    o state: key mask.
9475%
9476%    o key_symbol: Specifies a KeySym which indicates which side of the image
9477%      to trim.
9478%
9479%    o exception: return any errors or warnings in this structure.
9480%
9481*/
9482static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9483  const MagickStatusType state,const KeySym key_symbol,ExceptionInfo *exception)
9484{
9485  unsigned int
9486    quantum;
9487
9488  /*
9489    User specified a magnify factor or position.
9490  */
9491  quantum=1;
9492  if ((state & Mod1Mask) != 0)
9493    quantum=10;
9494  switch ((int) key_symbol)
9495  {
9496    case QuitCommand:
9497    {
9498      (void) XWithdrawWindow(display,windows->magnify.id,
9499        windows->magnify.screen);
9500      break;
9501    }
9502    case XK_Home:
9503    case XK_KP_Home:
9504    {
9505      windows->magnify.x=(int) windows->image.width/2;
9506      windows->magnify.y=(int) windows->image.height/2;
9507      break;
9508    }
9509    case XK_Left:
9510    case XK_KP_Left:
9511    {
9512      if (windows->magnify.x > 0)
9513        windows->magnify.x-=quantum;
9514      break;
9515    }
9516    case XK_Up:
9517    case XK_KP_Up:
9518    {
9519      if (windows->magnify.y > 0)
9520        windows->magnify.y-=quantum;
9521      break;
9522    }
9523    case XK_Right:
9524    case XK_KP_Right:
9525    {
9526      if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9527        windows->magnify.x+=quantum;
9528      break;
9529    }
9530    case XK_Down:
9531    case XK_KP_Down:
9532    {
9533      if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9534        windows->magnify.y+=quantum;
9535      break;
9536    }
9537    case XK_0:
9538    case XK_1:
9539    case XK_2:
9540    case XK_3:
9541    case XK_4:
9542    case XK_5:
9543    case XK_6:
9544    case XK_7:
9545    case XK_8:
9546    case XK_9:
9547    {
9548      windows->magnify.data=(key_symbol-XK_0);
9549      break;
9550    }
9551    case XK_KP_0:
9552    case XK_KP_1:
9553    case XK_KP_2:
9554    case XK_KP_3:
9555    case XK_KP_4:
9556    case XK_KP_5:
9557    case XK_KP_6:
9558    case XK_KP_7:
9559    case XK_KP_8:
9560    case XK_KP_9:
9561    {
9562      windows->magnify.data=(key_symbol-XK_KP_0);
9563      break;
9564    }
9565    default:
9566      break;
9567  }
9568  XMakeMagnifyImage(display,windows,exception);
9569}
9570
9571/*
9572%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9573%                                                                             %
9574%                                                                             %
9575%                                                                             %
9576+   X M a k e P a n I m a g e                                                 %
9577%                                                                             %
9578%                                                                             %
9579%                                                                             %
9580%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9581%
9582%  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9583%  icon window.
9584%
9585%  The format of the XMakePanImage method is:
9586%
9587%        void XMakePanImage(Display *display,XResourceInfo *resource_info,
9588%          XWindows *windows,Image *image,ExceptionInfo *exception)
9589%
9590%  A description of each parameter follows:
9591%
9592%    o display: Specifies a connection to an X server;  returned from
9593%      XOpenDisplay.
9594%
9595%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9596%
9597%    o windows: Specifies a pointer to a XWindows structure.
9598%
9599%    o image: the image.
9600%
9601%    o exception: return any errors or warnings in this structure.
9602%
9603*/
9604static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9605  XWindows *windows,Image *image,ExceptionInfo *exception)
9606{
9607  MagickStatusType
9608    status;
9609
9610  /*
9611    Create and display image for panning icon.
9612  */
9613  XSetCursorState(display,windows,MagickTrue);
9614  XCheckRefreshWindows(display,windows);
9615  windows->pan.x=(int) windows->image.x;
9616  windows->pan.y=(int) windows->image.y;
9617  status=XMakeImage(display,resource_info,&windows->pan,image,
9618    windows->pan.width,windows->pan.height,exception);
9619  if (status == MagickFalse)
9620    ThrowXWindowFatalException(ResourceLimitError,
9621     "MemoryAllocationFailed",image->filename);
9622  (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9623    windows->pan.pixmap);
9624  (void) XClearWindow(display,windows->pan.id);
9625  XDrawPanRectangle(display,windows);
9626  XSetCursorState(display,windows,MagickFalse);
9627}
9628
9629/*
9630%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9631%                                                                             %
9632%                                                                             %
9633%                                                                             %
9634+   X M a t t a E d i t I m a g e                                             %
9635%                                                                             %
9636%                                                                             %
9637%                                                                             %
9638%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9639%
9640%  XMatteEditImage() allows the user to interactively change the Matte channel
9641%  of an image.  If the image is PseudoClass it is promoted to DirectClass
9642%  before the matte information is stored.
9643%
9644%  The format of the XMatteEditImage method is:
9645%
9646%      MagickBooleanType XMatteEditImage(Display *display,
9647%        XResourceInfo *resource_info,XWindows *windows,Image **image,
9648%        ExceptionInfo *exception)
9649%
9650%  A description of each parameter follows:
9651%
9652%    o display: Specifies a connection to an X server;  returned from
9653%      XOpenDisplay.
9654%
9655%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9656%
9657%    o windows: Specifies a pointer to a XWindows structure.
9658%
9659%    o image: the image; returned from ReadImage.
9660%
9661%    o exception: return any errors or warnings in this structure.
9662%
9663*/
9664static MagickBooleanType XMatteEditImage(Display *display,
9665  XResourceInfo *resource_info,XWindows *windows,Image **image,
9666  ExceptionInfo *exception)
9667{
9668  static char
9669    matte[MaxTextExtent] = "0";
9670
9671  static const char
9672    *MatteEditMenu[] =
9673    {
9674      "Method",
9675      "Border Color",
9676      "Fuzz",
9677      "Matte Value",
9678      "Undo",
9679      "Help",
9680      "Dismiss",
9681      (char *) NULL
9682    };
9683
9684  static const ModeType
9685    MatteEditCommands[] =
9686    {
9687      MatteEditMethod,
9688      MatteEditBorderCommand,
9689      MatteEditFuzzCommand,
9690      MatteEditValueCommand,
9691      MatteEditUndoCommand,
9692      MatteEditHelpCommand,
9693      MatteEditDismissCommand
9694    };
9695
9696  static PaintMethod
9697    method = PointMethod;
9698
9699  static XColor
9700    border_color = { 0, 0, 0, 0, 0, 0 };
9701
9702  char
9703    command[MaxTextExtent],
9704    text[MaxTextExtent];
9705
9706  Cursor
9707    cursor;
9708
9709  int
9710    entry,
9711    id,
9712    x,
9713    x_offset,
9714    y,
9715    y_offset;
9716
9717  register int
9718    i;
9719
9720  register Quantum
9721    *q;
9722
9723  unsigned int
9724    height,
9725    width;
9726
9727  size_t
9728    state;
9729
9730  XEvent
9731    event;
9732
9733  /*
9734    Map Command widget.
9735  */
9736  (void) CloneString(&windows->command.name,"Matte Edit");
9737  windows->command.data=4;
9738  (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9739  (void) XMapRaised(display,windows->command.id);
9740  XClientMessage(display,windows->image.id,windows->im_protocols,
9741    windows->im_update_widget,CurrentTime);
9742  /*
9743    Make cursor.
9744  */
9745  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9746    resource_info->background_color,resource_info->foreground_color);
9747  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9748  /*
9749    Track pointer until button 1 is pressed.
9750  */
9751  XQueryPosition(display,windows->image.id,&x,&y);
9752  (void) XSelectInput(display,windows->image.id,
9753    windows->image.attributes.event_mask | PointerMotionMask);
9754  state=DefaultState;
9755  do
9756  {
9757    if (windows->info.mapped != MagickFalse)
9758      {
9759        /*
9760          Display pointer position.
9761        */
9762        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9763          x+windows->image.x,y+windows->image.y);
9764        XInfoWidget(display,windows,text);
9765      }
9766    /*
9767      Wait for next event.
9768    */
9769    XScreenEvent(display,windows,&event,exception);
9770    if (event.xany.window == windows->command.id)
9771      {
9772        /*
9773          Select a command from the Command widget.
9774        */
9775        id=XCommandWidget(display,windows,MatteEditMenu,&event);
9776        if (id < 0)
9777          {
9778            (void) XCheckDefineCursor(display,windows->image.id,cursor);
9779            continue;
9780          }
9781        switch (MatteEditCommands[id])
9782        {
9783          case MatteEditMethod:
9784          {
9785            char
9786              **methods;
9787
9788            /*
9789              Select a method from the pop-up menu.
9790            */
9791            methods=GetCommandOptions(MagickMethodOptions);
9792            if (methods == (char **) NULL)
9793              break;
9794            entry=XMenuWidget(display,windows,MatteEditMenu[id],
9795              (const char **) methods,command);
9796            if (entry >= 0)
9797              method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9798                MagickFalse,methods[entry]);
9799            methods=DestroyStringList(methods);
9800            break;
9801          }
9802          case MatteEditBorderCommand:
9803          {
9804            const char
9805              *ColorMenu[MaxNumberPens];
9806
9807            int
9808              pen_number;
9809
9810            /*
9811              Initialize menu selections.
9812            */
9813            for (i=0; i < (int) (MaxNumberPens-2); i++)
9814              ColorMenu[i]=resource_info->pen_colors[i];
9815            ColorMenu[MaxNumberPens-2]="Browser...";
9816            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9817            /*
9818              Select a pen color from the pop-up menu.
9819            */
9820            pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9821              (const char **) ColorMenu,command);
9822            if (pen_number < 0)
9823              break;
9824            if (pen_number == (MaxNumberPens-2))
9825              {
9826                static char
9827                  color_name[MaxTextExtent] = "gray";
9828
9829                /*
9830                  Select a pen color from a dialog.
9831                */
9832                resource_info->pen_colors[pen_number]=color_name;
9833                XColorBrowserWidget(display,windows,"Select",color_name);
9834                if (*color_name == '\0')
9835                  break;
9836              }
9837            /*
9838              Set border color.
9839            */
9840            (void) XParseColor(display,windows->map_info->colormap,
9841              resource_info->pen_colors[pen_number],&border_color);
9842            break;
9843          }
9844          case MatteEditFuzzCommand:
9845          {
9846            static char
9847              fuzz[MaxTextExtent];
9848
9849            static const char
9850              *FuzzMenu[] =
9851              {
9852                "0%",
9853                "2%",
9854                "5%",
9855                "10%",
9856                "15%",
9857                "Dialog...",
9858                (char *) NULL,
9859              };
9860
9861            /*
9862              Select a command from the pop-up menu.
9863            */
9864            entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9865              command);
9866            if (entry < 0)
9867              break;
9868            if (entry != 5)
9869              {
9870                (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
9871                  QuantumRange+1.0);
9872                break;
9873              }
9874            (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9875            (void) XDialogWidget(display,windows,"Ok",
9876              "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9877            if (*fuzz == '\0')
9878              break;
9879            (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
9880            (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
9881              1.0);
9882            break;
9883          }
9884          case MatteEditValueCommand:
9885          {
9886            static char
9887              message[MaxTextExtent];
9888
9889            static const char
9890              *MatteMenu[] =
9891              {
9892                "Opaque",
9893                "Transparent",
9894                "Dialog...",
9895                (char *) NULL,
9896              };
9897
9898            /*
9899              Select a command from the pop-up menu.
9900            */
9901            entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9902              command);
9903            if (entry < 0)
9904              break;
9905            if (entry != 2)
9906              {
9907                (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9908                  OpaqueAlpha);
9909                if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9910                  (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9911                    (Quantum) TransparentAlpha);
9912                break;
9913              }
9914            (void) FormatLocaleString(message,MaxTextExtent,
9915              "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9916              QuantumRange);
9917            (void) XDialogWidget(display,windows,"Matte",message,matte);
9918            if (*matte == '\0')
9919              break;
9920            break;
9921          }
9922          case MatteEditUndoCommand:
9923          {
9924            (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9925              image,exception);
9926            break;
9927          }
9928          case MatteEditHelpCommand:
9929          {
9930            XTextViewWidget(display,resource_info,windows,MagickFalse,
9931              "Help Viewer - Matte Edit",ImageMatteEditHelp);
9932            break;
9933          }
9934          case MatteEditDismissCommand:
9935          {
9936            /*
9937              Prematurely exit.
9938            */
9939            state|=EscapeState;
9940            state|=ExitState;
9941            break;
9942          }
9943          default:
9944            break;
9945        }
9946        (void) XCheckDefineCursor(display,windows->image.id,cursor);
9947        continue;
9948      }
9949    switch (event.type)
9950    {
9951      case ButtonPress:
9952      {
9953        if (event.xbutton.button != Button1)
9954          break;
9955        if ((event.xbutton.window != windows->image.id) &&
9956            (event.xbutton.window != windows->magnify.id))
9957          break;
9958        /*
9959          Update matte data.
9960        */
9961        x=event.xbutton.x;
9962        y=event.xbutton.y;
9963        (void) XMagickCommand(display,resource_info,windows,
9964          SaveToUndoBufferCommand,image,exception);
9965        state|=UpdateConfigurationState;
9966        break;
9967      }
9968      case ButtonRelease:
9969      {
9970        if (event.xbutton.button != Button1)
9971          break;
9972        if ((event.xbutton.window != windows->image.id) &&
9973            (event.xbutton.window != windows->magnify.id))
9974          break;
9975        /*
9976          Update colormap information.
9977        */
9978        x=event.xbutton.x;
9979        y=event.xbutton.y;
9980        XConfigureImageColormap(display,resource_info,windows,*image,exception);
9981        (void) XConfigureImage(display,resource_info,windows,*image,exception);
9982        XInfoWidget(display,windows,text);
9983        (void) XCheckDefineCursor(display,windows->image.id,cursor);
9984        state&=(~UpdateConfigurationState);
9985        break;
9986      }
9987      case Expose:
9988        break;
9989      case KeyPress:
9990      {
9991        char
9992          command[MaxTextExtent];
9993
9994        KeySym
9995          key_symbol;
9996
9997        if (event.xkey.window == windows->magnify.id)
9998          {
9999            Window
10000              window;
10001
10002            window=windows->magnify.id;
10003            while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
10004          }
10005        if (event.xkey.window != windows->image.id)
10006          break;
10007        /*
10008          Respond to a user key press.
10009        */
10010        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
10011          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10012        switch ((int) key_symbol)
10013        {
10014          case XK_Escape:
10015          case XK_F20:
10016          {
10017            /*
10018              Prematurely exit.
10019            */
10020            state|=ExitState;
10021            break;
10022          }
10023          case XK_F1:
10024          case XK_Help:
10025          {
10026            XTextViewWidget(display,resource_info,windows,MagickFalse,
10027              "Help Viewer - Matte Edit",ImageMatteEditHelp);
10028            break;
10029          }
10030          default:
10031          {
10032            (void) XBell(display,0);
10033            break;
10034          }
10035        }
10036        break;
10037      }
10038      case MotionNotify:
10039      {
10040        /*
10041          Map and unmap Info widget as cursor crosses its boundaries.
10042        */
10043        x=event.xmotion.x;
10044        y=event.xmotion.y;
10045        if (windows->info.mapped != MagickFalse)
10046          {
10047            if ((x < (int) (windows->info.x+windows->info.width)) &&
10048                (y < (int) (windows->info.y+windows->info.height)))
10049              (void) XWithdrawWindow(display,windows->info.id,
10050                windows->info.screen);
10051          }
10052        else
10053          if ((x > (int) (windows->info.x+windows->info.width)) ||
10054              (y > (int) (windows->info.y+windows->info.height)))
10055            (void) XMapWindow(display,windows->info.id);
10056        break;
10057      }
10058      default:
10059        break;
10060    }
10061    if (event.xany.window == windows->magnify.id)
10062      {
10063        x=windows->magnify.x-windows->image.x;
10064        y=windows->magnify.y-windows->image.y;
10065      }
10066    x_offset=x;
10067    y_offset=y;
10068    if ((state & UpdateConfigurationState) != 0)
10069      {
10070        CacheView
10071          *image_view;
10072
10073        int
10074          x,
10075          y;
10076
10077        /*
10078          Matte edit is relative to image configuration.
10079        */
10080        (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
10081          MagickTrue);
10082        XPutPixel(windows->image.ximage,x_offset,y_offset,
10083          windows->pixel_info->background_color.pixel);
10084        width=(unsigned int) (*image)->columns;
10085        height=(unsigned int) (*image)->rows;
10086        x=0;
10087        y=0;
10088        if (windows->image.crop_geometry != (char *) NULL)
10089          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
10090            &height);
10091        x_offset=(int) (width*(windows->image.x+x_offset)/
10092          windows->image.ximage->width+x);
10093        y_offset=(int) (height*(windows->image.y+y_offset)/
10094          windows->image.ximage->height+y);
10095        if ((x_offset < 0) || (y_offset < 0))
10096          continue;
10097        if ((x_offset >= (int) (*image)->columns) ||
10098            (y_offset >= (int) (*image)->rows))
10099          continue;
10100        if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10101          return(MagickFalse);
10102        (*image)->matte=MagickTrue;
10103        image_view=AcquireCacheView(*image);
10104        switch (method)
10105        {
10106          case PointMethod:
10107          default:
10108          {
10109            /*
10110              Update matte information using point algorithm.
10111            */
10112            q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10113              (ssize_t) y_offset,1,1,exception);
10114            if (q == (Quantum *) NULL)
10115              break;
10116            SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10117            (void) SyncCacheViewAuthenticPixels(image_view,exception);
10118            break;
10119          }
10120          case ReplaceMethod:
10121          {
10122            PixelInfo
10123              pixel,
10124              target;
10125
10126            Quantum
10127              virtual_pixel[CompositePixelChannel];
10128
10129            /*
10130              Update matte information using replace algorithm.
10131            */
10132            (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
10133              (ssize_t) y_offset,virtual_pixel,exception);
10134            target.red=virtual_pixel[RedPixelChannel];
10135            target.green=virtual_pixel[GreenPixelChannel];
10136            target.blue=virtual_pixel[BluePixelChannel];
10137            target.alpha=virtual_pixel[AlphaPixelChannel];
10138            for (y=0; y < (int) (*image)->rows; y++)
10139            {
10140              q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10141                (*image)->columns,1,exception);
10142              if (q == (Quantum *) NULL)
10143                break;
10144              for (x=0; x < (int) (*image)->columns; x++)
10145              {
10146                GetPixelInfoPixel(*image,q,&pixel);
10147                if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
10148                  SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10149                q+=GetPixelChannels(*image);
10150              }
10151              if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10152                break;
10153            }
10154            break;
10155          }
10156          case FloodfillMethod:
10157          case FillToBorderMethod:
10158          {
10159            ChannelType
10160              channel_mask;
10161
10162            DrawInfo
10163              *draw_info;
10164
10165            PixelInfo
10166              target;
10167
10168            /*
10169              Update matte information using floodfill algorithm.
10170            */
10171            (void) GetOneVirtualPixelInfo(*image,
10172              GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
10173              y_offset,&target,exception);
10174            if (method == FillToBorderMethod)
10175              {
10176                target.red=(MagickRealType) ScaleShortToQuantum(
10177                  border_color.red);
10178                target.green=(MagickRealType) ScaleShortToQuantum(
10179                  border_color.green);
10180                target.blue=(MagickRealType) ScaleShortToQuantum(
10181                  border_color.blue);
10182              }
10183            draw_info=CloneDrawInfo(resource_info->image_info,
10184              (DrawInfo *) NULL);
10185            draw_info->fill.alpha=ClampToQuantum(StringToDouble(matte,
10186              (char **) NULL));
10187            channel_mask=SetPixelChannelMask(*image,AlphaChannel);
10188            (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10189              x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
10190              MagickFalse : MagickTrue,exception);
10191            (void) SetPixelChannelMapMask(*image,channel_mask);
10192            draw_info=DestroyDrawInfo(draw_info);
10193            break;
10194          }
10195          case ResetMethod:
10196          {
10197            /*
10198              Update matte information using reset algorithm.
10199            */
10200            if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10201              return(MagickFalse);
10202            for (y=0; y < (int) (*image)->rows; y++)
10203            {
10204              q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10205                (*image)->columns,1,exception);
10206              if (q == (Quantum *) NULL)
10207                break;
10208              for (x=0; x < (int) (*image)->columns; x++)
10209              {
10210                SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10211                q+=GetPixelChannels(*image);
10212              }
10213              if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10214                break;
10215            }
10216            if (StringToLong(matte) == (long) OpaqueAlpha)
10217              (*image)->matte=MagickFalse;
10218            break;
10219          }
10220        }
10221        image_view=DestroyCacheView(image_view);
10222        state&=(~UpdateConfigurationState);
10223      }
10224  } while ((state & ExitState) == 0);
10225  (void) XSelectInput(display,windows->image.id,
10226    windows->image.attributes.event_mask);
10227  XSetCursorState(display,windows,MagickFalse);
10228  (void) XFreeCursor(display,cursor);
10229  return(MagickTrue);
10230}
10231
10232/*
10233%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10234%                                                                             %
10235%                                                                             %
10236%                                                                             %
10237+   X O p e n I m a g e                                                       %
10238%                                                                             %
10239%                                                                             %
10240%                                                                             %
10241%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10242%
10243%  XOpenImage() loads an image from a file.
10244%
10245%  The format of the XOpenImage method is:
10246%
10247%     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10248%       XWindows *windows,const unsigned int command)
10249%
10250%  A description of each parameter follows:
10251%
10252%    o display: Specifies a connection to an X server; returned from
10253%      XOpenDisplay.
10254%
10255%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10256%
10257%    o windows: Specifies a pointer to a XWindows structure.
10258%
10259%    o command: A value other than zero indicates that the file is selected
10260%      from the command line argument list.
10261%
10262*/
10263static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10264  XWindows *windows,const MagickBooleanType command)
10265{
10266  const MagickInfo
10267    *magick_info;
10268
10269  ExceptionInfo
10270    *exception;
10271
10272  Image
10273    *nexus;
10274
10275  ImageInfo
10276    *image_info;
10277
10278  static char
10279    filename[MaxTextExtent] = "\0";
10280
10281  /*
10282    Request file name from user.
10283  */
10284  if (command == MagickFalse)
10285    XFileBrowserWidget(display,windows,"Open",filename);
10286  else
10287    {
10288      char
10289        **filelist,
10290        **files;
10291
10292      int
10293        count,
10294        status;
10295
10296      register int
10297        i,
10298        j;
10299
10300      /*
10301        Select next image from the command line.
10302      */
10303      status=XGetCommand(display,windows->image.id,&files,&count);
10304      if (status == 0)
10305        {
10306          ThrowXWindowFatalException(XServerError,"UnableToGetProperty","...");
10307          return((Image *) NULL);
10308        }
10309      filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10310      if (filelist == (char **) NULL)
10311        {
10312          ThrowXWindowFatalException(ResourceLimitError,
10313            "MemoryAllocationFailed","...");
10314          (void) XFreeStringList(files);
10315          return((Image *) NULL);
10316        }
10317      j=0;
10318      for (i=1; i < count; i++)
10319        if (*files[i] != '-')
10320          filelist[j++]=files[i];
10321      filelist[j]=(char *) NULL;
10322      XListBrowserWidget(display,windows,&windows->widget,
10323        (const char **) filelist,"Load","Select Image to Load:",filename);
10324      filelist=(char **) RelinquishMagickMemory(filelist);
10325      (void) XFreeStringList(files);
10326    }
10327  if (*filename == '\0')
10328    return((Image *) NULL);
10329  image_info=CloneImageInfo(resource_info->image_info);
10330  (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10331    (void *) NULL);
10332  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10333  exception=AcquireExceptionInfo();
10334  (void) SetImageInfo(image_info,0,exception);
10335  if (LocaleCompare(image_info->magick,"X") == 0)
10336    {
10337      char
10338        seconds[MaxTextExtent];
10339
10340      /*
10341        User may want to delay the X server screen grab.
10342      */
10343      (void) CopyMagickString(seconds,"0",MaxTextExtent);
10344      (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10345        seconds);
10346      if (*seconds == '\0')
10347        return((Image *) NULL);
10348      XDelay(display,(size_t) (1000*StringToLong(seconds)));
10349    }
10350  magick_info=GetMagickInfo(image_info->magick,exception);
10351  if ((magick_info != (const MagickInfo *) NULL) &&
10352      (magick_info->raw != MagickFalse))
10353    {
10354      char
10355        geometry[MaxTextExtent];
10356
10357      /*
10358        Request image size from the user.
10359      */
10360      (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10361      if (image_info->size != (char *) NULL)
10362        (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10363      (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10364        geometry);
10365      (void) CloneString(&image_info->size,geometry);
10366    }
10367  /*
10368    Load the image.
10369  */
10370  XSetCursorState(display,windows,MagickTrue);
10371  XCheckRefreshWindows(display,windows);
10372  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10373  nexus=ReadImage(image_info,exception);
10374  CatchException(exception);
10375  XSetCursorState(display,windows,MagickFalse);
10376  if (nexus != (Image *) NULL)
10377    XClientMessage(display,windows->image.id,windows->im_protocols,
10378      windows->im_next_image,CurrentTime);
10379  else
10380    {
10381      char
10382        *text,
10383        **textlist;
10384
10385      /*
10386        Unknown image format.
10387      */
10388      text=FileToString(filename,~0,exception);
10389      if (text == (char *) NULL)
10390        return((Image *) NULL);
10391      textlist=StringToList(text);
10392      if (textlist != (char **) NULL)
10393        {
10394          char
10395            title[MaxTextExtent];
10396
10397          register int
10398            i;
10399
10400          (void) FormatLocaleString(title,MaxTextExtent,
10401            "Unknown format: %s",filename);
10402          XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10403            (const char **) textlist);
10404          for (i=0; textlist[i] != (char *) NULL; i++)
10405            textlist[i]=DestroyString(textlist[i]);
10406          textlist=(char **) RelinquishMagickMemory(textlist);
10407        }
10408      text=DestroyString(text);
10409    }
10410  exception=DestroyExceptionInfo(exception);
10411  image_info=DestroyImageInfo(image_info);
10412  return(nexus);
10413}
10414
10415/*
10416%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10417%                                                                             %
10418%                                                                             %
10419%                                                                             %
10420+   X P a n I m a g e                                                         %
10421%                                                                             %
10422%                                                                             %
10423%                                                                             %
10424%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10425%
10426%  XPanImage() pans the image until the mouse button is released.
10427%
10428%  The format of the XPanImage method is:
10429%
10430%      void XPanImage(Display *display,XWindows *windows,XEvent *event,
10431%        ExceptionInfo *exception)
10432%
10433%  A description of each parameter follows:
10434%
10435%    o display: Specifies a connection to an X server;  returned from
10436%      XOpenDisplay.
10437%
10438%    o windows: Specifies a pointer to a XWindows structure.
10439%
10440%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10441%      the entire image is refreshed.
10442%
10443%    o exception: return any errors or warnings in this structure.
10444%
10445*/
10446static void XPanImage(Display *display,XWindows *windows,XEvent *event,
10447  ExceptionInfo *exception)
10448{
10449  char
10450    text[MaxTextExtent];
10451
10452  Cursor
10453    cursor;
10454
10455  MagickRealType
10456    x_factor,
10457    y_factor;
10458
10459  RectangleInfo
10460    pan_info;
10461
10462  size_t
10463    state;
10464
10465  /*
10466    Define cursor.
10467  */
10468  if ((windows->image.ximage->width > (int) windows->image.width) &&
10469      (windows->image.ximage->height > (int) windows->image.height))
10470    cursor=XCreateFontCursor(display,XC_fleur);
10471  else
10472    if (windows->image.ximage->width > (int) windows->image.width)
10473      cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10474    else
10475      if (windows->image.ximage->height > (int) windows->image.height)
10476        cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10477      else
10478        cursor=XCreateFontCursor(display,XC_arrow);
10479  (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10480  /*
10481    Pan image as pointer moves until the mouse button is released.
10482  */
10483  x_factor=(MagickRealType) windows->image.ximage->width/windows->pan.width;
10484  y_factor=(MagickRealType) windows->image.ximage->height/windows->pan.height;
10485  pan_info.width=windows->pan.width*windows->image.width/
10486    windows->image.ximage->width;
10487  pan_info.height=windows->pan.height*windows->image.height/
10488    windows->image.ximage->height;
10489  pan_info.x=0;
10490  pan_info.y=0;
10491  state=UpdateConfigurationState;
10492  do
10493  {
10494    switch (event->type)
10495    {
10496      case ButtonPress:
10497      {
10498        /*
10499          User choose an initial pan location.
10500        */
10501        pan_info.x=(ssize_t) event->xbutton.x;
10502        pan_info.y=(ssize_t) event->xbutton.y;
10503        state|=UpdateConfigurationState;
10504        break;
10505      }
10506      case ButtonRelease:
10507      {
10508        /*
10509          User has finished panning the image.
10510        */
10511        pan_info.x=(ssize_t) event->xbutton.x;
10512        pan_info.y=(ssize_t) event->xbutton.y;
10513        state|=UpdateConfigurationState | ExitState;
10514        break;
10515      }
10516      case MotionNotify:
10517      {
10518        pan_info.x=(ssize_t) event->xmotion.x;
10519        pan_info.y=(ssize_t) event->xmotion.y;
10520        state|=UpdateConfigurationState;
10521      }
10522      default:
10523        break;
10524    }
10525    if ((state & UpdateConfigurationState) != 0)
10526      {
10527        /*
10528          Check boundary conditions.
10529        */
10530        if (pan_info.x < (ssize_t) (pan_info.width/2))
10531          pan_info.x=0;
10532        else
10533          pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10534        if (pan_info.x < 0)
10535          pan_info.x=0;
10536        else
10537          if ((int) (pan_info.x+windows->image.width) >
10538              windows->image.ximage->width)
10539            pan_info.x=(ssize_t)
10540              (windows->image.ximage->width-windows->image.width);
10541        if (pan_info.y < (ssize_t) (pan_info.height/2))
10542          pan_info.y=0;
10543        else
10544          pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10545        if (pan_info.y < 0)
10546          pan_info.y=0;
10547        else
10548          if ((int) (pan_info.y+windows->image.height) >
10549              windows->image.ximage->height)
10550            pan_info.y=(ssize_t)
10551              (windows->image.ximage->height-windows->image.height);
10552        if ((windows->image.x != (int) pan_info.x) ||
10553            (windows->image.y != (int) pan_info.y))
10554          {
10555            /*
10556              Display image pan offset.
10557            */
10558            windows->image.x=(int) pan_info.x;
10559            windows->image.y=(int) pan_info.y;
10560            (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
10561              windows->image.width,windows->image.height,windows->image.x,
10562              windows->image.y);
10563            XInfoWidget(display,windows,text);
10564            /*
10565              Refresh Image window.
10566            */
10567            XDrawPanRectangle(display,windows);
10568            XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10569          }
10570        state&=(~UpdateConfigurationState);
10571      }
10572    /*
10573      Wait for next event.
10574    */
10575    if ((state & ExitState) == 0)
10576      XScreenEvent(display,windows,event,exception);
10577  } while ((state & ExitState) == 0);
10578  /*
10579    Restore cursor.
10580  */
10581  (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10582  (void) XFreeCursor(display,cursor);
10583  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10584}
10585
10586/*
10587%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10588%                                                                             %
10589%                                                                             %
10590%                                                                             %
10591+   X P a s t e I m a g e                                                     %
10592%                                                                             %
10593%                                                                             %
10594%                                                                             %
10595%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10596%
10597%  XPasteImage() pastes an image previously saved with XCropImage in the X
10598%  window image at a location the user chooses with the pointer.
10599%
10600%  The format of the XPasteImage method is:
10601%
10602%      MagickBooleanType XPasteImage(Display *display,
10603%        XResourceInfo *resource_info,XWindows *windows,Image *image,
10604%        ExceptionInfo *exception)
10605%
10606%  A description of each parameter follows:
10607%
10608%    o display: Specifies a connection to an X server;  returned from
10609%      XOpenDisplay.
10610%
10611%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10612%
10613%    o windows: Specifies a pointer to a XWindows structure.
10614%
10615%    o image: the image; returned from ReadImage.
10616%
10617%    o exception: return any errors or warnings in this structure.
10618%
10619*/
10620static MagickBooleanType XPasteImage(Display *display,
10621  XResourceInfo *resource_info,XWindows *windows,Image *image,
10622  ExceptionInfo *exception)
10623{
10624  static const char
10625    *PasteMenu[] =
10626    {
10627      "Operator",
10628      "Help",
10629      "Dismiss",
10630      (char *) NULL
10631    };
10632
10633  static const ModeType
10634    PasteCommands[] =
10635    {
10636      PasteOperatorsCommand,
10637      PasteHelpCommand,
10638      PasteDismissCommand
10639    };
10640
10641  static CompositeOperator
10642    compose = CopyCompositeOp;
10643
10644  char
10645    text[MaxTextExtent];
10646
10647  Cursor
10648    cursor;
10649
10650  Image
10651    *paste_image;
10652
10653  int
10654    entry,
10655    id,
10656    x,
10657    y;
10658
10659  MagickRealType
10660    scale_factor;
10661
10662  RectangleInfo
10663    highlight_info,
10664    paste_info;
10665
10666  unsigned int
10667    height,
10668    width;
10669
10670  size_t
10671    state;
10672
10673  XEvent
10674    event;
10675
10676  /*
10677    Copy image.
10678  */
10679  if (resource_info->copy_image == (Image *) NULL)
10680    return(MagickFalse);
10681  paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,exception);
10682  /*
10683    Map Command widget.
10684  */
10685  (void) CloneString(&windows->command.name,"Paste");
10686  windows->command.data=1;
10687  (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10688  (void) XMapRaised(display,windows->command.id);
10689  XClientMessage(display,windows->image.id,windows->im_protocols,
10690    windows->im_update_widget,CurrentTime);
10691  /*
10692    Track pointer until button 1 is pressed.
10693  */
10694  XSetCursorState(display,windows,MagickFalse);
10695  XQueryPosition(display,windows->image.id,&x,&y);
10696  (void) XSelectInput(display,windows->image.id,
10697    windows->image.attributes.event_mask | PointerMotionMask);
10698  paste_info.x=(ssize_t) windows->image.x+x;
10699  paste_info.y=(ssize_t) windows->image.y+y;
10700  paste_info.width=0;
10701  paste_info.height=0;
10702  cursor=XCreateFontCursor(display,XC_ul_angle);
10703  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10704  state=DefaultState;
10705  do
10706  {
10707    if (windows->info.mapped != MagickFalse)
10708      {
10709        /*
10710          Display pointer position.
10711        */
10712        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
10713          (long) paste_info.x,(long) paste_info.y);
10714        XInfoWidget(display,windows,text);
10715      }
10716    highlight_info=paste_info;
10717    highlight_info.x=paste_info.x-windows->image.x;
10718    highlight_info.y=paste_info.y-windows->image.y;
10719    XHighlightRectangle(display,windows->image.id,
10720      windows->image.highlight_context,&highlight_info);
10721    /*
10722      Wait for next event.
10723    */
10724    XScreenEvent(display,windows,&event,exception);
10725    XHighlightRectangle(display,windows->image.id,
10726      windows->image.highlight_context,&highlight_info);
10727    if (event.xany.window == windows->command.id)
10728      {
10729        /*
10730          Select a command from the Command widget.
10731        */
10732        id=XCommandWidget(display,windows,PasteMenu,&event);
10733        if (id < 0)
10734          continue;
10735        switch (PasteCommands[id])
10736        {
10737          case PasteOperatorsCommand:
10738          {
10739            char
10740              command[MaxTextExtent],
10741              **operators;
10742
10743            /*
10744              Select a command from the pop-up menu.
10745            */
10746            operators=GetCommandOptions(MagickComposeOptions);
10747            if (operators == (char **) NULL)
10748              break;
10749            entry=XMenuWidget(display,windows,PasteMenu[id],
10750              (const char **) operators,command);
10751            if (entry >= 0)
10752              compose=(CompositeOperator) ParseCommandOption(
10753                MagickComposeOptions,MagickFalse,operators[entry]);
10754            operators=DestroyStringList(operators);
10755            break;
10756          }
10757          case PasteHelpCommand:
10758          {
10759            XTextViewWidget(display,resource_info,windows,MagickFalse,
10760              "Help Viewer - Image Composite",ImagePasteHelp);
10761            break;
10762          }
10763          case PasteDismissCommand:
10764          {
10765            /*
10766              Prematurely exit.
10767            */
10768            state|=EscapeState;
10769            state|=ExitState;
10770            break;
10771          }
10772          default:
10773            break;
10774        }
10775        continue;
10776      }
10777    switch (event.type)
10778    {
10779      case ButtonPress:
10780      {
10781        if (image->debug != MagickFalse)
10782          (void) LogMagickEvent(X11Event,GetMagickModule(),
10783            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10784            event.xbutton.button,event.xbutton.x,event.xbutton.y);
10785        if (event.xbutton.button != Button1)
10786          break;
10787        if (event.xbutton.window != windows->image.id)
10788          break;
10789        /*
10790          Paste rectangle is relative to image configuration.
10791        */
10792        width=(unsigned int) image->columns;
10793        height=(unsigned int) image->rows;
10794        x=0;
10795        y=0;
10796        if (windows->image.crop_geometry != (char *) NULL)
10797          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10798            &width,&height);
10799        scale_factor=(MagickRealType) windows->image.ximage->width/width;
10800        paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10801        scale_factor=(MagickRealType) windows->image.ximage->height/height;
10802        paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10803        (void) XCheckDefineCursor(display,windows->image.id,cursor);
10804        paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10805        paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10806        break;
10807      }
10808      case ButtonRelease:
10809      {
10810        if (image->debug != MagickFalse)
10811          (void) LogMagickEvent(X11Event,GetMagickModule(),
10812            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10813            event.xbutton.button,event.xbutton.x,event.xbutton.y);
10814        if (event.xbutton.button != Button1)
10815          break;
10816        if (event.xbutton.window != windows->image.id)
10817          break;
10818        if ((paste_info.width != 0) && (paste_info.height != 0))
10819          {
10820            /*
10821              User has selected the location of the paste image.
10822            */
10823            paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10824            paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10825            state|=ExitState;
10826          }
10827        break;
10828      }
10829      case Expose:
10830        break;
10831      case KeyPress:
10832      {
10833        char
10834          command[MaxTextExtent];
10835
10836        KeySym
10837          key_symbol;
10838
10839        int
10840          length;
10841
10842        if (event.xkey.window != windows->image.id)
10843          break;
10844        /*
10845          Respond to a user key press.
10846        */
10847        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10848          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10849        *(command+length)='\0';
10850        if (image->debug != MagickFalse)
10851          (void) LogMagickEvent(X11Event,GetMagickModule(),
10852            "Key press: 0x%lx (%s)",(long) key_symbol,command);
10853        switch ((int) key_symbol)
10854        {
10855          case XK_Escape:
10856          case XK_F20:
10857          {
10858            /*
10859              Prematurely exit.
10860            */
10861            paste_image=DestroyImage(paste_image);
10862            state|=EscapeState;
10863            state|=ExitState;
10864            break;
10865          }
10866          case XK_F1:
10867          case XK_Help:
10868          {
10869            (void) XSetFunction(display,windows->image.highlight_context,
10870              GXcopy);
10871            XTextViewWidget(display,resource_info,windows,MagickFalse,
10872              "Help Viewer - Image Composite",ImagePasteHelp);
10873            (void) XSetFunction(display,windows->image.highlight_context,
10874              GXinvert);
10875            break;
10876          }
10877          default:
10878          {
10879            (void) XBell(display,0);
10880            break;
10881          }
10882        }
10883        break;
10884      }
10885      case MotionNotify:
10886      {
10887        /*
10888          Map and unmap Info widget as text cursor crosses its boundaries.
10889        */
10890        x=event.xmotion.x;
10891        y=event.xmotion.y;
10892        if (windows->info.mapped != MagickFalse)
10893          {
10894            if ((x < (int) (windows->info.x+windows->info.width)) &&
10895                (y < (int) (windows->info.y+windows->info.height)))
10896              (void) XWithdrawWindow(display,windows->info.id,
10897                windows->info.screen);
10898          }
10899        else
10900          if ((x > (int) (windows->info.x+windows->info.width)) ||
10901              (y > (int) (windows->info.y+windows->info.height)))
10902            (void) XMapWindow(display,windows->info.id);
10903        paste_info.x=(ssize_t) windows->image.x+x;
10904        paste_info.y=(ssize_t) windows->image.y+y;
10905        break;
10906      }
10907      default:
10908      {
10909        if (image->debug != MagickFalse)
10910          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10911            event.type);
10912        break;
10913      }
10914    }
10915  } while ((state & ExitState) == 0);
10916  (void) XSelectInput(display,windows->image.id,
10917    windows->image.attributes.event_mask);
10918  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10919  XSetCursorState(display,windows,MagickFalse);
10920  (void) XFreeCursor(display,cursor);
10921  if ((state & EscapeState) != 0)
10922    return(MagickTrue);
10923  /*
10924    Image pasting is relative to image configuration.
10925  */
10926  XSetCursorState(display,windows,MagickTrue);
10927  XCheckRefreshWindows(display,windows);
10928  width=(unsigned int) image->columns;
10929  height=(unsigned int) image->rows;
10930  x=0;
10931  y=0;
10932  if (windows->image.crop_geometry != (char *) NULL)
10933    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10934  scale_factor=(MagickRealType) width/windows->image.ximage->width;
10935  paste_info.x+=x;
10936  paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10937  paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10938  scale_factor=(MagickRealType) height/windows->image.ximage->height;
10939  paste_info.y+=y;
10940  paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10941  paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10942  /*
10943    Paste image with X Image window.
10944  */
10945  (void) CompositeImage(image,compose,paste_image,paste_info.x,paste_info.y,
10946    exception);
10947  paste_image=DestroyImage(paste_image);
10948  XSetCursorState(display,windows,MagickFalse);
10949  /*
10950    Update image colormap.
10951  */
10952  XConfigureImageColormap(display,resource_info,windows,image,exception);
10953  (void) XConfigureImage(display,resource_info,windows,image,exception);
10954  return(MagickTrue);
10955}
10956
10957/*
10958%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10959%                                                                             %
10960%                                                                             %
10961%                                                                             %
10962+   X P r i n t I m a g e                                                     %
10963%                                                                             %
10964%                                                                             %
10965%                                                                             %
10966%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10967%
10968%  XPrintImage() prints an image to a Postscript printer.
10969%
10970%  The format of the XPrintImage method is:
10971%
10972%      MagickBooleanType XPrintImage(Display *display,
10973%        XResourceInfo *resource_info,XWindows *windows,Image *image,
10974%        ExceptionInfo *exception)
10975%
10976%  A description of each parameter follows:
10977%
10978%    o display: Specifies a connection to an X server; returned from
10979%      XOpenDisplay.
10980%
10981%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10982%
10983%    o windows: Specifies a pointer to a XWindows structure.
10984%
10985%    o image: the image.
10986%
10987%    o exception: return any errors or warnings in this structure.
10988%
10989*/
10990static MagickBooleanType XPrintImage(Display *display,
10991  XResourceInfo *resource_info,XWindows *windows,Image *image,
10992  ExceptionInfo *exception)
10993{
10994  char
10995    filename[MaxTextExtent],
10996    geometry[MaxTextExtent];
10997
10998  Image
10999    *print_image;
11000
11001  ImageInfo
11002    *image_info;
11003
11004  MagickStatusType
11005    status;
11006
11007  /*
11008    Request Postscript page geometry from user.
11009  */
11010  image_info=CloneImageInfo(resource_info->image_info);
11011  (void) FormatLocaleString(geometry,MaxTextExtent,"Letter");
11012  if (image_info->page != (char *) NULL)
11013    (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
11014  XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
11015    "Select Postscript Page Geometry:",geometry);
11016  if (*geometry == '\0')
11017    return(MagickTrue);
11018  image_info->page=GetPageGeometry(geometry);
11019  /*
11020    Apply image transforms.
11021  */
11022  XSetCursorState(display,windows,MagickTrue);
11023  XCheckRefreshWindows(display,windows);
11024  print_image=CloneImage(image,0,0,MagickTrue,exception);
11025  if (print_image == (Image *) NULL)
11026    return(MagickFalse);
11027  (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
11028    windows->image.ximage->width,windows->image.ximage->height);
11029  (void) TransformImage(&print_image,windows->image.crop_geometry,geometry,
11030    exception);
11031  /*
11032    Print image.
11033  */
11034  (void) AcquireUniqueFilename(filename);
11035  (void) FormatLocaleString(print_image->filename,MaxTextExtent,"print:%s",
11036    filename);
11037  status=WriteImage(image_info,print_image,exception);
11038  (void) RelinquishUniqueFileResource(filename);
11039  print_image=DestroyImage(print_image);
11040  image_info=DestroyImageInfo(image_info);
11041  XSetCursorState(display,windows,MagickFalse);
11042  return(status != 0 ? MagickTrue : MagickFalse);
11043}
11044
11045/*
11046%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11047%                                                                             %
11048%                                                                             %
11049%                                                                             %
11050+   X R O I I m a g e                                                         %
11051%                                                                             %
11052%                                                                             %
11053%                                                                             %
11054%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11055%
11056%  XROIImage() applies an image processing technique to a region of interest.
11057%
11058%  The format of the XROIImage method is:
11059%
11060%      MagickBooleanType XROIImage(Display *display,
11061%        XResourceInfo *resource_info,XWindows *windows,Image **image,
11062%        ExceptionInfo *exception)
11063%
11064%  A description of each parameter follows:
11065%
11066%    o display: Specifies a connection to an X server; returned from
11067%      XOpenDisplay.
11068%
11069%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11070%
11071%    o windows: Specifies a pointer to a XWindows structure.
11072%
11073%    o image: the image; returned from ReadImage.
11074%
11075%    o exception: return any errors or warnings in this structure.
11076%
11077*/
11078static MagickBooleanType XROIImage(Display *display,
11079  XResourceInfo *resource_info,XWindows *windows,Image **image,
11080  ExceptionInfo *exception)
11081{
11082#define ApplyMenus  7
11083
11084  static const char
11085    *ROIMenu[] =
11086    {
11087      "Help",
11088      "Dismiss",
11089      (char *) NULL
11090    },
11091    *ApplyMenu[] =
11092    {
11093      "File",
11094      "Edit",
11095      "Transform",
11096      "Enhance",
11097      "Effects",
11098      "F/X",
11099      "Miscellany",
11100      "Help",
11101      "Dismiss",
11102      (char *) NULL
11103    },
11104    *FileMenu[] =
11105    {
11106      "Save...",
11107      "Print...",
11108      (char *) NULL
11109    },
11110    *EditMenu[] =
11111    {
11112      "Undo",
11113      "Redo",
11114      (char *) NULL
11115    },
11116    *TransformMenu[] =
11117    {
11118      "Flop",
11119      "Flip",
11120      "Rotate Right",
11121      "Rotate Left",
11122      (char *) NULL
11123    },
11124    *EnhanceMenu[] =
11125    {
11126      "Hue...",
11127      "Saturation...",
11128      "Brightness...",
11129      "Gamma...",
11130      "Spiff",
11131      "Dull",
11132      "Contrast Stretch...",
11133      "Sigmoidal Contrast...",
11134      "Normalize",
11135      "Equalize",
11136      "Negate",
11137      "Grayscale",
11138      "Map...",
11139      "Quantize...",
11140      (char *) NULL
11141    },
11142    *EffectsMenu[] =
11143    {
11144      "Despeckle",
11145      "Emboss",
11146      "Reduce Noise",
11147      "Add Noise",
11148      "Sharpen...",
11149      "Blur...",
11150      "Threshold...",
11151      "Edge Detect...",
11152      "Spread...",
11153      "Shade...",
11154      "Raise...",
11155      "Segment...",
11156      (char *) NULL
11157    },
11158    *FXMenu[] =
11159    {
11160      "Solarize...",
11161      "Sepia Tone...",
11162      "Swirl...",
11163      "Implode...",
11164      "Vignette...",
11165      "Wave...",
11166      "Oil Paint...",
11167      "Charcoal Draw...",
11168      (char *) NULL
11169    },
11170    *MiscellanyMenu[] =
11171    {
11172      "Image Info",
11173      "Zoom Image",
11174      "Show Preview...",
11175      "Show Histogram",
11176      "Show Matte",
11177      (char *) NULL
11178    };
11179
11180  static const char
11181    **Menus[ApplyMenus] =
11182    {
11183      FileMenu,
11184      EditMenu,
11185      TransformMenu,
11186      EnhanceMenu,
11187      EffectsMenu,
11188      FXMenu,
11189      MiscellanyMenu
11190    };
11191
11192  static const CommandType
11193    ApplyCommands[] =
11194    {
11195      NullCommand,
11196      NullCommand,
11197      NullCommand,
11198      NullCommand,
11199      NullCommand,
11200      NullCommand,
11201      NullCommand,
11202      HelpCommand,
11203      QuitCommand
11204    },
11205    FileCommands[] =
11206    {
11207      SaveCommand,
11208      PrintCommand
11209    },
11210    EditCommands[] =
11211    {
11212      UndoCommand,
11213      RedoCommand
11214    },
11215    TransformCommands[] =
11216    {
11217      FlopCommand,
11218      FlipCommand,
11219      RotateRightCommand,
11220      RotateLeftCommand
11221    },
11222    EnhanceCommands[] =
11223    {
11224      HueCommand,
11225      SaturationCommand,
11226      BrightnessCommand,
11227      GammaCommand,
11228      SpiffCommand,
11229      DullCommand,
11230      ContrastStretchCommand,
11231      SigmoidalContrastCommand,
11232      NormalizeCommand,
11233      EqualizeCommand,
11234      NegateCommand,
11235      GrayscaleCommand,
11236      MapCommand,
11237      QuantizeCommand
11238    },
11239    EffectsCommands[] =
11240    {
11241      DespeckleCommand,
11242      EmbossCommand,
11243      ReduceNoiseCommand,
11244      AddNoiseCommand,
11245      SharpenCommand,
11246      BlurCommand,
11247      EdgeDetectCommand,
11248      SpreadCommand,
11249      ShadeCommand,
11250      RaiseCommand,
11251      SegmentCommand
11252    },
11253    FXCommands[] =
11254    {
11255      SolarizeCommand,
11256      SepiaToneCommand,
11257      SwirlCommand,
11258      ImplodeCommand,
11259      VignetteCommand,
11260      WaveCommand,
11261      OilPaintCommand,
11262      CharcoalDrawCommand
11263    },
11264    MiscellanyCommands[] =
11265    {
11266      InfoCommand,
11267      ZoomCommand,
11268      ShowPreviewCommand,
11269      ShowHistogramCommand,
11270      ShowMatteCommand
11271    },
11272    ROICommands[] =
11273    {
11274      ROIHelpCommand,
11275      ROIDismissCommand
11276    };
11277
11278  static const CommandType
11279    *Commands[ApplyMenus] =
11280    {
11281      FileCommands,
11282      EditCommands,
11283      TransformCommands,
11284      EnhanceCommands,
11285      EffectsCommands,
11286      FXCommands,
11287      MiscellanyCommands
11288    };
11289
11290  char
11291    command[MaxTextExtent],
11292    text[MaxTextExtent];
11293
11294  CommandType
11295    command_type;
11296
11297  Cursor
11298    cursor;
11299
11300  Image
11301    *roi_image;
11302
11303  int
11304    entry,
11305    id,
11306    x,
11307    y;
11308
11309  MagickRealType
11310    scale_factor;
11311
11312  MagickProgressMonitor
11313    progress_monitor;
11314
11315  RectangleInfo
11316    crop_info,
11317    highlight_info,
11318    roi_info;
11319
11320  unsigned int
11321    height,
11322    width;
11323
11324  size_t
11325    state;
11326
11327  XEvent
11328    event;
11329
11330  /*
11331    Map Command widget.
11332  */
11333  (void) CloneString(&windows->command.name,"ROI");
11334  windows->command.data=0;
11335  (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11336  (void) XMapRaised(display,windows->command.id);
11337  XClientMessage(display,windows->image.id,windows->im_protocols,
11338    windows->im_update_widget,CurrentTime);
11339  /*
11340    Track pointer until button 1 is pressed.
11341  */
11342  XQueryPosition(display,windows->image.id,&x,&y);
11343  (void) XSelectInput(display,windows->image.id,
11344    windows->image.attributes.event_mask | PointerMotionMask);
11345  roi_info.x=(ssize_t) windows->image.x+x;
11346  roi_info.y=(ssize_t) windows->image.y+y;
11347  roi_info.width=0;
11348  roi_info.height=0;
11349  cursor=XCreateFontCursor(display,XC_fleur);
11350  state=DefaultState;
11351  do
11352  {
11353    if (windows->info.mapped != MagickFalse)
11354      {
11355        /*
11356          Display pointer position.
11357        */
11358        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
11359          (long) roi_info.x,(long) roi_info.y);
11360        XInfoWidget(display,windows,text);
11361      }
11362    /*
11363      Wait for next event.
11364    */
11365    XScreenEvent(display,windows,&event,exception);
11366    if (event.xany.window == windows->command.id)
11367      {
11368        /*
11369          Select a command from the Command widget.
11370        */
11371        id=XCommandWidget(display,windows,ROIMenu,&event);
11372        if (id < 0)
11373          continue;
11374        switch (ROICommands[id])
11375        {
11376          case ROIHelpCommand:
11377          {
11378            XTextViewWidget(display,resource_info,windows,MagickFalse,
11379              "Help Viewer - Region of Interest",ImageROIHelp);
11380            break;
11381          }
11382          case ROIDismissCommand:
11383          {
11384            /*
11385              Prematurely exit.
11386            */
11387            state|=EscapeState;
11388            state|=ExitState;
11389            break;
11390          }
11391          default:
11392            break;
11393        }
11394        continue;
11395      }
11396    switch (event.type)
11397    {
11398      case ButtonPress:
11399      {
11400        if (event.xbutton.button != Button1)
11401          break;
11402        if (event.xbutton.window != windows->image.id)
11403          break;
11404        /*
11405          Note first corner of region of interest rectangle-- exit loop.
11406        */
11407        (void) XCheckDefineCursor(display,windows->image.id,cursor);
11408        roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11409        roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11410        state|=ExitState;
11411        break;
11412      }
11413      case ButtonRelease:
11414        break;
11415      case Expose:
11416        break;
11417      case KeyPress:
11418      {
11419        KeySym
11420          key_symbol;
11421
11422        if (event.xkey.window != windows->image.id)
11423          break;
11424        /*
11425          Respond to a user key press.
11426        */
11427        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11428          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11429        switch ((int) key_symbol)
11430        {
11431          case XK_Escape:
11432          case XK_F20:
11433          {
11434            /*
11435              Prematurely exit.
11436            */
11437            state|=EscapeState;
11438            state|=ExitState;
11439            break;
11440          }
11441          case XK_F1:
11442          case XK_Help:
11443          {
11444            XTextViewWidget(display,resource_info,windows,MagickFalse,
11445              "Help Viewer - Region of Interest",ImageROIHelp);
11446            break;
11447          }
11448          default:
11449          {
11450            (void) XBell(display,0);
11451            break;
11452          }
11453        }
11454        break;
11455      }
11456      case MotionNotify:
11457      {
11458        /*
11459          Map and unmap Info widget as text cursor crosses its boundaries.
11460        */
11461        x=event.xmotion.x;
11462        y=event.xmotion.y;
11463        if (windows->info.mapped != MagickFalse)
11464          {
11465            if ((x < (int) (windows->info.x+windows->info.width)) &&
11466                (y < (int) (windows->info.y+windows->info.height)))
11467              (void) XWithdrawWindow(display,windows->info.id,
11468                windows->info.screen);
11469          }
11470        else
11471          if ((x > (int) (windows->info.x+windows->info.width)) ||
11472              (y > (int) (windows->info.y+windows->info.height)))
11473            (void) XMapWindow(display,windows->info.id);
11474        roi_info.x=(ssize_t) windows->image.x+x;
11475        roi_info.y=(ssize_t) windows->image.y+y;
11476        break;
11477      }
11478      default:
11479        break;
11480    }
11481  } while ((state & ExitState) == 0);
11482  (void) XSelectInput(display,windows->image.id,
11483    windows->image.attributes.event_mask);
11484  if ((state & EscapeState) != 0)
11485    {
11486      /*
11487        User want to exit without region of interest.
11488      */
11489      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11490      (void) XFreeCursor(display,cursor);
11491      return(MagickTrue);
11492    }
11493  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11494  do
11495  {
11496    /*
11497      Size rectangle as pointer moves until the mouse button is released.
11498    */
11499    x=(int) roi_info.x;
11500    y=(int) roi_info.y;
11501    roi_info.width=0;
11502    roi_info.height=0;
11503    state=DefaultState;
11504    do
11505    {
11506      highlight_info=roi_info;
11507      highlight_info.x=roi_info.x-windows->image.x;
11508      highlight_info.y=roi_info.y-windows->image.y;
11509      if ((highlight_info.width > 3) && (highlight_info.height > 3))
11510        {
11511          /*
11512            Display info and draw region of interest rectangle.
11513          */
11514          if (windows->info.mapped == MagickFalse)
11515            (void) XMapWindow(display,windows->info.id);
11516          (void) FormatLocaleString(text,MaxTextExtent,
11517            " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11518            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11519          XInfoWidget(display,windows,text);
11520          XHighlightRectangle(display,windows->image.id,
11521            windows->image.highlight_context,&highlight_info);
11522        }
11523      else
11524        if (windows->info.mapped != MagickFalse)
11525          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11526      /*
11527        Wait for next event.
11528      */
11529      XScreenEvent(display,windows,&event,exception);
11530      if ((highlight_info.width > 3) && (highlight_info.height > 3))
11531        XHighlightRectangle(display,windows->image.id,
11532          windows->image.highlight_context,&highlight_info);
11533      switch (event.type)
11534      {
11535        case ButtonPress:
11536        {
11537          roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11538          roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11539          break;
11540        }
11541        case ButtonRelease:
11542        {
11543          /*
11544            User has committed to region of interest rectangle.
11545          */
11546          roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11547          roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11548          XSetCursorState(display,windows,MagickFalse);
11549          state|=ExitState;
11550          if (LocaleCompare(windows->command.name,"Apply") == 0)
11551            break;
11552          (void) CloneString(&windows->command.name,"Apply");
11553          windows->command.data=ApplyMenus;
11554          (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11555          break;
11556        }
11557        case Expose:
11558          break;
11559        case MotionNotify:
11560        {
11561          roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11562          roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11563        }
11564        default:
11565          break;
11566      }
11567      if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11568          ((state & ExitState) != 0))
11569        {
11570          /*
11571            Check boundary conditions.
11572          */
11573          if (roi_info.x < 0)
11574            roi_info.x=0;
11575          else
11576            if (roi_info.x > (ssize_t) windows->image.ximage->width)
11577              roi_info.x=(ssize_t) windows->image.ximage->width;
11578          if ((int) roi_info.x < x)
11579            roi_info.width=(unsigned int) (x-roi_info.x);
11580          else
11581            {
11582              roi_info.width=(unsigned int) (roi_info.x-x);
11583              roi_info.x=(ssize_t) x;
11584            }
11585          if (roi_info.y < 0)
11586            roi_info.y=0;
11587          else
11588            if (roi_info.y > (ssize_t) windows->image.ximage->height)
11589              roi_info.y=(ssize_t) windows->image.ximage->height;
11590          if ((int) roi_info.y < y)
11591            roi_info.height=(unsigned int) (y-roi_info.y);
11592          else
11593            {
11594              roi_info.height=(unsigned int) (roi_info.y-y);
11595              roi_info.y=(ssize_t) y;
11596            }
11597        }
11598    } while ((state & ExitState) == 0);
11599    /*
11600      Wait for user to grab a corner of the rectangle or press return.
11601    */
11602    state=DefaultState;
11603    command_type=NullCommand;
11604    (void) XMapWindow(display,windows->info.id);
11605    do
11606    {
11607      if (windows->info.mapped != MagickFalse)
11608        {
11609          /*
11610            Display pointer position.
11611          */
11612          (void) FormatLocaleString(text,MaxTextExtent,
11613            " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11614            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11615          XInfoWidget(display,windows,text);
11616        }
11617      highlight_info=roi_info;
11618      highlight_info.x=roi_info.x-windows->image.x;
11619      highlight_info.y=roi_info.y-windows->image.y;
11620      if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11621        {
11622          state|=EscapeState;
11623          state|=ExitState;
11624          break;
11625        }
11626      if ((state & UpdateRegionState) != 0)
11627        {
11628          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11629          switch (command_type)
11630          {
11631            case UndoCommand:
11632            case RedoCommand:
11633            {
11634              (void) XMagickCommand(display,resource_info,windows,command_type,
11635                image,exception);
11636              break;
11637            }
11638            default:
11639            {
11640              /*
11641                Region of interest is relative to image configuration.
11642              */
11643              progress_monitor=SetImageProgressMonitor(*image,
11644                (MagickProgressMonitor) NULL,(*image)->client_data);
11645              crop_info=roi_info;
11646              width=(unsigned int) (*image)->columns;
11647              height=(unsigned int) (*image)->rows;
11648              x=0;
11649              y=0;
11650              if (windows->image.crop_geometry != (char *) NULL)
11651                (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11652                  &width,&height);
11653              scale_factor=(MagickRealType) width/windows->image.ximage->width;
11654              crop_info.x+=x;
11655              crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11656              crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11657              scale_factor=(MagickRealType)
11658                height/windows->image.ximage->height;
11659              crop_info.y+=y;
11660              crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11661              crop_info.height=(unsigned int)
11662                (scale_factor*crop_info.height+0.5);
11663              roi_image=CropImage(*image,&crop_info,exception);
11664              (void) SetImageProgressMonitor(*image,progress_monitor,
11665                (*image)->client_data);
11666              if (roi_image == (Image *) NULL)
11667                continue;
11668              /*
11669                Apply image processing technique to the region of interest.
11670              */
11671              windows->image.orphan=MagickTrue;
11672              (void) XMagickCommand(display,resource_info,windows,command_type,
11673                &roi_image,exception);
11674              progress_monitor=SetImageProgressMonitor(*image,
11675                (MagickProgressMonitor) NULL,(*image)->client_data);
11676              (void) XMagickCommand(display,resource_info,windows,
11677                SaveToUndoBufferCommand,image,exception);
11678              windows->image.orphan=MagickFalse;
11679              (void) CompositeImage(*image,CopyCompositeOp,roi_image,
11680                crop_info.x,crop_info.y,exception);
11681              roi_image=DestroyImage(roi_image);
11682              (void) SetImageProgressMonitor(*image,progress_monitor,
11683                (*image)->client_data);
11684              break;
11685            }
11686          }
11687          if (command_type != InfoCommand)
11688            {
11689              XConfigureImageColormap(display,resource_info,windows,*image,
11690                exception);
11691              (void) XConfigureImage(display,resource_info,windows,*image,
11692                exception);
11693            }
11694          XCheckRefreshWindows(display,windows);
11695          XInfoWidget(display,windows,text);
11696          (void) XSetFunction(display,windows->image.highlight_context,
11697            GXinvert);
11698          state&=(~UpdateRegionState);
11699        }
11700      XHighlightRectangle(display,windows->image.id,
11701        windows->image.highlight_context,&highlight_info);
11702      XScreenEvent(display,windows,&event,exception);
11703      if (event.xany.window == windows->command.id)
11704        {
11705          /*
11706            Select a command from the Command widget.
11707          */
11708          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11709          command_type=NullCommand;
11710          id=XCommandWidget(display,windows,ApplyMenu,&event);
11711          if (id >= 0)
11712            {
11713              (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11714              command_type=ApplyCommands[id];
11715              if (id < ApplyMenus)
11716                {
11717                  /*
11718                    Select a command from a pop-up menu.
11719                  */
11720                  entry=XMenuWidget(display,windows,ApplyMenu[id],
11721                    (const char **) Menus[id],command);
11722                  if (entry >= 0)
11723                    {
11724                      (void) CopyMagickString(command,Menus[id][entry],
11725                        MaxTextExtent);
11726                      command_type=Commands[id][entry];
11727                    }
11728                }
11729            }
11730          (void) XSetFunction(display,windows->image.highlight_context,
11731            GXinvert);
11732          XHighlightRectangle(display,windows->image.id,
11733            windows->image.highlight_context,&highlight_info);
11734          if (command_type == HelpCommand)
11735            {
11736              (void) XSetFunction(display,windows->image.highlight_context,
11737                GXcopy);
11738              XTextViewWidget(display,resource_info,windows,MagickFalse,
11739                "Help Viewer - Region of Interest",ImageROIHelp);
11740              (void) XSetFunction(display,windows->image.highlight_context,
11741                GXinvert);
11742              continue;
11743            }
11744          if (command_type == QuitCommand)
11745            {
11746              /*
11747                exit.
11748              */
11749              state|=EscapeState;
11750              state|=ExitState;
11751              continue;
11752            }
11753          if (command_type != NullCommand)
11754            state|=UpdateRegionState;
11755          continue;
11756        }
11757      XHighlightRectangle(display,windows->image.id,
11758        windows->image.highlight_context,&highlight_info);
11759      switch (event.type)
11760      {
11761        case ButtonPress:
11762        {
11763          x=windows->image.x;
11764          y=windows->image.y;
11765          if (event.xbutton.button != Button1)
11766            break;
11767          if (event.xbutton.window != windows->image.id)
11768            break;
11769          x=windows->image.x+event.xbutton.x;
11770          y=windows->image.y+event.xbutton.y;
11771          if ((x < (int) (roi_info.x+RoiDelta)) &&
11772              (x > (int) (roi_info.x-RoiDelta)) &&
11773              (y < (int) (roi_info.y+RoiDelta)) &&
11774              (y > (int) (roi_info.y-RoiDelta)))
11775            {
11776              roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11777              roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11778              state|=UpdateConfigurationState;
11779              break;
11780            }
11781          if ((x < (int) (roi_info.x+RoiDelta)) &&
11782              (x > (int) (roi_info.x-RoiDelta)) &&
11783              (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11784              (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11785            {
11786              roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11787              state|=UpdateConfigurationState;
11788              break;
11789            }
11790          if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11791              (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11792              (y < (int) (roi_info.y+RoiDelta)) &&
11793              (y > (int) (roi_info.y-RoiDelta)))
11794            {
11795              roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11796              state|=UpdateConfigurationState;
11797              break;
11798            }
11799          if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11800              (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11801              (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11802              (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11803            {
11804              state|=UpdateConfigurationState;
11805              break;
11806            }
11807        }
11808        case ButtonRelease:
11809        {
11810          if (event.xbutton.window == windows->pan.id)
11811            if ((highlight_info.x != crop_info.x-windows->image.x) ||
11812                (highlight_info.y != crop_info.y-windows->image.y))
11813              XHighlightRectangle(display,windows->image.id,
11814                windows->image.highlight_context,&highlight_info);
11815          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11816            event.xbutton.time);
11817          break;
11818        }
11819        case Expose:
11820        {
11821          if (event.xexpose.window == windows->image.id)
11822            if (event.xexpose.count == 0)
11823              {
11824                event.xexpose.x=(int) highlight_info.x;
11825                event.xexpose.y=(int) highlight_info.y;
11826                event.xexpose.width=(int) highlight_info.width;
11827                event.xexpose.height=(int) highlight_info.height;
11828                XRefreshWindow(display,&windows->image,&event);
11829              }
11830          if (event.xexpose.window == windows->info.id)
11831            if (event.xexpose.count == 0)
11832              XInfoWidget(display,windows,text);
11833          break;
11834        }
11835        case KeyPress:
11836        {
11837          KeySym
11838            key_symbol;
11839
11840          if (event.xkey.window != windows->image.id)
11841            break;
11842          /*
11843            Respond to a user key press.
11844          */
11845          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11846            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11847          switch ((int) key_symbol)
11848          {
11849            case XK_Shift_L:
11850            case XK_Shift_R:
11851              break;
11852            case XK_Escape:
11853            case XK_F20:
11854              state|=EscapeState;
11855            case XK_Return:
11856            {
11857              state|=ExitState;
11858              break;
11859            }
11860            case XK_Home:
11861            case XK_KP_Home:
11862            {
11863              roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11864              roi_info.y=(ssize_t) (windows->image.height/2L-
11865                roi_info.height/2L);
11866              break;
11867            }
11868            case XK_Left:
11869            case XK_KP_Left:
11870            {
11871              roi_info.x--;
11872              break;
11873            }
11874            case XK_Up:
11875            case XK_KP_Up:
11876            case XK_Next:
11877            {
11878              roi_info.y--;
11879              break;
11880            }
11881            case XK_Right:
11882            case XK_KP_Right:
11883            {
11884              roi_info.x++;
11885              break;
11886            }
11887            case XK_Prior:
11888            case XK_Down:
11889            case XK_KP_Down:
11890            {
11891              roi_info.y++;
11892              break;
11893            }
11894            case XK_F1:
11895            case XK_Help:
11896            {
11897              (void) XSetFunction(display,windows->image.highlight_context,
11898                GXcopy);
11899              XTextViewWidget(display,resource_info,windows,MagickFalse,
11900                "Help Viewer - Region of Interest",ImageROIHelp);
11901              (void) XSetFunction(display,windows->image.highlight_context,
11902                GXinvert);
11903              break;
11904            }
11905            default:
11906            {
11907              command_type=XImageWindowCommand(display,resource_info,windows,
11908                event.xkey.state,key_symbol,image,exception);
11909              if (command_type != NullCommand)
11910                state|=UpdateRegionState;
11911              break;
11912            }
11913          }
11914          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11915            event.xkey.time);
11916          break;
11917        }
11918        case KeyRelease:
11919          break;
11920        case MotionNotify:
11921        {
11922          if (event.xbutton.window != windows->image.id)
11923            break;
11924          /*
11925            Map and unmap Info widget as text cursor crosses its boundaries.
11926          */
11927          x=event.xmotion.x;
11928          y=event.xmotion.y;
11929          if (windows->info.mapped != MagickFalse)
11930            {
11931              if ((x < (int) (windows->info.x+windows->info.width)) &&
11932                  (y < (int) (windows->info.y+windows->info.height)))
11933                (void) XWithdrawWindow(display,windows->info.id,
11934                  windows->info.screen);
11935            }
11936          else
11937            if ((x > (int) (windows->info.x+windows->info.width)) ||
11938                (y > (int) (windows->info.y+windows->info.height)))
11939              (void) XMapWindow(display,windows->info.id);
11940          roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11941          roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11942          break;
11943        }
11944        case SelectionRequest:
11945        {
11946          XSelectionEvent
11947            notify;
11948
11949          XSelectionRequestEvent
11950            *request;
11951
11952          /*
11953            Set primary selection.
11954          */
11955          (void) FormatLocaleString(text,MaxTextExtent,
11956            "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11957            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11958          request=(&(event.xselectionrequest));
11959          (void) XChangeProperty(request->display,request->requestor,
11960            request->property,request->target,8,PropModeReplace,
11961            (unsigned char *) text,(int) strlen(text));
11962          notify.type=SelectionNotify;
11963          notify.display=request->display;
11964          notify.requestor=request->requestor;
11965          notify.selection=request->selection;
11966          notify.target=request->target;
11967          notify.time=request->time;
11968          if (request->property == None)
11969            notify.property=request->target;
11970          else
11971            notify.property=request->property;
11972          (void) XSendEvent(request->display,request->requestor,False,0,
11973            (XEvent *) &notify);
11974        }
11975        default:
11976          break;
11977      }
11978      if ((state & UpdateConfigurationState) != 0)
11979        {
11980          (void) XPutBackEvent(display,&event);
11981          (void) XCheckDefineCursor(display,windows->image.id,cursor);
11982          break;
11983        }
11984    } while ((state & ExitState) == 0);
11985  } while ((state & ExitState) == 0);
11986  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11987  XSetCursorState(display,windows,MagickFalse);
11988  if ((state & EscapeState) != 0)
11989    return(MagickTrue);
11990  return(MagickTrue);
11991}
11992
11993/*
11994%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11995%                                                                             %
11996%                                                                             %
11997%                                                                             %
11998+   X R o t a t e I m a g e                                                   %
11999%                                                                             %
12000%                                                                             %
12001%                                                                             %
12002%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12003%
12004%  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
12005%  rotation angle is computed from the slope of a line drawn by the user.
12006%
12007%  The format of the XRotateImage method is:
12008%
12009%      MagickBooleanType XRotateImage(Display *display,
12010%        XResourceInfo *resource_info,XWindows *windows,double degrees,
12011%        Image **image,ExceptionInfo *exception)
12012%
12013%  A description of each parameter follows:
12014%
12015%    o display: Specifies a connection to an X server; returned from
12016%      XOpenDisplay.
12017%
12018%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12019%
12020%    o windows: Specifies a pointer to a XWindows structure.
12021%
12022%    o degrees: Specifies the number of degrees to rotate the image.
12023%
12024%    o image: the image.
12025%
12026%    o exception: return any errors or warnings in this structure.
12027%
12028*/
12029static MagickBooleanType XRotateImage(Display *display,
12030  XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
12031  ExceptionInfo *exception)
12032{
12033  static const char
12034    *RotateMenu[] =
12035    {
12036      "Pixel Color",
12037      "Direction",
12038      "Help",
12039      "Dismiss",
12040      (char *) NULL
12041    };
12042
12043  static ModeType
12044    direction = HorizontalRotateCommand;
12045
12046  static const ModeType
12047    DirectionCommands[] =
12048    {
12049      HorizontalRotateCommand,
12050      VerticalRotateCommand
12051    },
12052    RotateCommands[] =
12053    {
12054      RotateColorCommand,
12055      RotateDirectionCommand,
12056      RotateHelpCommand,
12057      RotateDismissCommand
12058    };
12059
12060  static unsigned int
12061    pen_id = 0;
12062
12063  char
12064    command[MaxTextExtent],
12065    text[MaxTextExtent];
12066
12067  Image
12068    *rotate_image;
12069
12070  int
12071    id,
12072    x,
12073    y;
12074
12075  MagickRealType
12076    normalized_degrees;
12077
12078  register int
12079    i;
12080
12081  unsigned int
12082    height,
12083    rotations,
12084    width;
12085
12086  if (degrees == 0.0)
12087    {
12088      unsigned int
12089        distance;
12090
12091      size_t
12092        state;
12093
12094      XEvent
12095        event;
12096
12097      XSegment
12098        rotate_info;
12099
12100      /*
12101        Map Command widget.
12102      */
12103      (void) CloneString(&windows->command.name,"Rotate");
12104      windows->command.data=2;
12105      (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12106      (void) XMapRaised(display,windows->command.id);
12107      XClientMessage(display,windows->image.id,windows->im_protocols,
12108        windows->im_update_widget,CurrentTime);
12109      /*
12110        Wait for first button press.
12111      */
12112      (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12113      XQueryPosition(display,windows->image.id,&x,&y);
12114      rotate_info.x1=x;
12115      rotate_info.y1=y;
12116      rotate_info.x2=x;
12117      rotate_info.y2=y;
12118      state=DefaultState;
12119      do
12120      {
12121        XHighlightLine(display,windows->image.id,
12122          windows->image.highlight_context,&rotate_info);
12123        /*
12124          Wait for next event.
12125        */
12126        XScreenEvent(display,windows,&event,exception);
12127        XHighlightLine(display,windows->image.id,
12128          windows->image.highlight_context,&rotate_info);
12129        if (event.xany.window == windows->command.id)
12130          {
12131            /*
12132              Select a command from the Command widget.
12133            */
12134            id=XCommandWidget(display,windows,RotateMenu,&event);
12135            if (id < 0)
12136              continue;
12137            (void) XSetFunction(display,windows->image.highlight_context,
12138              GXcopy);
12139            switch (RotateCommands[id])
12140            {
12141              case RotateColorCommand:
12142              {
12143                const char
12144                  *ColorMenu[MaxNumberPens];
12145
12146                int
12147                  pen_number;
12148
12149                XColor
12150                  color;
12151
12152                /*
12153                  Initialize menu selections.
12154                */
12155                for (i=0; i < (int) (MaxNumberPens-2); i++)
12156                  ColorMenu[i]=resource_info->pen_colors[i];
12157                ColorMenu[MaxNumberPens-2]="Browser...";
12158                ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12159                /*
12160                  Select a pen color from the pop-up menu.
12161                */
12162                pen_number=XMenuWidget(display,windows,RotateMenu[id],
12163                  (const char **) ColorMenu,command);
12164                if (pen_number < 0)
12165                  break;
12166                if (pen_number == (MaxNumberPens-2))
12167                  {
12168                    static char
12169                      color_name[MaxTextExtent] = "gray";
12170
12171                    /*
12172                      Select a pen color from a dialog.
12173                    */
12174                    resource_info->pen_colors[pen_number]=color_name;
12175                    XColorBrowserWidget(display,windows,"Select",color_name);
12176                    if (*color_name == '\0')
12177                      break;
12178                  }
12179                /*
12180                  Set pen color.
12181                */
12182                (void) XParseColor(display,windows->map_info->colormap,
12183                  resource_info->pen_colors[pen_number],&color);
12184                XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12185                  (unsigned int) MaxColors,&color);
12186                windows->pixel_info->pen_colors[pen_number]=color;
12187                pen_id=(unsigned int) pen_number;
12188                break;
12189              }
12190              case RotateDirectionCommand:
12191              {
12192                static const char
12193                  *Directions[] =
12194                  {
12195                    "horizontal",
12196                    "vertical",
12197                    (char *) NULL,
12198                  };
12199
12200                /*
12201                  Select a command from the pop-up menu.
12202                */
12203                id=XMenuWidget(display,windows,RotateMenu[id],
12204                  Directions,command);
12205                if (id >= 0)
12206                  direction=DirectionCommands[id];
12207                break;
12208              }
12209              case RotateHelpCommand:
12210              {
12211                XTextViewWidget(display,resource_info,windows,MagickFalse,
12212                  "Help Viewer - Image Rotation",ImageRotateHelp);
12213                break;
12214              }
12215              case RotateDismissCommand:
12216              {
12217                /*
12218                  Prematurely exit.
12219                */
12220                state|=EscapeState;
12221                state|=ExitState;
12222                break;
12223              }
12224              default:
12225                break;
12226            }
12227            (void) XSetFunction(display,windows->image.highlight_context,
12228              GXinvert);
12229            continue;
12230          }
12231        switch (event.type)
12232        {
12233          case ButtonPress:
12234          {
12235            if (event.xbutton.button != Button1)
12236              break;
12237            if (event.xbutton.window != windows->image.id)
12238              break;
12239            /*
12240              exit loop.
12241            */
12242            (void) XSetFunction(display,windows->image.highlight_context,
12243              GXcopy);
12244            rotate_info.x1=event.xbutton.x;
12245            rotate_info.y1=event.xbutton.y;
12246            state|=ExitState;
12247            break;
12248          }
12249          case ButtonRelease:
12250            break;
12251          case Expose:
12252            break;
12253          case KeyPress:
12254          {
12255            char
12256              command[MaxTextExtent];
12257
12258            KeySym
12259              key_symbol;
12260
12261            if (event.xkey.window != windows->image.id)
12262              break;
12263            /*
12264              Respond to a user key press.
12265            */
12266            (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12267              sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12268            switch ((int) key_symbol)
12269            {
12270              case XK_Escape:
12271              case XK_F20:
12272              {
12273                /*
12274                  Prematurely exit.
12275                */
12276                state|=EscapeState;
12277                state|=ExitState;
12278                break;
12279              }
12280              case XK_F1:
12281              case XK_Help:
12282              {
12283                (void) XSetFunction(display,windows->image.highlight_context,
12284                  GXcopy);
12285                XTextViewWidget(display,resource_info,windows,MagickFalse,
12286                  "Help Viewer - Image Rotation",ImageRotateHelp);
12287                (void) XSetFunction(display,windows->image.highlight_context,
12288                  GXinvert);
12289                break;
12290              }
12291              default:
12292              {
12293                (void) XBell(display,0);
12294                break;
12295              }
12296            }
12297            break;
12298          }
12299          case MotionNotify:
12300          {
12301            rotate_info.x1=event.xmotion.x;
12302            rotate_info.y1=event.xmotion.y;
12303          }
12304        }
12305        rotate_info.x2=rotate_info.x1;
12306        rotate_info.y2=rotate_info.y1;
12307        if (direction == HorizontalRotateCommand)
12308          rotate_info.x2+=32;
12309        else
12310          rotate_info.y2-=32;
12311      } while ((state & ExitState) == 0);
12312      (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12313      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12314      if ((state & EscapeState) != 0)
12315        return(MagickTrue);
12316      /*
12317        Draw line as pointer moves until the mouse button is released.
12318      */
12319      distance=0;
12320      (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12321      state=DefaultState;
12322      do
12323      {
12324        if (distance > 9)
12325          {
12326            /*
12327              Display info and draw rotation line.
12328            */
12329            if (windows->info.mapped == MagickFalse)
12330              (void) XMapWindow(display,windows->info.id);
12331            (void) FormatLocaleString(text,MaxTextExtent," %g",
12332              direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12333            XInfoWidget(display,windows,text);
12334            XHighlightLine(display,windows->image.id,
12335              windows->image.highlight_context,&rotate_info);
12336          }
12337        else
12338          if (windows->info.mapped != MagickFalse)
12339            (void) XWithdrawWindow(display,windows->info.id,
12340              windows->info.screen);
12341        /*
12342          Wait for next event.
12343        */
12344        XScreenEvent(display,windows,&event,exception);
12345        if (distance > 9)
12346          XHighlightLine(display,windows->image.id,
12347            windows->image.highlight_context,&rotate_info);
12348        switch (event.type)
12349        {
12350          case ButtonPress:
12351            break;
12352          case ButtonRelease:
12353          {
12354            /*
12355              User has committed to rotation line.
12356            */
12357            rotate_info.x2=event.xbutton.x;
12358            rotate_info.y2=event.xbutton.y;
12359            state|=ExitState;
12360            break;
12361          }
12362          case Expose:
12363            break;
12364          case MotionNotify:
12365          {
12366            rotate_info.x2=event.xmotion.x;
12367            rotate_info.y2=event.xmotion.y;
12368          }
12369          default:
12370            break;
12371        }
12372        /*
12373          Check boundary conditions.
12374        */
12375        if (rotate_info.x2 < 0)
12376          rotate_info.x2=0;
12377        else
12378          if (rotate_info.x2 > (int) windows->image.width)
12379            rotate_info.x2=(short) windows->image.width;
12380        if (rotate_info.y2 < 0)
12381          rotate_info.y2=0;
12382        else
12383          if (rotate_info.y2 > (int) windows->image.height)
12384            rotate_info.y2=(short) windows->image.height;
12385        /*
12386          Compute rotation angle from the slope of the line.
12387        */
12388        degrees=0.0;
12389        distance=(unsigned int)
12390          ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12391          ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12392        if (distance > 9)
12393          degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12394            rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12395      } while ((state & ExitState) == 0);
12396      (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12397      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12398      if (distance <= 9)
12399        return(MagickTrue);
12400    }
12401  if (direction == VerticalRotateCommand)
12402    degrees-=90.0;
12403  if (degrees == 0.0)
12404    return(MagickTrue);
12405  /*
12406    Rotate image.
12407  */
12408  normalized_degrees=degrees;
12409  while (normalized_degrees < -45.0)
12410    normalized_degrees+=360.0;
12411  for (rotations=0; normalized_degrees > 45.0; rotations++)
12412    normalized_degrees-=90.0;
12413  if (normalized_degrees != 0.0)
12414    (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12415      exception);
12416  XSetCursorState(display,windows,MagickTrue);
12417  XCheckRefreshWindows(display,windows);
12418  (*image)->background_color.red=ScaleShortToQuantum(
12419    windows->pixel_info->pen_colors[pen_id].red);
12420  (*image)->background_color.green=ScaleShortToQuantum(
12421    windows->pixel_info->pen_colors[pen_id].green);
12422  (*image)->background_color.blue=ScaleShortToQuantum(
12423    windows->pixel_info->pen_colors[pen_id].blue);
12424  rotate_image=RotateImage(*image,degrees,exception);
12425  XSetCursorState(display,windows,MagickFalse);
12426  if (rotate_image == (Image *) NULL)
12427    return(MagickFalse);
12428  *image=DestroyImage(*image);
12429  *image=rotate_image;
12430  if (windows->image.crop_geometry != (char *) NULL)
12431    {
12432      /*
12433        Rotate crop geometry.
12434      */
12435      width=(unsigned int) (*image)->columns;
12436      height=(unsigned int) (*image)->rows;
12437      (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12438      switch (rotations % 4)
12439      {
12440        default:
12441        case 0:
12442          break;
12443        case 1:
12444        {
12445          /*
12446            Rotate 90 degrees.
12447          */
12448          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12449            "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12450            (int) height-y,x);
12451          break;
12452        }
12453        case 2:
12454        {
12455          /*
12456            Rotate 180 degrees.
12457          */
12458          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12459            "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12460          break;
12461        }
12462        case 3:
12463        {
12464          /*
12465            Rotate 270 degrees.
12466          */
12467          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12468            "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12469          break;
12470        }
12471      }
12472    }
12473  if (windows->image.orphan != MagickFalse)
12474    return(MagickTrue);
12475  if (normalized_degrees != 0.0)
12476    {
12477      /*
12478        Update image colormap.
12479      */
12480      windows->image.window_changes.width=(int) (*image)->columns;
12481      windows->image.window_changes.height=(int) (*image)->rows;
12482      if (windows->image.crop_geometry != (char *) NULL)
12483        {
12484          /*
12485            Obtain dimensions of image from crop geometry.
12486          */
12487          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12488            &width,&height);
12489          windows->image.window_changes.width=(int) width;
12490          windows->image.window_changes.height=(int) height;
12491        }
12492      XConfigureImageColormap(display,resource_info,windows,*image,exception);
12493    }
12494  else
12495    if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12496      {
12497        windows->image.window_changes.width=windows->image.ximage->height;
12498        windows->image.window_changes.height=windows->image.ximage->width;
12499      }
12500  /*
12501    Update image configuration.
12502  */
12503  (void) XConfigureImage(display,resource_info,windows,*image,exception);
12504  return(MagickTrue);
12505}
12506
12507/*
12508%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12509%                                                                             %
12510%                                                                             %
12511%                                                                             %
12512+   X S a v e I m a g e                                                       %
12513%                                                                             %
12514%                                                                             %
12515%                                                                             %
12516%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12517%
12518%  XSaveImage() saves an image to a file.
12519%
12520%  The format of the XSaveImage method is:
12521%
12522%      MagickBooleanType XSaveImage(Display *display,
12523%        XResourceInfo *resource_info,XWindows *windows,Image *image,
12524%        ExceptionInfo *exception)
12525%
12526%  A description of each parameter follows:
12527%
12528%    o display: Specifies a connection to an X server; returned from
12529%      XOpenDisplay.
12530%
12531%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12532%
12533%    o windows: Specifies a pointer to a XWindows structure.
12534%
12535%    o image: the image.
12536%
12537%    o exception: return any errors or warnings in this structure.
12538%
12539*/
12540static MagickBooleanType XSaveImage(Display *display,
12541  XResourceInfo *resource_info,XWindows *windows,Image *image,
12542  ExceptionInfo *exception)
12543{
12544  char
12545    filename[MaxTextExtent],
12546    geometry[MaxTextExtent];
12547
12548  Image
12549    *save_image;
12550
12551  ImageInfo
12552    *image_info;
12553
12554  MagickStatusType
12555    status;
12556
12557  /*
12558    Request file name from user.
12559  */
12560  if (resource_info->write_filename != (char *) NULL)
12561    (void) CopyMagickString(filename,resource_info->write_filename,
12562      MaxTextExtent);
12563  else
12564    {
12565      char
12566        path[MaxTextExtent];
12567
12568      int
12569        status;
12570
12571      GetPathComponent(image->filename,HeadPath,path);
12572      GetPathComponent(image->filename,TailPath,filename);
12573      if (*path != '\0')
12574        {
12575          status=chdir(path);
12576          if (status == -1)
12577            (void) ThrowMagickException(exception,GetMagickModule(),
12578              FileOpenError,"UnableToOpenFile","%s",path);
12579        }
12580    }
12581  XFileBrowserWidget(display,windows,"Save",filename);
12582  if (*filename == '\0')
12583    return(MagickTrue);
12584  if (IsPathAccessible(filename) != MagickFalse)
12585    {
12586      int
12587        status;
12588
12589      /*
12590        File exists-- seek user's permission before overwriting.
12591      */
12592      status=XConfirmWidget(display,windows,"Overwrite",filename);
12593      if (status <= 0)
12594        return(MagickTrue);
12595    }
12596  image_info=CloneImageInfo(resource_info->image_info);
12597  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12598  (void) SetImageInfo(image_info,1,exception);
12599  if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12600      (LocaleCompare(image_info->magick,"JPG") == 0))
12601    {
12602      char
12603        quality[MaxTextExtent];
12604
12605      int
12606        status;
12607
12608      /*
12609        Request JPEG quality from user.
12610      */
12611      (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
12612        image->quality);
12613      status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12614        quality);
12615      if (*quality == '\0')
12616        return(MagickTrue);
12617      image->quality=StringToUnsignedLong(quality);
12618      image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12619    }
12620  if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12621      (LocaleCompare(image_info->magick,"PDF") == 0) ||
12622      (LocaleCompare(image_info->magick,"PS") == 0) ||
12623      (LocaleCompare(image_info->magick,"PS2") == 0))
12624    {
12625      char
12626        geometry[MaxTextExtent];
12627
12628      /*
12629        Request page geometry from user.
12630      */
12631      (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12632      if (LocaleCompare(image_info->magick,"PDF") == 0)
12633        (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12634      if (image_info->page != (char *) NULL)
12635        (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12636      XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12637        "Select page geometry:",geometry);
12638      if (*geometry != '\0')
12639        image_info->page=GetPageGeometry(geometry);
12640    }
12641  /*
12642    Apply image transforms.
12643  */
12644  XSetCursorState(display,windows,MagickTrue);
12645  XCheckRefreshWindows(display,windows);
12646  save_image=CloneImage(image,0,0,MagickTrue,exception);
12647  if (save_image == (Image *) NULL)
12648    return(MagickFalse);
12649  (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
12650    windows->image.ximage->width,windows->image.ximage->height);
12651  (void) TransformImage(&save_image,windows->image.crop_geometry,geometry,
12652    exception);
12653  /*
12654    Write image.
12655  */
12656  (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12657  status=WriteImage(image_info,save_image,exception);
12658  if (status != MagickFalse)
12659    image->taint=MagickFalse;
12660  save_image=DestroyImage(save_image);
12661  image_info=DestroyImageInfo(image_info);
12662  XSetCursorState(display,windows,MagickFalse);
12663  return(status != 0 ? MagickTrue : MagickFalse);
12664}
12665
12666/*
12667%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12668%                                                                             %
12669%                                                                             %
12670%                                                                             %
12671+   X S c r e e n E v e n t                                                   %
12672%                                                                             %
12673%                                                                             %
12674%                                                                             %
12675%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12676%
12677%  XScreenEvent() handles global events associated with the Pan and Magnify
12678%  windows.
12679%
12680%  The format of the XScreenEvent function is:
12681%
12682%      void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12683%        ExceptionInfo *exception)
12684%
12685%  A description of each parameter follows:
12686%
12687%    o display: Specifies a pointer to the Display structure;  returned from
12688%      XOpenDisplay.
12689%
12690%    o windows: Specifies a pointer to a XWindows structure.
12691%
12692%    o event: Specifies a pointer to a X11 XEvent structure.
12693%
12694%    o exception: return any errors or warnings in this structure.
12695%
12696*/
12697
12698#if defined(__cplusplus) || defined(c_plusplus)
12699extern "C" {
12700#endif
12701
12702static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12703{
12704  register XWindows
12705    *windows;
12706
12707  windows=(XWindows *) data;
12708  if ((event->type == ClientMessage) &&
12709      (event->xclient.window == windows->image.id))
12710    return(MagickFalse);
12711  return(MagickTrue);
12712}
12713
12714#if defined(__cplusplus) || defined(c_plusplus)
12715}
12716#endif
12717
12718static void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12719  ExceptionInfo *exception)
12720{
12721  register int
12722    x,
12723    y;
12724
12725  (void) XIfEvent(display,event,XPredicate,(char *) windows);
12726  if (event->xany.window == windows->command.id)
12727    return;
12728  switch (event->type)
12729  {
12730    case ButtonPress:
12731    case ButtonRelease:
12732    {
12733      if ((event->xbutton.button == Button3) &&
12734          (event->xbutton.state & Mod1Mask))
12735        {
12736          /*
12737            Convert Alt-Button3 to Button2.
12738          */
12739          event->xbutton.button=Button2;
12740          event->xbutton.state&=(~Mod1Mask);
12741        }
12742      if (event->xbutton.window == windows->backdrop.id)
12743        {
12744          (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12745            event->xbutton.time);
12746          break;
12747        }
12748      if (event->xbutton.window == windows->pan.id)
12749        {
12750          XPanImage(display,windows,event,exception);
12751          break;
12752        }
12753      if (event->xbutton.window == windows->image.id)
12754        if (event->xbutton.button == Button2)
12755          {
12756            /*
12757              Update magnified image.
12758            */
12759            x=event->xbutton.x;
12760            y=event->xbutton.y;
12761            if (x < 0)
12762              x=0;
12763            else
12764              if (x >= (int) windows->image.width)
12765                x=(int) (windows->image.width-1);
12766            windows->magnify.x=(int) windows->image.x+x;
12767            if (y < 0)
12768              y=0;
12769            else
12770             if (y >= (int) windows->image.height)
12771               y=(int) (windows->image.height-1);
12772            windows->magnify.y=windows->image.y+y;
12773            if (windows->magnify.mapped == MagickFalse)
12774              (void) XMapRaised(display,windows->magnify.id);
12775            XMakeMagnifyImage(display,windows,exception);
12776            if (event->type == ButtonRelease)
12777              (void) XWithdrawWindow(display,windows->info.id,
12778                windows->info.screen);
12779            break;
12780          }
12781      break;
12782    }
12783    case ClientMessage:
12784    {
12785      /*
12786        If client window delete message, exit.
12787      */
12788      if (event->xclient.message_type != windows->wm_protocols)
12789        break;
12790      if (*event->xclient.data.l != (long) windows->wm_delete_window)
12791        break;
12792      if (event->xclient.window == windows->magnify.id)
12793        {
12794          (void) XWithdrawWindow(display,windows->magnify.id,
12795            windows->magnify.screen);
12796          break;
12797        }
12798      break;
12799    }
12800    case ConfigureNotify:
12801    {
12802      if (event->xconfigure.window == windows->magnify.id)
12803        {
12804          unsigned int
12805            magnify;
12806
12807          /*
12808            Magnify window has a new configuration.
12809          */
12810          windows->magnify.width=(unsigned int) event->xconfigure.width;
12811          windows->magnify.height=(unsigned int) event->xconfigure.height;
12812          if (windows->magnify.mapped == MagickFalse)
12813            break;
12814          magnify=1;
12815          while ((int) magnify <= event->xconfigure.width)
12816            magnify<<=1;
12817          while ((int) magnify <= event->xconfigure.height)
12818            magnify<<=1;
12819          magnify>>=1;
12820          if (((int) magnify != event->xconfigure.width) ||
12821              ((int) magnify != event->xconfigure.height))
12822            {
12823              XWindowChanges
12824                window_changes;
12825
12826              window_changes.width=(int) magnify;
12827              window_changes.height=(int) magnify;
12828              (void) XReconfigureWMWindow(display,windows->magnify.id,
12829                windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12830                &window_changes);
12831              break;
12832            }
12833          XMakeMagnifyImage(display,windows,exception);
12834          break;
12835        }
12836      break;
12837    }
12838    case Expose:
12839    {
12840      if (event->xexpose.window == windows->image.id)
12841        {
12842          XRefreshWindow(display,&windows->image,event);
12843          break;
12844        }
12845      if (event->xexpose.window == windows->pan.id)
12846        if (event->xexpose.count == 0)
12847          {
12848            XDrawPanRectangle(display,windows);
12849            break;
12850          }
12851      if (event->xexpose.window == windows->magnify.id)
12852        if (event->xexpose.count == 0)
12853          {
12854            XMakeMagnifyImage(display,windows,exception);
12855            break;
12856          }
12857      break;
12858    }
12859    case KeyPress:
12860    {
12861      char
12862        command[MaxTextExtent];
12863
12864      KeySym
12865        key_symbol;
12866
12867      if (event->xkey.window != windows->magnify.id)
12868        break;
12869      /*
12870        Respond to a user key press.
12871      */
12872      (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12873        sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12874      XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol,
12875        exception);
12876      break;
12877    }
12878    case MapNotify:
12879    {
12880      if (event->xmap.window == windows->magnify.id)
12881        {
12882          windows->magnify.mapped=MagickTrue;
12883          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12884          break;
12885        }
12886      if (event->xmap.window == windows->info.id)
12887        {
12888          windows->info.mapped=MagickTrue;
12889          break;
12890        }
12891      break;
12892    }
12893    case MotionNotify:
12894    {
12895      while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12896      if (event->xmotion.window == windows->image.id)
12897        if (windows->magnify.mapped != MagickFalse)
12898          {
12899            /*
12900              Update magnified image.
12901            */
12902            x=event->xmotion.x;
12903            y=event->xmotion.y;
12904            if (x < 0)
12905              x=0;
12906            else
12907              if (x >= (int) windows->image.width)
12908                x=(int) (windows->image.width-1);
12909            windows->magnify.x=(int) windows->image.x+x;
12910            if (y < 0)
12911              y=0;
12912            else
12913             if (y >= (int) windows->image.height)
12914               y=(int) (windows->image.height-1);
12915            windows->magnify.y=windows->image.y+y;
12916            XMakeMagnifyImage(display,windows,exception);
12917          }
12918      break;
12919    }
12920    case UnmapNotify:
12921    {
12922      if (event->xunmap.window == windows->magnify.id)
12923        {
12924          windows->magnify.mapped=MagickFalse;
12925          break;
12926        }
12927      if (event->xunmap.window == windows->info.id)
12928        {
12929          windows->info.mapped=MagickFalse;
12930          break;
12931        }
12932      break;
12933    }
12934    default:
12935      break;
12936  }
12937}
12938
12939/*
12940%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12941%                                                                             %
12942%                                                                             %
12943%                                                                             %
12944+   X S e t C r o p G e o m e t r y                                           %
12945%                                                                             %
12946%                                                                             %
12947%                                                                             %
12948%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12949%
12950%  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12951%  and translates it to a cropping geometry relative to the image.
12952%
12953%  The format of the XSetCropGeometry method is:
12954%
12955%      void XSetCropGeometry(Display *display,XWindows *windows,
12956%        RectangleInfo *crop_info,Image *image)
12957%
12958%  A description of each parameter follows:
12959%
12960%    o display: Specifies a connection to an X server; returned from
12961%      XOpenDisplay.
12962%
12963%    o windows: Specifies a pointer to a XWindows structure.
12964%
12965%    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12966%      Image window to crop.
12967%
12968%    o image: the image.
12969%
12970*/
12971static void XSetCropGeometry(Display *display,XWindows *windows,
12972  RectangleInfo *crop_info,Image *image)
12973{
12974  char
12975    text[MaxTextExtent];
12976
12977  int
12978    x,
12979    y;
12980
12981  MagickRealType
12982    scale_factor;
12983
12984  unsigned int
12985    height,
12986    width;
12987
12988  if (windows->info.mapped != MagickFalse)
12989    {
12990      /*
12991        Display info on cropping rectangle.
12992      */
12993      (void) FormatLocaleString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
12994        (double) crop_info->width,(double) crop_info->height,(double)
12995        crop_info->x,(double) crop_info->y);
12996      XInfoWidget(display,windows,text);
12997    }
12998  /*
12999    Cropping geometry is relative to any previous crop geometry.
13000  */
13001  x=0;
13002  y=0;
13003  width=(unsigned int) image->columns;
13004  height=(unsigned int) image->rows;
13005  if (windows->image.crop_geometry != (char *) NULL)
13006    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13007  else
13008    windows->image.crop_geometry=AcquireString((char *) NULL);
13009  /*
13010    Define the crop geometry string from the cropping rectangle.
13011  */
13012  scale_factor=(MagickRealType) width/windows->image.ximage->width;
13013  if (crop_info->x > 0)
13014    x+=(int) (scale_factor*crop_info->x+0.5);
13015  width=(unsigned int) (scale_factor*crop_info->width+0.5);
13016  if (width == 0)
13017    width=1;
13018  scale_factor=(MagickRealType) height/windows->image.ximage->height;
13019  if (crop_info->y > 0)
13020    y+=(int) (scale_factor*crop_info->y+0.5);
13021  height=(unsigned int) (scale_factor*crop_info->height+0.5);
13022  if (height == 0)
13023    height=1;
13024  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
13025    "%ux%u%+d%+d",width,height,x,y);
13026}
13027
13028/*
13029%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13030%                                                                             %
13031%                                                                             %
13032%                                                                             %
13033+   X T i l e I m a g e                                                       %
13034%                                                                             %
13035%                                                                             %
13036%                                                                             %
13037%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13038%
13039%  XTileImage() loads or deletes a selected tile from a visual image directory.
13040%  The load or delete command is chosen from a menu.
13041%
13042%  The format of the XTileImage method is:
13043%
13044%      Image *XTileImage(Display *display,XResourceInfo *resource_info,
13045%        XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13046%
13047%  A description of each parameter follows:
13048%
13049%    o tile_image:  XTileImage reads or deletes the tile image
13050%      and returns it.  A null image is returned if an error occurs.
13051%
13052%    o display: Specifies a connection to an X server;  returned from
13053%      XOpenDisplay.
13054%
13055%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13056%
13057%    o windows: Specifies a pointer to a XWindows structure.
13058%
13059%    o image: the image; returned from ReadImage.
13060%
13061%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
13062%      the entire image is refreshed.
13063%
13064%    o exception: return any errors or warnings in this structure.
13065%
13066*/
13067static Image *XTileImage(Display *display,XResourceInfo *resource_info,
13068  XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13069{
13070  static const char
13071    *VerbMenu[] =
13072    {
13073      "Load",
13074      "Next",
13075      "Former",
13076      "Delete",
13077      "Update",
13078      (char *) NULL,
13079    };
13080
13081  static const ModeType
13082    TileCommands[] =
13083    {
13084      TileLoadCommand,
13085      TileNextCommand,
13086      TileFormerCommand,
13087      TileDeleteCommand,
13088      TileUpdateCommand
13089    };
13090
13091  char
13092    command[MaxTextExtent],
13093    filename[MaxTextExtent];
13094
13095  Image
13096    *tile_image;
13097
13098  int
13099    id,
13100    status,
13101    tile,
13102    x,
13103    y;
13104
13105  MagickRealType
13106    scale_factor;
13107
13108  register char
13109    *p,
13110    *q;
13111
13112  register int
13113    i;
13114
13115  unsigned int
13116    height,
13117    width;
13118
13119  /*
13120    Tile image is relative to montage image configuration.
13121  */
13122  x=0;
13123  y=0;
13124  width=(unsigned int) image->columns;
13125  height=(unsigned int) image->rows;
13126  if (windows->image.crop_geometry != (char *) NULL)
13127    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13128  scale_factor=(MagickRealType) width/windows->image.ximage->width;
13129  event->xbutton.x+=windows->image.x;
13130  event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13131  scale_factor=(MagickRealType) height/windows->image.ximage->height;
13132  event->xbutton.y+=windows->image.y;
13133  event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13134  /*
13135    Determine size and location of each tile in the visual image directory.
13136  */
13137  width=(unsigned int) image->columns;
13138  height=(unsigned int) image->rows;
13139  x=0;
13140  y=0;
13141  (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13142  tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13143    (event->xbutton.x-x)/width;
13144  if (tile < 0)
13145    {
13146      /*
13147        Button press is outside any tile.
13148      */
13149      (void) XBell(display,0);
13150      return((Image *) NULL);
13151    }
13152  /*
13153    Determine file name from the tile directory.
13154  */
13155  p=image->directory;
13156  for (i=tile; (i != 0) && (*p != '\0'); )
13157  {
13158    if (*p == '\n')
13159      i--;
13160    p++;
13161  }
13162  if (*p == '\0')
13163    {
13164      /*
13165        Button press is outside any tile.
13166      */
13167      (void) XBell(display,0);
13168      return((Image *) NULL);
13169    }
13170  /*
13171    Select a command from the pop-up menu.
13172  */
13173  id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13174  if (id < 0)
13175    return((Image *) NULL);
13176  q=p;
13177  while ((*q != '\n') && (*q != '\0'))
13178    q++;
13179  (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13180  /*
13181    Perform command for the selected tile.
13182  */
13183  XSetCursorState(display,windows,MagickTrue);
13184  XCheckRefreshWindows(display,windows);
13185  tile_image=NewImageList();
13186  switch (TileCommands[id])
13187  {
13188    case TileLoadCommand:
13189    {
13190      /*
13191        Load tile image.
13192      */
13193      XCheckRefreshWindows(display,windows);
13194      (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13195        MaxTextExtent);
13196      (void) CopyMagickString(resource_info->image_info->filename,filename,
13197        MaxTextExtent);
13198      tile_image=ReadImage(resource_info->image_info,exception);
13199      CatchException(exception);
13200      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13201      break;
13202    }
13203    case TileNextCommand:
13204    {
13205      /*
13206        Display next image.
13207      */
13208      XClientMessage(display,windows->image.id,windows->im_protocols,
13209        windows->im_next_image,CurrentTime);
13210      break;
13211    }
13212    case TileFormerCommand:
13213    {
13214      /*
13215        Display former image.
13216      */
13217      XClientMessage(display,windows->image.id,windows->im_protocols,
13218        windows->im_former_image,CurrentTime);
13219      break;
13220    }
13221    case TileDeleteCommand:
13222    {
13223      /*
13224        Delete tile image.
13225      */
13226      if (IsPathAccessible(filename) == MagickFalse)
13227        {
13228          XNoticeWidget(display,windows,"Image file does not exist:",filename);
13229          break;
13230        }
13231      status=XConfirmWidget(display,windows,"Really delete tile",filename);
13232      if (status <= 0)
13233        break;
13234      status=remove_utf8(filename) != 0 ? MagickTrue : MagickFalse;
13235      if (status != MagickFalse)
13236        {
13237          XNoticeWidget(display,windows,"Unable to delete image file:",
13238            filename);
13239          break;
13240        }
13241    }
13242    case TileUpdateCommand:
13243    {
13244      int
13245        x_offset,
13246        y_offset;
13247
13248      PixelInfo
13249        pixel;
13250
13251      Quantum
13252        virtual_pixel[CompositePixelChannel];
13253
13254      register int
13255        j;
13256
13257      register Quantum
13258        *s;
13259
13260      /*
13261        Ensure all the images exist.
13262      */
13263      tile=0;
13264      GetPixelInfo(image,&pixel);
13265      for (p=image->directory; *p != '\0'; p++)
13266      {
13267        CacheView
13268          *image_view;
13269
13270        q=p;
13271        while ((*q != '\n') && (*q != '\0'))
13272          q++;
13273        (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13274        p=q;
13275        if (IsPathAccessible(filename) != MagickFalse)
13276          {
13277            tile++;
13278            continue;
13279          }
13280        /*
13281          Overwrite tile with background color.
13282        */
13283        x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13284        y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13285        image_view=AcquireCacheView(image);
13286        (void) GetOneCacheViewVirtualPixel(image_view,0,0,virtual_pixel,
13287          exception);
13288        pixel.red=virtual_pixel[RedPixelChannel];
13289        pixel.green=virtual_pixel[GreenPixelChannel];
13290        pixel.blue=virtual_pixel[BluePixelChannel];
13291        pixel.alpha=virtual_pixel[AlphaPixelChannel];
13292        for (i=0; i < (int) height; i++)
13293        {
13294          s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13295            y_offset+i,width,1,exception);
13296          if (s == (Quantum *) NULL)
13297            break;
13298          for (j=0; j < (int) width; j++)
13299          {
13300            SetPixelInfoPixel(image,&pixel,s);
13301            s+=GetPixelChannels(image);
13302          }
13303          if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13304            break;
13305        }
13306        image_view=DestroyCacheView(image_view);
13307        tile++;
13308      }
13309      windows->image.window_changes.width=(int) image->columns;
13310      windows->image.window_changes.height=(int) image->rows;
13311      XConfigureImageColormap(display,resource_info,windows,image,exception);
13312      (void) XConfigureImage(display,resource_info,windows,image,exception);
13313      break;
13314    }
13315    default:
13316      break;
13317  }
13318  XSetCursorState(display,windows,MagickFalse);
13319  return(tile_image);
13320}
13321
13322/*
13323%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13324%                                                                             %
13325%                                                                             %
13326%                                                                             %
13327+   X T r a n s l a t e I m a g e                                             %
13328%                                                                             %
13329%                                                                             %
13330%                                                                             %
13331%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13332%
13333%  XTranslateImage() translates the image within an Image window by one pixel
13334%  as specified by the key symbol.  If the image has a `montage string the
13335%  translation is respect to the width and height contained within the string.
13336%
13337%  The format of the XTranslateImage method is:
13338%
13339%      void XTranslateImage(Display *display,XWindows *windows,
13340%        Image *image,const KeySym key_symbol)
13341%
13342%  A description of each parameter follows:
13343%
13344%    o display: Specifies a connection to an X server; returned from
13345%      XOpenDisplay.
13346%
13347%    o windows: Specifies a pointer to a XWindows structure.
13348%
13349%    o image: the image.
13350%
13351%    o key_symbol: Specifies a KeySym which indicates which side of the image
13352%      to trim.
13353%
13354*/
13355static void XTranslateImage(Display *display,XWindows *windows,
13356  Image *image,const KeySym key_symbol)
13357{
13358  char
13359    text[MaxTextExtent];
13360
13361  int
13362    x,
13363    y;
13364
13365  unsigned int
13366    x_offset,
13367    y_offset;
13368
13369  /*
13370    User specified a pan position offset.
13371  */
13372  x_offset=windows->image.width;
13373  y_offset=windows->image.height;
13374  if (image->montage != (char *) NULL)
13375    (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13376  switch ((int) key_symbol)
13377  {
13378    case XK_Home:
13379    case XK_KP_Home:
13380    {
13381      windows->image.x=(int) windows->image.width/2;
13382      windows->image.y=(int) windows->image.height/2;
13383      break;
13384    }
13385    case XK_Left:
13386    case XK_KP_Left:
13387    {
13388      windows->image.x-=x_offset;
13389      break;
13390    }
13391    case XK_Next:
13392    case XK_Up:
13393    case XK_KP_Up:
13394    {
13395      windows->image.y-=y_offset;
13396      break;
13397    }
13398    case XK_Right:
13399    case XK_KP_Right:
13400    {
13401      windows->image.x+=x_offset;
13402      break;
13403    }
13404    case XK_Prior:
13405    case XK_Down:
13406    case XK_KP_Down:
13407    {
13408      windows->image.y+=y_offset;
13409      break;
13410    }
13411    default:
13412      return;
13413  }
13414  /*
13415    Check boundary conditions.
13416  */
13417  if (windows->image.x < 0)
13418    windows->image.x=0;
13419  else
13420    if ((int) (windows->image.x+windows->image.width) >
13421        windows->image.ximage->width)
13422      windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13423  if (windows->image.y < 0)
13424    windows->image.y=0;
13425  else
13426    if ((int) (windows->image.y+windows->image.height) >
13427        windows->image.ximage->height)
13428      windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13429  /*
13430    Refresh Image window.
13431  */
13432  (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
13433    windows->image.width,windows->image.height,windows->image.x,
13434    windows->image.y);
13435  XInfoWidget(display,windows,text);
13436  XCheckRefreshWindows(display,windows);
13437  XDrawPanRectangle(display,windows);
13438  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13439  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13440}
13441
13442/*
13443%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13444%                                                                             %
13445%                                                                             %
13446%                                                                             %
13447+   X T r i m I m a g e                                                       %
13448%                                                                             %
13449%                                                                             %
13450%                                                                             %
13451%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13452%
13453%  XTrimImage() trims the edges from the Image window.
13454%
13455%  The format of the XTrimImage method is:
13456%
13457%      MagickBooleanType XTrimImage(Display *display,
13458%        XResourceInfo *resource_info,XWindows *windows,Image *image,
13459%        ExceptionInfo *exception)
13460%
13461%  A description of each parameter follows:
13462%
13463%    o display: Specifies a connection to an X server; returned from
13464%      XOpenDisplay.
13465%
13466%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13467%
13468%    o windows: Specifies a pointer to a XWindows structure.
13469%
13470%    o image: the image.
13471%
13472%    o exception: return any errors or warnings in this structure.
13473%
13474*/
13475static MagickBooleanType XTrimImage(Display *display,
13476  XResourceInfo *resource_info,XWindows *windows,Image *image,
13477  ExceptionInfo *exception)
13478{
13479  RectangleInfo
13480    trim_info;
13481
13482  register int
13483    x,
13484    y;
13485
13486  size_t
13487    background,
13488    pixel;
13489
13490  /*
13491    Trim edges from image.
13492  */
13493  XSetCursorState(display,windows,MagickTrue);
13494  XCheckRefreshWindows(display,windows);
13495  /*
13496    Crop the left edge.
13497  */
13498  background=XGetPixel(windows->image.ximage,0,0);
13499  trim_info.width=(size_t) windows->image.ximage->width;
13500  for (x=0; x < windows->image.ximage->width; x++)
13501  {
13502    for (y=0; y < windows->image.ximage->height; y++)
13503    {
13504      pixel=XGetPixel(windows->image.ximage,x,y);
13505      if (pixel != background)
13506        break;
13507    }
13508    if (y < windows->image.ximage->height)
13509      break;
13510  }
13511  trim_info.x=(ssize_t) x;
13512  if (trim_info.x == (ssize_t) windows->image.ximage->width)
13513    {
13514      XSetCursorState(display,windows,MagickFalse);
13515      return(MagickFalse);
13516    }
13517  /*
13518    Crop the right edge.
13519  */
13520  background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13521  for (x=windows->image.ximage->width-1; x != 0; x--)
13522  {
13523    for (y=0; y < windows->image.ximage->height; y++)
13524    {
13525      pixel=XGetPixel(windows->image.ximage,x,y);
13526      if (pixel != background)
13527        break;
13528    }
13529    if (y < windows->image.ximage->height)
13530      break;
13531  }
13532  trim_info.width=(size_t) (x-trim_info.x+1);
13533  /*
13534    Crop the top edge.
13535  */
13536  background=XGetPixel(windows->image.ximage,0,0);
13537  trim_info.height=(size_t) windows->image.ximage->height;
13538  for (y=0; y < windows->image.ximage->height; y++)
13539  {
13540    for (x=0; x < windows->image.ximage->width; x++)
13541    {
13542      pixel=XGetPixel(windows->image.ximage,x,y);
13543      if (pixel != background)
13544        break;
13545    }
13546    if (x < windows->image.ximage->width)
13547      break;
13548  }
13549  trim_info.y=(ssize_t) y;
13550  /*
13551    Crop the bottom edge.
13552  */
13553  background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13554  for (y=windows->image.ximage->height-1; y != 0; y--)
13555  {
13556    for (x=0; x < windows->image.ximage->width; x++)
13557    {
13558      pixel=XGetPixel(windows->image.ximage,x,y);
13559      if (pixel != background)
13560        break;
13561    }
13562    if (x < windows->image.ximage->width)
13563      break;
13564  }
13565  trim_info.height=(size_t) y-trim_info.y+1;
13566  if (((unsigned int) trim_info.width != windows->image.width) ||
13567      ((unsigned int) trim_info.height != windows->image.height))
13568    {
13569      /*
13570        Reconfigure Image window as defined by the trimming rectangle.
13571      */
13572      XSetCropGeometry(display,windows,&trim_info,image);
13573      windows->image.window_changes.width=(int) trim_info.width;
13574      windows->image.window_changes.height=(int) trim_info.height;
13575      (void) XConfigureImage(display,resource_info,windows,image,exception);
13576    }
13577  XSetCursorState(display,windows,MagickFalse);
13578  return(MagickTrue);
13579}
13580
13581/*
13582%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13583%                                                                             %
13584%                                                                             %
13585%                                                                             %
13586+   X V i s u a l D i r e c t o r y I m a g e                                 %
13587%                                                                             %
13588%                                                                             %
13589%                                                                             %
13590%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13591%
13592%  XVisualDirectoryImage() creates a Visual Image Directory.
13593%
13594%  The format of the XVisualDirectoryImage method is:
13595%
13596%      Image *XVisualDirectoryImage(Display *display,
13597%        XResourceInfo *resource_info,XWindows *windows,
13598%        ExceptionInfo *exception)
13599%
13600%  A description of each parameter follows:
13601%
13602%    o display: Specifies a connection to an X server; returned from
13603%      XOpenDisplay.
13604%
13605%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13606%
13607%    o windows: Specifies a pointer to a XWindows structure.
13608%
13609%    o exception: return any errors or warnings in this structure.
13610%
13611*/
13612static Image *XVisualDirectoryImage(Display *display,
13613  XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
13614{
13615#define TileImageTag  "Scale/Image"
13616#define XClientName  "montage"
13617
13618  char
13619    **filelist;
13620
13621  Image
13622    *images,
13623    *montage_image,
13624    *next_image,
13625    *thumbnail_image;
13626
13627  ImageInfo
13628    *read_info;
13629
13630  int
13631    number_files;
13632
13633  MagickBooleanType
13634    backdrop;
13635
13636  MagickStatusType
13637    status;
13638
13639  MontageInfo
13640    *montage_info;
13641
13642  RectangleInfo
13643    geometry;
13644
13645  register int
13646    i;
13647
13648  static char
13649    filename[MaxTextExtent] = "\0",
13650    filenames[MaxTextExtent] = "*";
13651
13652  XResourceInfo
13653    background_resources;
13654
13655  /*
13656    Request file name from user.
13657  */
13658  XFileBrowserWidget(display,windows,"Directory",filenames);
13659  if (*filenames == '\0')
13660    return((Image *) NULL);
13661  /*
13662    Expand the filenames.
13663  */
13664  filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13665  if (filelist == (char **) NULL)
13666    {
13667      ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13668        filenames);
13669      return((Image *) NULL);
13670    }
13671  number_files=1;
13672  filelist[0]=filenames;
13673  status=ExpandFilenames(&number_files,&filelist);
13674  if ((status == MagickFalse) || (number_files == 0))
13675    {
13676      if (number_files == 0)
13677        ThrowXWindowFatalException(ImageError,"NoImagesWereFound",filenames)
13678      else
13679        ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13680          filenames);
13681      return((Image *) NULL);
13682    }
13683  /*
13684    Set image background resources.
13685  */
13686  background_resources=(*resource_info);
13687  background_resources.window_id=AcquireString("");
13688  (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
13689    "0x%lx",windows->image.id);
13690  background_resources.backdrop=MagickTrue;
13691  /*
13692    Read each image and convert them to a tile.
13693  */
13694  backdrop=(windows->visual_info->klass == TrueColor) ||
13695    (windows->visual_info->klass == DirectColor) ? MagickTrue : MagickFalse;
13696  read_info=CloneImageInfo(resource_info->image_info);
13697  (void) SetImageOption(read_info,"jpeg:size","120x120");
13698  (void) CloneString(&read_info->size,DefaultTileGeometry);
13699  (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13700    (void *) NULL);
13701  images=NewImageList();
13702  XSetCursorState(display,windows,MagickTrue);
13703  XCheckRefreshWindows(display,windows);
13704  for (i=0; i < (int) number_files; i++)
13705  {
13706    (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13707    filelist[i]=DestroyString(filelist[i]);
13708    *read_info->magick='\0';
13709    next_image=ReadImage(read_info,exception);
13710    CatchException(exception);
13711    if (next_image != (Image *) NULL)
13712      {
13713        (void) DeleteImageProperty(next_image,"label");
13714        (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13715          read_info,next_image,DefaultTileLabel,exception),exception);
13716        (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13717          exception);
13718        thumbnail_image=ThumbnailImage(next_image,geometry.width,
13719          geometry.height,exception);
13720        if (thumbnail_image != (Image *) NULL)
13721          {
13722            next_image=DestroyImage(next_image);
13723            next_image=thumbnail_image;
13724          }
13725        if (backdrop)
13726          {
13727            (void) XDisplayBackgroundImage(display,&background_resources,
13728              next_image,exception);
13729            XSetCursorState(display,windows,MagickTrue);
13730          }
13731        AppendImageToList(&images,next_image);
13732        if (images->progress_monitor != (MagickProgressMonitor) NULL)
13733          {
13734            MagickBooleanType
13735              proceed;
13736
13737            proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13738              (MagickSizeType) number_files);
13739            if (proceed == MagickFalse)
13740              break;
13741          }
13742      }
13743  }
13744  filelist=(char **) RelinquishMagickMemory(filelist);
13745  if (images == (Image *) NULL)
13746    {
13747      read_info=DestroyImageInfo(read_info);
13748      XSetCursorState(display,windows,MagickFalse);
13749      ThrowXWindowFatalException(ImageError,"NoImagesWereLoaded",filenames);
13750      return((Image *) NULL);
13751    }
13752  /*
13753    Create the Visual Image Directory.
13754  */
13755  montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13756  montage_info->pointsize=10;
13757  if (resource_info->font != (char *) NULL)
13758    (void) CloneString(&montage_info->font,resource_info->font);
13759  (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13760  montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13761    images),exception);
13762  images=DestroyImageList(images);
13763  montage_info=DestroyMontageInfo(montage_info);
13764  read_info=DestroyImageInfo(read_info);
13765  XSetCursorState(display,windows,MagickFalse);
13766  if (montage_image == (Image *) NULL)
13767    return(montage_image);
13768  XClientMessage(display,windows->image.id,windows->im_protocols,
13769    windows->im_next_image,CurrentTime);
13770  return(montage_image);
13771}
13772
13773/*
13774%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13775%                                                                             %
13776%                                                                             %
13777%                                                                             %
13778%   X D i s p l a y B a c k g r o u n d I m a g e                             %
13779%                                                                             %
13780%                                                                             %
13781%                                                                             %
13782%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13783%
13784%  XDisplayBackgroundImage() displays an image in the background of a window.
13785%
13786%  The format of the XDisplayBackgroundImage method is:
13787%
13788%      MagickBooleanType XDisplayBackgroundImage(Display *display,
13789%        XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13790%
13791%  A description of each parameter follows:
13792%
13793%    o display: Specifies a connection to an X server;  returned from
13794%      XOpenDisplay.
13795%
13796%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13797%
13798%    o image: the image.
13799%
13800%    o exception: return any errors or warnings in this structure.
13801%
13802*/
13803MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13804  XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13805{
13806  char
13807    geometry[MaxTextExtent],
13808    visual_type[MaxTextExtent];
13809
13810  int
13811    height,
13812    status,
13813    width;
13814
13815  RectangleInfo
13816    geometry_info;
13817
13818  static XPixelInfo
13819    pixel;
13820
13821  static XStandardColormap
13822    *map_info;
13823
13824  static XVisualInfo
13825    *visual_info = (XVisualInfo *) NULL;
13826
13827  static XWindowInfo
13828    window_info;
13829
13830  size_t
13831    delay;
13832
13833  Window
13834    root_window;
13835
13836  XGCValues
13837    context_values;
13838
13839  XResourceInfo
13840    resources;
13841
13842  XWindowAttributes
13843    window_attributes;
13844
13845  /*
13846    Determine target window.
13847  */
13848  assert(image != (Image *) NULL);
13849  assert(image->signature == MagickSignature);
13850  if (image->debug != MagickFalse)
13851    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13852  resources=(*resource_info);
13853  window_info.id=(Window) NULL;
13854  root_window=XRootWindow(display,XDefaultScreen(display));
13855  if (LocaleCompare(resources.window_id,"root") == 0)
13856    window_info.id=root_window;
13857  else
13858    {
13859      if (isdigit((unsigned char) *resources.window_id) != 0)
13860        window_info.id=XWindowByID(display,root_window,
13861          (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13862      if (window_info.id == (Window) NULL)
13863        window_info.id=XWindowByName(display,root_window,resources.window_id);
13864    }
13865  if (window_info.id == (Window) NULL)
13866    {
13867      ThrowXWindowFatalException(XServerError,"NoWindowWithSpecifiedIDExists",
13868        resources.window_id);
13869      return(MagickFalse);
13870    }
13871  /*
13872    Determine window visual id.
13873  */
13874  window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13875  window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13876  (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13877  status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13878  if (status != 0)
13879    (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
13880      XVisualIDFromVisual(window_attributes.visual));
13881  if (visual_info == (XVisualInfo *) NULL)
13882    {
13883      /*
13884        Allocate standard colormap.
13885      */
13886      map_info=XAllocStandardColormap();
13887      if (map_info == (XStandardColormap *) NULL)
13888        ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13889          image->filename);
13890      map_info->colormap=(Colormap) NULL;
13891      pixel.pixels=(unsigned long *) NULL;
13892      /*
13893        Initialize visual info.
13894      */
13895      resources.map_type=(char *) NULL;
13896      resources.visual_type=visual_type;
13897      visual_info=XBestVisualInfo(display,map_info,&resources);
13898      if (visual_info == (XVisualInfo *) NULL)
13899        ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13900          resources.visual_type);
13901      /*
13902        Initialize window info.
13903      */
13904      window_info.ximage=(XImage *) NULL;
13905      window_info.matte_image=(XImage *) NULL;
13906      window_info.pixmap=(Pixmap) NULL;
13907      window_info.matte_pixmap=(Pixmap) NULL;
13908    }
13909  /*
13910    Free previous root colors.
13911  */
13912  if (window_info.id == root_window)
13913    (void) XDestroyWindowColors(display,root_window);
13914  /*
13915    Initialize Standard Colormap.
13916  */
13917  resources.colormap=SharedColormap;
13918  XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel,
13919    exception);
13920  /*
13921    Graphic context superclass.
13922  */
13923  context_values.background=pixel.background_color.pixel;
13924  context_values.foreground=pixel.foreground_color.pixel;
13925  pixel.annotate_context=XCreateGC(display,window_info.id,
13926    (size_t) (GCBackground | GCForeground),&context_values);
13927  if (pixel.annotate_context == (GC) NULL)
13928    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13929      image->filename);
13930  /*
13931    Initialize Image window attributes.
13932  */
13933  window_info.name=AcquireString("\0");
13934  window_info.icon_name=AcquireString("\0");
13935  XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13936    &resources,&window_info);
13937  /*
13938    Create the X image.
13939  */
13940  window_info.width=(unsigned int) image->columns;
13941  window_info.height=(unsigned int) image->rows;
13942  if ((image->columns != window_info.width) ||
13943      (image->rows != window_info.height))
13944    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13945      image->filename);
13946  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
13947    window_attributes.width,window_attributes.height);
13948  geometry_info.width=window_info.width;
13949  geometry_info.height=window_info.height;
13950  geometry_info.x=(ssize_t) window_info.x;
13951  geometry_info.y=(ssize_t) window_info.y;
13952  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13953    &geometry_info.width,&geometry_info.height);
13954  window_info.width=(unsigned int) geometry_info.width;
13955  window_info.height=(unsigned int) geometry_info.height;
13956  window_info.x=(int) geometry_info.x;
13957  window_info.y=(int) geometry_info.y;
13958  status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13959    window_info.height,exception);
13960  if (status == MagickFalse)
13961    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13962      image->filename);
13963  window_info.x=0;
13964  window_info.y=0;
13965  if (image->debug != MagickFalse)
13966    {
13967      (void) LogMagickEvent(X11Event,GetMagickModule(),
13968        "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13969        (double) image->columns,(double) image->rows);
13970      if (image->colors != 0)
13971        (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13972          image->colors);
13973      (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13974    }
13975  /*
13976    Adjust image dimensions as specified by backdrop or geometry options.
13977  */
13978  width=(int) window_info.width;
13979  height=(int) window_info.height;
13980  if (resources.backdrop != MagickFalse)
13981    {
13982      /*
13983        Center image on window.
13984      */
13985      window_info.x=(window_attributes.width/2)-
13986        (window_info.ximage->width/2);
13987      window_info.y=(window_attributes.height/2)-
13988        (window_info.ximage->height/2);
13989      width=window_attributes.width;
13990      height=window_attributes.height;
13991    }
13992  if ((resources.image_geometry != (char *) NULL) &&
13993      (*resources.image_geometry != '\0'))
13994    {
13995      char
13996        default_geometry[MaxTextExtent];
13997
13998      int
13999        flags,
14000        gravity;
14001
14002      XSizeHints
14003        *size_hints;
14004
14005      /*
14006        User specified geometry.
14007      */
14008      size_hints=XAllocSizeHints();
14009      if (size_hints == (XSizeHints *) NULL)
14010        ThrowXWindowFatalException(ResourceLimitFatalError,
14011          "MemoryAllocationFailed",image->filename);
14012      size_hints->flags=0L;
14013      (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
14014        width,height);
14015      flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
14016        default_geometry,window_info.border_width,size_hints,&window_info.x,
14017        &window_info.y,&width,&height,&gravity);
14018      if (flags & (XValue | YValue))
14019        {
14020          width=window_attributes.width;
14021          height=window_attributes.height;
14022        }
14023      (void) XFree((void *) size_hints);
14024    }
14025  /*
14026    Create the X pixmap.
14027  */
14028  window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
14029    (unsigned int) height,window_info.depth);
14030  if (window_info.pixmap == (Pixmap) NULL)
14031    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
14032      image->filename);
14033  /*
14034    Display pixmap on the window.
14035  */
14036  if (((unsigned int) width > window_info.width) ||
14037      ((unsigned int) height > window_info.height))
14038    (void) XFillRectangle(display,window_info.pixmap,
14039      window_info.annotate_context,0,0,(unsigned int) width,
14040      (unsigned int) height);
14041  (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
14042    window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
14043    window_info.width,(unsigned int) window_info.height);
14044  (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
14045  (void) XClearWindow(display,window_info.id);
14046  delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
14047  XDelay(display,delay == 0UL ? 10UL : delay);
14048  (void) XSync(display,MagickFalse);
14049  return(window_info.id == root_window ? MagickTrue : MagickFalse);
14050}
14051
14052/*
14053%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14054%                                                                             %
14055%                                                                             %
14056%                                                                             %
14057+   X D i s p l a y I m a g e                                                 %
14058%                                                                             %
14059%                                                                             %
14060%                                                                             %
14061%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14062%
14063%  XDisplayImage() displays an image via X11.  A new image is created and
14064%  returned if the user interactively transforms the displayed image.
14065%
14066%  The format of the XDisplayImage method is:
14067%
14068%      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14069%        char **argv,int argc,Image **image,size_t *state,
14070%        ExceptionInfo *exception)
14071%
14072%  A description of each parameter follows:
14073%
14074%    o nexus:  Method XDisplayImage returns an image when the
14075%      user chooses 'Open Image' from the command menu or picks a tile
14076%      from the image directory.  Otherwise a null image is returned.
14077%
14078%    o display: Specifies a connection to an X server;  returned from
14079%      XOpenDisplay.
14080%
14081%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14082%
14083%    o argv: Specifies the application's argument list.
14084%
14085%    o argc: Specifies the number of arguments.
14086%
14087%    o image: Specifies an address to an address of an Image structure;
14088%
14089%    o exception: return any errors or warnings in this structure.
14090%
14091*/
14092MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14093  char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
14094{
14095#define MagnifySize  256  /* must be a power of 2 */
14096#define MagickMenus  10
14097#define MagickTitle  "Commands"
14098
14099  static const char
14100    *CommandMenu[] =
14101    {
14102      "File",
14103      "Edit",
14104      "View",
14105      "Transform",
14106      "Enhance",
14107      "Effects",
14108      "F/X",
14109      "Image Edit",
14110      "Miscellany",
14111      "Help",
14112      (char *) NULL
14113    },
14114    *FileMenu[] =
14115    {
14116      "Open...",
14117      "Next",
14118      "Former",
14119      "Select...",
14120      "Save...",
14121      "Print...",
14122      "Delete...",
14123      "New...",
14124      "Visual Directory...",
14125      "Quit",
14126      (char *) NULL
14127    },
14128    *EditMenu[] =
14129    {
14130      "Undo",
14131      "Redo",
14132      "Cut",
14133      "Copy",
14134      "Paste",
14135      (char *) NULL
14136    },
14137    *ViewMenu[] =
14138    {
14139      "Half Size",
14140      "Original Size",
14141      "Double Size",
14142      "Resize...",
14143      "Apply",
14144      "Refresh",
14145      "Restore",
14146      (char *) NULL
14147    },
14148    *TransformMenu[] =
14149    {
14150      "Crop",
14151      "Chop",
14152      "Flop",
14153      "Flip",
14154      "Rotate Right",
14155      "Rotate Left",
14156      "Rotate...",
14157      "Shear...",
14158      "Roll...",
14159      "Trim Edges",
14160      (char *) NULL
14161    },
14162    *EnhanceMenu[] =
14163    {
14164      "Hue...",
14165      "Saturation...",
14166      "Brightness...",
14167      "Gamma...",
14168      "Spiff",
14169      "Dull",
14170      "Contrast Stretch...",
14171      "Sigmoidal Contrast...",
14172      "Normalize",
14173      "Equalize",
14174      "Negate",
14175      "Grayscale",
14176      "Map...",
14177      "Quantize...",
14178      (char *) NULL
14179    },
14180    *EffectsMenu[] =
14181    {
14182      "Despeckle",
14183      "Emboss",
14184      "Reduce Noise",
14185      "Add Noise...",
14186      "Sharpen...",
14187      "Blur...",
14188      "Threshold...",
14189      "Edge Detect...",
14190      "Spread...",
14191      "Shade...",
14192      "Raise...",
14193      "Segment...",
14194      (char *) NULL
14195    },
14196    *FXMenu[] =
14197    {
14198      "Solarize...",
14199      "Sepia Tone...",
14200      "Swirl...",
14201      "Implode...",
14202      "Vignette...",
14203      "Wave...",
14204      "Oil Paint...",
14205      "Charcoal Draw...",
14206      (char *) NULL
14207    },
14208    *ImageEditMenu[] =
14209    {
14210      "Annotate...",
14211      "Draw...",
14212      "Color...",
14213      "Matte...",
14214      "Composite...",
14215      "Add Border...",
14216      "Add Frame...",
14217      "Comment...",
14218      "Launch...",
14219      "Region of Interest...",
14220      (char *) NULL
14221    },
14222    *MiscellanyMenu[] =
14223    {
14224      "Image Info",
14225      "Zoom Image",
14226      "Show Preview...",
14227      "Show Histogram",
14228      "Show Matte",
14229      "Background...",
14230      "Slide Show...",
14231      "Preferences...",
14232      (char *) NULL
14233    },
14234    *HelpMenu[] =
14235    {
14236      "Overview",
14237      "Browse Documentation",
14238      "About Display",
14239      (char *) NULL
14240    },
14241    *ShortCutsMenu[] =
14242    {
14243      "Next",
14244      "Former",
14245      "Open...",
14246      "Save...",
14247      "Print...",
14248      "Undo",
14249      "Restore",
14250      "Image Info",
14251      "Quit",
14252      (char *) NULL
14253    },
14254    *VirtualMenu[] =
14255    {
14256      "Image Info",
14257      "Print",
14258      "Next",
14259      "Quit",
14260      (char *) NULL
14261    };
14262
14263  static const char
14264    **Menus[MagickMenus] =
14265    {
14266      FileMenu,
14267      EditMenu,
14268      ViewMenu,
14269      TransformMenu,
14270      EnhanceMenu,
14271      EffectsMenu,
14272      FXMenu,
14273      ImageEditMenu,
14274      MiscellanyMenu,
14275      HelpMenu
14276    };
14277
14278  static CommandType
14279    CommandMenus[] =
14280    {
14281      NullCommand,
14282      NullCommand,
14283      NullCommand,
14284      NullCommand,
14285      NullCommand,
14286      NullCommand,
14287      NullCommand,
14288      NullCommand,
14289      NullCommand,
14290      NullCommand,
14291    },
14292    FileCommands[] =
14293    {
14294      OpenCommand,
14295      NextCommand,
14296      FormerCommand,
14297      SelectCommand,
14298      SaveCommand,
14299      PrintCommand,
14300      DeleteCommand,
14301      NewCommand,
14302      VisualDirectoryCommand,
14303      QuitCommand
14304    },
14305    EditCommands[] =
14306    {
14307      UndoCommand,
14308      RedoCommand,
14309      CutCommand,
14310      CopyCommand,
14311      PasteCommand
14312    },
14313    ViewCommands[] =
14314    {
14315      HalfSizeCommand,
14316      OriginalSizeCommand,
14317      DoubleSizeCommand,
14318      ResizeCommand,
14319      ApplyCommand,
14320      RefreshCommand,
14321      RestoreCommand
14322    },
14323    TransformCommands[] =
14324    {
14325      CropCommand,
14326      ChopCommand,
14327      FlopCommand,
14328      FlipCommand,
14329      RotateRightCommand,
14330      RotateLeftCommand,
14331      RotateCommand,
14332      ShearCommand,
14333      RollCommand,
14334      TrimCommand
14335    },
14336    EnhanceCommands[] =
14337    {
14338      HueCommand,
14339      SaturationCommand,
14340      BrightnessCommand,
14341      GammaCommand,
14342      SpiffCommand,
14343      DullCommand,
14344      ContrastStretchCommand,
14345      SigmoidalContrastCommand,
14346      NormalizeCommand,
14347      EqualizeCommand,
14348      NegateCommand,
14349      GrayscaleCommand,
14350      MapCommand,
14351      QuantizeCommand
14352    },
14353    EffectsCommands[] =
14354    {
14355      DespeckleCommand,
14356      EmbossCommand,
14357      ReduceNoiseCommand,
14358      AddNoiseCommand,
14359      SharpenCommand,
14360      BlurCommand,
14361      ThresholdCommand,
14362      EdgeDetectCommand,
14363      SpreadCommand,
14364      ShadeCommand,
14365      RaiseCommand,
14366      SegmentCommand
14367    },
14368    FXCommands[] =
14369    {
14370      SolarizeCommand,
14371      SepiaToneCommand,
14372      SwirlCommand,
14373      ImplodeCommand,
14374      VignetteCommand,
14375      WaveCommand,
14376      OilPaintCommand,
14377      CharcoalDrawCommand
14378    },
14379    ImageEditCommands[] =
14380    {
14381      AnnotateCommand,
14382      DrawCommand,
14383      ColorCommand,
14384      MatteCommand,
14385      CompositeCommand,
14386      AddBorderCommand,
14387      AddFrameCommand,
14388      CommentCommand,
14389      LaunchCommand,
14390      RegionofInterestCommand
14391    },
14392    MiscellanyCommands[] =
14393    {
14394      InfoCommand,
14395      ZoomCommand,
14396      ShowPreviewCommand,
14397      ShowHistogramCommand,
14398      ShowMatteCommand,
14399      BackgroundCommand,
14400      SlideShowCommand,
14401      PreferencesCommand
14402    },
14403    HelpCommands[] =
14404    {
14405      HelpCommand,
14406      BrowseDocumentationCommand,
14407      VersionCommand
14408    },
14409    ShortCutsCommands[] =
14410    {
14411      NextCommand,
14412      FormerCommand,
14413      OpenCommand,
14414      SaveCommand,
14415      PrintCommand,
14416      UndoCommand,
14417      RestoreCommand,
14418      InfoCommand,
14419      QuitCommand
14420    },
14421    VirtualCommands[] =
14422    {
14423      InfoCommand,
14424      PrintCommand,
14425      NextCommand,
14426      QuitCommand
14427    };
14428
14429  static CommandType
14430    *Commands[MagickMenus] =
14431    {
14432      FileCommands,
14433      EditCommands,
14434      ViewCommands,
14435      TransformCommands,
14436      EnhanceCommands,
14437      EffectsCommands,
14438      FXCommands,
14439      ImageEditCommands,
14440      MiscellanyCommands,
14441      HelpCommands
14442    };
14443
14444  char
14445    command[MaxTextExtent],
14446    *directory,
14447    geometry[MaxTextExtent],
14448    resource_name[MaxTextExtent];
14449
14450  CommandType
14451    command_type;
14452
14453  Image
14454    *display_image,
14455    *nexus;
14456
14457  int
14458    entry,
14459    id;
14460
14461  KeySym
14462    key_symbol;
14463
14464  MagickStatusType
14465    context_mask,
14466    status;
14467
14468  RectangleInfo
14469    geometry_info;
14470
14471  register int
14472    i;
14473
14474  static char
14475    working_directory[MaxTextExtent];
14476
14477  static XPoint
14478    vid_info;
14479
14480  static XWindowInfo
14481    *magick_windows[MaxXWindows];
14482
14483  static unsigned int
14484    number_windows;
14485
14486  struct stat
14487    attributes;
14488
14489  time_t
14490    timer,
14491    timestamp,
14492    update_time;
14493
14494  unsigned int
14495    height,
14496    width;
14497
14498  size_t
14499    delay;
14500
14501  WarningHandler
14502    warning_handler;
14503
14504  Window
14505    root_window;
14506
14507  XClassHint
14508    *class_hints;
14509
14510  XEvent
14511    event;
14512
14513  XFontStruct
14514    *font_info;
14515
14516  XGCValues
14517    context_values;
14518
14519  XPixelInfo
14520    *icon_pixel,
14521    *pixel;
14522
14523  XResourceInfo
14524    *icon_resources;
14525
14526  XStandardColormap
14527    *icon_map,
14528    *map_info;
14529
14530  XVisualInfo
14531    *icon_visual,
14532    *visual_info;
14533
14534  XWindowChanges
14535    window_changes;
14536
14537  XWindows
14538    *windows;
14539
14540  XWMHints
14541    *manager_hints;
14542
14543  assert(image != (Image **) NULL);
14544  assert((*image)->signature == MagickSignature);
14545  if ((*image)->debug != MagickFalse)
14546    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14547  display_image=(*image);
14548  warning_handler=(WarningHandler) NULL;
14549  windows=XSetWindows((XWindows *) ~0);
14550  if (windows != (XWindows *) NULL)
14551    {
14552      int
14553        status;
14554
14555      status=chdir(working_directory);
14556      if (status == -1)
14557        (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14558          "UnableToOpenFile","%s",working_directory);
14559      warning_handler=resource_info->display_warnings ?
14560        SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14561      warning_handler=resource_info->display_warnings ?
14562        SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14563    }
14564  else
14565    {
14566      /*
14567        Allocate windows structure.
14568      */
14569      resource_info->colors=display_image->colors;
14570      windows=XSetWindows(XInitializeWindows(display,resource_info));
14571      if (windows == (XWindows *) NULL)
14572        ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14573          (*image)->filename);
14574      /*
14575        Initialize window id's.
14576      */
14577      number_windows=0;
14578      magick_windows[number_windows++]=(&windows->icon);
14579      magick_windows[number_windows++]=(&windows->backdrop);
14580      magick_windows[number_windows++]=(&windows->image);
14581      magick_windows[number_windows++]=(&windows->info);
14582      magick_windows[number_windows++]=(&windows->command);
14583      magick_windows[number_windows++]=(&windows->widget);
14584      magick_windows[number_windows++]=(&windows->popup);
14585      magick_windows[number_windows++]=(&windows->magnify);
14586      magick_windows[number_windows++]=(&windows->pan);
14587      for (i=0; i < (int) number_windows; i++)
14588        magick_windows[i]->id=(Window) NULL;
14589      vid_info.x=0;
14590      vid_info.y=0;
14591    }
14592  /*
14593    Initialize font info.
14594  */
14595  if (windows->font_info != (XFontStruct *) NULL)
14596    (void) XFreeFont(display,windows->font_info);
14597  windows->font_info=XBestFont(display,resource_info,MagickFalse);
14598  if (windows->font_info == (XFontStruct *) NULL)
14599    ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14600      resource_info->font);
14601  /*
14602    Initialize Standard Colormap.
14603  */
14604  map_info=windows->map_info;
14605  icon_map=windows->icon_map;
14606  visual_info=windows->visual_info;
14607  icon_visual=windows->icon_visual;
14608  pixel=windows->pixel_info;
14609  icon_pixel=windows->icon_pixel;
14610  font_info=windows->font_info;
14611  icon_resources=windows->icon_resources;
14612  class_hints=windows->class_hints;
14613  manager_hints=windows->manager_hints;
14614  root_window=XRootWindow(display,visual_info->screen);
14615  nexus=NewImageList();
14616  if (display_image->debug != MagickFalse)
14617    {
14618      (void) LogMagickEvent(X11Event,GetMagickModule(),
14619        "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14620        (double) display_image->scene,(double) display_image->columns,
14621        (double) display_image->rows);
14622      if (display_image->colors != 0)
14623        (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14624          display_image->colors);
14625      (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14626        display_image->magick);
14627    }
14628  XMakeStandardColormap(display,visual_info,resource_info,display_image,
14629    map_info,pixel,exception);
14630  display_image->taint=MagickFalse;
14631  /*
14632    Initialize graphic context.
14633  */
14634  windows->context.id=(Window) NULL;
14635  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14636    resource_info,&windows->context);
14637  (void) CloneString(&class_hints->res_name,resource_info->client_name);
14638  (void) CloneString(&class_hints->res_class,resource_info->client_name);
14639  class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14640  manager_hints->flags=InputHint | StateHint;
14641  manager_hints->input=MagickFalse;
14642  manager_hints->initial_state=WithdrawnState;
14643  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14644    &windows->context);
14645  if (display_image->debug != MagickFalse)
14646    (void) LogMagickEvent(X11Event,GetMagickModule(),
14647      "Window id: 0x%lx (context)",windows->context.id);
14648  context_values.background=pixel->background_color.pixel;
14649  context_values.font=font_info->fid;
14650  context_values.foreground=pixel->foreground_color.pixel;
14651  context_values.graphics_exposures=MagickFalse;
14652  context_mask=(MagickStatusType)
14653    (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14654  if (pixel->annotate_context != (GC) NULL)
14655    (void) XFreeGC(display,pixel->annotate_context);
14656  pixel->annotate_context=XCreateGC(display,windows->context.id,
14657    context_mask,&context_values);
14658  if (pixel->annotate_context == (GC) NULL)
14659    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14660      display_image->filename);
14661  context_values.background=pixel->depth_color.pixel;
14662  if (pixel->widget_context != (GC) NULL)
14663    (void) XFreeGC(display,pixel->widget_context);
14664  pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14665    &context_values);
14666  if (pixel->widget_context == (GC) NULL)
14667    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14668      display_image->filename);
14669  context_values.background=pixel->foreground_color.pixel;
14670  context_values.foreground=pixel->background_color.pixel;
14671  context_values.plane_mask=context_values.background ^
14672    context_values.foreground;
14673  if (pixel->highlight_context != (GC) NULL)
14674    (void) XFreeGC(display,pixel->highlight_context);
14675  pixel->highlight_context=XCreateGC(display,windows->context.id,
14676    (size_t) (context_mask | GCPlaneMask),&context_values);
14677  if (pixel->highlight_context == (GC) NULL)
14678    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14679      display_image->filename);
14680  (void) XDestroyWindow(display,windows->context.id);
14681  /*
14682    Initialize icon window.
14683  */
14684  XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14685    icon_resources,&windows->icon);
14686  windows->icon.geometry=resource_info->icon_geometry;
14687  XBestIconSize(display,&windows->icon,display_image);
14688  windows->icon.attributes.colormap=XDefaultColormap(display,
14689    icon_visual->screen);
14690  windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14691  manager_hints->flags=InputHint | StateHint;
14692  manager_hints->input=MagickFalse;
14693  manager_hints->initial_state=IconicState;
14694  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14695    &windows->icon);
14696  if (display_image->debug != MagickFalse)
14697    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14698      windows->icon.id);
14699  /*
14700    Initialize graphic context for icon window.
14701  */
14702  if (icon_pixel->annotate_context != (GC) NULL)
14703    (void) XFreeGC(display,icon_pixel->annotate_context);
14704  context_values.background=icon_pixel->background_color.pixel;
14705  context_values.foreground=icon_pixel->foreground_color.pixel;
14706  icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14707    (size_t) (GCBackground | GCForeground),&context_values);
14708  if (icon_pixel->annotate_context == (GC) NULL)
14709    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14710      display_image->filename);
14711  windows->icon.annotate_context=icon_pixel->annotate_context;
14712  /*
14713    Initialize Image window.
14714  */
14715  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14716    &windows->image);
14717  windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14718  if (resource_info->use_shared_memory == MagickFalse)
14719    windows->image.shared_memory=MagickFalse;
14720  if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14721    {
14722      char
14723        *title;
14724
14725      title=InterpretImageProperties(resource_info->image_info,display_image,
14726        resource_info->title,exception);
14727      (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
14728      (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
14729      title=DestroyString(title);
14730    }
14731  else
14732    {
14733      char
14734        filename[MaxTextExtent];
14735
14736      /*
14737        Window name is the base of the filename.
14738      */
14739      GetPathComponent(display_image->magick_filename,TailPath,filename);
14740      if (display_image->scene == 0)
14741        (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14742          "%s: %s",MagickPackageName,filename);
14743      else
14744        (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14745          "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14746          (double) display_image->scene,(double) GetImageListLength(
14747          display_image));
14748      (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
14749    }
14750  if (resource_info->immutable)
14751    windows->image.immutable=MagickTrue;
14752  windows->image.use_pixmap=resource_info->use_pixmap;
14753  windows->image.geometry=resource_info->image_geometry;
14754  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14755    XDisplayWidth(display,visual_info->screen),
14756    XDisplayHeight(display,visual_info->screen));
14757  geometry_info.width=display_image->columns;
14758  geometry_info.height=display_image->rows;
14759  geometry_info.x=0;
14760  geometry_info.y=0;
14761  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14762    &geometry_info.width,&geometry_info.height);
14763  windows->image.width=(unsigned int) geometry_info.width;
14764  windows->image.height=(unsigned int) geometry_info.height;
14765  windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14766    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14767    KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14768    PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14769  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14770    resource_info,&windows->backdrop);
14771  if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14772    {
14773      /*
14774        Initialize backdrop window.
14775      */
14776      windows->backdrop.x=0;
14777      windows->backdrop.y=0;
14778      (void) CloneString(&windows->backdrop.name,"Backdrop");
14779      windows->backdrop.flags=(size_t) (USSize | USPosition);
14780      windows->backdrop.width=(unsigned int)
14781        XDisplayWidth(display,visual_info->screen);
14782      windows->backdrop.height=(unsigned int)
14783        XDisplayHeight(display,visual_info->screen);
14784      windows->backdrop.border_width=0;
14785      windows->backdrop.immutable=MagickTrue;
14786      windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14787        ButtonReleaseMask;
14788      windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14789        StructureNotifyMask;
14790      manager_hints->flags=IconWindowHint | InputHint | StateHint;
14791      manager_hints->icon_window=windows->icon.id;
14792      manager_hints->input=MagickTrue;
14793      manager_hints->initial_state=resource_info->iconic ? IconicState :
14794        NormalState;
14795      XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14796        &windows->backdrop);
14797      if (display_image->debug != MagickFalse)
14798        (void) LogMagickEvent(X11Event,GetMagickModule(),
14799          "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14800      (void) XMapWindow(display,windows->backdrop.id);
14801      (void) XClearWindow(display,windows->backdrop.id);
14802      if (windows->image.id != (Window) NULL)
14803        {
14804          (void) XDestroyWindow(display,windows->image.id);
14805          windows->image.id=(Window) NULL;
14806        }
14807      /*
14808        Position image in the center the backdrop.
14809      */
14810      windows->image.flags|=USPosition;
14811      windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14812        (windows->image.width/2);
14813      windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14814        (windows->image.height/2);
14815    }
14816  manager_hints->flags=IconWindowHint | InputHint | StateHint;
14817  manager_hints->icon_window=windows->icon.id;
14818  manager_hints->input=MagickTrue;
14819  manager_hints->initial_state=resource_info->iconic ? IconicState :
14820    NormalState;
14821  if (windows->group_leader.id != (Window) NULL)
14822    {
14823      /*
14824        Follow the leader.
14825      */
14826      manager_hints->flags|=WindowGroupHint;
14827      manager_hints->window_group=windows->group_leader.id;
14828      (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14829      if (display_image->debug != MagickFalse)
14830        (void) LogMagickEvent(X11Event,GetMagickModule(),
14831          "Window id: 0x%lx (group leader)",windows->group_leader.id);
14832    }
14833  XMakeWindow(display,
14834    (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14835    argv,argc,class_hints,manager_hints,&windows->image);
14836  (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14837    XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14838  if (windows->group_leader.id != (Window) NULL)
14839    (void) XSetTransientForHint(display,windows->image.id,
14840      windows->group_leader.id);
14841  if (display_image->debug != MagickFalse)
14842    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14843      windows->image.id);
14844  /*
14845    Initialize Info widget.
14846  */
14847  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14848    &windows->info);
14849  (void) CloneString(&windows->info.name,"Info");
14850  (void) CloneString(&windows->info.icon_name,"Info");
14851  windows->info.border_width=1;
14852  windows->info.x=2;
14853  windows->info.y=2;
14854  windows->info.flags|=PPosition;
14855  windows->info.attributes.win_gravity=UnmapGravity;
14856  windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14857    StructureNotifyMask;
14858  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14859  manager_hints->input=MagickFalse;
14860  manager_hints->initial_state=NormalState;
14861  manager_hints->window_group=windows->image.id;
14862  XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14863    &windows->info);
14864  windows->info.highlight_stipple=XCreateBitmapFromData(display,
14865    windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14866  windows->info.shadow_stipple=XCreateBitmapFromData(display,
14867    windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14868  (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14869  if (windows->image.mapped != MagickFalse)
14870    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14871  if (display_image->debug != MagickFalse)
14872    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14873      windows->info.id);
14874  /*
14875    Initialize Command widget.
14876  */
14877  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14878    resource_info,&windows->command);
14879  windows->command.data=MagickMenus;
14880  (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14881  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
14882    resource_info->client_name);
14883  windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14884    resource_name,"geometry",(char *) NULL);
14885  (void) CloneString(&windows->command.name,MagickTitle);
14886  windows->command.border_width=0;
14887  windows->command.flags|=PPosition;
14888  windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14889    ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14890    OwnerGrabButtonMask | StructureNotifyMask;
14891  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14892  manager_hints->input=MagickTrue;
14893  manager_hints->initial_state=NormalState;
14894  manager_hints->window_group=windows->image.id;
14895  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14896    &windows->command);
14897  windows->command.highlight_stipple=XCreateBitmapFromData(display,
14898    windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14899    HighlightHeight);
14900  windows->command.shadow_stipple=XCreateBitmapFromData(display,
14901    windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14902  (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14903  if (windows->command.mapped != MagickFalse)
14904    (void) XMapRaised(display,windows->command.id);
14905  if (display_image->debug != MagickFalse)
14906    (void) LogMagickEvent(X11Event,GetMagickModule(),
14907      "Window id: 0x%lx (command)",windows->command.id);
14908  /*
14909    Initialize Widget window.
14910  */
14911  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14912    resource_info,&windows->widget);
14913  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
14914    resource_info->client_name);
14915  windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14916    resource_name,"geometry",(char *) NULL);
14917  windows->widget.border_width=0;
14918  windows->widget.flags|=PPosition;
14919  windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14920    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14921    KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14922    StructureNotifyMask;
14923  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14924  manager_hints->input=MagickTrue;
14925  manager_hints->initial_state=NormalState;
14926  manager_hints->window_group=windows->image.id;
14927  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14928    &windows->widget);
14929  windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14930    windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14931  windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14932    windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14933  (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14934  if (display_image->debug != MagickFalse)
14935    (void) LogMagickEvent(X11Event,GetMagickModule(),
14936      "Window id: 0x%lx (widget)",windows->widget.id);
14937  /*
14938    Initialize popup window.
14939  */
14940  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14941    resource_info,&windows->popup);
14942  windows->popup.border_width=0;
14943  windows->popup.flags|=PPosition;
14944  windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14945    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14946    KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14947  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14948  manager_hints->input=MagickTrue;
14949  manager_hints->initial_state=NormalState;
14950  manager_hints->window_group=windows->image.id;
14951  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14952    &windows->popup);
14953  windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14954    windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14955  windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14956    windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14957  (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14958  if (display_image->debug != MagickFalse)
14959    (void) LogMagickEvent(X11Event,GetMagickModule(),
14960      "Window id: 0x%lx (pop up)",windows->popup.id);
14961  /*
14962    Initialize Magnify window and cursor.
14963  */
14964  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14965    resource_info,&windows->magnify);
14966  if (resource_info->use_shared_memory == MagickFalse)
14967    windows->magnify.shared_memory=MagickFalse;
14968  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
14969    resource_info->client_name);
14970  windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14971    resource_name,"geometry",(char *) NULL);
14972  (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14973    resource_info->magnify);
14974  if (windows->magnify.cursor != (Cursor) NULL)
14975    (void) XFreeCursor(display,windows->magnify.cursor);
14976  windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14977    map_info->colormap,resource_info->background_color,
14978    resource_info->foreground_color);
14979  if (windows->magnify.cursor == (Cursor) NULL)
14980    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14981      display_image->filename);
14982  windows->magnify.width=MagnifySize;
14983  windows->magnify.height=MagnifySize;
14984  windows->magnify.flags|=PPosition;
14985  windows->magnify.min_width=MagnifySize;
14986  windows->magnify.min_height=MagnifySize;
14987  windows->magnify.width_inc=MagnifySize;
14988  windows->magnify.height_inc=MagnifySize;
14989  windows->magnify.data=resource_info->magnify;
14990  windows->magnify.attributes.cursor=windows->magnify.cursor;
14991  windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14992    ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14993    StructureNotifyMask;
14994  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14995  manager_hints->input=MagickTrue;
14996  manager_hints->initial_state=NormalState;
14997  manager_hints->window_group=windows->image.id;
14998  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14999    &windows->magnify);
15000  if (display_image->debug != MagickFalse)
15001    (void) LogMagickEvent(X11Event,GetMagickModule(),
15002      "Window id: 0x%lx (magnify)",windows->magnify.id);
15003  (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
15004  /*
15005    Initialize panning window.
15006  */
15007  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
15008    resource_info,&windows->pan);
15009  (void) CloneString(&windows->pan.name,"Pan Icon");
15010  windows->pan.width=windows->icon.width;
15011  windows->pan.height=windows->icon.height;
15012  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
15013    resource_info->client_name);
15014  windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
15015    resource_name,"geometry",(char *) NULL);
15016  (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
15017    &windows->pan.width,&windows->pan.height);
15018  windows->pan.flags|=PPosition;
15019  windows->pan.immutable=MagickTrue;
15020  windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
15021    ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
15022    StructureNotifyMask;
15023  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
15024  manager_hints->input=MagickFalse;
15025  manager_hints->initial_state=NormalState;
15026  manager_hints->window_group=windows->image.id;
15027  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
15028    &windows->pan);
15029  if (display_image->debug != MagickFalse)
15030    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
15031      windows->pan.id);
15032  (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
15033  if (windows->info.mapped != MagickFalse)
15034    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15035  if ((windows->image.mapped == MagickFalse) ||
15036      (windows->backdrop.id != (Window) NULL))
15037    (void) XMapWindow(display,windows->image.id);
15038  /*
15039    Set our progress monitor and warning handlers.
15040  */
15041  if (warning_handler == (WarningHandler) NULL)
15042    {
15043      warning_handler=resource_info->display_warnings ?
15044        SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
15045      warning_handler=resource_info->display_warnings ?
15046        SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
15047    }
15048  /*
15049    Initialize Image and Magnify X images.
15050  */
15051  windows->image.x=0;
15052  windows->image.y=0;
15053  windows->magnify.shape=MagickFalse;
15054  width=(unsigned int) display_image->columns;
15055  height=(unsigned int) display_image->rows;
15056  if ((display_image->columns != width) || (display_image->rows != height))
15057    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15058      display_image->filename);
15059  status=XMakeImage(display,resource_info,&windows->image,display_image,
15060    width,height,exception);
15061  if (status == MagickFalse)
15062    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15063      display_image->filename);
15064  status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
15065    windows->magnify.width,windows->magnify.height,exception);
15066  if (status == MagickFalse)
15067    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15068      display_image->filename);
15069  if (windows->magnify.mapped != MagickFalse)
15070    (void) XMapRaised(display,windows->magnify.id);
15071  if (windows->pan.mapped != MagickFalse)
15072    (void) XMapRaised(display,windows->pan.id);
15073  windows->image.window_changes.width=(int) display_image->columns;
15074  windows->image.window_changes.height=(int) display_image->rows;
15075  (void) XConfigureImage(display,resource_info,windows,display_image,exception);
15076  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15077  (void) XSync(display,MagickFalse);
15078  /*
15079    Respond to events.
15080  */
15081  delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
15082  timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15083  update_time=0;
15084  if (resource_info->update != MagickFalse)
15085    {
15086      MagickBooleanType
15087        status;
15088
15089      /*
15090        Determine when file data was last modified.
15091      */
15092      status=GetPathAttributes(display_image->filename,&attributes);
15093      if (status != MagickFalse)
15094        update_time=attributes.st_mtime;
15095    }
15096  *state&=(~FormerImageState);
15097  *state&=(~MontageImageState);
15098  *state&=(~NextImageState);
15099  do
15100  {
15101    /*
15102      Handle a window event.
15103    */
15104    if (windows->image.mapped != MagickFalse)
15105      if ((display_image->delay != 0) || (resource_info->update != 0))
15106        {
15107          if (timer < time((time_t *) NULL))
15108            {
15109              if (resource_info->update == MagickFalse)
15110                *state|=NextImageState | ExitState;
15111              else
15112                {
15113                  MagickBooleanType
15114                    status;
15115
15116                  /*
15117                    Determine if image file was modified.
15118                  */
15119                  status=GetPathAttributes(display_image->filename,&attributes);
15120                  if (status != MagickFalse)
15121                    if (update_time != attributes.st_mtime)
15122                      {
15123                        /*
15124                          Redisplay image.
15125                        */
15126                        (void) FormatLocaleString(
15127                          resource_info->image_info->filename,MaxTextExtent,
15128                          "%s:%s",display_image->magick,
15129                          display_image->filename);
15130                        nexus=ReadImage(resource_info->image_info,exception);
15131                        if (nexus != (Image *) NULL)
15132                          {
15133                            nexus=DestroyImage(nexus);
15134                            *state|=NextImageState | ExitState;
15135                          }
15136                      }
15137                  delay=display_image->delay/MagickMax(
15138                    display_image->ticks_per_second,1L);
15139                  timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15140                }
15141            }
15142          if (XEventsQueued(display,QueuedAfterFlush) == 0)
15143            {
15144              /*
15145                Do not block if delay > 0.
15146              */
15147              XDelay(display,SuspendTime << 2);
15148              continue;
15149            }
15150        }
15151    timestamp=time((time_t *) NULL);
15152    (void) XNextEvent(display,&event);
15153    if (windows->image.stasis == MagickFalse)
15154      windows->image.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15155        MagickTrue : MagickFalse;
15156    if (windows->magnify.stasis == MagickFalse)
15157      windows->magnify.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15158        MagickTrue : MagickFalse;
15159    if (event.xany.window == windows->command.id)
15160      {
15161        /*
15162          Select a command from the Command widget.
15163        */
15164        id=XCommandWidget(display,windows,CommandMenu,&event);
15165        if (id < 0)
15166          continue;
15167        (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15168        command_type=CommandMenus[id];
15169        if (id < MagickMenus)
15170          {
15171            /*
15172              Select a command from a pop-up menu.
15173            */
15174            entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15175              command);
15176            if (entry < 0)
15177              continue;
15178            (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15179            command_type=Commands[id][entry];
15180          }
15181        if (command_type != NullCommand)
15182          nexus=XMagickCommand(display,resource_info,windows,command_type,
15183            &display_image,exception);
15184        continue;
15185      }
15186    switch (event.type)
15187    {
15188      case ButtonPress:
15189      {
15190        if (display_image->debug != MagickFalse)
15191          (void) LogMagickEvent(X11Event,GetMagickModule(),
15192            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15193            event.xbutton.button,event.xbutton.x,event.xbutton.y);
15194        if ((event.xbutton.button == Button3) &&
15195            (event.xbutton.state & Mod1Mask))
15196          {
15197            /*
15198              Convert Alt-Button3 to Button2.
15199            */
15200            event.xbutton.button=Button2;
15201            event.xbutton.state&=(~Mod1Mask);
15202          }
15203        if (event.xbutton.window == windows->backdrop.id)
15204          {
15205            (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15206              event.xbutton.time);
15207            break;
15208          }
15209        if (event.xbutton.window == windows->image.id)
15210          {
15211            switch (event.xbutton.button)
15212            {
15213              case Button1:
15214              {
15215                if (resource_info->immutable)
15216                  {
15217                    /*
15218                      Select a command from the Virtual menu.
15219                    */
15220                    entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15221                      command);
15222                    if (entry >= 0)
15223                      nexus=XMagickCommand(display,resource_info,windows,
15224                        VirtualCommands[entry],&display_image,exception);
15225                    break;
15226                  }
15227                /*
15228                  Map/unmap Command widget.
15229                */
15230                if (windows->command.mapped != MagickFalse)
15231                  (void) XWithdrawWindow(display,windows->command.id,
15232                    windows->command.screen);
15233                else
15234                  {
15235                    (void) XCommandWidget(display,windows,CommandMenu,
15236                      (XEvent *) NULL);
15237                    (void) XMapRaised(display,windows->command.id);
15238                  }
15239                break;
15240              }
15241              case Button2:
15242              {
15243                /*
15244                  User pressed the image magnify button.
15245                */
15246                (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15247                  &display_image,exception);
15248                XMagnifyImage(display,windows,&event,exception);
15249                break;
15250              }
15251              case Button3:
15252              {
15253                if (resource_info->immutable)
15254                  {
15255                    /*
15256                      Select a command from the Virtual menu.
15257                    */
15258                    entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15259                      command);
15260                    if (entry >= 0)
15261                      nexus=XMagickCommand(display,resource_info,windows,
15262                        VirtualCommands[entry],&display_image,exception);
15263                    break;
15264                  }
15265                if (display_image->montage != (char *) NULL)
15266                  {
15267                    /*
15268                      Open or delete a tile from a visual image directory.
15269                    */
15270                    nexus=XTileImage(display,resource_info,windows,
15271                      display_image,&event,exception);
15272                    if (nexus != (Image *) NULL)
15273                      *state|=MontageImageState | NextImageState | ExitState;
15274                    vid_info.x=(short int) windows->image.x;
15275                    vid_info.y=(short int) windows->image.y;
15276                    break;
15277                  }
15278                /*
15279                  Select a command from the Short Cuts menu.
15280                */
15281                entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15282                  command);
15283                if (entry >= 0)
15284                  nexus=XMagickCommand(display,resource_info,windows,
15285                    ShortCutsCommands[entry],&display_image,exception);
15286                break;
15287              }
15288              case Button4:
15289              {
15290                /*
15291                  Wheel up.
15292                */
15293                XTranslateImage(display,windows,*image,XK_Up);
15294                break;
15295              }
15296              case Button5:
15297              {
15298                /*
15299                  Wheel down.
15300                */
15301                XTranslateImage(display,windows,*image,XK_Down);
15302                break;
15303              }
15304              default:
15305                break;
15306            }
15307            break;
15308          }
15309        if (event.xbutton.window == windows->magnify.id)
15310          {
15311            int
15312              factor;
15313
15314            static const char
15315              *MagnifyMenu[] =
15316              {
15317                "2",
15318                "4",
15319                "5",
15320                "6",
15321                "7",
15322                "8",
15323                "9",
15324                "3",
15325                (char *) NULL,
15326              };
15327
15328            static KeySym
15329              MagnifyCommands[] =
15330              {
15331                XK_2,
15332                XK_4,
15333                XK_5,
15334                XK_6,
15335                XK_7,
15336                XK_8,
15337                XK_9,
15338                XK_3
15339              };
15340
15341            /*
15342              Select a magnify factor from the pop-up menu.
15343            */
15344            factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15345            if (factor >= 0)
15346              XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor],
15347                exception);
15348            break;
15349          }
15350        if (event.xbutton.window == windows->pan.id)
15351          {
15352            switch (event.xbutton.button)
15353            {
15354              case Button4:
15355              {
15356                /*
15357                  Wheel up.
15358                */
15359                XTranslateImage(display,windows,*image,XK_Up);
15360                break;
15361              }
15362              case Button5:
15363              {
15364                /*
15365                  Wheel down.
15366                */
15367                XTranslateImage(display,windows,*image,XK_Down);
15368                break;
15369              }
15370              default:
15371              {
15372                XPanImage(display,windows,&event,exception);
15373                break;
15374              }
15375            }
15376            break;
15377          }
15378        delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15379          1L);
15380        timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15381        break;
15382      }
15383      case ButtonRelease:
15384      {
15385        if (display_image->debug != MagickFalse)
15386          (void) LogMagickEvent(X11Event,GetMagickModule(),
15387            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15388            event.xbutton.button,event.xbutton.x,event.xbutton.y);
15389        break;
15390      }
15391      case ClientMessage:
15392      {
15393        if (display_image->debug != MagickFalse)
15394          (void) LogMagickEvent(X11Event,GetMagickModule(),
15395            "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15396            event.xclient.message_type,event.xclient.format,(unsigned long)
15397            event.xclient.data.l[0]);
15398        if (event.xclient.message_type == windows->im_protocols)
15399          {
15400            if (*event.xclient.data.l == (long) windows->im_update_widget)
15401              {
15402                (void) CloneString(&windows->command.name,MagickTitle);
15403                windows->command.data=MagickMenus;
15404                (void) XCommandWidget(display,windows,CommandMenu,
15405                  (XEvent *) NULL);
15406                break;
15407              }
15408            if (*event.xclient.data.l == (long) windows->im_update_colormap)
15409              {
15410                /*
15411                  Update graphic context and window colormap.
15412                */
15413                for (i=0; i < (int) number_windows; i++)
15414                {
15415                  if (magick_windows[i]->id == windows->icon.id)
15416                    continue;
15417                  context_values.background=pixel->background_color.pixel;
15418                  context_values.foreground=pixel->foreground_color.pixel;
15419                  (void) XChangeGC(display,magick_windows[i]->annotate_context,
15420                    context_mask,&context_values);
15421                  (void) XChangeGC(display,magick_windows[i]->widget_context,
15422                    context_mask,&context_values);
15423                  context_values.background=pixel->foreground_color.pixel;
15424                  context_values.foreground=pixel->background_color.pixel;
15425                  context_values.plane_mask=context_values.background ^
15426                    context_values.foreground;
15427                  (void) XChangeGC(display,magick_windows[i]->highlight_context,
15428                    (size_t) (context_mask | GCPlaneMask),
15429                    &context_values);
15430                  magick_windows[i]->attributes.background_pixel=
15431                    pixel->background_color.pixel;
15432                  magick_windows[i]->attributes.border_pixel=
15433                    pixel->border_color.pixel;
15434                  magick_windows[i]->attributes.colormap=map_info->colormap;
15435                  (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15436                    (unsigned long) magick_windows[i]->mask,
15437                    &magick_windows[i]->attributes);
15438                }
15439                if (windows->pan.mapped != MagickFalse)
15440                  {
15441                    (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15442                      windows->pan.pixmap);
15443                    (void) XClearWindow(display,windows->pan.id);
15444                    XDrawPanRectangle(display,windows);
15445                  }
15446                if (windows->backdrop.id != (Window) NULL)
15447                  (void) XInstallColormap(display,map_info->colormap);
15448                break;
15449              }
15450            if (*event.xclient.data.l == (long) windows->im_former_image)
15451              {
15452                *state|=FormerImageState | ExitState;
15453                break;
15454              }
15455            if (*event.xclient.data.l == (long) windows->im_next_image)
15456              {
15457                *state|=NextImageState | ExitState;
15458                break;
15459              }
15460            if (*event.xclient.data.l == (long) windows->im_retain_colors)
15461              {
15462                *state|=RetainColorsState;
15463                break;
15464              }
15465            if (*event.xclient.data.l == (long) windows->im_exit)
15466              {
15467                *state|=ExitState;
15468                break;
15469              }
15470            break;
15471          }
15472        if (event.xclient.message_type == windows->dnd_protocols)
15473          {
15474            Atom
15475              selection,
15476              type;
15477
15478            int
15479              format,
15480              status;
15481
15482            unsigned char
15483              *data;
15484
15485            unsigned long
15486              after,
15487              length;
15488
15489            /*
15490              Display image named by the Drag-and-Drop selection.
15491            */
15492            if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15493              break;
15494            selection=XInternAtom(display,"DndSelection",MagickFalse);
15495            status=XGetWindowProperty(display,root_window,selection,0L,(long)
15496              MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15497              &length,&after,&data);
15498            if ((status != Success) || (length == 0))
15499              break;
15500            if (*event.xclient.data.l == 2)
15501              {
15502                /*
15503                  Offix DND.
15504                */
15505                (void) CopyMagickString(resource_info->image_info->filename,
15506                  (char *) data,MaxTextExtent);
15507              }
15508            else
15509              {
15510                /*
15511                  XDND.
15512                */
15513                if (strncmp((char *) data, "file:", 5) != 0)
15514                  {
15515                    (void) XFree((void *) data);
15516                    break;
15517                  }
15518                (void) CopyMagickString(resource_info->image_info->filename,
15519                  ((char *) data)+5,MaxTextExtent);
15520              }
15521            nexus=ReadImage(resource_info->image_info,exception);
15522            CatchException(exception);
15523            if (nexus != (Image *) NULL)
15524              *state|=NextImageState | ExitState;
15525            (void) XFree((void *) data);
15526            break;
15527          }
15528        /*
15529          If client window delete message, exit.
15530        */
15531        if (event.xclient.message_type != windows->wm_protocols)
15532          break;
15533        if (*event.xclient.data.l != (long) windows->wm_delete_window)
15534          break;
15535        (void) XWithdrawWindow(display,event.xclient.window,
15536          visual_info->screen);
15537        if (event.xclient.window == windows->image.id)
15538          {
15539            *state|=ExitState;
15540            break;
15541          }
15542        if (event.xclient.window == windows->pan.id)
15543          {
15544            /*
15545              Restore original image size when pan window is deleted.
15546            */
15547            windows->image.window_changes.width=windows->image.ximage->width;
15548            windows->image.window_changes.height=windows->image.ximage->height;
15549            (void) XConfigureImage(display,resource_info,windows,
15550              display_image,exception);
15551          }
15552        break;
15553      }
15554      case ConfigureNotify:
15555      {
15556        if (display_image->debug != MagickFalse)
15557          (void) LogMagickEvent(X11Event,GetMagickModule(),
15558            "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15559            event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15560            event.xconfigure.y,event.xconfigure.send_event);
15561        if (event.xconfigure.window == windows->image.id)
15562          {
15563            /*
15564              Image window has a new configuration.
15565            */
15566            if (event.xconfigure.send_event != 0)
15567              {
15568                XWindowChanges
15569                  window_changes;
15570
15571                /*
15572                  Position the transient windows relative of the Image window.
15573                */
15574                if (windows->command.geometry == (char *) NULL)
15575                  if (windows->command.mapped == MagickFalse)
15576                    {
15577                      windows->command.x=event.xconfigure.x-
15578                        windows->command.width-25;
15579                      windows->command.y=event.xconfigure.y;
15580                      XConstrainWindowPosition(display,&windows->command);
15581                      window_changes.x=windows->command.x;
15582                      window_changes.y=windows->command.y;
15583                      (void) XReconfigureWMWindow(display,windows->command.id,
15584                        windows->command.screen,(unsigned int) (CWX | CWY),
15585                        &window_changes);
15586                    }
15587                if (windows->widget.geometry == (char *) NULL)
15588                  if (windows->widget.mapped == MagickFalse)
15589                    {
15590                      windows->widget.x=event.xconfigure.x+
15591                        event.xconfigure.width/10;
15592                      windows->widget.y=event.xconfigure.y+
15593                        event.xconfigure.height/10;
15594                      XConstrainWindowPosition(display,&windows->widget);
15595                      window_changes.x=windows->widget.x;
15596                      window_changes.y=windows->widget.y;
15597                      (void) XReconfigureWMWindow(display,windows->widget.id,
15598                        windows->widget.screen,(unsigned int) (CWX | CWY),
15599                        &window_changes);
15600                    }
15601                if (windows->magnify.geometry == (char *) NULL)
15602                  if (windows->magnify.mapped == MagickFalse)
15603                    {
15604                      windows->magnify.x=event.xconfigure.x+
15605                        event.xconfigure.width+25;
15606                      windows->magnify.y=event.xconfigure.y;
15607                      XConstrainWindowPosition(display,&windows->magnify);
15608                      window_changes.x=windows->magnify.x;
15609                      window_changes.y=windows->magnify.y;
15610                      (void) XReconfigureWMWindow(display,windows->magnify.id,
15611                        windows->magnify.screen,(unsigned int) (CWX | CWY),
15612                        &window_changes);
15613                    }
15614                if (windows->pan.geometry == (char *) NULL)
15615                  if (windows->pan.mapped == MagickFalse)
15616                    {
15617                      windows->pan.x=event.xconfigure.x+
15618                        event.xconfigure.width+25;
15619                      windows->pan.y=event.xconfigure.y+
15620                        windows->magnify.height+50;
15621                      XConstrainWindowPosition(display,&windows->pan);
15622                      window_changes.x=windows->pan.x;
15623                      window_changes.y=windows->pan.y;
15624                      (void) XReconfigureWMWindow(display,windows->pan.id,
15625                        windows->pan.screen,(unsigned int) (CWX | CWY),
15626                        &window_changes);
15627                    }
15628              }
15629            if ((event.xconfigure.width == (int) windows->image.width) &&
15630                (event.xconfigure.height == (int) windows->image.height))
15631              break;
15632            windows->image.width=(unsigned int) event.xconfigure.width;
15633            windows->image.height=(unsigned int) event.xconfigure.height;
15634            windows->image.x=0;
15635            windows->image.y=0;
15636            if (display_image->montage != (char *) NULL)
15637              {
15638                windows->image.x=vid_info.x;
15639                windows->image.y=vid_info.y;
15640              }
15641            if ((windows->image.mapped != MagickFalse) &&
15642                (windows->image.stasis != MagickFalse))
15643              {
15644                /*
15645                  Update image window configuration.
15646                */
15647                windows->image.window_changes.width=event.xconfigure.width;
15648                windows->image.window_changes.height=event.xconfigure.height;
15649                (void) XConfigureImage(display,resource_info,windows,
15650                  display_image,exception);
15651              }
15652            /*
15653              Update pan window configuration.
15654            */
15655            if ((event.xconfigure.width < windows->image.ximage->width) ||
15656                (event.xconfigure.height < windows->image.ximage->height))
15657              {
15658                (void) XMapRaised(display,windows->pan.id);
15659                XDrawPanRectangle(display,windows);
15660              }
15661            else
15662              if (windows->pan.mapped != MagickFalse)
15663                (void) XWithdrawWindow(display,windows->pan.id,
15664                  windows->pan.screen);
15665            break;
15666          }
15667        if (event.xconfigure.window == windows->magnify.id)
15668          {
15669            unsigned int
15670              magnify;
15671
15672            /*
15673              Magnify window has a new configuration.
15674            */
15675            windows->magnify.width=(unsigned int) event.xconfigure.width;
15676            windows->magnify.height=(unsigned int) event.xconfigure.height;
15677            if (windows->magnify.mapped == MagickFalse)
15678              break;
15679            magnify=1;
15680            while ((int) magnify <= event.xconfigure.width)
15681              magnify<<=1;
15682            while ((int) magnify <= event.xconfigure.height)
15683              magnify<<=1;
15684            magnify>>=1;
15685            if (((int) magnify != event.xconfigure.width) ||
15686                ((int) magnify != event.xconfigure.height))
15687              {
15688                window_changes.width=(int) magnify;
15689                window_changes.height=(int) magnify;
15690                (void) XReconfigureWMWindow(display,windows->magnify.id,
15691                  windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15692                  &window_changes);
15693                break;
15694              }
15695            if ((windows->magnify.mapped != MagickFalse) &&
15696                (windows->magnify.stasis != MagickFalse))
15697              {
15698                status=XMakeImage(display,resource_info,&windows->magnify,
15699                  display_image,windows->magnify.width,windows->magnify.height,
15700                  exception);
15701                XMakeMagnifyImage(display,windows,exception);
15702              }
15703            break;
15704          }
15705        if ((windows->magnify.mapped != MagickFalse) &&
15706            (event.xconfigure.window == windows->pan.id))
15707          {
15708            /*
15709              Pan icon window has a new configuration.
15710            */
15711            if (event.xconfigure.send_event != 0)
15712              {
15713                windows->pan.x=event.xconfigure.x;
15714                windows->pan.y=event.xconfigure.y;
15715              }
15716            windows->pan.width=(unsigned int) event.xconfigure.width;
15717            windows->pan.height=(unsigned int) event.xconfigure.height;
15718            break;
15719          }
15720        if (event.xconfigure.window == windows->icon.id)
15721          {
15722            /*
15723              Icon window has a new configuration.
15724            */
15725            windows->icon.width=(unsigned int) event.xconfigure.width;
15726            windows->icon.height=(unsigned int) event.xconfigure.height;
15727            break;
15728          }
15729        break;
15730      }
15731      case DestroyNotify:
15732      {
15733        /*
15734          Group leader has exited.
15735        */
15736        if (display_image->debug != MagickFalse)
15737          (void) LogMagickEvent(X11Event,GetMagickModule(),
15738            "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15739        if (event.xdestroywindow.window == windows->group_leader.id)
15740          {
15741            *state|=ExitState;
15742            break;
15743          }
15744        break;
15745      }
15746      case EnterNotify:
15747      {
15748        /*
15749          Selectively install colormap.
15750        */
15751        if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15752          if (event.xcrossing.mode != NotifyUngrab)
15753            XInstallColormap(display,map_info->colormap);
15754        break;
15755      }
15756      case Expose:
15757      {
15758        if (display_image->debug != MagickFalse)
15759          (void) LogMagickEvent(X11Event,GetMagickModule(),
15760            "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15761            event.xexpose.width,event.xexpose.height,event.xexpose.x,
15762            event.xexpose.y);
15763        /*
15764          Refresh windows that are now exposed.
15765        */
15766        if ((event.xexpose.window == windows->image.id) &&
15767            (windows->image.mapped != MagickFalse))
15768          {
15769            XRefreshWindow(display,&windows->image,&event);
15770            delay=display_image->delay/MagickMax(
15771              display_image->ticks_per_second,1L);
15772            timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15773            break;
15774          }
15775        if ((event.xexpose.window == windows->magnify.id) &&
15776            (windows->magnify.mapped != MagickFalse))
15777          {
15778            XMakeMagnifyImage(display,windows,exception);
15779            break;
15780          }
15781        if (event.xexpose.window == windows->pan.id)
15782          {
15783            XDrawPanRectangle(display,windows);
15784            break;
15785          }
15786        if (event.xexpose.window == windows->icon.id)
15787          {
15788            XRefreshWindow(display,&windows->icon,&event);
15789            break;
15790          }
15791        break;
15792      }
15793      case KeyPress:
15794      {
15795        int
15796          length;
15797
15798        /*
15799          Respond to a user key press.
15800        */
15801        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15802          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15803        *(command+length)='\0';
15804        if (display_image->debug != MagickFalse)
15805          (void) LogMagickEvent(X11Event,GetMagickModule(),
15806            "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15807            key_symbol,command);
15808        if (event.xkey.window == windows->image.id)
15809          {
15810            command_type=XImageWindowCommand(display,resource_info,windows,
15811              event.xkey.state,key_symbol,&display_image,exception);
15812            if (command_type != NullCommand)
15813              nexus=XMagickCommand(display,resource_info,windows,command_type,
15814                &display_image,exception);
15815          }
15816        if (event.xkey.window == windows->magnify.id)
15817          XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol,
15818            exception);
15819        if (event.xkey.window == windows->pan.id)
15820          {
15821            if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15822              (void) XWithdrawWindow(display,windows->pan.id,
15823                windows->pan.screen);
15824            else
15825              if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15826                XTextViewWidget(display,resource_info,windows,MagickFalse,
15827                  "Help Viewer - Image Pan",ImagePanHelp);
15828              else
15829                XTranslateImage(display,windows,*image,key_symbol);
15830          }
15831        delay=display_image->delay/MagickMax(
15832          display_image->ticks_per_second,1L);
15833        timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15834        break;
15835      }
15836      case KeyRelease:
15837      {
15838        /*
15839          Respond to a user key release.
15840        */
15841        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15842          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15843        if (display_image->debug != MagickFalse)
15844          (void) LogMagickEvent(X11Event,GetMagickModule(),
15845            "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15846        break;
15847      }
15848      case LeaveNotify:
15849      {
15850        /*
15851          Selectively uninstall colormap.
15852        */
15853        if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15854          if (event.xcrossing.mode != NotifyUngrab)
15855            XUninstallColormap(display,map_info->colormap);
15856        break;
15857      }
15858      case MapNotify:
15859      {
15860        if (display_image->debug != MagickFalse)
15861          (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15862            event.xmap.window);
15863        if (event.xmap.window == windows->backdrop.id)
15864          {
15865            (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15866              CurrentTime);
15867            windows->backdrop.mapped=MagickTrue;
15868            break;
15869          }
15870        if (event.xmap.window == windows->image.id)
15871          {
15872            if (windows->backdrop.id != (Window) NULL)
15873              (void) XInstallColormap(display,map_info->colormap);
15874            if (LocaleCompare(display_image->magick,"LOGO") == 0)
15875              {
15876                if (LocaleCompare(display_image->filename,"LOGO") == 0)
15877                  nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15878              }
15879            if (((int) windows->image.width < windows->image.ximage->width) ||
15880                ((int) windows->image.height < windows->image.ximage->height))
15881              (void) XMapRaised(display,windows->pan.id);
15882            windows->image.mapped=MagickTrue;
15883            break;
15884          }
15885        if (event.xmap.window == windows->magnify.id)
15886          {
15887            XMakeMagnifyImage(display,windows,exception);
15888            windows->magnify.mapped=MagickTrue;
15889            (void) XWithdrawWindow(display,windows->info.id,
15890              windows->info.screen);
15891            break;
15892          }
15893        if (event.xmap.window == windows->pan.id)
15894          {
15895            XMakePanImage(display,resource_info,windows,display_image,
15896              exception);
15897            windows->pan.mapped=MagickTrue;
15898            break;
15899          }
15900        if (event.xmap.window == windows->info.id)
15901          {
15902            windows->info.mapped=MagickTrue;
15903            break;
15904          }
15905        if (event.xmap.window == windows->icon.id)
15906          {
15907            MagickBooleanType
15908              taint;
15909
15910            /*
15911              Create an icon image.
15912            */
15913            taint=display_image->taint;
15914            XMakeStandardColormap(display,icon_visual,icon_resources,
15915              display_image,icon_map,icon_pixel,exception);
15916            (void) XMakeImage(display,icon_resources,&windows->icon,
15917              display_image,windows->icon.width,windows->icon.height,
15918              exception);
15919            display_image->taint=taint;
15920            (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15921              windows->icon.pixmap);
15922            (void) XClearWindow(display,windows->icon.id);
15923            (void) XWithdrawWindow(display,windows->info.id,
15924              windows->info.screen);
15925            windows->icon.mapped=MagickTrue;
15926            break;
15927          }
15928        if (event.xmap.window == windows->command.id)
15929          {
15930            windows->command.mapped=MagickTrue;
15931            break;
15932          }
15933        if (event.xmap.window == windows->popup.id)
15934          {
15935            windows->popup.mapped=MagickTrue;
15936            break;
15937          }
15938        if (event.xmap.window == windows->widget.id)
15939          {
15940            windows->widget.mapped=MagickTrue;
15941            break;
15942          }
15943        break;
15944      }
15945      case MappingNotify:
15946      {
15947        (void) XRefreshKeyboardMapping(&event.xmapping);
15948        break;
15949      }
15950      case NoExpose:
15951        break;
15952      case PropertyNotify:
15953      {
15954        Atom
15955          type;
15956
15957        int
15958          format,
15959          status;
15960
15961        unsigned char
15962          *data;
15963
15964        unsigned long
15965          after,
15966          length;
15967
15968        if (display_image->debug != MagickFalse)
15969          (void) LogMagickEvent(X11Event,GetMagickModule(),
15970            "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15971            event.xproperty.atom,event.xproperty.state);
15972        if (event.xproperty.atom != windows->im_remote_command)
15973          break;
15974        /*
15975          Display image named by the remote command protocol.
15976        */
15977        status=XGetWindowProperty(display,event.xproperty.window,
15978          event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15979          AnyPropertyType,&type,&format,&length,&after,&data);
15980        if ((status != Success) || (length == 0))
15981          break;
15982        if (LocaleCompare((char *) data,"-quit") == 0)
15983          {
15984            XClientMessage(display,windows->image.id,windows->im_protocols,
15985              windows->im_exit,CurrentTime);
15986            (void) XFree((void *) data);
15987            break;
15988          }
15989        (void) CopyMagickString(resource_info->image_info->filename,
15990          (char *) data,MaxTextExtent);
15991        (void) XFree((void *) data);
15992        nexus=ReadImage(resource_info->image_info,exception);
15993        CatchException(exception);
15994        if (nexus != (Image *) NULL)
15995          *state|=NextImageState | ExitState;
15996        break;
15997      }
15998      case ReparentNotify:
15999      {
16000        if (display_image->debug != MagickFalse)
16001          (void) LogMagickEvent(X11Event,GetMagickModule(),
16002            "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
16003            event.xreparent.window);
16004        break;
16005      }
16006      case UnmapNotify:
16007      {
16008        if (display_image->debug != MagickFalse)
16009          (void) LogMagickEvent(X11Event,GetMagickModule(),
16010            "Unmap Notify: 0x%lx",event.xunmap.window);
16011        if (event.xunmap.window == windows->backdrop.id)
16012          {
16013            windows->backdrop.mapped=MagickFalse;
16014            break;
16015          }
16016        if (event.xunmap.window == windows->image.id)
16017          {
16018            windows->image.mapped=MagickFalse;
16019            break;
16020          }
16021        if (event.xunmap.window == windows->magnify.id)
16022          {
16023            windows->magnify.mapped=MagickFalse;
16024            break;
16025          }
16026        if (event.xunmap.window == windows->pan.id)
16027          {
16028            windows->pan.mapped=MagickFalse;
16029            break;
16030          }
16031        if (event.xunmap.window == windows->info.id)
16032          {
16033            windows->info.mapped=MagickFalse;
16034            break;
16035          }
16036        if (event.xunmap.window == windows->icon.id)
16037          {
16038            if (map_info->colormap == icon_map->colormap)
16039              XConfigureImageColormap(display,resource_info,windows,
16040                display_image,exception);
16041            (void) XFreeStandardColormap(display,icon_visual,icon_map,
16042              icon_pixel);
16043            windows->icon.mapped=MagickFalse;
16044            break;
16045          }
16046        if (event.xunmap.window == windows->command.id)
16047          {
16048            windows->command.mapped=MagickFalse;
16049            break;
16050          }
16051        if (event.xunmap.window == windows->popup.id)
16052          {
16053            if (windows->backdrop.id != (Window) NULL)
16054              (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16055                CurrentTime);
16056            windows->popup.mapped=MagickFalse;
16057            break;
16058          }
16059        if (event.xunmap.window == windows->widget.id)
16060          {
16061            if (windows->backdrop.id != (Window) NULL)
16062              (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16063                CurrentTime);
16064            windows->widget.mapped=MagickFalse;
16065            break;
16066          }
16067        break;
16068      }
16069      default:
16070      {
16071        if (display_image->debug != MagickFalse)
16072          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
16073            event.type);
16074        break;
16075      }
16076    }
16077  } while (!(*state & ExitState));
16078  if ((*state & ExitState) == 0)
16079    (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
16080      &display_image,exception);
16081  else
16082    if (resource_info->confirm_edit != MagickFalse)
16083      {
16084        /*
16085          Query user if image has changed.
16086        */
16087        if ((resource_info->immutable == MagickFalse) &&
16088            (display_image->taint != MagickFalse))
16089          {
16090            int
16091              status;
16092
16093            status=XConfirmWidget(display,windows,"Your image changed.",
16094              "Do you want to save it");
16095            if (status == 0)
16096              *state&=(~ExitState);
16097            else
16098              if (status > 0)
16099                (void) XMagickCommand(display,resource_info,windows,SaveCommand,
16100                  &display_image,exception);
16101          }
16102      }
16103  if ((windows->visual_info->klass == GrayScale) ||
16104      (windows->visual_info->klass == PseudoColor) ||
16105      (windows->visual_info->klass == DirectColor))
16106    {
16107      /*
16108        Withdraw pan and Magnify window.
16109      */
16110      if (windows->info.mapped != MagickFalse)
16111        (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16112      if (windows->magnify.mapped != MagickFalse)
16113        (void) XWithdrawWindow(display,windows->magnify.id,
16114          windows->magnify.screen);
16115      if (windows->command.mapped != MagickFalse)
16116        (void) XWithdrawWindow(display,windows->command.id,
16117          windows->command.screen);
16118    }
16119  if (windows->pan.mapped != MagickFalse)
16120    (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16121  if (resource_info->backdrop == MagickFalse)
16122    if (windows->backdrop.mapped)
16123      {
16124        (void) XWithdrawWindow(display,windows->backdrop.id,
16125          windows->backdrop.screen);
16126        (void) XDestroyWindow(display,windows->backdrop.id);
16127        windows->backdrop.id=(Window) NULL;
16128        (void) XWithdrawWindow(display,windows->image.id,
16129          windows->image.screen);
16130        (void) XDestroyWindow(display,windows->image.id);
16131        windows->image.id=(Window) NULL;
16132      }
16133  XSetCursorState(display,windows,MagickTrue);
16134  XCheckRefreshWindows(display,windows);
16135  if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16136    *state&=(~ExitState);
16137  if (*state & ExitState)
16138    {
16139      /*
16140        Free Standard Colormap.
16141      */
16142      (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16143      if (resource_info->map_type == (char *) NULL)
16144        (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16145      /*
16146        Free X resources.
16147      */
16148      if (resource_info->copy_image != (Image *) NULL)
16149        {
16150          resource_info->copy_image=DestroyImage(resource_info->copy_image);
16151          resource_info->copy_image=NewImageList();
16152        }
16153      DestroyXResources();
16154    }
16155  (void) XSync(display,MagickFalse);
16156  /*
16157    Restore our progress monitor and warning handlers.
16158  */
16159  (void) SetErrorHandler(warning_handler);
16160  (void) SetWarningHandler(warning_handler);
16161  /*
16162    Change to home directory.
16163  */
16164  directory=getcwd(working_directory,MaxTextExtent);
16165  (void) directory;
16166  {
16167    int
16168      status;
16169
16170    status=chdir(resource_info->home_directory);
16171    if (status == -1)
16172      (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
16173        "UnableToOpenFile","%s",resource_info->home_directory);
16174  }
16175  *image=display_image;
16176  return(nexus);
16177}
16178#else
16179
16180/*
16181%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16182%                                                                             %
16183%                                                                             %
16184%                                                                             %
16185+   D i s p l a y I m a g e s                                                 %
16186%                                                                             %
16187%                                                                             %
16188%                                                                             %
16189%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16190%
16191%  DisplayImages() displays an image sequence to any X window screen.  It
16192%  returns a value other than 0 if successful.  Check the exception member
16193%  of image to determine the reason for any failure.
16194%
16195%  The format of the DisplayImages method is:
16196%
16197%      MagickBooleanType DisplayImages(const ImageInfo *image_info,
16198%        Image *images,ExceptionInfo *exception)
16199%
16200%  A description of each parameter follows:
16201%
16202%    o image_info: the image info.
16203%
16204%    o image: the image.
16205%
16206%    o exception: return any errors or warnings in this structure.
16207%
16208*/
16209MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16210  Image *image,ExceptionInfo *exception)
16211{
16212  assert(image_info != (const ImageInfo *) NULL);
16213  assert(image_info->signature == MagickSignature);
16214  assert(image != (Image *) NULL);
16215  assert(image->signature == MagickSignature);
16216  if (image->debug != MagickFalse)
16217    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16218  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16219    "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image->filename);
16220  return(MagickFalse);
16221}
16222
16223/*
16224%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16225%                                                                             %
16226%                                                                             %
16227%                                                                             %
16228+   R e m o t e D i s p l a y C o m m a n d                                   %
16229%                                                                             %
16230%                                                                             %
16231%                                                                             %
16232%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16233%
16234%  RemoteDisplayCommand() encourages a remote display program to display the
16235%  specified image filename.
16236%
16237%  The format of the RemoteDisplayCommand method is:
16238%
16239%      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16240%        const char *window,const char *filename,ExceptionInfo *exception)
16241%
16242%  A description of each parameter follows:
16243%
16244%    o image_info: the image info.
16245%
16246%    o window: Specifies the name or id of an X window.
16247%
16248%    o filename: the name of the image filename to display.
16249%
16250%    o exception: return any errors or warnings in this structure.
16251%
16252*/
16253MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16254  const char *window,const char *filename,ExceptionInfo *exception)
16255{
16256  assert(image_info != (const ImageInfo *) NULL);
16257  assert(image_info->signature == MagickSignature);
16258  assert(filename != (char *) NULL);
16259  (void) window;
16260  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16261  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16262    "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image_info->filename);
16263  return(MagickFalse);
16264}
16265#endif
16266