display.c revision ea1a8aa04a9fe1500104284407c1cc06d20da699
1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3%                                                                             %
4%                                                                             %
5%                                                                             %
6%               DDDD   IIIII  SSSSS  PPPP   L       AAA   Y   Y               %
7%               D   D    I    SS     P   P  L      A   A   Y Y                %
8%               D   D    I     SSS   PPPP   L      AAAAA    Y                 %
9%               D   D    I       SS  P      L      A   A    Y                 %
10%               DDDD   IIIII  SSSSS  P      LLLLL  A   A    Y                 %
11%                                                                             %
12%                                                                             %
13%        MagickCore Methods to Interactively Display and Edit an Image        %
14%                                                                             %
15%                             Software Design                                 %
16%                               John Cristy                                   %
17%                                July 1992                                    %
18%                                                                             %
19%                                                                             %
20%  Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization      %
21%  dedicated to making software imaging solutions freely available.           %
22%                                                                             %
23%  You may not use this file except in compliance with the License.  You may  %
24%  obtain a copy of the License at                                            %
25%                                                                             %
26%    http://www.imagemagick.org/script/license.php                            %
27%                                                                             %
28%  Unless required by applicable law or agreed to in writing, software        %
29%  distributed under the License is distributed on an "AS IS" BASIS,          %
30%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31%  See the License for the specific language governing permissions and        %
32%  limitations under the License.                                             %
33%                                                                             %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37*/
38
39/*
40  Include declarations.
41*/
42#include "MagickCore/studio.h"
43#include "MagickCore/artifact.h"
44#include "MagickCore/blob.h"
45#include "MagickCore/cache.h"
46#include "MagickCore/client.h"
47#include "MagickCore/color.h"
48#include "MagickCore/colorspace.h"
49#include "MagickCore/composite.h"
50#include "MagickCore/constitute.h"
51#include "MagickCore/decorate.h"
52#include "MagickCore/delegate.h"
53#include "MagickCore/display.h"
54#include "MagickCore/display-private.h"
55#include "MagickCore/draw.h"
56#include "MagickCore/effect.h"
57#include "MagickCore/enhance.h"
58#include "MagickCore/exception.h"
59#include "MagickCore/exception-private.h"
60#include "MagickCore/fx.h"
61#include "MagickCore/geometry.h"
62#include "MagickCore/image.h"
63#include "MagickCore/image-private.h"
64#include "MagickCore/list.h"
65#include "MagickCore/log.h"
66#include "MagickCore/magick.h"
67#include "MagickCore/memory_.h"
68#include "MagickCore/monitor.h"
69#include "MagickCore/monitor-private.h"
70#include "MagickCore/montage.h"
71#include "MagickCore/option.h"
72#include "MagickCore/paint.h"
73#include "MagickCore/pixel.h"
74#include "MagickCore/pixel-accessor.h"
75#include "MagickCore/PreRvIcccm.h"
76#include "MagickCore/property.h"
77#include "MagickCore/quantum.h"
78#include "MagickCore/quantum-private.h"
79#include "MagickCore/resize.h"
80#include "MagickCore/resource_.h"
81#include "MagickCore/shear.h"
82#include "MagickCore/segment.h"
83#include "MagickCore/string_.h"
84#include "MagickCore/string-private.h"
85#include "MagickCore/transform.h"
86#include "MagickCore/threshold.h"
87#include "MagickCore/utility.h"
88#include "MagickCore/utility-private.h"
89#include "MagickCore/version.h"
90#include "MagickCore/widget.h"
91#include "MagickCore/widget-private.h"
92#include "MagickCore/xwindow.h"
93#include "MagickCore/xwindow-private.h"
94
95#if defined(MAGICKCORE_X11_DELEGATE)
96/*
97  Define declarations.
98*/
99#define MaxColors  MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
100
101/*
102  Constant declarations.
103*/
104static const unsigned char
105  HighlightBitmap[8] =
106  {
107    0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
108  },
109  OpaqueBitmap[8] =
110  {
111    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
112  },
113  ShadowBitmap[8] =
114  {
115    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
116  };
117
118static const char
119  *PageSizes[] =
120  {
121    "Letter",
122    "Tabloid",
123    "Ledger",
124    "Legal",
125    "Statement",
126    "Executive",
127    "A3",
128    "A4",
129    "A5",
130    "B4",
131    "B5",
132    "Folio",
133    "Quarto",
134    "10x14",
135    (char *) NULL
136  };
137
138/*
139  Help widget declarations.
140*/
141static const char
142  *ImageAnnotateHelp[] =
143  {
144    "In annotate mode, the Command widget has these options:",
145    "",
146    "    Font Name",
147    "      fixed",
148    "      variable",
149    "      5x8",
150    "      6x10",
151    "      7x13bold",
152    "      8x13bold",
153    "      9x15bold",
154    "      10x20",
155    "      12x24",
156    "      Browser...",
157    "    Font Color",
158    "      black",
159    "      blue",
160    "      cyan",
161    "      green",
162    "      gray",
163    "      red",
164    "      magenta",
165    "      yellow",
166    "      white",
167    "      transparent",
168    "      Browser...",
169    "    Font Color",
170    "      black",
171    "      blue",
172    "      cyan",
173    "      green",
174    "      gray",
175    "      red",
176    "      magenta",
177    "      yellow",
178    "      white",
179    "      transparent",
180    "      Browser...",
181    "    Rotate Text",
182    "      -90",
183    "      -45",
184    "      -30",
185    "      0",
186    "      30",
187    "      45",
188    "      90",
189    "      180",
190    "      Dialog...",
191    "    Help",
192    "    Dismiss",
193    "",
194    "Choose a font name from the Font Name sub-menu.  Additional",
195    "font names can be specified with the font browser.  You can",
196    "change the menu names by setting the X resources font1",
197    "through font9.",
198    "",
199    "Choose a font color from the Font Color sub-menu.",
200    "Additional font colors can be specified with the color",
201    "browser.  You can change the menu colors by setting the X",
202    "resources pen1 through pen9.",
203    "",
204    "If you select the color browser and press Grab, you can",
205    "choose the font color by moving the pointer to the desired",
206    "color on the screen and press any button.",
207    "",
208    "If you choose to rotate the text, choose Rotate Text from the",
209    "menu and select an angle.  Typically you will only want to",
210    "rotate one line of text at a time.  Depending on the angle you",
211    "choose, subsequent lines may end up overwriting each other.",
212    "",
213    "Choosing a font and its color is optional.  The default font",
214    "is fixed and the default color is black.  However, you must",
215    "choose a location to begin entering text and press button 1.",
216    "An underscore character will appear at the location of the",
217    "pointer.  The cursor changes to a pencil to indicate you are",
218    "in text mode.  To exit immediately, press Dismiss.",
219    "",
220    "In text mode, any key presses will display the character at",
221    "the location of the underscore and advance the underscore",
222    "cursor.  Enter your text and once completed press Apply to",
223    "finish your image annotation.  To correct errors press BACK",
224    "SPACE.  To delete an entire line of text, press DELETE.  Any",
225    "text that exceeds the boundaries of the image window is",
226    "automagically continued onto the next line.",
227    "",
228    "The actual color you request for the font is saved in the",
229    "image.  However, the color that appears in your image window",
230    "may be different.  For example, on a monochrome screen the",
231    "text will appear black or white even if you choose the color",
232    "red as the font color.  However, the image saved to a file",
233    "with -write is written with red lettering.  To assure the",
234    "correct color text in the final image, any PseudoClass image",
235    "is promoted to DirectClass (see miff(5)).  To force a",
236    "PseudoClass image to remain PseudoClass, use -colors.",
237    (char *) NULL,
238  },
239  *ImageChopHelp[] =
240  {
241    "In chop mode, the Command widget has these options:",
242    "",
243    "    Direction",
244    "      horizontal",
245    "      vertical",
246    "    Help",
247    "    Dismiss",
248    "",
249    "If the you choose the horizontal direction (this the",
250    "default), the area of the image between the two horizontal",
251    "endpoints of the chop line is removed.  Otherwise, the area",
252    "of the image between the two vertical endpoints of the chop",
253    "line is removed.",
254    "",
255    "Select a location within the image window to begin your chop,",
256    "press and hold any button.  Next, move the pointer to",
257    "another location in the image.  As you move a line will",
258    "connect the initial location and the pointer.  When you",
259    "release the button, the area within the image to chop is",
260    "determined by which direction you choose from the Command",
261    "widget.",
262    "",
263    "To cancel the image chopping, move the pointer back to the",
264    "starting point of the line and release the button.",
265    (char *) NULL,
266  },
267  *ImageColorEditHelp[] =
268  {
269    "In color edit mode, the Command widget has these options:",
270    "",
271    "    Method",
272    "      point",
273    "      replace",
274    "      floodfill",
275    "      filltoborder",
276    "      reset",
277    "    Pixel Color",
278    "      black",
279    "      blue",
280    "      cyan",
281    "      green",
282    "      gray",
283    "      red",
284    "      magenta",
285    "      yellow",
286    "      white",
287    "      Browser...",
288    "    Border Color",
289    "      black",
290    "      blue",
291    "      cyan",
292    "      green",
293    "      gray",
294    "      red",
295    "      magenta",
296    "      yellow",
297    "      white",
298    "      Browser...",
299    "    Fuzz",
300    "      0%",
301    "      2%",
302    "      5%",
303    "      10%",
304    "      15%",
305    "      Dialog...",
306    "    Undo",
307    "    Help",
308    "    Dismiss",
309    "",
310    "Choose a color editing method from the Method sub-menu",
311    "of the Command widget.  The point method recolors any pixel",
312    "selected with the pointer until the button is released.  The",
313    "replace method recolors any pixel that matches the color of",
314    "the pixel you select with a button press.  Floodfill recolors",
315    "any pixel that matches the color of the pixel you select with",
316    "a button press and is a neighbor.  Whereas filltoborder recolors",
317    "any neighbor pixel that is not the border color.  Finally reset",
318    "changes the entire image to the designated color.",
319    "",
320    "Next, choose a pixel color from the Pixel Color sub-menu.",
321    "Additional pixel colors can be specified with the color",
322    "browser.  You can change the menu colors by setting the X",
323    "resources pen1 through pen9.",
324    "",
325    "Now press button 1 to select a pixel within the image window",
326    "to change its color.  Additional pixels may be recolored as",
327    "prescribed by the method you choose.",
328    "",
329    "If the Magnify widget is mapped, it can be helpful in positioning",
330    "your pointer within the image (refer to button 2).",
331    "",
332    "The actual color you request for the pixels is saved in the",
333    "image.  However, the color that appears in your image window",
334    "may be different.  For example, on a monochrome screen the",
335    "pixel will appear black or white even if you choose the",
336    "color red as the pixel color.  However, the image saved to a",
337    "file with -write is written with red pixels.  To assure the",
338    "correct color text in the final image, any PseudoClass image",
339    "is promoted to DirectClass (see miff(5)).  To force a",
340    "PseudoClass image to remain PseudoClass, use -colors.",
341    (char *) NULL,
342  },
343  *ImageCompositeHelp[] =
344  {
345    "First a widget window is displayed requesting you to enter an",
346    "image name. Press Composite, Grab or type a file name.",
347    "Press Cancel if you choose not to create a composite image.",
348    "When you choose Grab, move the pointer to the desired window",
349    "and press any button.",
350    "",
351    "If the Composite image does not have any matte information,",
352    "you are informed and the file browser is displayed again.",
353    "Enter the name of a mask image.  The image is typically",
354    "grayscale and the same size as the composite image.  If the",
355    "image is not grayscale, it is converted to grayscale and the",
356    "resulting intensities are used as matte information.",
357    "",
358    "A small window appears showing the location of the cursor in",
359    "the image window. You are now in composite mode.  To exit",
360    "immediately, press Dismiss.  In composite mode, the Command",
361    "widget has these options:",
362    "",
363    "    Operators",
364    "      Over",
365    "      In",
366    "      Out",
367    "      Atop",
368    "      Xor",
369    "      Plus",
370    "      Minus",
371    "      Add",
372    "      Subtract",
373    "      Difference",
374    "      Multiply",
375    "      Bumpmap",
376    "      Copy",
377    "      CopyRed",
378    "      CopyGreen",
379    "      CopyBlue",
380    "      CopyOpacity",
381    "      Clear",
382    "    Dissolve",
383    "    Displace",
384    "    Help",
385    "    Dismiss",
386    "",
387    "Choose a composite operation from the Operators sub-menu of",
388    "the Command widget.  How each operator behaves is described",
389    "below.  Image window is the image currently displayed on",
390    "your X server and image is the image obtained with the File",
391    "Browser widget.",
392    "",
393    "Over     The result is the union of the two image shapes,",
394    "         with image obscuring image window in the region of",
395    "         overlap.",
396    "",
397    "In       The result is simply image cut by the shape of",
398    "         image window.  None of the image data of image",
399    "         window is in the result.",
400    "",
401    "Out      The resulting image is image with the shape of",
402    "         image window cut out.",
403    "",
404    "Atop     The result is the same shape as image image window,",
405    "         with image obscuring image window where the image",
406    "         shapes overlap.  Note this differs from over",
407    "         because the portion of image outside image window's",
408    "         shape does not appear in the result.",
409    "",
410    "Xor      The result is the image data from both image and",
411    "         image window that is outside the overlap region.",
412    "         The overlap region is blank.",
413    "",
414    "Plus     The result is just the sum of the image data.",
415    "         Output values are cropped to QuantumRange (no overflow).",
416    "",
417    "Minus    The result of image - image window, with underflow",
418    "         cropped to zero.",
419    "",
420    "Add      The result of image + image window, with overflow",
421    "         wrapping around (mod 256).",
422    "",
423    "Subtract The result of image - image window, with underflow",
424    "         wrapping around (mod 256).  The add and subtract",
425    "         operators can be used to perform reversible",
426    "         transformations.",
427    "",
428    "Difference",
429    "         The result of abs(image - image window).  This",
430    "         useful for comparing two very similar images.",
431    "",
432    "Multiply",
433    "         The result of image * image window.  This",
434    "         useful for the creation of drop-shadows.",
435    "",
436    "Bumpmap  The result of surface normals from image * image",
437    "         window.",
438    "",
439    "Copy     The resulting image is image window replaced with",
440    "         image.  Here the matte information is ignored.",
441    "",
442    "CopyRed  The red layer of the image window is replace with",
443    "         the red layer of the image.  The other layers are",
444    "         untouched.",
445    "",
446    "CopyGreen",
447    "         The green layer of the image window is replace with",
448    "         the green layer of the image.  The other layers are",
449    "         untouched.",
450    "",
451    "CopyBlue The blue layer of the image window is replace with",
452    "         the blue layer of the image.  The other layers are",
453    "         untouched.",
454    "",
455    "CopyOpacity",
456    "         The matte layer of the image window is replace with",
457    "         the matte layer of the image.  The other layers are",
458    "         untouched.",
459    "",
460    "The image compositor requires a matte, or alpha channel in",
461    "the image for some operations.  This extra channel usually",
462    "defines a mask which represents a sort of a cookie-cutter",
463    "for the image.  This the case when matte is opaque (full",
464    "coverage) for pixels inside the shape, zero outside, and",
465    "between 0 and QuantumRange on the boundary.  If image does not",
466    "have a matte channel, it is initialized with 0 for any pixel",
467    "matching in color to pixel location (0,0), otherwise QuantumRange.",
468    "",
469    "If you choose Dissolve, the composite operator becomes Over.  The",
470    "image matte channel percent transparency is initialized to factor.",
471    "The image window is initialized to (100-factor). Where factor is the",
472    "value you specify in the Dialog widget.",
473    "",
474    "Displace shifts the image pixels as defined by a displacement",
475    "map.  With this option, image is used as a displacement map.",
476    "Black, within the displacement map, is a maximum positive",
477    "displacement.  White is a maximum negative displacement and",
478    "middle gray is neutral.  The displacement is scaled to determine",
479    "the pixel shift.  By default, the displacement applies in both the",
480    "horizontal and vertical directions.  However, if you specify a mask,",
481    "image is the horizontal X displacement and mask the vertical Y",
482    "displacement.",
483    "",
484    "Note that matte information for image window is not retained",
485    "for colormapped X server visuals (e.g. StaticColor,",
486    "StaticColor, GrayScale, PseudoColor).  Correct compositing",
487    "behavior may require a TrueColor or DirectColor visual or a",
488    "Standard Colormap.",
489    "",
490    "Choosing a composite operator is optional.  The default",
491    "operator is replace.  However, you must choose a location to",
492    "composite your image and press button 1.  Press and hold the",
493    "button before releasing and an outline of the image will",
494    "appear to help you identify your location.",
495    "",
496    "The actual colors of the composite image is saved.  However,",
497    "the color that appears in image window may be different.",
498    "For example, on a monochrome screen image window will appear",
499    "black or white even though your composited image may have",
500    "many colors.  If the image is saved to a file it is written",
501    "with the correct colors.  To assure the correct colors are",
502    "saved in the final image, any PseudoClass image is promoted",
503    "to DirectClass (see miff(5)).  To force a PseudoClass image",
504    "to remain PseudoClass, use -colors.",
505    (char *) NULL,
506  },
507  *ImageCutHelp[] =
508  {
509    "In cut mode, the Command widget has these options:",
510    "",
511    "    Help",
512    "    Dismiss",
513    "",
514    "To define a cut region, press button 1 and drag.  The",
515    "cut region is defined by a highlighted rectangle that",
516    "expands or contracts as it follows the pointer.  Once you",
517    "are satisfied with the cut region, release the button.",
518    "You are now in rectify mode.  In rectify mode, the Command",
519    "widget has these options:",
520    "",
521    "    Cut",
522    "    Help",
523    "    Dismiss",
524    "",
525    "You can make adjustments by moving the pointer to one of the",
526    "cut rectangle corners, pressing a button, and dragging.",
527    "Finally, press Cut to commit your copy region.  To",
528    "exit without cutting the image, press Dismiss.",
529    (char *) NULL,
530  },
531  *ImageCopyHelp[] =
532  {
533    "In copy mode, the Command widget has these options:",
534    "",
535    "    Help",
536    "    Dismiss",
537    "",
538    "To define a copy region, press button 1 and drag.  The",
539    "copy region is defined by a highlighted rectangle that",
540    "expands or contracts as it follows the pointer.  Once you",
541    "are satisfied with the copy region, release the button.",
542    "You are now in rectify mode.  In rectify mode, the Command",
543    "widget has these options:",
544    "",
545    "    Copy",
546    "    Help",
547    "    Dismiss",
548    "",
549    "You can make adjustments by moving the pointer to one of the",
550    "copy rectangle corners, pressing a button, and dragging.",
551    "Finally, press Copy to commit your copy region.  To",
552    "exit without copying the image, press Dismiss.",
553    (char *) NULL,
554  },
555  *ImageCropHelp[] =
556  {
557    "In crop mode, the Command widget has these options:",
558    "",
559    "    Help",
560    "    Dismiss",
561    "",
562    "To define a cropping region, press button 1 and drag.  The",
563    "cropping region is defined by a highlighted rectangle that",
564    "expands or contracts as it follows the pointer.  Once you",
565    "are satisfied with the cropping region, release the button.",
566    "You are now in rectify mode.  In rectify mode, the Command",
567    "widget has these options:",
568    "",
569    "    Crop",
570    "    Help",
571    "    Dismiss",
572    "",
573    "You can make adjustments by moving the pointer to one of the",
574    "cropping rectangle corners, pressing a button, and dragging.",
575    "Finally, press Crop to commit your cropping region.  To",
576    "exit without cropping the image, press Dismiss.",
577    (char *) NULL,
578  },
579  *ImageDrawHelp[] =
580  {
581    "The cursor changes to a crosshair to indicate you are in",
582    "draw mode.  To exit immediately, press Dismiss.  In draw mode,",
583    "the Command widget has these options:",
584    "",
585    "    Element",
586    "      point",
587    "      line",
588    "      rectangle",
589    "      fill rectangle",
590    "      circle",
591    "      fill circle",
592    "      ellipse",
593    "      fill ellipse",
594    "      polygon",
595    "      fill polygon",
596    "    Color",
597    "      black",
598    "      blue",
599    "      cyan",
600    "      green",
601    "      gray",
602    "      red",
603    "      magenta",
604    "      yellow",
605    "      white",
606    "      transparent",
607    "      Browser...",
608    "    Stipple",
609    "      Brick",
610    "      Diagonal",
611    "      Scales",
612    "      Vertical",
613    "      Wavy",
614    "      Translucent",
615    "      Opaque",
616    "      Open...",
617    "    Width",
618    "      1",
619    "      2",
620    "      4",
621    "      8",
622    "      16",
623    "      Dialog...",
624    "    Undo",
625    "    Help",
626    "    Dismiss",
627    "",
628    "Choose a drawing primitive from the Element sub-menu.",
629    "",
630    "Choose a color from the Color sub-menu.  Additional",
631    "colors can be specified with the color browser.",
632    "",
633    "If you choose the color browser and press Grab, you can",
634    "select the color by moving the pointer to the desired",
635    "color on the screen and press any button.  The transparent",
636    "color updates the image matte channel and is useful for",
637    "image compositing.",
638    "",
639    "Choose a stipple, if appropriate, from the Stipple sub-menu.",
640    "Additional stipples can be specified with the file browser.",
641    "Stipples obtained from the file browser must be on disk in the",
642    "X11 bitmap format.",
643    "",
644    "Choose a width, if appropriate, from the Width sub-menu.  To",
645    "choose a specific width select the Dialog widget.",
646    "",
647    "Choose a point in the Image window and press button 1 and",
648    "hold.  Next, move the pointer to another location in the",
649    "image.  As you move, a line connects the initial location and",
650    "the pointer.  When you release the button, the image is",
651    "updated with the primitive you just drew.  For polygons, the",
652    "image is updated when you press and release the button without",
653    "moving the pointer.",
654    "",
655    "To cancel image drawing, move the pointer back to the",
656    "starting point of the line and release the button.",
657    (char *) NULL,
658  },
659  *DisplayHelp[] =
660  {
661    "BUTTONS",
662    "  The effects of each button press is described below.  Three",
663    "  buttons are required.  If you have a two button mouse,",
664    "  button 1 and 3 are returned.  Press ALT and button 3 to",
665    "  simulate button 2.",
666    "",
667    "  1    Press this button to map or unmap the Command widget.",
668    "",
669    "  2    Press and drag to define a region of the image to",
670    "       magnify.",
671    "",
672    "  3    Press and drag to choose from a select set of commands.",
673    "       This button behaves differently if the image being",
674    "       displayed is a visual image directory.  Here, choose a",
675    "       particular tile of the directory and press this button and",
676    "       drag to select a command from a pop-up menu.  Choose from",
677    "       these menu items:",
678    "",
679    "           Open",
680    "           Next",
681    "           Former",
682    "           Delete",
683    "           Update",
684    "",
685    "       If you choose Open, the image represented by the tile is",
686    "       displayed.  To return to the visual image directory, choose",
687    "       Next from the Command widget.  Next and Former moves to the",
688    "       next or former image respectively.  Choose Delete to delete",
689    "       a particular image tile.  Finally, choose Update to",
690    "       synchronize all the image tiles with their respective",
691    "       images.",
692    "",
693    "COMMAND WIDGET",
694    "  The Command widget lists a number of sub-menus and commands.",
695    "  They are",
696    "",
697    "      File",
698    "        Open...",
699    "        Next",
700    "        Former",
701    "        Select...",
702    "        Save...",
703    "        Print...",
704    "        Delete...",
705    "        New...",
706    "        Visual Directory...",
707    "        Quit",
708    "      Edit",
709    "        Undo",
710    "        Redo",
711    "        Cut",
712    "        Copy",
713    "        Paste",
714    "      View",
715    "        Half Size",
716    "        Original Size",
717    "        Double Size",
718    "        Resize...",
719    "        Apply",
720    "        Refresh",
721    "        Restore",
722    "      Transform",
723    "        Crop",
724    "        Chop",
725    "        Flop",
726    "        Flip",
727    "        Rotate Right",
728    "        Rotate Left",
729    "        Rotate...",
730    "        Shear...",
731    "        Roll...",
732    "        Trim Edges",
733    "      Enhance",
734    "        Brightness...",
735    "        Saturation...",
736    "        Hue...",
737    "        Gamma...",
738    "        Sharpen...",
739    "        Dull",
740    "        Contrast Stretch...",
741    "        Sigmoidal Contrast...",
742    "        Normalize",
743    "        Equalize",
744    "        Negate",
745    "        Grayscale",
746    "        Map...",
747    "        Quantize...",
748    "      Effects",
749    "        Despeckle",
750    "        Emboss",
751    "        Reduce Noise",
752    "        Add Noise",
753    "        Sharpen...",
754    "        Blur...",
755    "        Threshold...",
756    "        Edge Detect...",
757    "        Spread...",
758    "        Shade...",
759    "        Painting...",
760    "        Segment...",
761    "      F/X",
762    "        Solarize...",
763    "        Sepia Tone...",
764    "        Swirl...",
765    "        Implode...",
766    "        Vignette...",
767    "        Wave...",
768    "        Oil Painting...",
769    "        Charcoal Drawing...",
770    "      Image Edit",
771    "        Annotate...",
772    "        Draw...",
773    "        Color...",
774    "        Matte...",
775    "        Composite...",
776    "        Add Border...",
777    "        Add Frame...",
778    "        Comment...",
779    "        Launch...",
780    "        Region of Interest...",
781    "      Miscellany",
782    "        Image Info",
783    "        Zoom Image",
784    "        Show Preview...",
785    "        Show Histogram",
786    "        Show Matte",
787    "        Background...",
788    "        Slide Show",
789    "        Preferences...",
790    "      Help",
791    "        Overview",
792    "        Browse Documentation",
793    "        About Display",
794    "",
795    "  Menu items with a indented triangle have a sub-menu.  They",
796    "  are represented above as the indented items.  To access a",
797    "  sub-menu item, move the pointer to the appropriate menu and",
798    "  press a button and drag.  When you find the desired sub-menu",
799    "  item, release the button and the command is executed.  Move",
800    "  the pointer away from the sub-menu if you decide not to",
801    "  execute a particular command.",
802    "",
803    "KEYBOARD ACCELERATORS",
804    "  Accelerators are one or two key presses that effect a",
805    "  particular command.  The keyboard accelerators that",
806    "  display(1) understands is:",
807    "",
808    "  Ctl+O     Press to open an image from a file.",
809    "",
810    "  space     Press to display the next image.",
811    "",
812    "            If the image is a multi-paged document such as a Postscript",
813    "            document, you can skip ahead several pages by preceding",
814    "            this command with a number.  For example to display the",
815    "            third page beyond the current page, press 3<space>.",
816    "",
817    "  backspace Press to display the former image.",
818    "",
819    "            If the image is a multi-paged document such as a Postscript",
820    "            document, you can skip behind several pages by preceding",
821    "            this command with a number.  For example to display the",
822    "            third page preceding the current page, press 3<backspace>.",
823    "",
824    "  Ctl+S     Press to write the image to a file.",
825    "",
826    "  Ctl+P     Press to print the image to a Postscript printer.",
827    "",
828    "  Ctl+D     Press to delete an image file.",
829    "",
830    "  Ctl+N     Press to create a blank canvas.",
831    "",
832    "  Ctl+Q     Press to discard all images and exit program.",
833    "",
834    "  Ctl+Z     Press to undo last image transformation.",
835    "",
836    "  Ctl+R     Press to redo last image transformation.",
837    "",
838    "  Ctl+X     Press to cut a region of the image.",
839    "",
840    "  Ctl+C     Press to copy a region of the image.",
841    "",
842    "  Ctl+V     Press to paste a region to the image.",
843    "",
844    "  <         Press to half the image size.",
845    "",
846    "  -         Press to return to the original image size.",
847    "",
848    "  >         Press to double the image size.",
849    "",
850    "  %         Press to resize the image to a width and height you",
851    "            specify.",
852    "",
853    "Cmd-A       Press to make any image transformations permanent."
854    "",
855    "            By default, any image size transformations are applied",
856    "            to the original image to create the image displayed on",
857    "            the X server.  However, the transformations are not",
858    "            permanent (i.e. the original image does not change",
859    "            size only the X image does).  For example, if you",
860    "            press > the X image will appear to double in size,",
861    "            but the original image will in fact remain the same size.",
862    "            To force the original image to double in size, press >",
863    "            followed by Cmd-A.",
864    "",
865    "  @         Press to refresh the image window.",
866    "",
867    "  C         Press to cut out a rectangular region of the image.",
868    "",
869    "  [         Press to chop the image.",
870    "",
871    "  H         Press to flop image in the horizontal direction.",
872    "",
873    "  V         Press to flip image in the vertical direction.",
874    "",
875    "  /         Press to rotate the image 90 degrees clockwise.",
876    "",
877    " \\         Press to rotate the image 90 degrees counter-clockwise.",
878    "",
879    "  *         Press to rotate the image the number of degrees you",
880    "            specify.",
881    "",
882    "  S         Press to shear the image the number of degrees you",
883    "            specify.",
884    "",
885    "  R         Press to roll the image.",
886    "",
887    "  T         Press to trim the image edges.",
888    "",
889    "  Shft-H    Press to vary the image hue.",
890    "",
891    "  Shft-S    Press to vary the color saturation.",
892    "",
893    "  Shft-L    Press to vary the color brightness.",
894    "",
895    "  Shft-G    Press to gamma correct the image.",
896    "",
897    "  Shft-C    Press to sharpen the image contrast.",
898    "",
899    "  Shft-Z    Press to dull the image contrast.",
900    "",
901    "  =         Press to perform histogram equalization on the image.",
902    "",
903    "  Shft-N    Press to perform histogram normalization on the image.",
904    "",
905    "  Shft-~    Press to negate the colors of the image.",
906    "",
907    "  .         Press to convert the image colors to gray.",
908    "",
909    "  Shft-#    Press to set the maximum number of unique colors in the",
910    "            image.",
911    "",
912    "  F2        Press to reduce the speckles in an image.",
913    "",
914    "  F3        Press to eliminate peak noise from an image.",
915    "",
916    "  F4        Press to add noise to an image.",
917    "",
918    "  F5        Press to sharpen an image.",
919    "",
920    "  F6        Press to delete an image file.",
921    "",
922    "  F7        Press to threshold the image.",
923    "",
924    "  F8        Press to detect edges within an image.",
925    "",
926    "  F9        Press to emboss an image.",
927    "",
928    "  F10       Press to displace pixels by a random amount.",
929    "",
930    "  F11       Press to negate all pixels above the threshold level.",
931    "",
932    "  F12       Press to shade the image using a distant light source.",
933    "",
934    "  F13       Press to lighten or darken image edges to create a 3-D effect.",
935    "",
936    "  F14       Press to segment the image by color.",
937    "",
938    "  Meta-S    Press to swirl image pixels about the center.",
939    "",
940    "  Meta-I    Press to implode image pixels about the center.",
941    "",
942    "  Meta-W    Press to alter an image along a sine wave.",
943    "",
944    "  Meta-P    Press to simulate an oil painting.",
945    "",
946    "  Meta-C    Press to simulate a charcoal drawing.",
947    "",
948    "  Alt-A     Press to annotate the image with text.",
949    "",
950    "  Alt-D     Press to draw on an image.",
951    "",
952    "  Alt-P     Press to edit an image pixel color.",
953    "",
954    "  Alt-M     Press to edit the image matte information.",
955    "",
956    "  Alt-V     Press to composite the image with another.",
957    "",
958    "  Alt-B     Press to add a border to the image.",
959    "",
960    "  Alt-F     Press to add an ornamental border to the image.",
961    "",
962    "  Alt-Shft-!",
963    "            Press to add an image comment.",
964    "",
965    "  Ctl-A     Press to apply image processing techniques to a region",
966    "            of interest.",
967    "",
968    "  Shft-?    Press to display information about the image.",
969    "",
970    "  Shft-+    Press to map the zoom image window.",
971    "",
972    "  Shft-P    Press to preview an image enhancement, effect, or f/x.",
973    "",
974    "  F1        Press to display helpful information about display(1).",
975    "",
976    "  Find      Press to browse documentation about ImageMagick.",
977    "",
978    "  1-9       Press to change the level of magnification.",
979    "",
980    "  Use the arrow keys to move the image one pixel up, down,",
981    "  left, or right within the magnify window.  Be sure to first",
982    "  map the magnify window by pressing button 2.",
983    "",
984    "  Press ALT and one of the arrow keys to trim off one pixel",
985    "  from any side of the image.",
986    (char *) NULL,
987  },
988  *ImageMatteEditHelp[] =
989  {
990    "Matte information within an image is useful for some",
991    "operations such as image compositing (See IMAGE",
992    "COMPOSITING).  This extra channel usually defines a mask",
993    "which represents a sort of a cookie-cutter for the image.",
994    "This the case when matte is opaque (full coverage) for",
995    "pixels inside the shape, zero outside, and between 0 and",
996    "QuantumRange on the boundary.",
997    "",
998    "A small window appears showing the location of the cursor in",
999    "the image window. You are now in matte edit mode.  To exit",
1000    "immediately, press Dismiss.  In matte edit mode, the Command",
1001    "widget has these options:",
1002    "",
1003    "    Method",
1004    "      point",
1005    "      replace",
1006    "      floodfill",
1007    "      filltoborder",
1008    "      reset",
1009    "    Border Color",
1010    "      black",
1011    "      blue",
1012    "      cyan",
1013    "      green",
1014    "      gray",
1015    "      red",
1016    "      magenta",
1017    "      yellow",
1018    "      white",
1019    "      Browser...",
1020    "    Fuzz",
1021    "      0%",
1022    "      2%",
1023    "      5%",
1024    "      10%",
1025    "      15%",
1026    "      Dialog...",
1027    "    Matte",
1028    "      Opaque",
1029    "      Transparent",
1030    "      Dialog...",
1031    "    Undo",
1032    "    Help",
1033    "    Dismiss",
1034    "",
1035    "Choose a matte editing method from the Method sub-menu of",
1036    "the Command widget.  The point method changes the matte value",
1037    "of any pixel selected with the pointer until the button is",
1038    "is released.  The replace method changes the matte value of",
1039    "any pixel that matches the color of the pixel you select with",
1040    "a button press.  Floodfill changes the matte value of any pixel",
1041    "that matches the color of the pixel you select with a button",
1042    "press and is a neighbor.  Whereas filltoborder changes the matte",
1043    "value any neighbor pixel that is not the border color.  Finally",
1044    "reset changes the entire image to the designated matte value.",
1045    "",
1046    "Choose Matte Value and pick Opaque or Transarent.  For other values",
1047    "select the Dialog entry.  Here a dialog appears requesting a matte",
1048    "value.  The value you select is assigned as the opacity value of the",
1049    "selected pixel or pixels.",
1050    "",
1051    "Now, press any button to select a pixel within the image",
1052    "window to change its matte value.",
1053    "",
1054    "If the Magnify widget is mapped, it can be helpful in positioning",
1055    "your pointer within the image (refer to button 2).",
1056    "",
1057    "Matte information is only valid in a DirectClass image.",
1058    "Therefore, any PseudoClass image is promoted to DirectClass",
1059    "(see miff(5)).  Note that matte information for PseudoClass",
1060    "is not retained for colormapped X server visuals (e.g.",
1061    "StaticColor, StaticColor, GrayScale, PseudoColor) unless you",
1062    "immediately save your image to a file (refer to Write).",
1063    "Correct matte editing behavior may require a TrueColor or",
1064    "DirectColor visual or a Standard Colormap.",
1065    (char *) NULL,
1066  },
1067  *ImagePanHelp[] =
1068  {
1069    "When an image exceeds the width or height of the X server",
1070    "screen, display maps a small panning icon.  The rectangle",
1071    "within the panning icon shows the area that is currently",
1072    "displayed in the image window.  To pan about the image,",
1073    "press any button and drag the pointer within the panning",
1074    "icon.  The pan rectangle moves with the pointer and the",
1075    "image window is updated to reflect the location of the",
1076    "rectangle within the panning icon.  When you have selected",
1077    "the area of the image you wish to view, release the button.",
1078    "",
1079    "Use the arrow keys to pan the image one pixel up, down,",
1080    "left, or right within the image window.",
1081    "",
1082    "The panning icon is withdrawn if the image becomes smaller",
1083    "than the dimensions of the X server screen.",
1084    (char *) NULL,
1085  },
1086  *ImagePasteHelp[] =
1087  {
1088    "A small window appears showing the location of the cursor in",
1089    "the image window. You are now in paste mode.  To exit",
1090    "immediately, press Dismiss.  In paste mode, the Command",
1091    "widget has these options:",
1092    "",
1093    "    Operators",
1094    "      over",
1095    "      in",
1096    "      out",
1097    "      atop",
1098    "      xor",
1099    "      plus",
1100    "      minus",
1101    "      add",
1102    "      subtract",
1103    "      difference",
1104    "      replace",
1105    "    Help",
1106    "    Dismiss",
1107    "",
1108    "Choose a composite operation from the Operators sub-menu of",
1109    "the Command widget.  How each operator behaves is described",
1110    "below.  Image window is the image currently displayed on",
1111    "your X server and image is the image obtained with the File",
1112    "Browser widget.",
1113    "",
1114    "Over     The result is the union of the two image shapes,",
1115    "         with image obscuring image window in the region of",
1116    "         overlap.",
1117    "",
1118    "In       The result is simply image cut by the shape of",
1119    "         image window.  None of the image data of image",
1120    "         window is in the result.",
1121    "",
1122    "Out      The resulting image is image with the shape of",
1123    "         image window cut out.",
1124    "",
1125    "Atop     The result is the same shape as image image window,",
1126    "         with image obscuring image window where the image",
1127    "         shapes overlap.  Note this differs from over",
1128    "         because the portion of image outside image window's",
1129    "         shape does not appear in the result.",
1130    "",
1131    "Xor      The result is the image data from both image and",
1132    "         image window that is outside the overlap region.",
1133    "         The overlap region is blank.",
1134    "",
1135    "Plus     The result is just the sum of the image data.",
1136    "         Output values are cropped to QuantumRange (no overflow).",
1137    "         This operation is independent of the matte",
1138    "         channels.",
1139    "",
1140    "Minus    The result of image - image window, with underflow",
1141    "         cropped to zero.",
1142    "",
1143    "Add      The result of image + image window, with overflow",
1144    "         wrapping around (mod 256).",
1145    "",
1146    "Subtract The result of image - image window, with underflow",
1147    "         wrapping around (mod 256).  The add and subtract",
1148    "         operators can be used to perform reversible",
1149    "         transformations.",
1150    "",
1151    "Difference",
1152    "         The result of abs(image - image window).  This",
1153    "         useful for comparing two very similar images.",
1154    "",
1155    "Copy     The resulting image is image window replaced with",
1156    "         image.  Here the matte information is ignored.",
1157    "",
1158    "CopyRed  The red layer of the image window is replace with",
1159    "         the red layer of the image.  The other layers are",
1160    "         untouched.",
1161    "",
1162    "CopyGreen",
1163    "         The green layer of the image window is replace with",
1164    "         the green layer of the image.  The other layers are",
1165    "         untouched.",
1166    "",
1167    "CopyBlue The blue layer of the image window is replace with",
1168    "         the blue layer of the image.  The other layers are",
1169    "         untouched.",
1170    "",
1171    "CopyOpacity",
1172    "         The matte layer of the image window is replace with",
1173    "         the matte layer of the image.  The other layers are",
1174    "         untouched.",
1175    "",
1176    "The image compositor requires a matte, or alpha channel in",
1177    "the image for some operations.  This extra channel usually",
1178    "defines a mask which represents a sort of a cookie-cutter",
1179    "for the image.  This the case when matte is opaque (full",
1180    "coverage) for pixels inside the shape, zero outside, and",
1181    "between 0 and QuantumRange on the boundary.  If image does not",
1182    "have a matte channel, it is initialized with 0 for any pixel",
1183    "matching in color to pixel location (0,0), otherwise QuantumRange.",
1184    "",
1185    "Note that matte information for image window is not retained",
1186    "for colormapped X server visuals (e.g. StaticColor,",
1187    "StaticColor, GrayScale, PseudoColor).  Correct compositing",
1188    "behavior may require a TrueColor or DirectColor visual or a",
1189    "Standard Colormap.",
1190    "",
1191    "Choosing a composite operator is optional.  The default",
1192    "operator is replace.  However, you must choose a location to",
1193    "paste your image and press button 1.  Press and hold the",
1194    "button before releasing and an outline of the image will",
1195    "appear to help you identify your location.",
1196    "",
1197    "The actual colors of the pasted image is saved.  However,",
1198    "the color that appears in image window may be different.",
1199    "For example, on a monochrome screen image window will appear",
1200    "black or white even though your pasted image may have",
1201    "many colors.  If the image is saved to a file it is written",
1202    "with the correct colors.  To assure the correct colors are",
1203    "saved in the final image, any PseudoClass image is promoted",
1204    "to DirectClass (see miff(5)).  To force a PseudoClass image",
1205    "to remain PseudoClass, use -colors.",
1206    (char *) NULL,
1207  },
1208  *ImageROIHelp[] =
1209  {
1210    "In region of interest mode, the Command widget has these",
1211    "options:",
1212    "",
1213    "    Help",
1214    "    Dismiss",
1215    "",
1216    "To define a region of interest, press button 1 and drag.",
1217    "The region of interest is defined by a highlighted rectangle",
1218    "that expands or contracts as it follows the pointer.  Once",
1219    "you are satisfied with the region of interest, release the",
1220    "button.  You are now in apply mode.  In apply mode the",
1221    "Command widget has these options:",
1222    "",
1223    "      File",
1224    "        Save...",
1225    "        Print...",
1226    "      Edit",
1227    "        Undo",
1228    "        Redo",
1229    "      Transform",
1230    "        Flop",
1231    "        Flip",
1232    "        Rotate Right",
1233    "        Rotate Left",
1234    "      Enhance",
1235    "        Hue...",
1236    "        Saturation...",
1237    "        Brightness...",
1238    "        Gamma...",
1239    "        Spiff",
1240    "        Dull",
1241    "        Contrast Stretch",
1242    "        Sigmoidal Contrast...",
1243    "        Normalize",
1244    "        Equalize",
1245    "        Negate",
1246    "        Grayscale",
1247    "        Map...",
1248    "        Quantize...",
1249    "      Effects",
1250    "        Despeckle",
1251    "        Emboss",
1252    "        Reduce Noise",
1253    "        Sharpen...",
1254    "        Blur...",
1255    "        Threshold...",
1256    "        Edge Detect...",
1257    "        Spread...",
1258    "        Shade...",
1259    "        Raise...",
1260    "        Segment...",
1261    "      F/X",
1262    "        Solarize...",
1263    "        Sepia Tone...",
1264    "        Swirl...",
1265    "        Implode...",
1266    "        Vignette...",
1267    "        Wave...",
1268    "        Oil Painting...",
1269    "        Charcoal Drawing...",
1270    "      Miscellany",
1271    "        Image Info",
1272    "        Zoom Image",
1273    "        Show Preview...",
1274    "        Show Histogram",
1275    "        Show Matte",
1276    "      Help",
1277    "      Dismiss",
1278    "",
1279    "You can make adjustments to the region of interest by moving",
1280    "the pointer to one of the rectangle corners, pressing a",
1281    "button, and dragging.  Finally, choose an image processing",
1282    "technique from the Command widget.  You can choose more than",
1283    "one image processing technique to apply to an area.",
1284    "Alternatively, you can move the region of interest before",
1285    "applying another image processing technique.  To exit, press",
1286    "Dismiss.",
1287    (char *) NULL,
1288  },
1289  *ImageRotateHelp[] =
1290  {
1291    "In rotate mode, the Command widget has these options:",
1292    "",
1293    "    Pixel Color",
1294    "      black",
1295    "      blue",
1296    "      cyan",
1297    "      green",
1298    "      gray",
1299    "      red",
1300    "      magenta",
1301    "      yellow",
1302    "      white",
1303    "      Browser...",
1304    "    Direction",
1305    "      horizontal",
1306    "      vertical",
1307    "    Help",
1308    "    Dismiss",
1309    "",
1310    "Choose a background color from the Pixel Color sub-menu.",
1311    "Additional background colors can be specified with the color",
1312    "browser.  You can change the menu colors by setting the X",
1313    "resources pen1 through pen9.",
1314    "",
1315    "If you choose the color browser and press Grab, you can",
1316    "select the background color by moving the pointer to the",
1317    "desired color on the screen and press any button.",
1318    "",
1319    "Choose a point in the image window and press this button and",
1320    "hold.  Next, move the pointer to another location in the",
1321    "image.  As you move a line connects the initial location and",
1322    "the pointer.  When you release the button, the degree of",
1323    "image rotation is determined by the slope of the line you",
1324    "just drew.  The slope is relative to the direction you",
1325    "choose from the Direction sub-menu of the Command widget.",
1326    "",
1327    "To cancel the image rotation, move the pointer back to the",
1328    "starting point of the line and release the button.",
1329    (char *) NULL,
1330  };
1331
1332/*
1333  Enumeration declarations.
1334*/
1335typedef enum
1336{
1337  CopyMode,
1338  CropMode,
1339  CutMode
1340} ClipboardMode;
1341
1342typedef enum
1343{
1344  OpenCommand,
1345  NextCommand,
1346  FormerCommand,
1347  SelectCommand,
1348  SaveCommand,
1349  PrintCommand,
1350  DeleteCommand,
1351  NewCommand,
1352  VisualDirectoryCommand,
1353  QuitCommand,
1354  UndoCommand,
1355  RedoCommand,
1356  CutCommand,
1357  CopyCommand,
1358  PasteCommand,
1359  HalfSizeCommand,
1360  OriginalSizeCommand,
1361  DoubleSizeCommand,
1362  ResizeCommand,
1363  ApplyCommand,
1364  RefreshCommand,
1365  RestoreCommand,
1366  CropCommand,
1367  ChopCommand,
1368  FlopCommand,
1369  FlipCommand,
1370  RotateRightCommand,
1371  RotateLeftCommand,
1372  RotateCommand,
1373  ShearCommand,
1374  RollCommand,
1375  TrimCommand,
1376  HueCommand,
1377  SaturationCommand,
1378  BrightnessCommand,
1379  GammaCommand,
1380  SpiffCommand,
1381  DullCommand,
1382  ContrastStretchCommand,
1383  SigmoidalContrastCommand,
1384  NormalizeCommand,
1385  EqualizeCommand,
1386  NegateCommand,
1387  GrayscaleCommand,
1388  MapCommand,
1389  QuantizeCommand,
1390  DespeckleCommand,
1391  EmbossCommand,
1392  ReduceNoiseCommand,
1393  AddNoiseCommand,
1394  SharpenCommand,
1395  BlurCommand,
1396  ThresholdCommand,
1397  EdgeDetectCommand,
1398  SpreadCommand,
1399  ShadeCommand,
1400  RaiseCommand,
1401  SegmentCommand,
1402  SolarizeCommand,
1403  SepiaToneCommand,
1404  SwirlCommand,
1405  ImplodeCommand,
1406  VignetteCommand,
1407  WaveCommand,
1408  OilPaintCommand,
1409  CharcoalDrawCommand,
1410  AnnotateCommand,
1411  DrawCommand,
1412  ColorCommand,
1413  MatteCommand,
1414  CompositeCommand,
1415  AddBorderCommand,
1416  AddFrameCommand,
1417  CommentCommand,
1418  LaunchCommand,
1419  RegionofInterestCommand,
1420  ROIHelpCommand,
1421  ROIDismissCommand,
1422  InfoCommand,
1423  ZoomCommand,
1424  ShowPreviewCommand,
1425  ShowHistogramCommand,
1426  ShowMatteCommand,
1427  BackgroundCommand,
1428  SlideShowCommand,
1429  PreferencesCommand,
1430  HelpCommand,
1431  BrowseDocumentationCommand,
1432  VersionCommand,
1433  SaveToUndoBufferCommand,
1434  FreeBuffersCommand,
1435  NullCommand
1436} CommandType;
1437
1438typedef enum
1439{
1440  AnnotateNameCommand,
1441  AnnotateFontColorCommand,
1442  AnnotateBackgroundColorCommand,
1443  AnnotateRotateCommand,
1444  AnnotateHelpCommand,
1445  AnnotateDismissCommand,
1446  TextHelpCommand,
1447  TextApplyCommand,
1448  ChopDirectionCommand,
1449  ChopHelpCommand,
1450  ChopDismissCommand,
1451  HorizontalChopCommand,
1452  VerticalChopCommand,
1453  ColorEditMethodCommand,
1454  ColorEditColorCommand,
1455  ColorEditBorderCommand,
1456  ColorEditFuzzCommand,
1457  ColorEditUndoCommand,
1458  ColorEditHelpCommand,
1459  ColorEditDismissCommand,
1460  CompositeOperatorsCommand,
1461  CompositeDissolveCommand,
1462  CompositeDisplaceCommand,
1463  CompositeHelpCommand,
1464  CompositeDismissCommand,
1465  CropHelpCommand,
1466  CropDismissCommand,
1467  RectifyCopyCommand,
1468  RectifyHelpCommand,
1469  RectifyDismissCommand,
1470  DrawElementCommand,
1471  DrawColorCommand,
1472  DrawStippleCommand,
1473  DrawWidthCommand,
1474  DrawUndoCommand,
1475  DrawHelpCommand,
1476  DrawDismissCommand,
1477  MatteEditMethod,
1478  MatteEditBorderCommand,
1479  MatteEditFuzzCommand,
1480  MatteEditValueCommand,
1481  MatteEditUndoCommand,
1482  MatteEditHelpCommand,
1483  MatteEditDismissCommand,
1484  PasteOperatorsCommand,
1485  PasteHelpCommand,
1486  PasteDismissCommand,
1487  RotateColorCommand,
1488  RotateDirectionCommand,
1489  RotateCropCommand,
1490  RotateSharpenCommand,
1491  RotateHelpCommand,
1492  RotateDismissCommand,
1493  HorizontalRotateCommand,
1494  VerticalRotateCommand,
1495  TileLoadCommand,
1496  TileNextCommand,
1497  TileFormerCommand,
1498  TileDeleteCommand,
1499  TileUpdateCommand
1500} ModeType;
1501
1502/*
1503  Stipples.
1504*/
1505#define BricksWidth  20
1506#define BricksHeight  20
1507#define DiagonalWidth  16
1508#define DiagonalHeight  16
1509#define HighlightWidth  8
1510#define HighlightHeight  8
1511#define OpaqueWidth  8
1512#define OpaqueHeight  8
1513#define ScalesWidth  16
1514#define ScalesHeight  16
1515#define ShadowWidth  8
1516#define ShadowHeight  8
1517#define VerticalWidth  16
1518#define VerticalHeight  16
1519#define WavyWidth  16
1520#define WavyHeight  16
1521
1522/*
1523  Constant declaration.
1524*/
1525static const int
1526  RoiDelta = 8;
1527
1528static const unsigned char
1529  BricksBitmap[] =
1530  {
1531    0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1532    0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1533    0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1534    0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1535    0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1536  },
1537  DiagonalBitmap[] =
1538  {
1539    0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1540    0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1541    0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1542  },
1543  ScalesBitmap[] =
1544  {
1545    0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1546    0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1547    0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1548  },
1549  VerticalBitmap[] =
1550  {
1551    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1552    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1553    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1554  },
1555  WavyBitmap[] =
1556  {
1557    0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1558    0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1559    0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1560  };
1561
1562/*
1563  Function prototypes.
1564*/
1565static CommandType
1566  XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1567    const MagickStatusType,KeySym,Image **,ExceptionInfo *);
1568
1569static Image
1570  *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
1571    Image **,ExceptionInfo *),
1572  *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1573  *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *,
1574    ExceptionInfo *),
1575  *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *,
1576    ExceptionInfo *);
1577
1578static MagickBooleanType
1579  XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *,
1580    ExceptionInfo *),
1581  XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **,
1582    ExceptionInfo *),
1583  XChopImage(Display *,XResourceInfo *,XWindows *,Image **,
1584    ExceptionInfo *),
1585  XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode,
1586    ExceptionInfo *),
1587  XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1588    ExceptionInfo *),
1589  XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *,
1590    ExceptionInfo *),
1591  XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1592  XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1593    ExceptionInfo *),
1594  XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1595    ExceptionInfo *),
1596  XPasteImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1597  XPrintImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1598  XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **,
1599    ExceptionInfo *),
1600  XROIImage(Display *,XResourceInfo *,XWindows *,Image **,ExceptionInfo *),
1601  XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1602  XTrimImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
1603
1604static void
1605  XDrawPanRectangle(Display *,XWindows *),
1606  XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **,
1607    ExceptionInfo *),
1608  XMagnifyImage(Display *,XWindows *,XEvent *),
1609  XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1610  XPanImage(Display *,XWindows *,XEvent *),
1611  XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1612    const KeySym),
1613  XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1614  XScreenEvent(Display *,XWindows *,XEvent *),
1615  XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1616
1617/*
1618%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1619%                                                                             %
1620%                                                                             %
1621%                                                                             %
1622%   D i s p l a y I m a g e s                                                 %
1623%                                                                             %
1624%                                                                             %
1625%                                                                             %
1626%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1627%
1628%  DisplayImages() displays an image sequence to any X window screen.  It
1629%  returns a value other than 0 if successful.  Check the exception member
1630%  of image to determine the reason for any failure.
1631%
1632%  The format of the DisplayImages method is:
1633%
1634%      MagickBooleanType DisplayImages(const ImageInfo *image_info,
1635%        Image *images,ExceptionInfo *exception)
1636%
1637%  A description of each parameter follows:
1638%
1639%    o image_info: the image info.
1640%
1641%    o image: the image.
1642%
1643%    o exception: return any errors or warnings in this structure.
1644%
1645*/
1646MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1647  Image *images,ExceptionInfo *exception)
1648{
1649  char
1650    *argv[1];
1651
1652  Display
1653    *display;
1654
1655  Image
1656    *image;
1657
1658  register ssize_t
1659    i;
1660
1661  size_t
1662    state;
1663
1664  XrmDatabase
1665    resource_database;
1666
1667  XResourceInfo
1668    resource_info;
1669
1670  assert(image_info != (const ImageInfo *) NULL);
1671  assert(image_info->signature == MagickSignature);
1672  assert(images != (Image *) NULL);
1673  assert(images->signature == MagickSignature);
1674  if (images->debug != MagickFalse)
1675    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1676  display=XOpenDisplay(image_info->server_name);
1677  if (display == (Display *) NULL)
1678    {
1679      (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1680        "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1681      return(MagickFalse);
1682    }
1683  if (exception->severity != UndefinedException)
1684    CatchException(exception);
1685  (void) XSetErrorHandler(XError);
1686  resource_database=XGetResourceDatabase(display,GetClientName());
1687  (void) ResetMagickMemory(&resource_info,0,sizeof(resource_info));
1688  XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1689  if (image_info->page != (char *) NULL)
1690    resource_info.image_geometry=AcquireString(image_info->page);
1691  resource_info.immutable=MagickTrue;
1692  argv[0]=AcquireString(GetClientName());
1693  state=DefaultState;
1694  for (i=0; (state & ExitState) == 0; i++)
1695  {
1696    if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1697      break;
1698    image=GetImageFromList(images,i % GetImageListLength(images));
1699    (void) XDisplayImage(display,&resource_info,argv,1,&image,&state,exception);
1700  }
1701  SetErrorHandler((ErrorHandler) NULL);
1702  SetWarningHandler((WarningHandler) NULL);
1703  argv[0]=DestroyString(argv[0]);
1704  (void) XCloseDisplay(display);
1705  XDestroyResourceInfo(&resource_info);
1706  if (exception->severity != UndefinedException)
1707    return(MagickFalse);
1708  return(MagickTrue);
1709}
1710
1711/*
1712%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1713%                                                                             %
1714%                                                                             %
1715%                                                                             %
1716%   R e m o t e D i s p l a y C o m m a n d                                   %
1717%                                                                             %
1718%                                                                             %
1719%                                                                             %
1720%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1721%
1722%  RemoteDisplayCommand() encourages a remote display program to display the
1723%  specified image filename.
1724%
1725%  The format of the RemoteDisplayCommand method is:
1726%
1727%      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1728%        const char *window,const char *filename,ExceptionInfo *exception)
1729%
1730%  A description of each parameter follows:
1731%
1732%    o image_info: the image info.
1733%
1734%    o window: Specifies the name or id of an X window.
1735%
1736%    o filename: the name of the image filename to display.
1737%
1738%    o exception: return any errors or warnings in this structure.
1739%
1740*/
1741MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1742  const char *window,const char *filename,ExceptionInfo *exception)
1743{
1744  Display
1745    *display;
1746
1747  MagickStatusType
1748    status;
1749
1750  assert(image_info != (const ImageInfo *) NULL);
1751  assert(image_info->signature == MagickSignature);
1752  assert(filename != (char *) NULL);
1753  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1754  display=XOpenDisplay(image_info->server_name);
1755  if (display == (Display *) NULL)
1756    {
1757      (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1758        "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1759      return(MagickFalse);
1760    }
1761  (void) XSetErrorHandler(XError);
1762  status=XRemoteCommand(display,window,filename);
1763  (void) XCloseDisplay(display);
1764  return(status != 0 ? MagickTrue : MagickFalse);
1765}
1766
1767/*
1768%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1769%                                                                             %
1770%                                                                             %
1771%                                                                             %
1772+   X A n n o t a t e E d i t I m a g e                                       %
1773%                                                                             %
1774%                                                                             %
1775%                                                                             %
1776%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1777%
1778%  XAnnotateEditImage() annotates the image with text.
1779%
1780%  The format of the XAnnotateEditImage method is:
1781%
1782%      MagickBooleanType XAnnotateEditImage(Display *display,
1783%        XResourceInfo *resource_info,XWindows *windows,Image *image,
1784%        ExceptionInfo *exception)
1785%
1786%  A description of each parameter follows:
1787%
1788%    o display: Specifies a connection to an X server;  returned from
1789%      XOpenDisplay.
1790%
1791%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1792%
1793%    o windows: Specifies a pointer to a XWindows structure.
1794%
1795%    o image: the image; returned from ReadImage.
1796%
1797*/
1798
1799static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
1800{
1801  if (x > y)
1802    return(x);
1803  return(y);
1804}
1805
1806static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
1807{
1808  if (x < y)
1809    return(x);
1810  return(y);
1811}
1812
1813static MagickBooleanType XAnnotateEditImage(Display *display,
1814  XResourceInfo *resource_info,XWindows *windows,Image *image,
1815  ExceptionInfo *exception)
1816{
1817  static const char
1818    *AnnotateMenu[] =
1819    {
1820      "Font Name",
1821      "Font Color",
1822      "Box Color",
1823      "Rotate Text",
1824      "Help",
1825      "Dismiss",
1826      (char *) NULL
1827    },
1828    *TextMenu[] =
1829    {
1830      "Help",
1831      "Apply",
1832      (char *) NULL
1833    };
1834
1835  static const ModeType
1836    AnnotateCommands[] =
1837    {
1838      AnnotateNameCommand,
1839      AnnotateFontColorCommand,
1840      AnnotateBackgroundColorCommand,
1841      AnnotateRotateCommand,
1842      AnnotateHelpCommand,
1843      AnnotateDismissCommand
1844    },
1845    TextCommands[] =
1846    {
1847      TextHelpCommand,
1848      TextApplyCommand
1849    };
1850
1851  static MagickBooleanType
1852    transparent_box = MagickTrue,
1853    transparent_pen = MagickFalse;
1854
1855  static MagickRealType
1856    degrees = 0.0;
1857
1858  static unsigned int
1859    box_id = MaxNumberPens-2,
1860    font_id = 0,
1861    pen_id = 0;
1862
1863  char
1864    command[MaxTextExtent],
1865    text[MaxTextExtent];
1866
1867  const char
1868    *ColorMenu[MaxNumberPens+1];
1869
1870  Cursor
1871    cursor;
1872
1873  GC
1874    annotate_context;
1875
1876  int
1877    id,
1878    pen_number,
1879    status,
1880    x,
1881    y;
1882
1883  KeySym
1884    key_symbol;
1885
1886  register char
1887    *p;
1888
1889  register ssize_t
1890    i;
1891
1892  unsigned int
1893    height,
1894    width;
1895
1896  size_t
1897    state;
1898
1899  XAnnotateInfo
1900    *annotate_info,
1901    *previous_info;
1902
1903  XColor
1904    color;
1905
1906  XFontStruct
1907    *font_info;
1908
1909  XEvent
1910    event,
1911    text_event;
1912
1913  /*
1914    Map Command widget.
1915  */
1916  (void) CloneString(&windows->command.name,"Annotate");
1917  windows->command.data=4;
1918  (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1919  (void) XMapRaised(display,windows->command.id);
1920  XClientMessage(display,windows->image.id,windows->im_protocols,
1921    windows->im_update_widget,CurrentTime);
1922  /*
1923    Track pointer until button 1 is pressed.
1924  */
1925  XQueryPosition(display,windows->image.id,&x,&y);
1926  (void) XSelectInput(display,windows->image.id,
1927    windows->image.attributes.event_mask | PointerMotionMask);
1928  cursor=XCreateFontCursor(display,XC_left_side);
1929  (void) XCheckDefineCursor(display,windows->image.id,cursor);
1930  state=DefaultState;
1931  do
1932  {
1933    if (windows->info.mapped != MagickFalse)
1934      {
1935        /*
1936          Display pointer position.
1937        */
1938        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
1939          x+windows->image.x,y+windows->image.y);
1940        XInfoWidget(display,windows,text);
1941      }
1942    /*
1943      Wait for next event.
1944    */
1945    XScreenEvent(display,windows,&event);
1946    if (event.xany.window == windows->command.id)
1947      {
1948        /*
1949          Select a command from the Command widget.
1950        */
1951        id=XCommandWidget(display,windows,AnnotateMenu,&event);
1952        (void) XCheckDefineCursor(display,windows->image.id,cursor);
1953        if (id < 0)
1954          continue;
1955        switch (AnnotateCommands[id])
1956        {
1957          case AnnotateNameCommand:
1958          {
1959            const char
1960              *FontMenu[MaxNumberFonts];
1961
1962            int
1963              font_number;
1964
1965            /*
1966              Initialize menu selections.
1967            */
1968            for (i=0; i < MaxNumberFonts; i++)
1969              FontMenu[i]=resource_info->font_name[i];
1970            FontMenu[MaxNumberFonts-2]="Browser...";
1971            FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1972            /*
1973              Select a font name from the pop-up menu.
1974            */
1975            font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1976              (const char **) FontMenu,command);
1977            if (font_number < 0)
1978              break;
1979            if (font_number == (MaxNumberFonts-2))
1980              {
1981                static char
1982                  font_name[MaxTextExtent] = "fixed";
1983
1984                /*
1985                  Select a font name from a browser.
1986                */
1987                resource_info->font_name[font_number]=font_name;
1988                XFontBrowserWidget(display,windows,"Select",font_name);
1989                if (*font_name == '\0')
1990                  break;
1991              }
1992            /*
1993              Initialize font info.
1994            */
1995            font_info=XLoadQueryFont(display,resource_info->font_name[
1996              font_number]);
1997            if (font_info == (XFontStruct *) NULL)
1998              {
1999                XNoticeWidget(display,windows,"Unable to load font:",
2000                  resource_info->font_name[font_number]);
2001                break;
2002              }
2003            font_id=(unsigned int) font_number;
2004            (void) XFreeFont(display,font_info);
2005            break;
2006          }
2007          case AnnotateFontColorCommand:
2008          {
2009            /*
2010              Initialize menu selections.
2011            */
2012            for (i=0; i < (int) (MaxNumberPens-2); i++)
2013              ColorMenu[i]=resource_info->pen_colors[i];
2014            ColorMenu[MaxNumberPens-2]="transparent";
2015            ColorMenu[MaxNumberPens-1]="Browser...";
2016            ColorMenu[MaxNumberPens]=(const char *) NULL;
2017            /*
2018              Select a pen color from the pop-up menu.
2019            */
2020            pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2021              (const char **) ColorMenu,command);
2022            if (pen_number < 0)
2023              break;
2024            transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
2025              MagickFalse;
2026            if (transparent_pen != MagickFalse)
2027              break;
2028            if (pen_number == (MaxNumberPens-1))
2029              {
2030                static char
2031                  color_name[MaxTextExtent] = "gray";
2032
2033                /*
2034                  Select a pen color from a dialog.
2035                */
2036                resource_info->pen_colors[pen_number]=color_name;
2037                XColorBrowserWidget(display,windows,"Select",color_name);
2038                if (*color_name == '\0')
2039                  break;
2040              }
2041            /*
2042              Set pen color.
2043            */
2044            (void) XParseColor(display,windows->map_info->colormap,
2045              resource_info->pen_colors[pen_number],&color);
2046            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2047              (unsigned int) MaxColors,&color);
2048            windows->pixel_info->pen_colors[pen_number]=color;
2049            pen_id=(unsigned int) pen_number;
2050            break;
2051          }
2052          case AnnotateBackgroundColorCommand:
2053          {
2054            /*
2055              Initialize menu selections.
2056            */
2057            for (i=0; i < (int) (MaxNumberPens-2); i++)
2058              ColorMenu[i]=resource_info->pen_colors[i];
2059            ColorMenu[MaxNumberPens-2]="transparent";
2060            ColorMenu[MaxNumberPens-1]="Browser...";
2061            ColorMenu[MaxNumberPens]=(const char *) NULL;
2062            /*
2063              Select a pen color from the pop-up menu.
2064            */
2065            pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2066              (const char **) ColorMenu,command);
2067            if (pen_number < 0)
2068              break;
2069            transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2070              MagickFalse;
2071            if (transparent_box != MagickFalse)
2072              break;
2073            if (pen_number == (MaxNumberPens-1))
2074              {
2075                static char
2076                  color_name[MaxTextExtent] = "gray";
2077
2078                /*
2079                  Select a pen color from a dialog.
2080                */
2081                resource_info->pen_colors[pen_number]=color_name;
2082                XColorBrowserWidget(display,windows,"Select",color_name);
2083                if (*color_name == '\0')
2084                  break;
2085              }
2086            /*
2087              Set pen color.
2088            */
2089            (void) XParseColor(display,windows->map_info->colormap,
2090              resource_info->pen_colors[pen_number],&color);
2091            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2092              (unsigned int) MaxColors,&color);
2093            windows->pixel_info->pen_colors[pen_number]=color;
2094            box_id=(unsigned int) pen_number;
2095            break;
2096          }
2097          case AnnotateRotateCommand:
2098          {
2099            int
2100              entry;
2101
2102            static char
2103              angle[MaxTextExtent] = "30.0";
2104
2105            static const char
2106              *RotateMenu[] =
2107              {
2108                "-90",
2109                "-45",
2110                "-30",
2111                "0",
2112                "30",
2113                "45",
2114                "90",
2115                "180",
2116                "Dialog...",
2117                (char *) NULL,
2118              };
2119
2120            /*
2121              Select a command from the pop-up menu.
2122            */
2123            entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2124              command);
2125            if (entry < 0)
2126              break;
2127            if (entry != 8)
2128              {
2129                degrees=InterpretLocaleValue(RotateMenu[entry],(char **) NULL);
2130                break;
2131              }
2132            (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2133              angle);
2134            if (*angle == '\0')
2135              break;
2136            degrees=InterpretLocaleValue(angle,(char **) NULL);
2137            break;
2138          }
2139          case AnnotateHelpCommand:
2140          {
2141            XTextViewWidget(display,resource_info,windows,MagickFalse,
2142              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2143            break;
2144          }
2145          case AnnotateDismissCommand:
2146          {
2147            /*
2148              Prematurely exit.
2149            */
2150            state|=EscapeState;
2151            state|=ExitState;
2152            break;
2153          }
2154          default:
2155            break;
2156        }
2157        continue;
2158      }
2159    switch (event.type)
2160    {
2161      case ButtonPress:
2162      {
2163        if (event.xbutton.button != Button1)
2164          break;
2165        if (event.xbutton.window != windows->image.id)
2166          break;
2167        /*
2168          Change to text entering mode.
2169        */
2170        x=event.xbutton.x;
2171        y=event.xbutton.y;
2172        state|=ExitState;
2173        break;
2174      }
2175      case ButtonRelease:
2176        break;
2177      case Expose:
2178        break;
2179      case KeyPress:
2180      {
2181        if (event.xkey.window != windows->image.id)
2182          break;
2183        /*
2184          Respond to a user key press.
2185        */
2186        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2187          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2188        switch ((int) key_symbol)
2189        {
2190          case XK_Escape:
2191          case XK_F20:
2192          {
2193            /*
2194              Prematurely exit.
2195            */
2196            state|=EscapeState;
2197            state|=ExitState;
2198            break;
2199          }
2200          case XK_F1:
2201          case XK_Help:
2202          {
2203            XTextViewWidget(display,resource_info,windows,MagickFalse,
2204              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2205            break;
2206          }
2207          default:
2208          {
2209            (void) XBell(display,0);
2210            break;
2211          }
2212        }
2213        break;
2214      }
2215      case MotionNotify:
2216      {
2217        /*
2218          Map and unmap Info widget as cursor crosses its boundaries.
2219        */
2220        x=event.xmotion.x;
2221        y=event.xmotion.y;
2222        if (windows->info.mapped != MagickFalse)
2223          {
2224            if ((x < (int) (windows->info.x+windows->info.width)) &&
2225                (y < (int) (windows->info.y+windows->info.height)))
2226              (void) XWithdrawWindow(display,windows->info.id,
2227                windows->info.screen);
2228          }
2229        else
2230          if ((x > (int) (windows->info.x+windows->info.width)) ||
2231              (y > (int) (windows->info.y+windows->info.height)))
2232            (void) XMapWindow(display,windows->info.id);
2233        break;
2234      }
2235      default:
2236        break;
2237    }
2238  } while ((state & ExitState) == 0);
2239  (void) XSelectInput(display,windows->image.id,
2240    windows->image.attributes.event_mask);
2241  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2242  if ((state & EscapeState) != 0)
2243    return(MagickTrue);
2244  /*
2245    Set font info and check boundary conditions.
2246  */
2247  font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2248  if (font_info == (XFontStruct *) NULL)
2249    {
2250      XNoticeWidget(display,windows,"Unable to load font:",
2251        resource_info->font_name[font_id]);
2252      font_info=windows->font_info;
2253    }
2254  if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2255    x=(int) windows->image.width-font_info->max_bounds.width;
2256  if (y < (int) (font_info->ascent+font_info->descent))
2257    y=(int) font_info->ascent+font_info->descent;
2258  if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2259      ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2260    return(MagickFalse);
2261  /*
2262    Initialize annotate structure.
2263  */
2264  annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2265  if (annotate_info == (XAnnotateInfo *) NULL)
2266    return(MagickFalse);
2267  XGetAnnotateInfo(annotate_info);
2268  annotate_info->x=x;
2269  annotate_info->y=y;
2270  if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
2271    annotate_info->stencil=OpaqueStencil;
2272  else
2273    if (transparent_box == MagickFalse)
2274      annotate_info->stencil=BackgroundStencil;
2275    else
2276      annotate_info->stencil=ForegroundStencil;
2277  annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2278  annotate_info->degrees=degrees;
2279  annotate_info->font_info=font_info;
2280  annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2281    windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
2282    sizeof(*annotate_info->text));
2283  if (annotate_info->text == (char *) NULL)
2284    return(MagickFalse);
2285  /*
2286    Create cursor and set graphic context.
2287  */
2288  cursor=XCreateFontCursor(display,XC_pencil);
2289  (void) XCheckDefineCursor(display,windows->image.id,cursor);
2290  annotate_context=windows->image.annotate_context;
2291  (void) XSetFont(display,annotate_context,font_info->fid);
2292  (void) XSetBackground(display,annotate_context,
2293    windows->pixel_info->pen_colors[box_id].pixel);
2294  (void) XSetForeground(display,annotate_context,
2295    windows->pixel_info->pen_colors[pen_id].pixel);
2296  /*
2297    Begin annotating the image with text.
2298  */
2299  (void) CloneString(&windows->command.name,"Text");
2300  windows->command.data=0;
2301  (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2302  state=DefaultState;
2303  (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2304  text_event.xexpose.width=(int) font_info->max_bounds.width;
2305  text_event.xexpose.height=font_info->max_bounds.ascent+
2306    font_info->max_bounds.descent;
2307  p=annotate_info->text;
2308  do
2309  {
2310    /*
2311      Display text cursor.
2312    */
2313    *p='\0';
2314    (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2315    /*
2316      Wait for next event.
2317    */
2318    XScreenEvent(display,windows,&event);
2319    if (event.xany.window == windows->command.id)
2320      {
2321        /*
2322          Select a command from the Command widget.
2323        */
2324        (void) XSetBackground(display,annotate_context,
2325          windows->pixel_info->background_color.pixel);
2326        (void) XSetForeground(display,annotate_context,
2327          windows->pixel_info->foreground_color.pixel);
2328        id=XCommandWidget(display,windows,AnnotateMenu,&event);
2329        (void) XSetBackground(display,annotate_context,
2330          windows->pixel_info->pen_colors[box_id].pixel);
2331        (void) XSetForeground(display,annotate_context,
2332          windows->pixel_info->pen_colors[pen_id].pixel);
2333        if (id < 0)
2334          continue;
2335        switch (TextCommands[id])
2336        {
2337          case TextHelpCommand:
2338          {
2339            XTextViewWidget(display,resource_info,windows,MagickFalse,
2340              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2341            (void) XCheckDefineCursor(display,windows->image.id,cursor);
2342            break;
2343          }
2344          case TextApplyCommand:
2345          {
2346            /*
2347              Finished annotating.
2348            */
2349            annotate_info->width=(unsigned int) XTextWidth(font_info,
2350              annotate_info->text,(int) strlen(annotate_info->text));
2351            XRefreshWindow(display,&windows->image,&text_event);
2352            state|=ExitState;
2353            break;
2354          }
2355          default:
2356            break;
2357        }
2358        continue;
2359      }
2360    /*
2361      Erase text cursor.
2362    */
2363    text_event.xexpose.x=x;
2364    text_event.xexpose.y=y-font_info->max_bounds.ascent;
2365    (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2366      (unsigned int) text_event.xexpose.width,(unsigned int)
2367      text_event.xexpose.height,MagickFalse);
2368    XRefreshWindow(display,&windows->image,&text_event);
2369    switch (event.type)
2370    {
2371      case ButtonPress:
2372      {
2373        if (event.xbutton.window != windows->image.id)
2374          break;
2375        if (event.xbutton.button == Button2)
2376          {
2377            /*
2378              Request primary selection.
2379            */
2380            (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2381              windows->image.id,CurrentTime);
2382            break;
2383          }
2384        break;
2385      }
2386      case Expose:
2387      {
2388        if (event.xexpose.count == 0)
2389          {
2390            XAnnotateInfo
2391              *text_info;
2392
2393            /*
2394              Refresh Image window.
2395            */
2396            XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2397            text_info=annotate_info;
2398            while (text_info != (XAnnotateInfo *) NULL)
2399            {
2400              if (annotate_info->stencil == ForegroundStencil)
2401                (void) XDrawString(display,windows->image.id,annotate_context,
2402                  text_info->x,text_info->y,text_info->text,
2403                  (int) strlen(text_info->text));
2404              else
2405                (void) XDrawImageString(display,windows->image.id,
2406                  annotate_context,text_info->x,text_info->y,text_info->text,
2407                  (int) strlen(text_info->text));
2408              text_info=text_info->previous;
2409            }
2410            (void) XDrawString(display,windows->image.id,annotate_context,
2411              x,y,"_",1);
2412          }
2413        break;
2414      }
2415      case KeyPress:
2416      {
2417        int
2418          length;
2419
2420        if (event.xkey.window != windows->image.id)
2421          break;
2422        /*
2423          Respond to a user key press.
2424        */
2425        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2426          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2427        *(command+length)='\0';
2428        if (((event.xkey.state & ControlMask) != 0) ||
2429            ((event.xkey.state & Mod1Mask) != 0))
2430          state|=ModifierState;
2431        if ((state & ModifierState) != 0)
2432          switch ((int) key_symbol)
2433          {
2434            case XK_u:
2435            case XK_U:
2436            {
2437              key_symbol=DeleteCommand;
2438              break;
2439            }
2440            default:
2441              break;
2442          }
2443        switch ((int) key_symbol)
2444        {
2445          case XK_BackSpace:
2446          {
2447            /*
2448              Erase one character.
2449            */
2450            if (p == annotate_info->text)
2451              {
2452                if (annotate_info->previous == (XAnnotateInfo *) NULL)
2453                  break;
2454                else
2455                  {
2456                    /*
2457                      Go to end of the previous line of text.
2458                    */
2459                    annotate_info=annotate_info->previous;
2460                    p=annotate_info->text;
2461                    x=annotate_info->x+annotate_info->width;
2462                    y=annotate_info->y;
2463                    if (annotate_info->width != 0)
2464                      p+=strlen(annotate_info->text);
2465                    break;
2466                  }
2467              }
2468            p--;
2469            x-=XTextWidth(font_info,p,1);
2470            text_event.xexpose.x=x;
2471            text_event.xexpose.y=y-font_info->max_bounds.ascent;
2472            XRefreshWindow(display,&windows->image,&text_event);
2473            break;
2474          }
2475          case XK_bracketleft:
2476          {
2477            key_symbol=XK_Escape;
2478            break;
2479          }
2480          case DeleteCommand:
2481          {
2482            /*
2483              Erase the entire line of text.
2484            */
2485            while (p != annotate_info->text)
2486            {
2487              p--;
2488              x-=XTextWidth(font_info,p,1);
2489              text_event.xexpose.x=x;
2490              XRefreshWindow(display,&windows->image,&text_event);
2491            }
2492            break;
2493          }
2494          case XK_Escape:
2495          case XK_F20:
2496          {
2497            /*
2498              Finished annotating.
2499            */
2500            annotate_info->width=(unsigned int) XTextWidth(font_info,
2501              annotate_info->text,(int) strlen(annotate_info->text));
2502            XRefreshWindow(display,&windows->image,&text_event);
2503            state|=ExitState;
2504            break;
2505          }
2506          default:
2507          {
2508            /*
2509              Draw a single character on the Image window.
2510            */
2511            if ((state & ModifierState) != 0)
2512              break;
2513            if (*command == '\0')
2514              break;
2515            *p=(*command);
2516            if (annotate_info->stencil == ForegroundStencil)
2517              (void) XDrawString(display,windows->image.id,annotate_context,
2518                x,y,p,1);
2519            else
2520              (void) XDrawImageString(display,windows->image.id,
2521                annotate_context,x,y,p,1);
2522            x+=XTextWidth(font_info,p,1);
2523            p++;
2524            if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2525              break;
2526          }
2527          case XK_Return:
2528          case XK_KP_Enter:
2529          {
2530            /*
2531              Advance to the next line of text.
2532            */
2533            *p='\0';
2534            annotate_info->width=(unsigned int) XTextWidth(font_info,
2535              annotate_info->text,(int) strlen(annotate_info->text));
2536            if (annotate_info->next != (XAnnotateInfo *) NULL)
2537              {
2538                /*
2539                  Line of text already exists.
2540                */
2541                annotate_info=annotate_info->next;
2542                x=annotate_info->x;
2543                y=annotate_info->y;
2544                p=annotate_info->text;
2545                break;
2546              }
2547            annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2548              sizeof(*annotate_info->next));
2549            if (annotate_info->next == (XAnnotateInfo *) NULL)
2550              return(MagickFalse);
2551            *annotate_info->next=(*annotate_info);
2552            annotate_info->next->previous=annotate_info;
2553            annotate_info=annotate_info->next;
2554            annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2555              windows->image.width/MagickMax((ssize_t)
2556              font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2557            if (annotate_info->text == (char *) NULL)
2558              return(MagickFalse);
2559            annotate_info->y+=annotate_info->height;
2560            if (annotate_info->y > (int) windows->image.height)
2561              annotate_info->y=(int) annotate_info->height;
2562            annotate_info->next=(XAnnotateInfo *) NULL;
2563            x=annotate_info->x;
2564            y=annotate_info->y;
2565            p=annotate_info->text;
2566            break;
2567          }
2568        }
2569        break;
2570      }
2571      case KeyRelease:
2572      {
2573        /*
2574          Respond to a user key release.
2575        */
2576        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2577          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2578        state&=(~ModifierState);
2579        break;
2580      }
2581      case SelectionNotify:
2582      {
2583        Atom
2584          type;
2585
2586        int
2587          format;
2588
2589        unsigned char
2590          *data;
2591
2592        unsigned long
2593          after,
2594          length;
2595
2596        /*
2597          Obtain response from primary selection.
2598        */
2599        if (event.xselection.property == (Atom) None)
2600          break;
2601        status=XGetWindowProperty(display,event.xselection.requestor,
2602          event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
2603          &type,&format,&length,&after,&data);
2604        if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2605            (length == 0))
2606          break;
2607        /*
2608          Annotate Image window with primary selection.
2609        */
2610        for (i=0; i < (ssize_t) length; i++)
2611        {
2612          if ((char) data[i] != '\n')
2613            {
2614              /*
2615                Draw a single character on the Image window.
2616              */
2617              *p=(char) data[i];
2618              (void) XDrawString(display,windows->image.id,annotate_context,
2619                x,y,p,1);
2620              x+=XTextWidth(font_info,p,1);
2621              p++;
2622              if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2623                continue;
2624            }
2625          /*
2626            Advance to the next line of text.
2627          */
2628          *p='\0';
2629          annotate_info->width=(unsigned int) XTextWidth(font_info,
2630            annotate_info->text,(int) strlen(annotate_info->text));
2631          if (annotate_info->next != (XAnnotateInfo *) NULL)
2632            {
2633              /*
2634                Line of text already exists.
2635              */
2636              annotate_info=annotate_info->next;
2637              x=annotate_info->x;
2638              y=annotate_info->y;
2639              p=annotate_info->text;
2640              continue;
2641            }
2642          annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2643            sizeof(*annotate_info->next));
2644          if (annotate_info->next == (XAnnotateInfo *) NULL)
2645            return(MagickFalse);
2646          *annotate_info->next=(*annotate_info);
2647          annotate_info->next->previous=annotate_info;
2648          annotate_info=annotate_info->next;
2649          annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2650            windows->image.width/MagickMax((ssize_t)
2651            font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2652          if (annotate_info->text == (char *) NULL)
2653            return(MagickFalse);
2654          annotate_info->y+=annotate_info->height;
2655          if (annotate_info->y > (int) windows->image.height)
2656            annotate_info->y=(int) annotate_info->height;
2657          annotate_info->next=(XAnnotateInfo *) NULL;
2658          x=annotate_info->x;
2659          y=annotate_info->y;
2660          p=annotate_info->text;
2661        }
2662        (void) XFree((void *) data);
2663        break;
2664      }
2665      default:
2666        break;
2667    }
2668  } while ((state & ExitState) == 0);
2669  (void) XFreeCursor(display,cursor);
2670  /*
2671    Annotation is relative to image configuration.
2672  */
2673  width=(unsigned int) image->columns;
2674  height=(unsigned int) image->rows;
2675  x=0;
2676  y=0;
2677  if (windows->image.crop_geometry != (char *) NULL)
2678    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2679  /*
2680    Initialize annotated image.
2681  */
2682  XSetCursorState(display,windows,MagickTrue);
2683  XCheckRefreshWindows(display,windows);
2684  while (annotate_info != (XAnnotateInfo *) NULL)
2685  {
2686    if (annotate_info->width == 0)
2687      {
2688        /*
2689          No text on this line--  go to the next line of text.
2690        */
2691        previous_info=annotate_info->previous;
2692        annotate_info->text=(char *)
2693          RelinquishMagickMemory(annotate_info->text);
2694        annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2695        annotate_info=previous_info;
2696        continue;
2697      }
2698    /*
2699      Determine pixel index for box and pen color.
2700    */
2701    windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2702    if (windows->pixel_info->colors != 0)
2703      for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2704        if (windows->pixel_info->pixels[i] ==
2705            windows->pixel_info->pen_colors[box_id].pixel)
2706          {
2707            windows->pixel_info->box_index=(unsigned short) i;
2708            break;
2709          }
2710    windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2711    if (windows->pixel_info->colors != 0)
2712      for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2713        if (windows->pixel_info->pixels[i] ==
2714            windows->pixel_info->pen_colors[pen_id].pixel)
2715          {
2716            windows->pixel_info->pen_index=(unsigned short) i;
2717            break;
2718          }
2719    /*
2720      Define the annotate geometry string.
2721    */
2722    annotate_info->x=(int)
2723      width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2724    annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2725      windows->image.y)/windows->image.ximage->height;
2726    (void) FormatLocaleString(annotate_info->geometry,MaxTextExtent,
2727      "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2728      height*annotate_info->height/windows->image.ximage->height,
2729      annotate_info->x+x,annotate_info->y+y);
2730    /*
2731      Annotate image with text.
2732    */
2733    status=XAnnotateImage(display,windows->pixel_info,annotate_info,image);
2734    if (status == 0)
2735      return(MagickFalse);
2736    /*
2737      Free up memory.
2738    */
2739    previous_info=annotate_info->previous;
2740    annotate_info->text=DestroyString(annotate_info->text);
2741    annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2742    annotate_info=previous_info;
2743  }
2744  (void) XSetForeground(display,annotate_context,
2745    windows->pixel_info->foreground_color.pixel);
2746  (void) XSetBackground(display,annotate_context,
2747    windows->pixel_info->background_color.pixel);
2748  (void) XSetFont(display,annotate_context,windows->font_info->fid);
2749  XSetCursorState(display,windows,MagickFalse);
2750  (void) XFreeFont(display,font_info);
2751  /*
2752    Update image configuration.
2753  */
2754  XConfigureImageColormap(display,resource_info,windows,image);
2755  (void) XConfigureImage(display,resource_info,windows,image,exception);
2756  return(MagickTrue);
2757}
2758
2759/*
2760%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2761%                                                                             %
2762%                                                                             %
2763%                                                                             %
2764+   X B a c k g r o u n d I m a g e                                           %
2765%                                                                             %
2766%                                                                             %
2767%                                                                             %
2768%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2769%
2770%  XBackgroundImage() displays the image in the background of a window.
2771%
2772%  The format of the XBackgroundImage method is:
2773%
2774%      MagickBooleanType XBackgroundImage(Display *display,
2775%        XResourceInfo *resource_info,XWindows *windows,Image **image,
2776%        ExceptionInfo *exception)
2777%
2778%  A description of each parameter follows:
2779%
2780%    o display: Specifies a connection to an X server; returned from
2781%      XOpenDisplay.
2782%
2783%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2784%
2785%    o windows: Specifies a pointer to a XWindows structure.
2786%
2787%    o image: the image.
2788%
2789%    o exception: return any errors or warnings in this structure.
2790%
2791*/
2792static MagickBooleanType XBackgroundImage(Display *display,
2793  XResourceInfo *resource_info,XWindows *windows,Image **image,
2794  ExceptionInfo *exception)
2795{
2796#define BackgroundImageTag  "Background/Image"
2797
2798  int
2799    status;
2800
2801  static char
2802    window_id[MaxTextExtent] = "root";
2803
2804  XResourceInfo
2805    background_resources;
2806
2807  /*
2808    Put image in background.
2809  */
2810  status=XDialogWidget(display,windows,"Background",
2811    "Enter window id (id 0x00 selects window with pointer):",window_id);
2812  if (*window_id == '\0')
2813    return(MagickFalse);
2814  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
2815    exception);
2816  XInfoWidget(display,windows,BackgroundImageTag);
2817  XSetCursorState(display,windows,MagickTrue);
2818  XCheckRefreshWindows(display,windows);
2819  background_resources=(*resource_info);
2820  background_resources.window_id=window_id;
2821  background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2822  status=XDisplayBackgroundImage(display,&background_resources,*image,
2823    exception);
2824  if (status != MagickFalse)
2825    XClientMessage(display,windows->image.id,windows->im_protocols,
2826      windows->im_retain_colors,CurrentTime);
2827  XSetCursorState(display,windows,MagickFalse);
2828  (void) XMagickCommand(display,resource_info,windows,UndoCommand,image,
2829    exception);
2830  return(MagickTrue);
2831}
2832
2833/*
2834%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2835%                                                                             %
2836%                                                                             %
2837%                                                                             %
2838+   X C h o p I m a g e                                                       %
2839%                                                                             %
2840%                                                                             %
2841%                                                                             %
2842%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2843%
2844%  XChopImage() chops the X image.
2845%
2846%  The format of the XChopImage method is:
2847%
2848%    MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2849%      XWindows *windows,Image **image,ExceptionInfo *exception)
2850%
2851%  A description of each parameter follows:
2852%
2853%    o display: Specifies a connection to an X server; returned from
2854%      XOpenDisplay.
2855%
2856%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2857%
2858%    o windows: Specifies a pointer to a XWindows structure.
2859%
2860%    o image: the image.
2861%
2862%    o exception: return any errors or warnings in this structure.
2863%
2864*/
2865static MagickBooleanType XChopImage(Display *display,
2866  XResourceInfo *resource_info,XWindows *windows,Image **image,
2867  ExceptionInfo *exception)
2868{
2869  static const char
2870    *ChopMenu[] =
2871    {
2872      "Direction",
2873      "Help",
2874      "Dismiss",
2875      (char *) NULL
2876    };
2877
2878  static ModeType
2879    direction = HorizontalChopCommand;
2880
2881  static const ModeType
2882    ChopCommands[] =
2883    {
2884      ChopDirectionCommand,
2885      ChopHelpCommand,
2886      ChopDismissCommand
2887    },
2888    DirectionCommands[] =
2889    {
2890      HorizontalChopCommand,
2891      VerticalChopCommand
2892    };
2893
2894  char
2895    text[MaxTextExtent];
2896
2897  Image
2898    *chop_image;
2899
2900  int
2901    id,
2902    x,
2903    y;
2904
2905  MagickRealType
2906    scale_factor;
2907
2908  RectangleInfo
2909    chop_info;
2910
2911  unsigned int
2912    distance,
2913    height,
2914    width;
2915
2916  size_t
2917    state;
2918
2919  XEvent
2920    event;
2921
2922  XSegment
2923    segment_info;
2924
2925  /*
2926    Map Command widget.
2927  */
2928  (void) CloneString(&windows->command.name,"Chop");
2929  windows->command.data=1;
2930  (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2931  (void) XMapRaised(display,windows->command.id);
2932  XClientMessage(display,windows->image.id,windows->im_protocols,
2933    windows->im_update_widget,CurrentTime);
2934  /*
2935    Track pointer until button 1 is pressed.
2936  */
2937  XQueryPosition(display,windows->image.id,&x,&y);
2938  (void) XSelectInput(display,windows->image.id,
2939    windows->image.attributes.event_mask | PointerMotionMask);
2940  state=DefaultState;
2941  do
2942  {
2943    if (windows->info.mapped != MagickFalse)
2944      {
2945        /*
2946          Display pointer position.
2947        */
2948        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
2949          x+windows->image.x,y+windows->image.y);
2950        XInfoWidget(display,windows,text);
2951      }
2952    /*
2953      Wait for next event.
2954    */
2955    XScreenEvent(display,windows,&event);
2956    if (event.xany.window == windows->command.id)
2957      {
2958        /*
2959          Select a command from the Command widget.
2960        */
2961        id=XCommandWidget(display,windows,ChopMenu,&event);
2962        if (id < 0)
2963          continue;
2964        switch (ChopCommands[id])
2965        {
2966          case ChopDirectionCommand:
2967          {
2968            char
2969              command[MaxTextExtent];
2970
2971            static const char
2972              *Directions[] =
2973              {
2974                "horizontal",
2975                "vertical",
2976                (char *) NULL,
2977              };
2978
2979            /*
2980              Select a command from the pop-up menu.
2981            */
2982            id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2983            if (id >= 0)
2984              direction=DirectionCommands[id];
2985            break;
2986          }
2987          case ChopHelpCommand:
2988          {
2989            XTextViewWidget(display,resource_info,windows,MagickFalse,
2990              "Help Viewer - Image Chop",ImageChopHelp);
2991            break;
2992          }
2993          case ChopDismissCommand:
2994          {
2995            /*
2996              Prematurely exit.
2997            */
2998            state|=EscapeState;
2999            state|=ExitState;
3000            break;
3001          }
3002          default:
3003            break;
3004        }
3005        continue;
3006      }
3007    switch (event.type)
3008    {
3009      case ButtonPress:
3010      {
3011        if (event.xbutton.button != Button1)
3012          break;
3013        if (event.xbutton.window != windows->image.id)
3014          break;
3015        /*
3016          User has committed to start point of chopping line.
3017        */
3018        segment_info.x1=(short int) event.xbutton.x;
3019        segment_info.x2=(short int) event.xbutton.x;
3020        segment_info.y1=(short int) event.xbutton.y;
3021        segment_info.y2=(short int) event.xbutton.y;
3022        state|=ExitState;
3023        break;
3024      }
3025      case ButtonRelease:
3026        break;
3027      case Expose:
3028        break;
3029      case KeyPress:
3030      {
3031        char
3032          command[MaxTextExtent];
3033
3034        KeySym
3035          key_symbol;
3036
3037        if (event.xkey.window != windows->image.id)
3038          break;
3039        /*
3040          Respond to a user key press.
3041        */
3042        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3043          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3044        switch ((int) key_symbol)
3045        {
3046          case XK_Escape:
3047          case XK_F20:
3048          {
3049            /*
3050              Prematurely exit.
3051            */
3052            state|=EscapeState;
3053            state|=ExitState;
3054            break;
3055          }
3056          case XK_F1:
3057          case XK_Help:
3058          {
3059            (void) XSetFunction(display,windows->image.highlight_context,
3060              GXcopy);
3061            XTextViewWidget(display,resource_info,windows,MagickFalse,
3062              "Help Viewer - Image Chop",ImageChopHelp);
3063            (void) XSetFunction(display,windows->image.highlight_context,
3064              GXinvert);
3065            break;
3066          }
3067          default:
3068          {
3069            (void) XBell(display,0);
3070            break;
3071          }
3072        }
3073        break;
3074      }
3075      case MotionNotify:
3076      {
3077        /*
3078          Map and unmap Info widget as text cursor crosses its boundaries.
3079        */
3080        x=event.xmotion.x;
3081        y=event.xmotion.y;
3082        if (windows->info.mapped != MagickFalse)
3083          {
3084            if ((x < (int) (windows->info.x+windows->info.width)) &&
3085                (y < (int) (windows->info.y+windows->info.height)))
3086              (void) XWithdrawWindow(display,windows->info.id,
3087                windows->info.screen);
3088          }
3089        else
3090          if ((x > (int) (windows->info.x+windows->info.width)) ||
3091              (y > (int) (windows->info.y+windows->info.height)))
3092            (void) XMapWindow(display,windows->info.id);
3093      }
3094    }
3095  } while ((state & ExitState) == 0);
3096  (void) XSelectInput(display,windows->image.id,
3097    windows->image.attributes.event_mask);
3098  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3099  if ((state & EscapeState) != 0)
3100    return(MagickTrue);
3101  /*
3102    Draw line as pointer moves until the mouse button is released.
3103  */
3104  chop_info.width=0;
3105  chop_info.height=0;
3106  chop_info.x=0;
3107  chop_info.y=0;
3108  distance=0;
3109  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3110  state=DefaultState;
3111  do
3112  {
3113    if (distance > 9)
3114      {
3115        /*
3116          Display info and draw chopping line.
3117        */
3118        if (windows->info.mapped == MagickFalse)
3119          (void) XMapWindow(display,windows->info.id);
3120        (void) FormatLocaleString(text,MaxTextExtent,
3121          " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3122          chop_info.height,(double) chop_info.x,(double) chop_info.y);
3123        XInfoWidget(display,windows,text);
3124        XHighlightLine(display,windows->image.id,
3125          windows->image.highlight_context,&segment_info);
3126      }
3127    else
3128      if (windows->info.mapped != MagickFalse)
3129        (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3130    /*
3131      Wait for next event.
3132    */
3133    XScreenEvent(display,windows,&event);
3134    if (distance > 9)
3135      XHighlightLine(display,windows->image.id,
3136        windows->image.highlight_context,&segment_info);
3137    switch (event.type)
3138    {
3139      case ButtonPress:
3140      {
3141        segment_info.x2=(short int) event.xmotion.x;
3142        segment_info.y2=(short int) event.xmotion.y;
3143        break;
3144      }
3145      case ButtonRelease:
3146      {
3147        /*
3148          User has committed to chopping line.
3149        */
3150        segment_info.x2=(short int) event.xbutton.x;
3151        segment_info.y2=(short int) event.xbutton.y;
3152        state|=ExitState;
3153        break;
3154      }
3155      case Expose:
3156        break;
3157      case MotionNotify:
3158      {
3159        segment_info.x2=(short int) event.xmotion.x;
3160        segment_info.y2=(short int) event.xmotion.y;
3161      }
3162      default:
3163        break;
3164    }
3165    /*
3166      Check boundary conditions.
3167    */
3168    if (segment_info.x2 < 0)
3169      segment_info.x2=0;
3170    else
3171      if (segment_info.x2 > windows->image.ximage->width)
3172        segment_info.x2=windows->image.ximage->width;
3173    if (segment_info.y2 < 0)
3174      segment_info.y2=0;
3175    else
3176      if (segment_info.y2 > windows->image.ximage->height)
3177        segment_info.y2=windows->image.ximage->height;
3178    distance=(unsigned int)
3179      (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3180       ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3181    /*
3182      Compute chopping geometry.
3183    */
3184    if (direction == HorizontalChopCommand)
3185      {
3186        chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3187        chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3188        chop_info.height=0;
3189        chop_info.y=0;
3190        if (segment_info.x1 > (int) segment_info.x2)
3191          {
3192            chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3193            chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3194          }
3195      }
3196    else
3197      {
3198        chop_info.width=0;
3199        chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3200        chop_info.x=0;
3201        chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3202        if (segment_info.y1 > segment_info.y2)
3203          {
3204            chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3205            chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3206          }
3207      }
3208  } while ((state & ExitState) == 0);
3209  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3210  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3211  if (distance <= 9)
3212    return(MagickTrue);
3213  /*
3214    Image chopping is relative to image configuration.
3215  */
3216  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
3217    exception);
3218  XSetCursorState(display,windows,MagickTrue);
3219  XCheckRefreshWindows(display,windows);
3220  windows->image.window_changes.width=windows->image.ximage->width-
3221    (unsigned int) chop_info.width;
3222  windows->image.window_changes.height=windows->image.ximage->height-
3223    (unsigned int) chop_info.height;
3224  width=(unsigned int) (*image)->columns;
3225  height=(unsigned int) (*image)->rows;
3226  x=0;
3227  y=0;
3228  if (windows->image.crop_geometry != (char *) NULL)
3229    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3230  scale_factor=(MagickRealType) width/windows->image.ximage->width;
3231  chop_info.x+=x;
3232  chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3233  chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3234  scale_factor=(MagickRealType) height/windows->image.ximage->height;
3235  chop_info.y+=y;
3236  chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3237  chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3238  /*
3239    Chop image.
3240  */
3241  chop_image=ChopImage(*image,&chop_info,exception);
3242  XSetCursorState(display,windows,MagickFalse);
3243  if (chop_image == (Image *) NULL)
3244    return(MagickFalse);
3245  *image=DestroyImage(*image);
3246  *image=chop_image;
3247  /*
3248    Update image configuration.
3249  */
3250  XConfigureImageColormap(display,resource_info,windows,*image);
3251  (void) XConfigureImage(display,resource_info,windows,*image,exception);
3252  return(MagickTrue);
3253}
3254
3255/*
3256%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3257%                                                                             %
3258%                                                                             %
3259%                                                                             %
3260+   X C o l o r E d i t I m a g e                                             %
3261%                                                                             %
3262%                                                                             %
3263%                                                                             %
3264%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3265%
3266%  XColorEditImage() allows the user to interactively change the color of one
3267%  pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3268%
3269%  The format of the XColorEditImage method is:
3270%
3271%      MagickBooleanType XColorEditImage(Display *display,
3272%        XResourceInfo *resource_info,XWindows *windows,Image **image,
3273%          ExceptionInfo *exception)
3274%
3275%  A description of each parameter follows:
3276%
3277%    o display: Specifies a connection to an X server;  returned from
3278%      XOpenDisplay.
3279%
3280%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3281%
3282%    o windows: Specifies a pointer to a XWindows structure.
3283%
3284%    o image: the image; returned from ReadImage.
3285%
3286%    o exception: return any errors or warnings in this structure.
3287%
3288*/
3289static MagickBooleanType XColorEditImage(Display *display,
3290  XResourceInfo *resource_info,XWindows *windows,Image **image,
3291  ExceptionInfo *exception)
3292{
3293  static const char
3294    *ColorEditMenu[] =
3295    {
3296      "Method",
3297      "Pixel Color",
3298      "Border Color",
3299      "Fuzz",
3300      "Undo",
3301      "Help",
3302      "Dismiss",
3303      (char *) NULL
3304    };
3305
3306  static const ModeType
3307    ColorEditCommands[] =
3308    {
3309      ColorEditMethodCommand,
3310      ColorEditColorCommand,
3311      ColorEditBorderCommand,
3312      ColorEditFuzzCommand,
3313      ColorEditUndoCommand,
3314      ColorEditHelpCommand,
3315      ColorEditDismissCommand
3316    };
3317
3318  static PaintMethod
3319    method = PointMethod;
3320
3321  static unsigned int
3322    pen_id = 0;
3323
3324  static XColor
3325    border_color = { 0, 0, 0, 0, 0, 0 };
3326
3327  char
3328    command[MaxTextExtent],
3329    text[MaxTextExtent];
3330
3331  Cursor
3332    cursor;
3333
3334  int
3335    entry,
3336    id,
3337    x,
3338    x_offset,
3339    y,
3340    y_offset;
3341
3342  register Quantum
3343    *q;
3344
3345  register ssize_t
3346    i;
3347
3348  unsigned int
3349    height,
3350    width;
3351
3352  size_t
3353    state;
3354
3355  XColor
3356    color;
3357
3358  XEvent
3359    event;
3360
3361  /*
3362    Map Command widget.
3363  */
3364  (void) CloneString(&windows->command.name,"Color Edit");
3365  windows->command.data=4;
3366  (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3367  (void) XMapRaised(display,windows->command.id);
3368  XClientMessage(display,windows->image.id,windows->im_protocols,
3369    windows->im_update_widget,CurrentTime);
3370  /*
3371    Make cursor.
3372  */
3373  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3374    resource_info->background_color,resource_info->foreground_color);
3375  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3376  /*
3377    Track pointer until button 1 is pressed.
3378  */
3379  XQueryPosition(display,windows->image.id,&x,&y);
3380  (void) XSelectInput(display,windows->image.id,
3381    windows->image.attributes.event_mask | PointerMotionMask);
3382  state=DefaultState;
3383  do
3384  {
3385    if (windows->info.mapped != MagickFalse)
3386      {
3387        /*
3388          Display pointer position.
3389        */
3390        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
3391          x+windows->image.x,y+windows->image.y);
3392        XInfoWidget(display,windows,text);
3393      }
3394    /*
3395      Wait for next event.
3396    */
3397    XScreenEvent(display,windows,&event);
3398    if (event.xany.window == windows->command.id)
3399      {
3400        /*
3401          Select a command from the Command widget.
3402        */
3403        id=XCommandWidget(display,windows,ColorEditMenu,&event);
3404        if (id < 0)
3405          {
3406            (void) XCheckDefineCursor(display,windows->image.id,cursor);
3407            continue;
3408          }
3409        switch (ColorEditCommands[id])
3410        {
3411          case ColorEditMethodCommand:
3412          {
3413            char
3414              **methods;
3415
3416            /*
3417              Select a method from the pop-up menu.
3418            */
3419            methods=(char **) GetCommandOptions(MagickMethodOptions);
3420            if (methods == (char **) NULL)
3421              break;
3422            entry=XMenuWidget(display,windows,ColorEditMenu[id],
3423              (const char **) methods,command);
3424            if (entry >= 0)
3425              method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3426                MagickFalse,methods[entry]);
3427            methods=DestroyStringList(methods);
3428            break;
3429          }
3430          case ColorEditColorCommand:
3431          {
3432            const char
3433              *ColorMenu[MaxNumberPens];
3434
3435            int
3436              pen_number;
3437
3438            /*
3439              Initialize menu selections.
3440            */
3441            for (i=0; i < (int) (MaxNumberPens-2); i++)
3442              ColorMenu[i]=resource_info->pen_colors[i];
3443            ColorMenu[MaxNumberPens-2]="Browser...";
3444            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3445            /*
3446              Select a pen color from the pop-up menu.
3447            */
3448            pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3449              (const char **) ColorMenu,command);
3450            if (pen_number < 0)
3451              break;
3452            if (pen_number == (MaxNumberPens-2))
3453              {
3454                static char
3455                  color_name[MaxTextExtent] = "gray";
3456
3457                /*
3458                  Select a pen color from a dialog.
3459                */
3460                resource_info->pen_colors[pen_number]=color_name;
3461                XColorBrowserWidget(display,windows,"Select",color_name);
3462                if (*color_name == '\0')
3463                  break;
3464              }
3465            /*
3466              Set pen color.
3467            */
3468            (void) XParseColor(display,windows->map_info->colormap,
3469              resource_info->pen_colors[pen_number],&color);
3470            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3471              (unsigned int) MaxColors,&color);
3472            windows->pixel_info->pen_colors[pen_number]=color;
3473            pen_id=(unsigned int) pen_number;
3474            break;
3475          }
3476          case ColorEditBorderCommand:
3477          {
3478            const char
3479              *ColorMenu[MaxNumberPens];
3480
3481            int
3482              pen_number;
3483
3484            /*
3485              Initialize menu selections.
3486            */
3487            for (i=0; i < (int) (MaxNumberPens-2); i++)
3488              ColorMenu[i]=resource_info->pen_colors[i];
3489            ColorMenu[MaxNumberPens-2]="Browser...";
3490            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3491            /*
3492              Select a pen color from the pop-up menu.
3493            */
3494            pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3495              (const char **) ColorMenu,command);
3496            if (pen_number < 0)
3497              break;
3498            if (pen_number == (MaxNumberPens-2))
3499              {
3500                static char
3501                  color_name[MaxTextExtent] = "gray";
3502
3503                /*
3504                  Select a pen color from a dialog.
3505                */
3506                resource_info->pen_colors[pen_number]=color_name;
3507                XColorBrowserWidget(display,windows,"Select",color_name);
3508                if (*color_name == '\0')
3509                  break;
3510              }
3511            /*
3512              Set border color.
3513            */
3514            (void) XParseColor(display,windows->map_info->colormap,
3515              resource_info->pen_colors[pen_number],&border_color);
3516            break;
3517          }
3518          case ColorEditFuzzCommand:
3519          {
3520            static char
3521              fuzz[MaxTextExtent];
3522
3523            static const char
3524              *FuzzMenu[] =
3525              {
3526                "0%",
3527                "2%",
3528                "5%",
3529                "10%",
3530                "15%",
3531                "Dialog...",
3532                (char *) NULL,
3533              };
3534
3535            /*
3536              Select a command from the pop-up menu.
3537            */
3538            entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3539              command);
3540            if (entry < 0)
3541              break;
3542            if (entry != 5)
3543              {
3544                (*image)->fuzz=SiPrefixToDouble(FuzzMenu[entry],(double)
3545                  QuantumRange+1.0);
3546                break;
3547              }
3548            (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
3549            (void) XDialogWidget(display,windows,"Ok",
3550              "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3551            if (*fuzz == '\0')
3552              break;
3553            (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
3554            (*image)->fuzz=SiPrefixToDouble(fuzz,(double) QuantumRange+1.0);
3555            break;
3556          }
3557          case ColorEditUndoCommand:
3558          {
3559            (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3560              image,exception);
3561            break;
3562          }
3563          case ColorEditHelpCommand:
3564          default:
3565          {
3566            XTextViewWidget(display,resource_info,windows,MagickFalse,
3567              "Help Viewer - Image Annotation",ImageColorEditHelp);
3568            break;
3569          }
3570          case ColorEditDismissCommand:
3571          {
3572            /*
3573              Prematurely exit.
3574            */
3575            state|=EscapeState;
3576            state|=ExitState;
3577            break;
3578          }
3579        }
3580        (void) XCheckDefineCursor(display,windows->image.id,cursor);
3581        continue;
3582      }
3583    switch (event.type)
3584    {
3585      case ButtonPress:
3586      {
3587        if (event.xbutton.button != Button1)
3588          break;
3589        if ((event.xbutton.window != windows->image.id) &&
3590            (event.xbutton.window != windows->magnify.id))
3591          break;
3592        /*
3593          exit loop.
3594        */
3595        x=event.xbutton.x;
3596        y=event.xbutton.y;
3597        (void) XMagickCommand(display,resource_info,windows,
3598          SaveToUndoBufferCommand,image,exception);
3599        state|=UpdateConfigurationState;
3600        break;
3601      }
3602      case ButtonRelease:
3603      {
3604        if (event.xbutton.button != Button1)
3605          break;
3606        if ((event.xbutton.window != windows->image.id) &&
3607            (event.xbutton.window != windows->magnify.id))
3608          break;
3609        /*
3610          Update colormap information.
3611        */
3612        x=event.xbutton.x;
3613        y=event.xbutton.y;
3614        XConfigureImageColormap(display,resource_info,windows,*image);
3615        (void) XConfigureImage(display,resource_info,windows,*image,exception);
3616        XInfoWidget(display,windows,text);
3617        (void) XCheckDefineCursor(display,windows->image.id,cursor);
3618        state&=(~UpdateConfigurationState);
3619        break;
3620      }
3621      case Expose:
3622        break;
3623      case KeyPress:
3624      {
3625        KeySym
3626          key_symbol;
3627
3628        if (event.xkey.window == windows->magnify.id)
3629          {
3630            Window
3631              window;
3632
3633            window=windows->magnify.id;
3634            while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3635          }
3636        if (event.xkey.window != windows->image.id)
3637          break;
3638        /*
3639          Respond to a user key press.
3640        */
3641        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3642          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3643        switch ((int) key_symbol)
3644        {
3645          case XK_Escape:
3646          case XK_F20:
3647          {
3648            /*
3649              Prematurely exit.
3650            */
3651            state|=ExitState;
3652            break;
3653          }
3654          case XK_F1:
3655          case XK_Help:
3656          {
3657            XTextViewWidget(display,resource_info,windows,MagickFalse,
3658              "Help Viewer - Image Annotation",ImageColorEditHelp);
3659            break;
3660          }
3661          default:
3662          {
3663            (void) XBell(display,0);
3664            break;
3665          }
3666        }
3667        break;
3668      }
3669      case MotionNotify:
3670      {
3671        /*
3672          Map and unmap Info widget as cursor crosses its boundaries.
3673        */
3674        x=event.xmotion.x;
3675        y=event.xmotion.y;
3676        if (windows->info.mapped != MagickFalse)
3677          {
3678            if ((x < (int) (windows->info.x+windows->info.width)) &&
3679                (y < (int) (windows->info.y+windows->info.height)))
3680              (void) XWithdrawWindow(display,windows->info.id,
3681                windows->info.screen);
3682          }
3683        else
3684          if ((x > (int) (windows->info.x+windows->info.width)) ||
3685              (y > (int) (windows->info.y+windows->info.height)))
3686            (void) XMapWindow(display,windows->info.id);
3687        break;
3688      }
3689      default:
3690        break;
3691    }
3692    if (event.xany.window == windows->magnify.id)
3693      {
3694        x=windows->magnify.x-windows->image.x;
3695        y=windows->magnify.y-windows->image.y;
3696      }
3697    x_offset=x;
3698    y_offset=y;
3699    if ((state & UpdateConfigurationState) != 0)
3700      {
3701        CacheView
3702          *image_view;
3703
3704        int
3705          x,
3706          y;
3707
3708        /*
3709          Pixel edit is relative to image configuration.
3710        */
3711        (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3712          MagickTrue);
3713        color=windows->pixel_info->pen_colors[pen_id];
3714        XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3715        width=(unsigned int) (*image)->columns;
3716        height=(unsigned int) (*image)->rows;
3717        x=0;
3718        y=0;
3719        if (windows->image.crop_geometry != (char *) NULL)
3720          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3721            &width,&height);
3722        x_offset=(int)
3723          (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3724        y_offset=(int)
3725          (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3726        if ((x_offset < 0) || (y_offset < 0))
3727          continue;
3728        if ((x_offset >= (int) (*image)->columns) ||
3729            (y_offset >= (int) (*image)->rows))
3730          continue;
3731        image_view=AcquireCacheView(*image);
3732        switch (method)
3733        {
3734          case PointMethod:
3735          default:
3736          {
3737            /*
3738              Update color information using point algorithm.
3739            */
3740            if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3741              return(MagickFalse);
3742            q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3743              (ssize_t) y_offset,1,1,exception);
3744            if (q == (Quantum *) NULL)
3745              break;
3746            SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3747            SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3748            SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3749            (void) SyncCacheViewAuthenticPixels(image_view,exception);
3750            break;
3751          }
3752          case ReplaceMethod:
3753          {
3754            PixelInfo
3755              pixel,
3756              target;
3757
3758            Quantum
3759              virtual_pixel[MaxPixelChannels];
3760
3761            /*
3762              Update color information using replace algorithm.
3763            */
3764            (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
3765              (ssize_t) y_offset,virtual_pixel,exception);
3766            target.red=virtual_pixel[RedPixelChannel];
3767            target.green=virtual_pixel[GreenPixelChannel];
3768            target.blue=virtual_pixel[BluePixelChannel];
3769            target.alpha=virtual_pixel[AlphaPixelChannel];
3770            if ((*image)->storage_class == DirectClass)
3771              {
3772                for (y=0; y < (int) (*image)->rows; y++)
3773                {
3774                  q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3775                    (*image)->columns,1,exception);
3776                  if (q == (Quantum *) NULL)
3777                    break;
3778                  for (x=0; x < (int) (*image)->columns; x++)
3779                  {
3780                    GetPixelInfoPixel(*image,q,&pixel);
3781                    if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
3782                      {
3783                        SetPixelRed(*image,ScaleShortToQuantum(
3784                          color.red),q);
3785                        SetPixelGreen(*image,ScaleShortToQuantum(
3786                          color.green),q);
3787                        SetPixelBlue(*image,ScaleShortToQuantum(
3788                          color.blue),q);
3789                      }
3790                    q+=GetPixelChannels(*image);
3791                  }
3792                  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3793                    break;
3794                }
3795              }
3796            else
3797              {
3798                for (i=0; i < (ssize_t) (*image)->colors; i++)
3799                  if (IsFuzzyEquivalencePixelInfo((*image)->colormap+i,&target))
3800                    {
3801                      (*image)->colormap[i].red=ScaleShortToQuantum(
3802                        color.red);
3803                      (*image)->colormap[i].green=ScaleShortToQuantum(
3804                        color.green);
3805                      (*image)->colormap[i].blue=ScaleShortToQuantum(
3806                        color.blue);
3807                    }
3808                (void) SyncImage(*image,exception);
3809              }
3810            break;
3811          }
3812          case FloodfillMethod:
3813          case FillToBorderMethod:
3814          {
3815            DrawInfo
3816              *draw_info;
3817
3818            PixelInfo
3819              target;
3820
3821            /*
3822              Update color information using floodfill algorithm.
3823            */
3824            (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
3825              (ssize_t) y_offset,&target,exception);
3826            if (method == FillToBorderMethod)
3827              {
3828                target.red=(MagickRealType)
3829                  ScaleShortToQuantum(border_color.red);
3830                target.green=(MagickRealType)
3831                  ScaleShortToQuantum(border_color.green);
3832                target.blue=(MagickRealType)
3833                  ScaleShortToQuantum(border_color.blue);
3834              }
3835            draw_info=CloneDrawInfo(resource_info->image_info,
3836              (DrawInfo *) NULL);
3837            (void) QueryColorCompliance(resource_info->pen_colors[pen_id],
3838              AllCompliance,&draw_info->fill,exception);
3839            (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
3840              x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
3841              MagickFalse : MagickTrue,exception);
3842            draw_info=DestroyDrawInfo(draw_info);
3843            break;
3844          }
3845          case ResetMethod:
3846          {
3847            /*
3848              Update color information using reset algorithm.
3849            */
3850            if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3851              return(MagickFalse);
3852            for (y=0; y < (int) (*image)->rows; y++)
3853            {
3854              q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3855                (*image)->columns,1,exception);
3856              if (q == (Quantum *) NULL)
3857                break;
3858              for (x=0; x < (int) (*image)->columns; x++)
3859              {
3860                SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3861                SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3862                SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3863                q+=GetPixelChannels(*image);
3864              }
3865              if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3866                break;
3867            }
3868            break;
3869          }
3870        }
3871        image_view=DestroyCacheView(image_view);
3872        state&=(~UpdateConfigurationState);
3873      }
3874  } while ((state & ExitState) == 0);
3875  (void) XSelectInput(display,windows->image.id,
3876    windows->image.attributes.event_mask);
3877  XSetCursorState(display,windows,MagickFalse);
3878  (void) XFreeCursor(display,cursor);
3879  return(MagickTrue);
3880}
3881
3882/*
3883%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3884%                                                                             %
3885%                                                                             %
3886%                                                                             %
3887+   X C o m p o s i t e I m a g e                                             %
3888%                                                                             %
3889%                                                                             %
3890%                                                                             %
3891%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3892%
3893%  XCompositeImage() requests an image name from the user, reads the image and
3894%  composites it with the X window image at a location the user chooses with
3895%  the pointer.
3896%
3897%  The format of the XCompositeImage method is:
3898%
3899%      MagickBooleanType XCompositeImage(Display *display,
3900%        XResourceInfo *resource_info,XWindows *windows,Image *image,
3901%        ExceptionInfo *exception)
3902%
3903%  A description of each parameter follows:
3904%
3905%    o display: Specifies a connection to an X server;  returned from
3906%      XOpenDisplay.
3907%
3908%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3909%
3910%    o windows: Specifies a pointer to a XWindows structure.
3911%
3912%    o image: the image; returned from ReadImage.
3913%
3914%    o exception: return any errors or warnings in this structure.
3915%
3916*/
3917static MagickBooleanType XCompositeImage(Display *display,
3918  XResourceInfo *resource_info,XWindows *windows,Image *image,
3919  ExceptionInfo *exception)
3920{
3921  static char
3922    displacement_geometry[MaxTextExtent] = "30x30",
3923    filename[MaxTextExtent] = "\0";
3924
3925  static const char
3926    *CompositeMenu[] =
3927    {
3928      "Operators",
3929      "Dissolve",
3930      "Displace",
3931      "Help",
3932      "Dismiss",
3933      (char *) NULL
3934    };
3935
3936  static CompositeOperator
3937    compose = CopyCompositeOp;
3938
3939  static const ModeType
3940    CompositeCommands[] =
3941    {
3942      CompositeOperatorsCommand,
3943      CompositeDissolveCommand,
3944      CompositeDisplaceCommand,
3945      CompositeHelpCommand,
3946      CompositeDismissCommand
3947    };
3948
3949  char
3950    text[MaxTextExtent];
3951
3952  Cursor
3953    cursor;
3954
3955  Image
3956    *composite_image;
3957
3958  int
3959    entry,
3960    id,
3961    x,
3962    y;
3963
3964  MagickRealType
3965    blend,
3966    scale_factor;
3967
3968  RectangleInfo
3969    highlight_info,
3970    composite_info;
3971
3972  unsigned int
3973    height,
3974    width;
3975
3976  size_t
3977    state;
3978
3979  XEvent
3980    event;
3981
3982  /*
3983    Request image file name from user.
3984  */
3985  XFileBrowserWidget(display,windows,"Composite",filename);
3986  if (*filename == '\0')
3987    return(MagickTrue);
3988  /*
3989    Read image.
3990  */
3991  XSetCursorState(display,windows,MagickTrue);
3992  XCheckRefreshWindows(display,windows);
3993  (void) CopyMagickString(resource_info->image_info->filename,filename,
3994    MaxTextExtent);
3995  composite_image=ReadImage(resource_info->image_info,exception);
3996  CatchException(exception);
3997  XSetCursorState(display,windows,MagickFalse);
3998  if (composite_image == (Image *) NULL)
3999    return(MagickFalse);
4000  /*
4001    Map Command widget.
4002  */
4003  (void) CloneString(&windows->command.name,"Composite");
4004  windows->command.data=1;
4005  (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
4006  (void) XMapRaised(display,windows->command.id);
4007  XClientMessage(display,windows->image.id,windows->im_protocols,
4008    windows->im_update_widget,CurrentTime);
4009  /*
4010    Track pointer until button 1 is pressed.
4011  */
4012  XQueryPosition(display,windows->image.id,&x,&y);
4013  (void) XSelectInput(display,windows->image.id,
4014    windows->image.attributes.event_mask | PointerMotionMask);
4015  composite_info.x=(ssize_t) windows->image.x+x;
4016  composite_info.y=(ssize_t) windows->image.y+y;
4017  composite_info.width=0;
4018  composite_info.height=0;
4019  cursor=XCreateFontCursor(display,XC_ul_angle);
4020  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4021  blend=0.0;
4022  state=DefaultState;
4023  do
4024  {
4025    if (windows->info.mapped != MagickFalse)
4026      {
4027        /*
4028          Display pointer position.
4029        */
4030        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4031          (long) composite_info.x,(long) composite_info.y);
4032        XInfoWidget(display,windows,text);
4033      }
4034    highlight_info=composite_info;
4035    highlight_info.x=composite_info.x-windows->image.x;
4036    highlight_info.y=composite_info.y-windows->image.y;
4037    XHighlightRectangle(display,windows->image.id,
4038      windows->image.highlight_context,&highlight_info);
4039    /*
4040      Wait for next event.
4041    */
4042    XScreenEvent(display,windows,&event);
4043    XHighlightRectangle(display,windows->image.id,
4044      windows->image.highlight_context,&highlight_info);
4045    if (event.xany.window == windows->command.id)
4046      {
4047        /*
4048          Select a command from the Command widget.
4049        */
4050        id=XCommandWidget(display,windows,CompositeMenu,&event);
4051        if (id < 0)
4052          continue;
4053        switch (CompositeCommands[id])
4054        {
4055          case CompositeOperatorsCommand:
4056          {
4057            char
4058              command[MaxTextExtent],
4059              **operators;
4060
4061            /*
4062              Select a command from the pop-up menu.
4063            */
4064            operators=GetCommandOptions(MagickComposeOptions);
4065            if (operators == (char **) NULL)
4066              break;
4067            entry=XMenuWidget(display,windows,CompositeMenu[id],
4068              (const char **) operators,command);
4069            if (entry >= 0)
4070              compose=(CompositeOperator) ParseCommandOption(
4071                MagickComposeOptions,MagickFalse,operators[entry]);
4072            operators=DestroyStringList(operators);
4073            break;
4074          }
4075          case CompositeDissolveCommand:
4076          {
4077            static char
4078              factor[MaxTextExtent] = "20.0";
4079
4080            /*
4081              Dissolve the two images a given percent.
4082            */
4083            (void) XSetFunction(display,windows->image.highlight_context,
4084              GXcopy);
4085            (void) XDialogWidget(display,windows,"Dissolve",
4086              "Enter the blend factor (0.0 - 99.9%):",factor);
4087            (void) XSetFunction(display,windows->image.highlight_context,
4088              GXinvert);
4089            if (*factor == '\0')
4090              break;
4091            blend=InterpretLocaleValue(factor,(char **) NULL);
4092            compose=DissolveCompositeOp;
4093            break;
4094          }
4095          case CompositeDisplaceCommand:
4096          {
4097            /*
4098              Get horizontal and vertical scale displacement geometry.
4099            */
4100            (void) XSetFunction(display,windows->image.highlight_context,
4101              GXcopy);
4102            (void) XDialogWidget(display,windows,"Displace",
4103              "Enter the horizontal and vertical scale:",displacement_geometry);
4104            (void) XSetFunction(display,windows->image.highlight_context,
4105              GXinvert);
4106            if (*displacement_geometry == '\0')
4107              break;
4108            compose=DisplaceCompositeOp;
4109            break;
4110          }
4111          case CompositeHelpCommand:
4112          {
4113            (void) XSetFunction(display,windows->image.highlight_context,
4114              GXcopy);
4115            XTextViewWidget(display,resource_info,windows,MagickFalse,
4116              "Help Viewer - Image Composite",ImageCompositeHelp);
4117            (void) XSetFunction(display,windows->image.highlight_context,
4118              GXinvert);
4119            break;
4120          }
4121          case CompositeDismissCommand:
4122          {
4123            /*
4124              Prematurely exit.
4125            */
4126            state|=EscapeState;
4127            state|=ExitState;
4128            break;
4129          }
4130          default:
4131            break;
4132        }
4133        continue;
4134      }
4135    switch (event.type)
4136    {
4137      case ButtonPress:
4138      {
4139        if (image->debug != MagickFalse)
4140          (void) LogMagickEvent(X11Event,GetMagickModule(),
4141            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4142            event.xbutton.button,event.xbutton.x,event.xbutton.y);
4143        if (event.xbutton.button != Button1)
4144          break;
4145        if (event.xbutton.window != windows->image.id)
4146          break;
4147        /*
4148          Change cursor.
4149        */
4150        composite_info.width=composite_image->columns;
4151        composite_info.height=composite_image->rows;
4152        (void) XCheckDefineCursor(display,windows->image.id,cursor);
4153        composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4154        composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4155        break;
4156      }
4157      case ButtonRelease:
4158      {
4159        if (image->debug != MagickFalse)
4160          (void) LogMagickEvent(X11Event,GetMagickModule(),
4161            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4162            event.xbutton.button,event.xbutton.x,event.xbutton.y);
4163        if (event.xbutton.button != Button1)
4164          break;
4165        if (event.xbutton.window != windows->image.id)
4166          break;
4167        if ((composite_info.width != 0) && (composite_info.height != 0))
4168          {
4169            /*
4170              User has selected the location of the composite image.
4171            */
4172            composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4173            composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4174            state|=ExitState;
4175          }
4176        break;
4177      }
4178      case Expose:
4179        break;
4180      case KeyPress:
4181      {
4182        char
4183          command[MaxTextExtent];
4184
4185        KeySym
4186          key_symbol;
4187
4188        int
4189          length;
4190
4191        if (event.xkey.window != windows->image.id)
4192          break;
4193        /*
4194          Respond to a user key press.
4195        */
4196        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4197          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4198        *(command+length)='\0';
4199        if (image->debug != MagickFalse)
4200          (void) LogMagickEvent(X11Event,GetMagickModule(),
4201            "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4202        switch ((int) key_symbol)
4203        {
4204          case XK_Escape:
4205          case XK_F20:
4206          {
4207            /*
4208              Prematurely exit.
4209            */
4210            composite_image=DestroyImage(composite_image);
4211            state|=EscapeState;
4212            state|=ExitState;
4213            break;
4214          }
4215          case XK_F1:
4216          case XK_Help:
4217          {
4218            (void) XSetFunction(display,windows->image.highlight_context,
4219              GXcopy);
4220            XTextViewWidget(display,resource_info,windows,MagickFalse,
4221              "Help Viewer - Image Composite",ImageCompositeHelp);
4222            (void) XSetFunction(display,windows->image.highlight_context,
4223              GXinvert);
4224            break;
4225          }
4226          default:
4227          {
4228            (void) XBell(display,0);
4229            break;
4230          }
4231        }
4232        break;
4233      }
4234      case MotionNotify:
4235      {
4236        /*
4237          Map and unmap Info widget as text cursor crosses its boundaries.
4238        */
4239        x=event.xmotion.x;
4240        y=event.xmotion.y;
4241        if (windows->info.mapped != MagickFalse)
4242          {
4243            if ((x < (int) (windows->info.x+windows->info.width)) &&
4244                (y < (int) (windows->info.y+windows->info.height)))
4245              (void) XWithdrawWindow(display,windows->info.id,
4246                windows->info.screen);
4247          }
4248        else
4249          if ((x > (int) (windows->info.x+windows->info.width)) ||
4250              (y > (int) (windows->info.y+windows->info.height)))
4251            (void) XMapWindow(display,windows->info.id);
4252        composite_info.x=(ssize_t) windows->image.x+x;
4253        composite_info.y=(ssize_t) windows->image.y+y;
4254        break;
4255      }
4256      default:
4257      {
4258        if (image->debug != MagickFalse)
4259          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4260            event.type);
4261        break;
4262      }
4263    }
4264  } while ((state & ExitState) == 0);
4265  (void) XSelectInput(display,windows->image.id,
4266    windows->image.attributes.event_mask);
4267  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4268  XSetCursorState(display,windows,MagickFalse);
4269  (void) XFreeCursor(display,cursor);
4270  if ((state & EscapeState) != 0)
4271    return(MagickTrue);
4272  /*
4273    Image compositing is relative to image configuration.
4274  */
4275  XSetCursorState(display,windows,MagickTrue);
4276  XCheckRefreshWindows(display,windows);
4277  width=(unsigned int) image->columns;
4278  height=(unsigned int) image->rows;
4279  x=0;
4280  y=0;
4281  if (windows->image.crop_geometry != (char *) NULL)
4282    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4283  scale_factor=(MagickRealType) width/windows->image.ximage->width;
4284  composite_info.x+=x;
4285  composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4286  composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4287  scale_factor=(MagickRealType) height/windows->image.ximage->height;
4288  composite_info.y+=y;
4289  composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4290  composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4291  if ((composite_info.width != composite_image->columns) ||
4292      (composite_info.height != composite_image->rows))
4293    {
4294      Image
4295        *resize_image;
4296
4297      /*
4298        Scale composite image.
4299      */
4300      resize_image=ResizeImage(composite_image,composite_info.width,
4301        composite_info.height,composite_image->filter,composite_image->blur,
4302        exception);
4303      composite_image=DestroyImage(composite_image);
4304      if (resize_image == (Image *) NULL)
4305        {
4306          XSetCursorState(display,windows,MagickFalse);
4307          return(MagickFalse);
4308        }
4309      composite_image=resize_image;
4310    }
4311  if (compose == DisplaceCompositeOp)
4312    (void) SetImageArtifact(composite_image,"compose:args",
4313      displacement_geometry);
4314  if (blend != 0.0)
4315    {
4316      CacheView
4317        *image_view;
4318
4319      int
4320        y;
4321
4322      Quantum
4323        opacity;
4324
4325      register int
4326        x;
4327
4328      register Quantum
4329        *q;
4330
4331      /*
4332        Create mattes for blending.
4333      */
4334      (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,exception);
4335      opacity=(Quantum) (ScaleQuantumToChar((Quantum) QuantumRange)-
4336        ((ssize_t) ScaleQuantumToChar((Quantum) QuantumRange)*blend)/100);
4337      if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
4338        return(MagickFalse);
4339      image->matte=MagickTrue;
4340      image_view=AcquireCacheView(image);
4341      for (y=0; y < (int) image->rows; y++)
4342      {
4343        q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4344          exception);
4345        if (q == (Quantum *) NULL)
4346          break;
4347        for (x=0; x < (int) image->columns; x++)
4348        {
4349          SetPixelAlpha(image,opacity,q);
4350          q+=GetPixelChannels(image);
4351        }
4352        if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4353          break;
4354      }
4355      image_view=DestroyCacheView(image_view);
4356    }
4357  /*
4358    Composite image with X Image window.
4359  */
4360  (void) CompositeImage(image,compose,composite_image,composite_info.x,
4361    composite_info.y,exception);
4362  composite_image=DestroyImage(composite_image);
4363  XSetCursorState(display,windows,MagickFalse);
4364  /*
4365    Update image configuration.
4366  */
4367  XConfigureImageColormap(display,resource_info,windows,image);
4368  (void) XConfigureImage(display,resource_info,windows,image,exception);
4369  return(MagickTrue);
4370}
4371
4372/*
4373%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4374%                                                                             %
4375%                                                                             %
4376%                                                                             %
4377+   X C o n f i g u r e I m a g e                                             %
4378%                                                                             %
4379%                                                                             %
4380%                                                                             %
4381%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4382%
4383%  XConfigureImage() creates a new X image.  It also notifies the window
4384%  manager of the new image size and configures the transient widows.
4385%
4386%  The format of the XConfigureImage method is:
4387%
4388%      MagickBooleanType XConfigureImage(Display *display,
4389%        XResourceInfo *resource_info,XWindows *windows,Image *image,
4390%        ExceptionInfo *exception)
4391%
4392%  A description of each parameter follows:
4393%
4394%    o display: Specifies a connection to an X server; returned from
4395%      XOpenDisplay.
4396%
4397%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4398%
4399%    o windows: Specifies a pointer to a XWindows structure.
4400%
4401%    o image: the image.
4402%
4403%    o exception: return any errors or warnings in this structure.
4404%
4405%    o exception: return any errors or warnings in this structure.
4406%
4407*/
4408static MagickBooleanType XConfigureImage(Display *display,
4409  XResourceInfo *resource_info,XWindows *windows,Image *image,
4410  ExceptionInfo *exception)
4411{
4412  char
4413    geometry[MaxTextExtent];
4414
4415  MagickStatusType
4416    status;
4417
4418  size_t
4419    mask,
4420    height,
4421    width;
4422
4423  ssize_t
4424    x,
4425    y;
4426
4427  XSizeHints
4428    *size_hints;
4429
4430  XWindowChanges
4431    window_changes;
4432
4433  /*
4434    Dismiss if window dimensions are zero.
4435  */
4436  width=(unsigned int) windows->image.window_changes.width;
4437  height=(unsigned int) windows->image.window_changes.height;
4438  if (image->debug != MagickFalse)
4439    (void) LogMagickEvent(X11Event,GetMagickModule(),
4440      "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4441      windows->image.ximage->height,(double) width,(double) height);
4442  if ((width*height) == 0)
4443    return(MagickTrue);
4444  x=0;
4445  y=0;
4446  /*
4447    Resize image to fit Image window dimensions.
4448  */
4449  XSetCursorState(display,windows,MagickTrue);
4450  (void) XFlush(display);
4451  if (((int) width != windows->image.ximage->width) ||
4452      ((int) height != windows->image.ximage->height))
4453    image->taint=MagickTrue;
4454  windows->magnify.x=(int)
4455    width*windows->magnify.x/windows->image.ximage->width;
4456  windows->magnify.y=(int)
4457    height*windows->magnify.y/windows->image.ximage->height;
4458  windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4459  windows->image.y=(int)
4460    (height*windows->image.y/windows->image.ximage->height);
4461  status=XMakeImage(display,resource_info,&windows->image,image,
4462    (unsigned int) width,(unsigned int) height,exception);
4463  if (status == MagickFalse)
4464    XNoticeWidget(display,windows,"Unable to configure X image:",
4465      windows->image.name);
4466  /*
4467    Notify window manager of the new configuration.
4468  */
4469  if (resource_info->image_geometry != (char *) NULL)
4470    (void) FormatLocaleString(geometry,MaxTextExtent,"%s>!",
4471      resource_info->image_geometry);
4472  else
4473    (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
4474      XDisplayWidth(display,windows->image.screen),
4475      XDisplayHeight(display,windows->image.screen));
4476  (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4477  window_changes.width=(int) width;
4478  if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4479    window_changes.width=XDisplayWidth(display,windows->image.screen);
4480  window_changes.height=(int) height;
4481  if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4482    window_changes.height=XDisplayHeight(display,windows->image.screen);
4483  mask=(size_t) (CWWidth | CWHeight);
4484  if (resource_info->backdrop)
4485    {
4486      mask|=CWX | CWY;
4487      window_changes.x=(int)
4488        ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4489      window_changes.y=(int)
4490        ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4491    }
4492  (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4493    (unsigned int) mask,&window_changes);
4494  (void) XClearWindow(display,windows->image.id);
4495  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4496  /*
4497    Update Magnify window configuration.
4498  */
4499  if (windows->magnify.mapped != MagickFalse)
4500    XMakeMagnifyImage(display,windows);
4501  windows->pan.crop_geometry=windows->image.crop_geometry;
4502  XBestIconSize(display,&windows->pan,image);
4503  while (((windows->pan.width << 1) < MaxIconSize) &&
4504         ((windows->pan.height << 1) < MaxIconSize))
4505  {
4506    windows->pan.width<<=1;
4507    windows->pan.height<<=1;
4508  }
4509  if (windows->pan.geometry != (char *) NULL)
4510    (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4511      &windows->pan.width,&windows->pan.height);
4512  window_changes.width=(int) windows->pan.width;
4513  window_changes.height=(int) windows->pan.height;
4514  size_hints=XAllocSizeHints();
4515  if (size_hints != (XSizeHints *) NULL)
4516    {
4517      /*
4518        Set new size hints.
4519      */
4520      size_hints->flags=PSize | PMinSize | PMaxSize;
4521      size_hints->width=window_changes.width;
4522      size_hints->height=window_changes.height;
4523      size_hints->min_width=size_hints->width;
4524      size_hints->min_height=size_hints->height;
4525      size_hints->max_width=size_hints->width;
4526      size_hints->max_height=size_hints->height;
4527      (void) XSetNormalHints(display,windows->pan.id,size_hints);
4528      (void) XFree((void *) size_hints);
4529    }
4530  (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4531    (unsigned int) (CWWidth | CWHeight),&window_changes);
4532  /*
4533    Update icon window configuration.
4534  */
4535  windows->icon.crop_geometry=windows->image.crop_geometry;
4536  XBestIconSize(display,&windows->icon,image);
4537  window_changes.width=(int) windows->icon.width;
4538  window_changes.height=(int) windows->icon.height;
4539  (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4540    (unsigned int) (CWWidth | CWHeight),&window_changes);
4541  XSetCursorState(display,windows,MagickFalse);
4542  return(status != 0 ? MagickTrue : MagickFalse);
4543}
4544
4545/*
4546%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4547%                                                                             %
4548%                                                                             %
4549%                                                                             %
4550+   X C r o p I m a g e                                                       %
4551%                                                                             %
4552%                                                                             %
4553%                                                                             %
4554%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4555%
4556%  XCropImage() allows the user to select a region of the image and crop, copy,
4557%  or cut it.  For copy or cut, the image can subsequently be composited onto
4558%  the image with XPasteImage.
4559%
4560%  The format of the XCropImage method is:
4561%
4562%      MagickBooleanType XCropImage(Display *display,
4563%        XResourceInfo *resource_info,XWindows *windows,Image *image,
4564%        const ClipboardMode mode,ExceptionInfo *exception)
4565%
4566%  A description of each parameter follows:
4567%
4568%    o display: Specifies a connection to an X server; returned from
4569%      XOpenDisplay.
4570%
4571%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4572%
4573%    o windows: Specifies a pointer to a XWindows structure.
4574%
4575%    o image: the image; returned from ReadImage.
4576%
4577%    o mode: This unsigned value specified whether the image should be
4578%      cropped, copied, or cut.
4579%
4580%    o exception: return any errors or warnings in this structure.
4581%
4582*/
4583static MagickBooleanType XCropImage(Display *display,
4584  XResourceInfo *resource_info,XWindows *windows,Image *image,
4585  const ClipboardMode mode,ExceptionInfo *exception)
4586{
4587  static const char
4588    *CropModeMenu[] =
4589    {
4590      "Help",
4591      "Dismiss",
4592      (char *) NULL
4593    },
4594    *RectifyModeMenu[] =
4595    {
4596      "Crop",
4597      "Help",
4598      "Dismiss",
4599      (char *) NULL
4600    };
4601
4602  static const ModeType
4603    CropCommands[] =
4604    {
4605      CropHelpCommand,
4606      CropDismissCommand
4607    },
4608    RectifyCommands[] =
4609    {
4610      RectifyCopyCommand,
4611      RectifyHelpCommand,
4612      RectifyDismissCommand
4613    };
4614
4615  CacheView
4616    *image_view;
4617
4618  char
4619    command[MaxTextExtent],
4620    text[MaxTextExtent];
4621
4622  Cursor
4623    cursor;
4624
4625  int
4626    id,
4627    x,
4628    y;
4629
4630  KeySym
4631    key_symbol;
4632
4633  Image
4634    *crop_image;
4635
4636  MagickRealType
4637    scale_factor;
4638
4639  RectangleInfo
4640    crop_info,
4641    highlight_info;
4642
4643  register Quantum
4644    *q;
4645
4646  unsigned int
4647    height,
4648    width;
4649
4650  size_t
4651    state;
4652
4653  XEvent
4654    event;
4655
4656  /*
4657    Map Command widget.
4658  */
4659  switch (mode)
4660  {
4661    case CopyMode:
4662    {
4663      (void) CloneString(&windows->command.name,"Copy");
4664      break;
4665    }
4666    case CropMode:
4667    {
4668      (void) CloneString(&windows->command.name,"Crop");
4669      break;
4670    }
4671    case CutMode:
4672    {
4673      (void) CloneString(&windows->command.name,"Cut");
4674      break;
4675    }
4676  }
4677  RectifyModeMenu[0]=windows->command.name;
4678  windows->command.data=0;
4679  (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4680  (void) XMapRaised(display,windows->command.id);
4681  XClientMessage(display,windows->image.id,windows->im_protocols,
4682    windows->im_update_widget,CurrentTime);
4683  /*
4684    Track pointer until button 1 is pressed.
4685  */
4686  XQueryPosition(display,windows->image.id,&x,&y);
4687  (void) XSelectInput(display,windows->image.id,
4688    windows->image.attributes.event_mask | PointerMotionMask);
4689  crop_info.x=(ssize_t) windows->image.x+x;
4690  crop_info.y=(ssize_t) windows->image.y+y;
4691  crop_info.width=0;
4692  crop_info.height=0;
4693  cursor=XCreateFontCursor(display,XC_fleur);
4694  state=DefaultState;
4695  do
4696  {
4697    if (windows->info.mapped != MagickFalse)
4698      {
4699        /*
4700          Display pointer position.
4701        */
4702        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4703          (long) crop_info.x,(long) crop_info.y);
4704        XInfoWidget(display,windows,text);
4705      }
4706    /*
4707      Wait for next event.
4708    */
4709    XScreenEvent(display,windows,&event);
4710    if (event.xany.window == windows->command.id)
4711      {
4712        /*
4713          Select a command from the Command widget.
4714        */
4715        id=XCommandWidget(display,windows,CropModeMenu,&event);
4716        if (id < 0)
4717          continue;
4718        switch (CropCommands[id])
4719        {
4720          case CropHelpCommand:
4721          {
4722            switch (mode)
4723            {
4724              case CopyMode:
4725              {
4726                XTextViewWidget(display,resource_info,windows,MagickFalse,
4727                  "Help Viewer - Image Copy",ImageCopyHelp);
4728                break;
4729              }
4730              case CropMode:
4731              {
4732                XTextViewWidget(display,resource_info,windows,MagickFalse,
4733                  "Help Viewer - Image Crop",ImageCropHelp);
4734                break;
4735              }
4736              case CutMode:
4737              {
4738                XTextViewWidget(display,resource_info,windows,MagickFalse,
4739                  "Help Viewer - Image Cut",ImageCutHelp);
4740                break;
4741              }
4742            }
4743            break;
4744          }
4745          case CropDismissCommand:
4746          {
4747            /*
4748              Prematurely exit.
4749            */
4750            state|=EscapeState;
4751            state|=ExitState;
4752            break;
4753          }
4754          default:
4755            break;
4756        }
4757        continue;
4758      }
4759    switch (event.type)
4760    {
4761      case ButtonPress:
4762      {
4763        if (event.xbutton.button != Button1)
4764          break;
4765        if (event.xbutton.window != windows->image.id)
4766          break;
4767        /*
4768          Note first corner of cropping rectangle-- exit loop.
4769        */
4770        (void) XCheckDefineCursor(display,windows->image.id,cursor);
4771        crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4772        crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4773        state|=ExitState;
4774        break;
4775      }
4776      case ButtonRelease:
4777        break;
4778      case Expose:
4779        break;
4780      case KeyPress:
4781      {
4782        if (event.xkey.window != windows->image.id)
4783          break;
4784        /*
4785          Respond to a user key press.
4786        */
4787        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4788          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4789        switch ((int) key_symbol)
4790        {
4791          case XK_Escape:
4792          case XK_F20:
4793          {
4794            /*
4795              Prematurely exit.
4796            */
4797            state|=EscapeState;
4798            state|=ExitState;
4799            break;
4800          }
4801          case XK_F1:
4802          case XK_Help:
4803          {
4804            switch (mode)
4805            {
4806              case CopyMode:
4807              {
4808                XTextViewWidget(display,resource_info,windows,MagickFalse,
4809                  "Help Viewer - Image Copy",ImageCopyHelp);
4810                break;
4811              }
4812              case CropMode:
4813              {
4814                XTextViewWidget(display,resource_info,windows,MagickFalse,
4815                  "Help Viewer - Image Crop",ImageCropHelp);
4816                break;
4817              }
4818              case CutMode:
4819              {
4820                XTextViewWidget(display,resource_info,windows,MagickFalse,
4821                  "Help Viewer - Image Cut",ImageCutHelp);
4822                break;
4823              }
4824            }
4825            break;
4826          }
4827          default:
4828          {
4829            (void) XBell(display,0);
4830            break;
4831          }
4832        }
4833        break;
4834      }
4835      case MotionNotify:
4836      {
4837        if (event.xmotion.window != windows->image.id)
4838          break;
4839        /*
4840          Map and unmap Info widget as text cursor crosses its boundaries.
4841        */
4842        x=event.xmotion.x;
4843        y=event.xmotion.y;
4844        if (windows->info.mapped != MagickFalse)
4845          {
4846            if ((x < (int) (windows->info.x+windows->info.width)) &&
4847                (y < (int) (windows->info.y+windows->info.height)))
4848              (void) XWithdrawWindow(display,windows->info.id,
4849                windows->info.screen);
4850          }
4851        else
4852          if ((x > (int) (windows->info.x+windows->info.width)) ||
4853              (y > (int) (windows->info.y+windows->info.height)))
4854            (void) XMapWindow(display,windows->info.id);
4855        crop_info.x=(ssize_t) windows->image.x+x;
4856        crop_info.y=(ssize_t) windows->image.y+y;
4857        break;
4858      }
4859      default:
4860        break;
4861    }
4862  } while ((state & ExitState) == 0);
4863  (void) XSelectInput(display,windows->image.id,
4864    windows->image.attributes.event_mask);
4865  if ((state & EscapeState) != 0)
4866    {
4867      /*
4868        User want to exit without cropping.
4869      */
4870      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4871      (void) XFreeCursor(display,cursor);
4872      return(MagickTrue);
4873    }
4874  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4875  do
4876  {
4877    /*
4878      Size rectangle as pointer moves until the mouse button is released.
4879    */
4880    x=(int) crop_info.x;
4881    y=(int) crop_info.y;
4882    crop_info.width=0;
4883    crop_info.height=0;
4884    state=DefaultState;
4885    do
4886    {
4887      highlight_info=crop_info;
4888      highlight_info.x=crop_info.x-windows->image.x;
4889      highlight_info.y=crop_info.y-windows->image.y;
4890      if ((highlight_info.width > 3) && (highlight_info.height > 3))
4891        {
4892          /*
4893            Display info and draw cropping rectangle.
4894          */
4895          if (windows->info.mapped == MagickFalse)
4896            (void) XMapWindow(display,windows->info.id);
4897          (void) FormatLocaleString(text,MaxTextExtent,
4898            " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4899            crop_info.height,(double) crop_info.x,(double) crop_info.y);
4900          XInfoWidget(display,windows,text);
4901          XHighlightRectangle(display,windows->image.id,
4902            windows->image.highlight_context,&highlight_info);
4903        }
4904      else
4905        if (windows->info.mapped != MagickFalse)
4906          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4907      /*
4908        Wait for next event.
4909      */
4910      XScreenEvent(display,windows,&event);
4911      if ((highlight_info.width > 3) && (highlight_info.height > 3))
4912        XHighlightRectangle(display,windows->image.id,
4913          windows->image.highlight_context,&highlight_info);
4914      switch (event.type)
4915      {
4916        case ButtonPress:
4917        {
4918          crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4919          crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4920          break;
4921        }
4922        case ButtonRelease:
4923        {
4924          /*
4925            User has committed to cropping rectangle.
4926          */
4927          crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4928          crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4929          XSetCursorState(display,windows,MagickFalse);
4930          state|=ExitState;
4931          windows->command.data=0;
4932          (void) XCommandWidget(display,windows,RectifyModeMenu,
4933            (XEvent *) NULL);
4934          break;
4935        }
4936        case Expose:
4937          break;
4938        case MotionNotify:
4939        {
4940          crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4941          crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4942        }
4943        default:
4944          break;
4945      }
4946      if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4947          ((state & ExitState) != 0))
4948        {
4949          /*
4950            Check boundary conditions.
4951          */
4952          if (crop_info.x < 0)
4953            crop_info.x=0;
4954          else
4955            if (crop_info.x > (ssize_t) windows->image.ximage->width)
4956              crop_info.x=(ssize_t) windows->image.ximage->width;
4957          if ((int) crop_info.x < x)
4958            crop_info.width=(unsigned int) (x-crop_info.x);
4959          else
4960            {
4961              crop_info.width=(unsigned int) (crop_info.x-x);
4962              crop_info.x=(ssize_t) x;
4963            }
4964          if (crop_info.y < 0)
4965            crop_info.y=0;
4966          else
4967            if (crop_info.y > (ssize_t) windows->image.ximage->height)
4968              crop_info.y=(ssize_t) windows->image.ximage->height;
4969          if ((int) crop_info.y < y)
4970            crop_info.height=(unsigned int) (y-crop_info.y);
4971          else
4972            {
4973              crop_info.height=(unsigned int) (crop_info.y-y);
4974              crop_info.y=(ssize_t) y;
4975            }
4976        }
4977    } while ((state & ExitState) == 0);
4978    /*
4979      Wait for user to grab a corner of the rectangle or press return.
4980    */
4981    state=DefaultState;
4982    (void) XMapWindow(display,windows->info.id);
4983    do
4984    {
4985      if (windows->info.mapped != MagickFalse)
4986        {
4987          /*
4988            Display pointer position.
4989          */
4990          (void) FormatLocaleString(text,MaxTextExtent,
4991            " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4992            crop_info.height,(double) crop_info.x,(double) crop_info.y);
4993          XInfoWidget(display,windows,text);
4994        }
4995      highlight_info=crop_info;
4996      highlight_info.x=crop_info.x-windows->image.x;
4997      highlight_info.y=crop_info.y-windows->image.y;
4998      if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4999        {
5000          state|=EscapeState;
5001          state|=ExitState;
5002          break;
5003        }
5004      XHighlightRectangle(display,windows->image.id,
5005        windows->image.highlight_context,&highlight_info);
5006      XScreenEvent(display,windows,&event);
5007      if (event.xany.window == windows->command.id)
5008        {
5009          /*
5010            Select a command from the Command widget.
5011          */
5012          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5013          id=XCommandWidget(display,windows,RectifyModeMenu,&event);
5014          (void) XSetFunction(display,windows->image.highlight_context,
5015            GXinvert);
5016          XHighlightRectangle(display,windows->image.id,
5017            windows->image.highlight_context,&highlight_info);
5018          if (id >= 0)
5019            switch (RectifyCommands[id])
5020            {
5021              case RectifyCopyCommand:
5022              {
5023                state|=ExitState;
5024                break;
5025              }
5026              case RectifyHelpCommand:
5027              {
5028                (void) XSetFunction(display,windows->image.highlight_context,
5029                  GXcopy);
5030                switch (mode)
5031                {
5032                  case CopyMode:
5033                  {
5034                    XTextViewWidget(display,resource_info,windows,MagickFalse,
5035                      "Help Viewer - Image Copy",ImageCopyHelp);
5036                    break;
5037                  }
5038                  case CropMode:
5039                  {
5040                    XTextViewWidget(display,resource_info,windows,MagickFalse,
5041                      "Help Viewer - Image Crop",ImageCropHelp);
5042                    break;
5043                  }
5044                  case CutMode:
5045                  {
5046                    XTextViewWidget(display,resource_info,windows,MagickFalse,
5047                      "Help Viewer - Image Cut",ImageCutHelp);
5048                    break;
5049                  }
5050                }
5051                (void) XSetFunction(display,windows->image.highlight_context,
5052                  GXinvert);
5053                break;
5054              }
5055              case RectifyDismissCommand:
5056              {
5057                /*
5058                  Prematurely exit.
5059                */
5060                state|=EscapeState;
5061                state|=ExitState;
5062                break;
5063              }
5064              default:
5065                break;
5066            }
5067          continue;
5068        }
5069      XHighlightRectangle(display,windows->image.id,
5070        windows->image.highlight_context,&highlight_info);
5071      switch (event.type)
5072      {
5073        case ButtonPress:
5074        {
5075          if (event.xbutton.button != Button1)
5076            break;
5077          if (event.xbutton.window != windows->image.id)
5078            break;
5079          x=windows->image.x+event.xbutton.x;
5080          y=windows->image.y+event.xbutton.y;
5081          if ((x < (int) (crop_info.x+RoiDelta)) &&
5082              (x > (int) (crop_info.x-RoiDelta)) &&
5083              (y < (int) (crop_info.y+RoiDelta)) &&
5084              (y > (int) (crop_info.y-RoiDelta)))
5085            {
5086              crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5087              crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5088              state|=UpdateConfigurationState;
5089              break;
5090            }
5091          if ((x < (int) (crop_info.x+RoiDelta)) &&
5092              (x > (int) (crop_info.x-RoiDelta)) &&
5093              (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5094              (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5095            {
5096              crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5097              state|=UpdateConfigurationState;
5098              break;
5099            }
5100          if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5101              (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5102              (y < (int) (crop_info.y+RoiDelta)) &&
5103              (y > (int) (crop_info.y-RoiDelta)))
5104            {
5105              crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5106              state|=UpdateConfigurationState;
5107              break;
5108            }
5109          if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5110              (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5111              (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5112              (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5113            {
5114              state|=UpdateConfigurationState;
5115              break;
5116            }
5117        }
5118        case ButtonRelease:
5119        {
5120          if (event.xbutton.window == windows->pan.id)
5121            if ((highlight_info.x != crop_info.x-windows->image.x) ||
5122                (highlight_info.y != crop_info.y-windows->image.y))
5123              XHighlightRectangle(display,windows->image.id,
5124                windows->image.highlight_context,&highlight_info);
5125          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5126            event.xbutton.time);
5127          break;
5128        }
5129        case Expose:
5130        {
5131          if (event.xexpose.window == windows->image.id)
5132            if (event.xexpose.count == 0)
5133              {
5134                event.xexpose.x=(int) highlight_info.x;
5135                event.xexpose.y=(int) highlight_info.y;
5136                event.xexpose.width=(int) highlight_info.width;
5137                event.xexpose.height=(int) highlight_info.height;
5138                XRefreshWindow(display,&windows->image,&event);
5139              }
5140          if (event.xexpose.window == windows->info.id)
5141            if (event.xexpose.count == 0)
5142              XInfoWidget(display,windows,text);
5143          break;
5144        }
5145        case KeyPress:
5146        {
5147          if (event.xkey.window != windows->image.id)
5148            break;
5149          /*
5150            Respond to a user key press.
5151          */
5152          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5153            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5154          switch ((int) key_symbol)
5155          {
5156            case XK_Escape:
5157            case XK_F20:
5158              state|=EscapeState;
5159            case XK_Return:
5160            {
5161              state|=ExitState;
5162              break;
5163            }
5164            case XK_Home:
5165            case XK_KP_Home:
5166            {
5167              crop_info.x=(ssize_t) (windows->image.width/2L-
5168                crop_info.width/2L);
5169              crop_info.y=(ssize_t) (windows->image.height/2L-
5170                crop_info.height/2L);
5171              break;
5172            }
5173            case XK_Left:
5174            case XK_KP_Left:
5175            {
5176              crop_info.x--;
5177              break;
5178            }
5179            case XK_Up:
5180            case XK_KP_Up:
5181            case XK_Next:
5182            {
5183              crop_info.y--;
5184              break;
5185            }
5186            case XK_Right:
5187            case XK_KP_Right:
5188            {
5189              crop_info.x++;
5190              break;
5191            }
5192            case XK_Prior:
5193            case XK_Down:
5194            case XK_KP_Down:
5195            {
5196              crop_info.y++;
5197              break;
5198            }
5199            case XK_F1:
5200            case XK_Help:
5201            {
5202              (void) XSetFunction(display,windows->image.highlight_context,
5203                GXcopy);
5204              switch (mode)
5205              {
5206                case CopyMode:
5207                {
5208                  XTextViewWidget(display,resource_info,windows,MagickFalse,
5209                    "Help Viewer - Image Copy",ImageCopyHelp);
5210                  break;
5211                }
5212                case CropMode:
5213                {
5214                  XTextViewWidget(display,resource_info,windows,MagickFalse,
5215                    "Help Viewer - Image Cropg",ImageCropHelp);
5216                  break;
5217                }
5218                case CutMode:
5219                {
5220                  XTextViewWidget(display,resource_info,windows,MagickFalse,
5221                    "Help Viewer - Image Cutg",ImageCutHelp);
5222                  break;
5223                }
5224              }
5225              (void) XSetFunction(display,windows->image.highlight_context,
5226                GXinvert);
5227              break;
5228            }
5229            default:
5230            {
5231              (void) XBell(display,0);
5232              break;
5233            }
5234          }
5235          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5236            event.xkey.time);
5237          break;
5238        }
5239        case KeyRelease:
5240          break;
5241        case MotionNotify:
5242        {
5243          if (event.xmotion.window != windows->image.id)
5244            break;
5245          /*
5246            Map and unmap Info widget as text cursor crosses its boundaries.
5247          */
5248          x=event.xmotion.x;
5249          y=event.xmotion.y;
5250          if (windows->info.mapped != MagickFalse)
5251            {
5252              if ((x < (int) (windows->info.x+windows->info.width)) &&
5253                  (y < (int) (windows->info.y+windows->info.height)))
5254                (void) XWithdrawWindow(display,windows->info.id,
5255                  windows->info.screen);
5256            }
5257          else
5258            if ((x > (int) (windows->info.x+windows->info.width)) ||
5259                (y > (int) (windows->info.y+windows->info.height)))
5260              (void) XMapWindow(display,windows->info.id);
5261          crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5262          crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5263          break;
5264        }
5265        case SelectionRequest:
5266        {
5267          XSelectionEvent
5268            notify;
5269
5270          XSelectionRequestEvent
5271            *request;
5272
5273          /*
5274            Set primary selection.
5275          */
5276          (void) FormatLocaleString(text,MaxTextExtent,
5277            "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5278            crop_info.height,(double) crop_info.x,(double) crop_info.y);
5279          request=(&(event.xselectionrequest));
5280          (void) XChangeProperty(request->display,request->requestor,
5281            request->property,request->target,8,PropModeReplace,
5282            (unsigned char *) text,(int) strlen(text));
5283          notify.type=SelectionNotify;
5284          notify.display=request->display;
5285          notify.requestor=request->requestor;
5286          notify.selection=request->selection;
5287          notify.target=request->target;
5288          notify.time=request->time;
5289          if (request->property == None)
5290            notify.property=request->target;
5291          else
5292            notify.property=request->property;
5293          (void) XSendEvent(request->display,request->requestor,False,0,
5294            (XEvent *) &notify);
5295        }
5296        default:
5297          break;
5298      }
5299      if ((state & UpdateConfigurationState) != 0)
5300        {
5301          (void) XPutBackEvent(display,&event);
5302          (void) XCheckDefineCursor(display,windows->image.id,cursor);
5303          break;
5304        }
5305    } while ((state & ExitState) == 0);
5306  } while ((state & ExitState) == 0);
5307  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5308  XSetCursorState(display,windows,MagickFalse);
5309  if ((state & EscapeState) != 0)
5310    return(MagickTrue);
5311  if (mode == CropMode)
5312    if (((int) crop_info.width != windows->image.ximage->width) ||
5313        ((int) crop_info.height != windows->image.ximage->height))
5314      {
5315        /*
5316          Reconfigure Image window as defined by cropping rectangle.
5317        */
5318        XSetCropGeometry(display,windows,&crop_info,image);
5319        windows->image.window_changes.width=(int) crop_info.width;
5320        windows->image.window_changes.height=(int) crop_info.height;
5321        (void) XConfigureImage(display,resource_info,windows,image,exception);
5322        return(MagickTrue);
5323      }
5324  /*
5325    Copy image before applying image transforms.
5326  */
5327  XSetCursorState(display,windows,MagickTrue);
5328  XCheckRefreshWindows(display,windows);
5329  width=(unsigned int) image->columns;
5330  height=(unsigned int) image->rows;
5331  x=0;
5332  y=0;
5333  if (windows->image.crop_geometry != (char *) NULL)
5334    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5335  scale_factor=(MagickRealType) width/windows->image.ximage->width;
5336  crop_info.x+=x;
5337  crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5338  crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5339  scale_factor=(MagickRealType) height/windows->image.ximage->height;
5340  crop_info.y+=y;
5341  crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5342  crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5343  crop_image=CropImage(image,&crop_info,exception);
5344  XSetCursorState(display,windows,MagickFalse);
5345  if (crop_image == (Image *) NULL)
5346    return(MagickFalse);
5347  if (resource_info->copy_image != (Image *) NULL)
5348    resource_info->copy_image=DestroyImage(resource_info->copy_image);
5349  resource_info->copy_image=crop_image;
5350  if (mode == CopyMode)
5351    {
5352      (void) XConfigureImage(display,resource_info,windows,image,exception);
5353      return(MagickTrue);
5354    }
5355  /*
5356    Cut image.
5357  */
5358  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
5359    return(MagickFalse);
5360  image->matte=MagickTrue;
5361  image_view=AcquireCacheView(image);
5362  for (y=0; y < (int) crop_info.height; y++)
5363  {
5364    q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5365      crop_info.width,1,exception);
5366    if (q == (Quantum *) NULL)
5367      break;
5368    for (x=0; x < (int) crop_info.width; x++)
5369    {
5370      SetPixelAlpha(image,TransparentAlpha,q);
5371      q+=GetPixelChannels(image);
5372    }
5373    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5374      break;
5375  }
5376  image_view=DestroyCacheView(image_view);
5377  /*
5378    Update image configuration.
5379  */
5380  XConfigureImageColormap(display,resource_info,windows,image);
5381  (void) XConfigureImage(display,resource_info,windows,image,exception);
5382  return(MagickTrue);
5383}
5384
5385/*
5386%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5387%                                                                             %
5388%                                                                             %
5389%                                                                             %
5390+   X D r a w I m a g e                                                       %
5391%                                                                             %
5392%                                                                             %
5393%                                                                             %
5394%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5395%
5396%  XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5397%  the image.
5398%
5399%  The format of the XDrawEditImage method is:
5400%
5401%      MagickBooleanType XDrawEditImage(Display *display,
5402%        XResourceInfo *resource_info,XWindows *windows,Image **image,
5403%        ExceptionInfo *exception)
5404%
5405%  A description of each parameter follows:
5406%
5407%    o display: Specifies a connection to an X server; returned from
5408%      XOpenDisplay.
5409%
5410%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5411%
5412%    o windows: Specifies a pointer to a XWindows structure.
5413%
5414%    o image: the image.
5415%
5416%    o exception: return any errors or warnings in this structure.
5417%
5418*/
5419static MagickBooleanType XDrawEditImage(Display *display,
5420  XResourceInfo *resource_info,XWindows *windows,Image **image,
5421  ExceptionInfo *exception)
5422{
5423  static const char
5424    *DrawMenu[] =
5425    {
5426      "Element",
5427      "Color",
5428      "Stipple",
5429      "Width",
5430      "Undo",
5431      "Help",
5432      "Dismiss",
5433      (char *) NULL
5434    };
5435
5436  static ElementType
5437    element = PointElement;
5438
5439  static const ModeType
5440    DrawCommands[] =
5441    {
5442      DrawElementCommand,
5443      DrawColorCommand,
5444      DrawStippleCommand,
5445      DrawWidthCommand,
5446      DrawUndoCommand,
5447      DrawHelpCommand,
5448      DrawDismissCommand
5449    };
5450
5451  static Pixmap
5452    stipple = (Pixmap) NULL;
5453
5454  static unsigned int
5455    pen_id = 0,
5456    line_width = 1;
5457
5458  char
5459    command[MaxTextExtent],
5460    text[MaxTextExtent];
5461
5462  Cursor
5463    cursor;
5464
5465  int
5466    entry,
5467    id,
5468    number_coordinates,
5469    x,
5470    y;
5471
5472  MagickRealType
5473    degrees;
5474
5475  MagickStatusType
5476    status;
5477
5478  RectangleInfo
5479    rectangle_info;
5480
5481  register int
5482    i;
5483
5484  unsigned int
5485    distance,
5486    height,
5487    max_coordinates,
5488    width;
5489
5490  size_t
5491    state;
5492
5493  Window
5494    root_window;
5495
5496  XDrawInfo
5497    draw_info;
5498
5499  XEvent
5500    event;
5501
5502  XPoint
5503    *coordinate_info;
5504
5505  XSegment
5506    line_info;
5507
5508  /*
5509    Allocate polygon info.
5510  */
5511  max_coordinates=2048;
5512  coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5513    sizeof(*coordinate_info));
5514  if (coordinate_info == (XPoint *) NULL)
5515    {
5516      (void) ThrowMagickException(exception,GetMagickModule(),
5517        ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5518      return(MagickFalse);
5519    }
5520  /*
5521    Map Command widget.
5522  */
5523  (void) CloneString(&windows->command.name,"Draw");
5524  windows->command.data=4;
5525  (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5526  (void) XMapRaised(display,windows->command.id);
5527  XClientMessage(display,windows->image.id,windows->im_protocols,
5528    windows->im_update_widget,CurrentTime);
5529  /*
5530    Wait for first button press.
5531  */
5532  root_window=XRootWindow(display,XDefaultScreen(display));
5533  draw_info.stencil=OpaqueStencil;
5534  status=MagickTrue;
5535  cursor=XCreateFontCursor(display,XC_tcross);
5536  for ( ; ; )
5537  {
5538    XQueryPosition(display,windows->image.id,&x,&y);
5539    (void) XSelectInput(display,windows->image.id,
5540      windows->image.attributes.event_mask | PointerMotionMask);
5541    (void) XCheckDefineCursor(display,windows->image.id,cursor);
5542    state=DefaultState;
5543    do
5544    {
5545      if (windows->info.mapped != MagickFalse)
5546        {
5547          /*
5548            Display pointer position.
5549          */
5550          (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
5551            x+windows->image.x,y+windows->image.y);
5552          XInfoWidget(display,windows,text);
5553        }
5554      /*
5555        Wait for next event.
5556      */
5557      XScreenEvent(display,windows,&event);
5558      if (event.xany.window == windows->command.id)
5559        {
5560          /*
5561            Select a command from the Command widget.
5562          */
5563          id=XCommandWidget(display,windows,DrawMenu,&event);
5564          if (id < 0)
5565            continue;
5566          switch (DrawCommands[id])
5567          {
5568            case DrawElementCommand:
5569            {
5570              static const char
5571                *Elements[] =
5572                {
5573                  "point",
5574                  "line",
5575                  "rectangle",
5576                  "fill rectangle",
5577                  "circle",
5578                  "fill circle",
5579                  "ellipse",
5580                  "fill ellipse",
5581                  "polygon",
5582                  "fill polygon",
5583                  (char *) NULL,
5584                };
5585
5586              /*
5587                Select a command from the pop-up menu.
5588              */
5589              element=(ElementType) (XMenuWidget(display,windows,
5590                DrawMenu[id],Elements,command)+1);
5591              break;
5592            }
5593            case DrawColorCommand:
5594            {
5595              const char
5596                *ColorMenu[MaxNumberPens+1];
5597
5598              int
5599                pen_number;
5600
5601              MagickBooleanType
5602                transparent;
5603
5604              XColor
5605                color;
5606
5607              /*
5608                Initialize menu selections.
5609              */
5610              for (i=0; i < (int) (MaxNumberPens-2); i++)
5611                ColorMenu[i]=resource_info->pen_colors[i];
5612              ColorMenu[MaxNumberPens-2]="transparent";
5613              ColorMenu[MaxNumberPens-1]="Browser...";
5614              ColorMenu[MaxNumberPens]=(char *) NULL;
5615              /*
5616                Select a pen color from the pop-up menu.
5617              */
5618              pen_number=XMenuWidget(display,windows,DrawMenu[id],
5619                (const char **) ColorMenu,command);
5620              if (pen_number < 0)
5621                break;
5622              transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5623                MagickFalse;
5624              if (transparent != MagickFalse)
5625                {
5626                  draw_info.stencil=TransparentStencil;
5627                  break;
5628                }
5629              if (pen_number == (MaxNumberPens-1))
5630                {
5631                  static char
5632                    color_name[MaxTextExtent] = "gray";
5633
5634                  /*
5635                    Select a pen color from a dialog.
5636                  */
5637                  resource_info->pen_colors[pen_number]=color_name;
5638                  XColorBrowserWidget(display,windows,"Select",color_name);
5639                  if (*color_name == '\0')
5640                    break;
5641                }
5642              /*
5643                Set pen color.
5644              */
5645              (void) XParseColor(display,windows->map_info->colormap,
5646                resource_info->pen_colors[pen_number],&color);
5647              XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5648                (unsigned int) MaxColors,&color);
5649              windows->pixel_info->pen_colors[pen_number]=color;
5650              pen_id=(unsigned int) pen_number;
5651              draw_info.stencil=OpaqueStencil;
5652              break;
5653            }
5654            case DrawStippleCommand:
5655            {
5656              Image
5657                *stipple_image;
5658
5659              ImageInfo
5660                *image_info;
5661
5662              int
5663                status;
5664
5665              static char
5666                filename[MaxTextExtent] = "\0";
5667
5668              static const char
5669                *StipplesMenu[] =
5670                {
5671                  "Brick",
5672                  "Diagonal",
5673                  "Scales",
5674                  "Vertical",
5675                  "Wavy",
5676                  "Translucent",
5677                  "Opaque",
5678                  (char *) NULL,
5679                  (char *) NULL,
5680                };
5681
5682              /*
5683                Select a command from the pop-up menu.
5684              */
5685              StipplesMenu[7]="Open...";
5686              entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5687                command);
5688              if (entry < 0)
5689                break;
5690              if (stipple != (Pixmap) NULL)
5691                (void) XFreePixmap(display,stipple);
5692              stipple=(Pixmap) NULL;
5693              if (entry != 7)
5694                {
5695                  switch (entry)
5696                  {
5697                    case 0:
5698                    {
5699                      stipple=XCreateBitmapFromData(display,root_window,
5700                        (char *) BricksBitmap,BricksWidth,BricksHeight);
5701                      break;
5702                    }
5703                    case 1:
5704                    {
5705                      stipple=XCreateBitmapFromData(display,root_window,
5706                        (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5707                      break;
5708                    }
5709                    case 2:
5710                    {
5711                      stipple=XCreateBitmapFromData(display,root_window,
5712                        (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5713                      break;
5714                    }
5715                    case 3:
5716                    {
5717                      stipple=XCreateBitmapFromData(display,root_window,
5718                        (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5719                      break;
5720                    }
5721                    case 4:
5722                    {
5723                      stipple=XCreateBitmapFromData(display,root_window,
5724                        (char *) WavyBitmap,WavyWidth,WavyHeight);
5725                      break;
5726                    }
5727                    case 5:
5728                    {
5729                      stipple=XCreateBitmapFromData(display,root_window,
5730                        (char *) HighlightBitmap,HighlightWidth,
5731                        HighlightHeight);
5732                      break;
5733                    }
5734                    case 6:
5735                    default:
5736                    {
5737                      stipple=XCreateBitmapFromData(display,root_window,
5738                        (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5739                      break;
5740                    }
5741                  }
5742                  break;
5743                }
5744              XFileBrowserWidget(display,windows,"Stipple",filename);
5745              if (*filename == '\0')
5746                break;
5747              /*
5748                Read image.
5749              */
5750              XSetCursorState(display,windows,MagickTrue);
5751              XCheckRefreshWindows(display,windows);
5752              image_info=AcquireImageInfo();
5753              (void) CopyMagickString(image_info->filename,filename,
5754                MaxTextExtent);
5755              stipple_image=ReadImage(image_info,exception);
5756              CatchException(exception);
5757              XSetCursorState(display,windows,MagickFalse);
5758              if (stipple_image == (Image *) NULL)
5759                break;
5760              (void) AcquireUniqueFileResource(filename);
5761              (void) FormatLocaleString(stipple_image->filename,MaxTextExtent,
5762                "xbm:%s",filename);
5763              (void) WriteImage(image_info,stipple_image,exception);
5764              stipple_image=DestroyImage(stipple_image);
5765              image_info=DestroyImageInfo(image_info);
5766              status=XReadBitmapFile(display,root_window,filename,&width,
5767                &height,&stipple,&x,&y);
5768              (void) RelinquishUniqueFileResource(filename);
5769              if ((status != BitmapSuccess) != 0)
5770                XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5771                  filename);
5772              break;
5773            }
5774            case DrawWidthCommand:
5775            {
5776              static char
5777                width[MaxTextExtent] = "0";
5778
5779              static const char
5780                *WidthsMenu[] =
5781                {
5782                  "1",
5783                  "2",
5784                  "4",
5785                  "8",
5786                  "16",
5787                  "Dialog...",
5788                  (char *) NULL,
5789                };
5790
5791              /*
5792                Select a command from the pop-up menu.
5793              */
5794              entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5795                command);
5796              if (entry < 0)
5797                break;
5798              if (entry != 5)
5799                {
5800                  line_width=(unsigned int) StringToUnsignedLong(
5801                    WidthsMenu[entry]);
5802                  break;
5803                }
5804              (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5805                width);
5806              if (*width == '\0')
5807                break;
5808              line_width=(unsigned int) StringToUnsignedLong(width);
5809              break;
5810            }
5811            case DrawUndoCommand:
5812            {
5813              (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5814                image,exception);
5815              break;
5816            }
5817            case DrawHelpCommand:
5818            {
5819              XTextViewWidget(display,resource_info,windows,MagickFalse,
5820                "Help Viewer - Image Rotation",ImageDrawHelp);
5821              (void) XCheckDefineCursor(display,windows->image.id,cursor);
5822              break;
5823            }
5824            case DrawDismissCommand:
5825            {
5826              /*
5827                Prematurely exit.
5828              */
5829              state|=EscapeState;
5830              state|=ExitState;
5831              break;
5832            }
5833            default:
5834              break;
5835          }
5836          (void) XCheckDefineCursor(display,windows->image.id,cursor);
5837          continue;
5838        }
5839      switch (event.type)
5840      {
5841        case ButtonPress:
5842        {
5843          if (event.xbutton.button != Button1)
5844            break;
5845          if (event.xbutton.window != windows->image.id)
5846            break;
5847          /*
5848            exit loop.
5849          */
5850          x=event.xbutton.x;
5851          y=event.xbutton.y;
5852          state|=ExitState;
5853          break;
5854        }
5855        case ButtonRelease:
5856          break;
5857        case Expose:
5858          break;
5859        case KeyPress:
5860        {
5861          KeySym
5862            key_symbol;
5863
5864          if (event.xkey.window != windows->image.id)
5865            break;
5866          /*
5867            Respond to a user key press.
5868          */
5869          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5870            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5871          switch ((int) key_symbol)
5872          {
5873            case XK_Escape:
5874            case XK_F20:
5875            {
5876              /*
5877                Prematurely exit.
5878              */
5879              state|=EscapeState;
5880              state|=ExitState;
5881              break;
5882            }
5883            case XK_F1:
5884            case XK_Help:
5885            {
5886              XTextViewWidget(display,resource_info,windows,MagickFalse,
5887                "Help Viewer - Image Rotation",ImageDrawHelp);
5888              break;
5889            }
5890            default:
5891            {
5892              (void) XBell(display,0);
5893              break;
5894            }
5895          }
5896          break;
5897        }
5898        case MotionNotify:
5899        {
5900          /*
5901            Map and unmap Info widget as text cursor crosses its boundaries.
5902          */
5903          x=event.xmotion.x;
5904          y=event.xmotion.y;
5905          if (windows->info.mapped != MagickFalse)
5906            {
5907              if ((x < (int) (windows->info.x+windows->info.width)) &&
5908                  (y < (int) (windows->info.y+windows->info.height)))
5909                (void) XWithdrawWindow(display,windows->info.id,
5910                  windows->info.screen);
5911            }
5912          else
5913            if ((x > (int) (windows->info.x+windows->info.width)) ||
5914                (y > (int) (windows->info.y+windows->info.height)))
5915              (void) XMapWindow(display,windows->info.id);
5916          break;
5917        }
5918      }
5919    } while ((state & ExitState) == 0);
5920    (void) XSelectInput(display,windows->image.id,
5921      windows->image.attributes.event_mask);
5922    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5923    if ((state & EscapeState) != 0)
5924      break;
5925    /*
5926      Draw element as pointer moves until the button is released.
5927    */
5928    distance=0;
5929    degrees=0.0;
5930    line_info.x1=x;
5931    line_info.y1=y;
5932    line_info.x2=x;
5933    line_info.y2=y;
5934    rectangle_info.x=(ssize_t) x;
5935    rectangle_info.y=(ssize_t) y;
5936    rectangle_info.width=0;
5937    rectangle_info.height=0;
5938    number_coordinates=1;
5939    coordinate_info->x=x;
5940    coordinate_info->y=y;
5941    (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5942    state=DefaultState;
5943    do
5944    {
5945      switch (element)
5946      {
5947        case PointElement:
5948        default:
5949        {
5950          if (number_coordinates > 1)
5951            {
5952              (void) XDrawLines(display,windows->image.id,
5953                windows->image.highlight_context,coordinate_info,
5954                number_coordinates,CoordModeOrigin);
5955              (void) FormatLocaleString(text,MaxTextExtent," %+d%+d",
5956                coordinate_info[number_coordinates-1].x,
5957                coordinate_info[number_coordinates-1].y);
5958              XInfoWidget(display,windows,text);
5959            }
5960          break;
5961        }
5962        case LineElement:
5963        {
5964          if (distance > 9)
5965            {
5966              /*
5967                Display angle of the line.
5968              */
5969              degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5970                line_info.y1),(double) (line_info.x2-line_info.x1)));
5971              (void) FormatLocaleString(text,MaxTextExtent," %g",
5972                (double) degrees);
5973              XInfoWidget(display,windows,text);
5974              XHighlightLine(display,windows->image.id,
5975                windows->image.highlight_context,&line_info);
5976            }
5977          else
5978            if (windows->info.mapped != MagickFalse)
5979              (void) XWithdrawWindow(display,windows->info.id,
5980                windows->info.screen);
5981          break;
5982        }
5983        case RectangleElement:
5984        case FillRectangleElement:
5985        {
5986          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5987            {
5988              /*
5989                Display info and draw drawing rectangle.
5990              */
5991              (void) FormatLocaleString(text,MaxTextExtent,
5992                " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5993                (double) rectangle_info.height,(double) rectangle_info.x,
5994                (double) rectangle_info.y);
5995              XInfoWidget(display,windows,text);
5996              XHighlightRectangle(display,windows->image.id,
5997                windows->image.highlight_context,&rectangle_info);
5998            }
5999          else
6000            if (windows->info.mapped != MagickFalse)
6001              (void) XWithdrawWindow(display,windows->info.id,
6002                windows->info.screen);
6003          break;
6004        }
6005        case CircleElement:
6006        case FillCircleElement:
6007        case EllipseElement:
6008        case FillEllipseElement:
6009        {
6010          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6011            {
6012              /*
6013                Display info and draw drawing rectangle.
6014              */
6015              (void) FormatLocaleString(text,MaxTextExtent,
6016                " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
6017                (double) rectangle_info.height,(double) rectangle_info.x,
6018                (double) rectangle_info.y);
6019              XInfoWidget(display,windows,text);
6020              XHighlightEllipse(display,windows->image.id,
6021                windows->image.highlight_context,&rectangle_info);
6022            }
6023          else
6024            if (windows->info.mapped != MagickFalse)
6025              (void) XWithdrawWindow(display,windows->info.id,
6026                windows->info.screen);
6027          break;
6028        }
6029        case PolygonElement:
6030        case FillPolygonElement:
6031        {
6032          if (number_coordinates > 1)
6033            (void) XDrawLines(display,windows->image.id,
6034              windows->image.highlight_context,coordinate_info,
6035              number_coordinates,CoordModeOrigin);
6036          if (distance > 9)
6037            {
6038              /*
6039                Display angle of the line.
6040              */
6041              degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
6042                line_info.y1),(double) (line_info.x2-line_info.x1)));
6043              (void) FormatLocaleString(text,MaxTextExtent," %g",
6044                (double) degrees);
6045              XInfoWidget(display,windows,text);
6046              XHighlightLine(display,windows->image.id,
6047                windows->image.highlight_context,&line_info);
6048            }
6049          else
6050            if (windows->info.mapped != MagickFalse)
6051              (void) XWithdrawWindow(display,windows->info.id,
6052                windows->info.screen);
6053          break;
6054        }
6055      }
6056      /*
6057        Wait for next event.
6058      */
6059      XScreenEvent(display,windows,&event);
6060      switch (element)
6061      {
6062        case PointElement:
6063        default:
6064        {
6065          if (number_coordinates > 1)
6066            (void) XDrawLines(display,windows->image.id,
6067              windows->image.highlight_context,coordinate_info,
6068              number_coordinates,CoordModeOrigin);
6069          break;
6070        }
6071        case LineElement:
6072        {
6073          if (distance > 9)
6074            XHighlightLine(display,windows->image.id,
6075              windows->image.highlight_context,&line_info);
6076          break;
6077        }
6078        case RectangleElement:
6079        case FillRectangleElement:
6080        {
6081          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6082            XHighlightRectangle(display,windows->image.id,
6083              windows->image.highlight_context,&rectangle_info);
6084          break;
6085        }
6086        case CircleElement:
6087        case FillCircleElement:
6088        case EllipseElement:
6089        case FillEllipseElement:
6090        {
6091          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6092            XHighlightEllipse(display,windows->image.id,
6093              windows->image.highlight_context,&rectangle_info);
6094          break;
6095        }
6096        case PolygonElement:
6097        case FillPolygonElement:
6098        {
6099          if (number_coordinates > 1)
6100            (void) XDrawLines(display,windows->image.id,
6101              windows->image.highlight_context,coordinate_info,
6102              number_coordinates,CoordModeOrigin);
6103          if (distance > 9)
6104            XHighlightLine(display,windows->image.id,
6105              windows->image.highlight_context,&line_info);
6106          break;
6107        }
6108      }
6109      switch (event.type)
6110      {
6111        case ButtonPress:
6112          break;
6113        case ButtonRelease:
6114        {
6115          /*
6116            User has committed to element.
6117          */
6118          line_info.x2=event.xbutton.x;
6119          line_info.y2=event.xbutton.y;
6120          rectangle_info.x=(ssize_t) event.xbutton.x;
6121          rectangle_info.y=(ssize_t) event.xbutton.y;
6122          coordinate_info[number_coordinates].x=event.xbutton.x;
6123          coordinate_info[number_coordinates].y=event.xbutton.y;
6124          if (((element != PolygonElement) &&
6125               (element != FillPolygonElement)) || (distance <= 9))
6126            {
6127              state|=ExitState;
6128              break;
6129            }
6130          number_coordinates++;
6131          if (number_coordinates < (int) max_coordinates)
6132            {
6133              line_info.x1=event.xbutton.x;
6134              line_info.y1=event.xbutton.y;
6135              break;
6136            }
6137          max_coordinates<<=1;
6138          coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6139            max_coordinates,sizeof(*coordinate_info));
6140          if (coordinate_info == (XPoint *) NULL)
6141            (void) ThrowMagickException(exception,GetMagickModule(),
6142              ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6143          break;
6144        }
6145        case Expose:
6146          break;
6147        case MotionNotify:
6148        {
6149          if (event.xmotion.window != windows->image.id)
6150            break;
6151          if (element != PointElement)
6152            {
6153              line_info.x2=event.xmotion.x;
6154              line_info.y2=event.xmotion.y;
6155              rectangle_info.x=(ssize_t) event.xmotion.x;
6156              rectangle_info.y=(ssize_t) event.xmotion.y;
6157              break;
6158            }
6159          coordinate_info[number_coordinates].x=event.xbutton.x;
6160          coordinate_info[number_coordinates].y=event.xbutton.y;
6161          number_coordinates++;
6162          if (number_coordinates < (int) max_coordinates)
6163            break;
6164          max_coordinates<<=1;
6165          coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6166            max_coordinates,sizeof(*coordinate_info));
6167          if (coordinate_info == (XPoint *) NULL)
6168            (void) ThrowMagickException(exception,GetMagickModule(),
6169              ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6170          break;
6171        }
6172        default:
6173          break;
6174      }
6175      /*
6176        Check boundary conditions.
6177      */
6178      if (line_info.x2 < 0)
6179        line_info.x2=0;
6180      else
6181        if (line_info.x2 > (int) windows->image.width)
6182          line_info.x2=(short) windows->image.width;
6183      if (line_info.y2 < 0)
6184        line_info.y2=0;
6185      else
6186        if (line_info.y2 > (int) windows->image.height)
6187          line_info.y2=(short) windows->image.height;
6188      distance=(unsigned int)
6189        (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6190         ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6191      if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6192          ((state & ExitState) != 0))
6193        {
6194          if (rectangle_info.x < 0)
6195            rectangle_info.x=0;
6196          else
6197            if (rectangle_info.x > (ssize_t) windows->image.width)
6198              rectangle_info.x=(ssize_t) windows->image.width;
6199          if ((int) rectangle_info.x < x)
6200            rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6201          else
6202            {
6203              rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6204              rectangle_info.x=(ssize_t) x;
6205            }
6206          if (rectangle_info.y < 0)
6207            rectangle_info.y=0;
6208          else
6209            if (rectangle_info.y > (ssize_t) windows->image.height)
6210              rectangle_info.y=(ssize_t) windows->image.height;
6211          if ((int) rectangle_info.y < y)
6212            rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6213          else
6214            {
6215              rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6216              rectangle_info.y=(ssize_t) y;
6217            }
6218        }
6219    } while ((state & ExitState) == 0);
6220    (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6221    if ((element == PointElement) || (element == PolygonElement) ||
6222        (element == FillPolygonElement))
6223      {
6224        /*
6225          Determine polygon bounding box.
6226        */
6227        rectangle_info.x=(ssize_t) coordinate_info->x;
6228        rectangle_info.y=(ssize_t) coordinate_info->y;
6229        x=coordinate_info->x;
6230        y=coordinate_info->y;
6231        for (i=1; i < number_coordinates; i++)
6232        {
6233          if (coordinate_info[i].x > x)
6234            x=coordinate_info[i].x;
6235          if (coordinate_info[i].y > y)
6236            y=coordinate_info[i].y;
6237          if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6238            rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6239          if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6240            rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6241        }
6242        rectangle_info.width=(size_t) (x-rectangle_info.x);
6243        rectangle_info.height=(size_t) (y-rectangle_info.y);
6244        for (i=0; i < number_coordinates; i++)
6245        {
6246          coordinate_info[i].x-=rectangle_info.x;
6247          coordinate_info[i].y-=rectangle_info.y;
6248        }
6249      }
6250    else
6251      if (distance <= 9)
6252        continue;
6253      else
6254        if ((element == RectangleElement) ||
6255            (element == CircleElement) || (element == EllipseElement))
6256          {
6257            rectangle_info.width--;
6258            rectangle_info.height--;
6259          }
6260    /*
6261      Drawing is relative to image configuration.
6262    */
6263    draw_info.x=(int) rectangle_info.x;
6264    draw_info.y=(int) rectangle_info.y;
6265    (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6266      image,exception);
6267    width=(unsigned int) (*image)->columns;
6268    height=(unsigned int) (*image)->rows;
6269    x=0;
6270    y=0;
6271    if (windows->image.crop_geometry != (char *) NULL)
6272      (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6273    draw_info.x+=windows->image.x-(line_width/2);
6274    if (draw_info.x < 0)
6275      draw_info.x=0;
6276    draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6277    draw_info.y+=windows->image.y-(line_width/2);
6278    if (draw_info.y < 0)
6279      draw_info.y=0;
6280    draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6281    draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6282    if (draw_info.width > (unsigned int) (*image)->columns)
6283      draw_info.width=(unsigned int) (*image)->columns;
6284    draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6285    if (draw_info.height > (unsigned int) (*image)->rows)
6286      draw_info.height=(unsigned int) (*image)->rows;
6287    (void) FormatLocaleString(draw_info.geometry,MaxTextExtent,"%ux%u%+d%+d",
6288      width*draw_info.width/windows->image.ximage->width,
6289      height*draw_info.height/windows->image.ximage->height,
6290      draw_info.x+x,draw_info.y+y);
6291    /*
6292      Initialize drawing attributes.
6293    */
6294    draw_info.degrees=0.0;
6295    draw_info.element=element;
6296    draw_info.stipple=stipple;
6297    draw_info.line_width=line_width;
6298    draw_info.line_info=line_info;
6299    if (line_info.x1 > (int) (line_width/2))
6300      draw_info.line_info.x1=(short) line_width/2;
6301    if (line_info.y1 > (int) (line_width/2))
6302      draw_info.line_info.y1=(short) line_width/2;
6303    draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6304    draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6305    if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6306      {
6307        draw_info.line_info.x2=(-draw_info.line_info.x2);
6308        draw_info.line_info.y2=(-draw_info.line_info.y2);
6309      }
6310    if (draw_info.line_info.x2 < 0)
6311      {
6312        draw_info.line_info.x2=(-draw_info.line_info.x2);
6313        Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6314      }
6315    if (draw_info.line_info.y2 < 0)
6316      {
6317        draw_info.line_info.y2=(-draw_info.line_info.y2);
6318        Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6319      }
6320    draw_info.rectangle_info=rectangle_info;
6321    if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6322      draw_info.rectangle_info.x=(ssize_t) line_width/2;
6323    if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6324      draw_info.rectangle_info.y=(ssize_t) line_width/2;
6325    draw_info.number_coordinates=(unsigned int) number_coordinates;
6326    draw_info.coordinate_info=coordinate_info;
6327    windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6328    /*
6329      Draw element on image.
6330    */
6331    XSetCursorState(display,windows,MagickTrue);
6332    XCheckRefreshWindows(display,windows);
6333    status=XDrawImage(display,windows->pixel_info,&draw_info,*image);
6334    XSetCursorState(display,windows,MagickFalse);
6335    /*
6336      Update image colormap and return to image drawing.
6337    */
6338    XConfigureImageColormap(display,resource_info,windows,*image);
6339    (void) XConfigureImage(display,resource_info,windows,*image,exception);
6340  }
6341  XSetCursorState(display,windows,MagickFalse);
6342  coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6343  return(status != 0 ? MagickTrue : MagickFalse);
6344}
6345
6346/*
6347%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6348%                                                                             %
6349%                                                                             %
6350%                                                                             %
6351+   X D r a w P a n R e c t a n g l e                                         %
6352%                                                                             %
6353%                                                                             %
6354%                                                                             %
6355%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6356%
6357%  XDrawPanRectangle() draws a rectangle in the pan window.  The pan window
6358%  displays a zoom image and the rectangle shows which portion of the image is
6359%  displayed in the Image window.
6360%
6361%  The format of the XDrawPanRectangle method is:
6362%
6363%      XDrawPanRectangle(Display *display,XWindows *windows)
6364%
6365%  A description of each parameter follows:
6366%
6367%    o display: Specifies a connection to an X server;  returned from
6368%      XOpenDisplay.
6369%
6370%    o windows: Specifies a pointer to a XWindows structure.
6371%
6372*/
6373static void XDrawPanRectangle(Display *display,XWindows *windows)
6374{
6375  MagickRealType
6376    scale_factor;
6377
6378  RectangleInfo
6379    highlight_info;
6380
6381  /*
6382    Determine dimensions of the panning rectangle.
6383  */
6384  scale_factor=(MagickRealType) windows->pan.width/windows->image.ximage->width;
6385  highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6386  highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6387  scale_factor=(MagickRealType)
6388    windows->pan.height/windows->image.ximage->height;
6389  highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6390  highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6391  /*
6392    Display the panning rectangle.
6393  */
6394  (void) XClearWindow(display,windows->pan.id);
6395  XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6396    &highlight_info);
6397}
6398
6399/*
6400%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6401%                                                                             %
6402%                                                                             %
6403%                                                                             %
6404+   X I m a g e C a c h e                                                     %
6405%                                                                             %
6406%                                                                             %
6407%                                                                             %
6408%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6409%
6410%  XImageCache() handles the creation, manipulation, and destruction of the
6411%  image cache (undo and redo buffers).
6412%
6413%  The format of the XImageCache method is:
6414%
6415%      void XImageCache(Display *display,XResourceInfo *resource_info,
6416%        XWindows *windows,const CommandType command,Image **image,
6417%        ExceptionInfo *exception)
6418%
6419%  A description of each parameter follows:
6420%
6421%    o display: Specifies a connection to an X server; returned from
6422%      XOpenDisplay.
6423%
6424%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6425%
6426%    o windows: Specifies a pointer to a XWindows structure.
6427%
6428%    o command: Specifies a command to perform.
6429%
6430%    o image: the image;  XImageCache may transform the image and return a new
6431%      image pointer.
6432%
6433%    o exception: return any errors or warnings in this structure.
6434%
6435*/
6436static void XImageCache(Display *display,XResourceInfo *resource_info,
6437  XWindows *windows,const CommandType command,Image **image,
6438  ExceptionInfo *exception)
6439{
6440  Image
6441    *cache_image;
6442
6443  static Image
6444    *redo_image = (Image *) NULL,
6445    *undo_image = (Image *) NULL;
6446
6447  switch (command)
6448  {
6449    case FreeBuffersCommand:
6450    {
6451      /*
6452        Free memory from the undo and redo cache.
6453      */
6454      while (undo_image != (Image *) NULL)
6455      {
6456        cache_image=undo_image;
6457        undo_image=GetPreviousImageInList(undo_image);
6458        cache_image->list=DestroyImage(cache_image->list);
6459        cache_image=DestroyImage(cache_image);
6460      }
6461      undo_image=NewImageList();
6462      if (redo_image != (Image *) NULL)
6463        redo_image=DestroyImage(redo_image);
6464      redo_image=NewImageList();
6465      return;
6466    }
6467    case UndoCommand:
6468    {
6469      char
6470        image_geometry[MaxTextExtent];
6471
6472      /*
6473        Undo the last image transformation.
6474      */
6475      if (undo_image == (Image *) NULL)
6476        {
6477          (void) XBell(display,0);
6478          return;
6479        }
6480      cache_image=undo_image;
6481      undo_image=GetPreviousImageInList(undo_image);
6482      windows->image.window_changes.width=(int) cache_image->columns;
6483      windows->image.window_changes.height=(int) cache_image->rows;
6484      (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
6485        windows->image.ximage->width,windows->image.ximage->height);
6486      (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
6487        exception);
6488      if (windows->image.crop_geometry != (char *) NULL)
6489        windows->image.crop_geometry=(char *) RelinquishMagickMemory(
6490          windows->image.crop_geometry);
6491      windows->image.crop_geometry=cache_image->geometry;
6492      if (redo_image != (Image *) NULL)
6493        redo_image=DestroyImage(redo_image);
6494      redo_image=(*image);
6495      *image=cache_image->list;
6496      cache_image=DestroyImage(cache_image);
6497      if (windows->image.orphan != MagickFalse)
6498        return;
6499      XConfigureImageColormap(display,resource_info,windows,*image);
6500      (void) XConfigureImage(display,resource_info,windows,*image,exception);
6501      return;
6502    }
6503    case CutCommand:
6504    case PasteCommand:
6505    case ApplyCommand:
6506    case HalfSizeCommand:
6507    case OriginalSizeCommand:
6508    case DoubleSizeCommand:
6509    case ResizeCommand:
6510    case TrimCommand:
6511    case CropCommand:
6512    case ChopCommand:
6513    case FlipCommand:
6514    case FlopCommand:
6515    case RotateRightCommand:
6516    case RotateLeftCommand:
6517    case RotateCommand:
6518    case ShearCommand:
6519    case RollCommand:
6520    case NegateCommand:
6521    case ContrastStretchCommand:
6522    case SigmoidalContrastCommand:
6523    case NormalizeCommand:
6524    case EqualizeCommand:
6525    case HueCommand:
6526    case SaturationCommand:
6527    case BrightnessCommand:
6528    case GammaCommand:
6529    case SpiffCommand:
6530    case DullCommand:
6531    case GrayscaleCommand:
6532    case MapCommand:
6533    case QuantizeCommand:
6534    case DespeckleCommand:
6535    case EmbossCommand:
6536    case ReduceNoiseCommand:
6537    case AddNoiseCommand:
6538    case SharpenCommand:
6539    case BlurCommand:
6540    case ThresholdCommand:
6541    case EdgeDetectCommand:
6542    case SpreadCommand:
6543    case ShadeCommand:
6544    case RaiseCommand:
6545    case SegmentCommand:
6546    case SolarizeCommand:
6547    case SepiaToneCommand:
6548    case SwirlCommand:
6549    case ImplodeCommand:
6550    case VignetteCommand:
6551    case WaveCommand:
6552    case OilPaintCommand:
6553    case CharcoalDrawCommand:
6554    case AnnotateCommand:
6555    case AddBorderCommand:
6556    case AddFrameCommand:
6557    case CompositeCommand:
6558    case CommentCommand:
6559    case LaunchCommand:
6560    case RegionofInterestCommand:
6561    case SaveToUndoBufferCommand:
6562    case RedoCommand:
6563    {
6564      Image
6565        *previous_image;
6566
6567      ssize_t
6568        bytes;
6569
6570      bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelInfo));
6571      if (undo_image != (Image *) NULL)
6572        {
6573          /*
6574            Ensure the undo cache has enough memory available.
6575          */
6576          previous_image=undo_image;
6577          while (previous_image != (Image *) NULL)
6578          {
6579            bytes+=previous_image->list->columns*previous_image->list->rows*
6580              sizeof(PixelInfo);
6581            if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6582              {
6583                previous_image=GetPreviousImageInList(previous_image);
6584                continue;
6585              }
6586            bytes-=previous_image->list->columns*previous_image->list->rows*
6587              sizeof(PixelInfo);
6588            if (previous_image == undo_image)
6589              undo_image=NewImageList();
6590            else
6591              previous_image->next->previous=NewImageList();
6592            break;
6593          }
6594          while (previous_image != (Image *) NULL)
6595          {
6596            /*
6597              Delete any excess memory from undo cache.
6598            */
6599            cache_image=previous_image;
6600            previous_image=GetPreviousImageInList(previous_image);
6601            cache_image->list=DestroyImage(cache_image->list);
6602            cache_image=DestroyImage(cache_image);
6603          }
6604        }
6605      if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6606        break;
6607      /*
6608        Save image before transformations are applied.
6609      */
6610      cache_image=AcquireImage((ImageInfo *) NULL,exception);
6611      if (cache_image == (Image *) NULL)
6612        break;
6613      XSetCursorState(display,windows,MagickTrue);
6614      XCheckRefreshWindows(display,windows);
6615      cache_image->list=CloneImage(*image,0,0,MagickTrue,exception);
6616      XSetCursorState(display,windows,MagickFalse);
6617      if (cache_image->list == (Image *) NULL)
6618        {
6619          cache_image=DestroyImage(cache_image);
6620          break;
6621        }
6622      cache_image->columns=(size_t) windows->image.ximage->width;
6623      cache_image->rows=(size_t) windows->image.ximage->height;
6624      cache_image->geometry=windows->image.crop_geometry;
6625      if (windows->image.crop_geometry != (char *) NULL)
6626        {
6627          cache_image->geometry=AcquireString((char *) NULL);
6628          (void) CopyMagickString(cache_image->geometry,
6629            windows->image.crop_geometry,MaxTextExtent);
6630        }
6631      if (undo_image == (Image *) NULL)
6632        {
6633          undo_image=cache_image;
6634          break;
6635        }
6636      undo_image->next=cache_image;
6637      undo_image->next->previous=undo_image;
6638      undo_image=undo_image->next;
6639      break;
6640    }
6641    default:
6642      break;
6643  }
6644  if (command == RedoCommand)
6645    {
6646      /*
6647        Redo the last image transformation.
6648      */
6649      if (redo_image == (Image *) NULL)
6650        {
6651          (void) XBell(display,0);
6652          return;
6653        }
6654      windows->image.window_changes.width=(int) redo_image->columns;
6655      windows->image.window_changes.height=(int) redo_image->rows;
6656      if (windows->image.crop_geometry != (char *) NULL)
6657        windows->image.crop_geometry=(char *)
6658          RelinquishMagickMemory(windows->image.crop_geometry);
6659      windows->image.crop_geometry=redo_image->geometry;
6660      *image=DestroyImage(*image);
6661      *image=redo_image;
6662      redo_image=NewImageList();
6663      if (windows->image.orphan != MagickFalse)
6664        return;
6665      XConfigureImageColormap(display,resource_info,windows,*image);
6666      (void) XConfigureImage(display,resource_info,windows,*image,exception);
6667      return;
6668    }
6669  if (command != InfoCommand)
6670    return;
6671  /*
6672    Display image info.
6673  */
6674  XSetCursorState(display,windows,MagickTrue);
6675  XCheckRefreshWindows(display,windows);
6676  XDisplayImageInfo(display,resource_info,windows,undo_image,*image);
6677  XSetCursorState(display,windows,MagickFalse);
6678}
6679
6680/*
6681%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6682%                                                                             %
6683%                                                                             %
6684%                                                                             %
6685+   X I m a g e W i n d o w C o m m a n d                                     %
6686%                                                                             %
6687%                                                                             %
6688%                                                                             %
6689%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6690%
6691%  XImageWindowCommand() makes a transform to the image or Image window as
6692%  specified by a user menu button or keyboard command.
6693%
6694%  The format of the XImageWindowCommand method is:
6695%
6696%      CommandType XImageWindowCommand(Display *display,
6697%        XResourceInfo *resource_info,XWindows *windows,
6698%        const MagickStatusType state,KeySym key_symbol,Image **image,
6699%        ExceptionInfo *exception)
6700%
6701%  A description of each parameter follows:
6702%
6703%    o nexus:  Method XImageWindowCommand returns an image when the
6704%      user chooses 'Open Image' from the command menu.  Otherwise a null
6705%      image is returned.
6706%
6707%    o display: Specifies a connection to an X server; returned from
6708%      XOpenDisplay.
6709%
6710%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6711%
6712%    o windows: Specifies a pointer to a XWindows structure.
6713%
6714%    o state: key mask.
6715%
6716%    o key_symbol: Specifies a command to perform.
6717%
6718%    o image: the image;  XImageWIndowCommand may transform the image and
6719%      return a new image pointer.
6720%
6721%    o exception: return any errors or warnings in this structure.
6722%
6723*/
6724static CommandType XImageWindowCommand(Display *display,
6725  XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6726  KeySym key_symbol,Image **image,ExceptionInfo *exception)
6727{
6728  static char
6729    delta[MaxTextExtent] = "";
6730
6731  static const char
6732    Digits[] = "01234567890";
6733
6734  static KeySym
6735    last_symbol = XK_0;
6736
6737  if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6738    {
6739      if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6740        {
6741          *delta='\0';
6742          resource_info->quantum=1;
6743        }
6744      last_symbol=key_symbol;
6745      delta[strlen(delta)+1]='\0';
6746      delta[strlen(delta)]=Digits[key_symbol-XK_0];
6747      resource_info->quantum=StringToLong(delta);
6748      return(NullCommand);
6749    }
6750  last_symbol=key_symbol;
6751  if (resource_info->immutable)
6752    {
6753      /*
6754        Virtual image window has a restricted command set.
6755      */
6756      switch (key_symbol)
6757      {
6758        case XK_question:
6759          return(InfoCommand);
6760        case XK_p:
6761        case XK_Print:
6762          return(PrintCommand);
6763        case XK_space:
6764          return(NextCommand);
6765        case XK_q:
6766        case XK_Escape:
6767          return(QuitCommand);
6768        default:
6769          break;
6770      }
6771      return(NullCommand);
6772    }
6773  switch ((int) key_symbol)
6774  {
6775    case XK_o:
6776    {
6777      if ((state & ControlMask) == 0)
6778        break;
6779      return(OpenCommand);
6780    }
6781    case XK_space:
6782      return(NextCommand);
6783    case XK_BackSpace:
6784      return(FormerCommand);
6785    case XK_s:
6786    {
6787      if ((state & Mod1Mask) != 0)
6788        return(SwirlCommand);
6789      if ((state & ControlMask) == 0)
6790        return(ShearCommand);
6791      return(SaveCommand);
6792    }
6793    case XK_p:
6794    case XK_Print:
6795    {
6796      if ((state & Mod1Mask) != 0)
6797        return(OilPaintCommand);
6798      if ((state & Mod4Mask) != 0)
6799        return(ColorCommand);
6800      if ((state & ControlMask) == 0)
6801        return(NullCommand);
6802      return(PrintCommand);
6803    }
6804    case XK_d:
6805    {
6806      if ((state & Mod4Mask) != 0)
6807        return(DrawCommand);
6808      if ((state & ControlMask) == 0)
6809        return(NullCommand);
6810      return(DeleteCommand);
6811    }
6812    case XK_Select:
6813    {
6814      if ((state & ControlMask) == 0)
6815        return(NullCommand);
6816      return(SelectCommand);
6817    }
6818    case XK_n:
6819    {
6820      if ((state & ControlMask) == 0)
6821        return(NullCommand);
6822      return(NewCommand);
6823    }
6824    case XK_q:
6825    case XK_Escape:
6826      return(QuitCommand);
6827    case XK_z:
6828    case XK_Undo:
6829    {
6830      if ((state & ControlMask) == 0)
6831        return(NullCommand);
6832      return(UndoCommand);
6833    }
6834    case XK_r:
6835    case XK_Redo:
6836    {
6837      if ((state & ControlMask) == 0)
6838        return(RollCommand);
6839      return(RedoCommand);
6840    }
6841    case XK_x:
6842    {
6843      if ((state & ControlMask) == 0)
6844        return(NullCommand);
6845      return(CutCommand);
6846    }
6847    case XK_c:
6848    {
6849      if ((state & Mod1Mask) != 0)
6850        return(CharcoalDrawCommand);
6851      if ((state & ControlMask) == 0)
6852        return(CropCommand);
6853      return(CopyCommand);
6854    }
6855    case XK_v:
6856    case XK_Insert:
6857    {
6858      if ((state & Mod4Mask) != 0)
6859        return(CompositeCommand);
6860      if ((state & ControlMask) == 0)
6861        return(FlipCommand);
6862      return(PasteCommand);
6863    }
6864    case XK_less:
6865      return(HalfSizeCommand);
6866    case XK_minus:
6867      return(OriginalSizeCommand);
6868    case XK_greater:
6869      return(DoubleSizeCommand);
6870    case XK_percent:
6871      return(ResizeCommand);
6872    case XK_at:
6873      return(RefreshCommand);
6874    case XK_bracketleft:
6875      return(ChopCommand);
6876    case XK_h:
6877      return(FlopCommand);
6878    case XK_slash:
6879      return(RotateRightCommand);
6880    case XK_backslash:
6881      return(RotateLeftCommand);
6882    case XK_asterisk:
6883      return(RotateCommand);
6884    case XK_t:
6885      return(TrimCommand);
6886    case XK_H:
6887      return(HueCommand);
6888    case XK_S:
6889      return(SaturationCommand);
6890    case XK_L:
6891      return(BrightnessCommand);
6892    case XK_G:
6893      return(GammaCommand);
6894    case XK_C:
6895      return(SpiffCommand);
6896    case XK_Z:
6897      return(DullCommand);
6898    case XK_N:
6899      return(NormalizeCommand);
6900    case XK_equal:
6901      return(EqualizeCommand);
6902    case XK_asciitilde:
6903      return(NegateCommand);
6904    case XK_period:
6905      return(GrayscaleCommand);
6906    case XK_numbersign:
6907      return(QuantizeCommand);
6908    case XK_F2:
6909      return(DespeckleCommand);
6910    case XK_F3:
6911      return(EmbossCommand);
6912    case XK_F4:
6913      return(ReduceNoiseCommand);
6914    case XK_F5:
6915      return(AddNoiseCommand);
6916    case XK_F6:
6917      return(SharpenCommand);
6918    case XK_F7:
6919      return(BlurCommand);
6920    case XK_F8:
6921      return(ThresholdCommand);
6922    case XK_F9:
6923      return(EdgeDetectCommand);
6924    case XK_F10:
6925      return(SpreadCommand);
6926    case XK_F11:
6927      return(ShadeCommand);
6928    case XK_F12:
6929      return(RaiseCommand);
6930    case XK_F13:
6931      return(SegmentCommand);
6932    case XK_i:
6933    {
6934      if ((state & Mod1Mask) == 0)
6935        return(NullCommand);
6936      return(ImplodeCommand);
6937    }
6938    case XK_w:
6939    {
6940      if ((state & Mod1Mask) == 0)
6941        return(NullCommand);
6942      return(WaveCommand);
6943    }
6944    case XK_m:
6945    {
6946      if ((state & Mod4Mask) == 0)
6947        return(NullCommand);
6948      return(MatteCommand);
6949    }
6950    case XK_b:
6951    {
6952      if ((state & Mod4Mask) == 0)
6953        return(NullCommand);
6954      return(AddBorderCommand);
6955    }
6956    case XK_f:
6957    {
6958      if ((state & Mod4Mask) == 0)
6959        return(NullCommand);
6960      return(AddFrameCommand);
6961    }
6962    case XK_exclam:
6963    {
6964      if ((state & Mod4Mask) == 0)
6965        return(NullCommand);
6966      return(CommentCommand);
6967    }
6968    case XK_a:
6969    {
6970      if ((state & Mod1Mask) != 0)
6971        return(ApplyCommand);
6972      if ((state & Mod4Mask) != 0)
6973        return(AnnotateCommand);
6974      if ((state & ControlMask) == 0)
6975        return(NullCommand);
6976      return(RegionofInterestCommand);
6977    }
6978    case XK_question:
6979      return(InfoCommand);
6980    case XK_plus:
6981      return(ZoomCommand);
6982    case XK_P:
6983    {
6984      if ((state & ShiftMask) == 0)
6985        return(NullCommand);
6986      return(ShowPreviewCommand);
6987    }
6988    case XK_Execute:
6989      return(LaunchCommand);
6990    case XK_F1:
6991      return(HelpCommand);
6992    case XK_Find:
6993      return(BrowseDocumentationCommand);
6994    case XK_Menu:
6995    {
6996      (void) XMapRaised(display,windows->command.id);
6997      return(NullCommand);
6998    }
6999    case XK_Next:
7000    case XK_Prior:
7001    case XK_Home:
7002    case XK_KP_Home:
7003    {
7004      XTranslateImage(display,windows,*image,key_symbol);
7005      return(NullCommand);
7006    }
7007    case XK_Up:
7008    case XK_KP_Up:
7009    case XK_Down:
7010    case XK_KP_Down:
7011    case XK_Left:
7012    case XK_KP_Left:
7013    case XK_Right:
7014    case XK_KP_Right:
7015    {
7016      if ((state & Mod1Mask) != 0)
7017        {
7018          RectangleInfo
7019            crop_info;
7020
7021          /*
7022            Trim one pixel from edge of image.
7023          */
7024          crop_info.x=0;
7025          crop_info.y=0;
7026          crop_info.width=(size_t) windows->image.ximage->width;
7027          crop_info.height=(size_t) windows->image.ximage->height;
7028          if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
7029            {
7030              if (resource_info->quantum >= (int) crop_info.height)
7031                resource_info->quantum=(int) crop_info.height-1;
7032              crop_info.height-=resource_info->quantum;
7033            }
7034          if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
7035            {
7036              if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
7037                resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
7038              crop_info.y+=resource_info->quantum;
7039              crop_info.height-=resource_info->quantum;
7040            }
7041          if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
7042            {
7043              if (resource_info->quantum >= (int) crop_info.width)
7044                resource_info->quantum=(int) crop_info.width-1;
7045              crop_info.width-=resource_info->quantum;
7046            }
7047          if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
7048            {
7049              if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
7050                resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
7051              crop_info.x+=resource_info->quantum;
7052              crop_info.width-=resource_info->quantum;
7053            }
7054          if ((int) (windows->image.x+windows->image.width) >
7055              (int) crop_info.width)
7056            windows->image.x=(int) (crop_info.width-windows->image.width);
7057          if ((int) (windows->image.y+windows->image.height) >
7058              (int) crop_info.height)
7059            windows->image.y=(int) (crop_info.height-windows->image.height);
7060          XSetCropGeometry(display,windows,&crop_info,*image);
7061          windows->image.window_changes.width=(int) crop_info.width;
7062          windows->image.window_changes.height=(int) crop_info.height;
7063          (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
7064          (void) XConfigureImage(display,resource_info,windows,*image,
7065            exception);
7066          return(NullCommand);
7067        }
7068      XTranslateImage(display,windows,*image,key_symbol);
7069      return(NullCommand);
7070    }
7071    default:
7072      return(NullCommand);
7073  }
7074  return(NullCommand);
7075}
7076
7077/*
7078%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7079%                                                                             %
7080%                                                                             %
7081%                                                                             %
7082+   X M a g i c k C o m m a n d                                               %
7083%                                                                             %
7084%                                                                             %
7085%                                                                             %
7086%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7087%
7088%  XMagickCommand() makes a transform to the image or Image window as
7089%  specified by a user menu button or keyboard command.
7090%
7091%  The format of the XMagickCommand method is:
7092%
7093%      Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7094%        XWindows *windows,const CommandType command,Image **image,
7095%        ExceptionInfo *exception)
7096%
7097%  A description of each parameter follows:
7098%
7099%    o display: Specifies a connection to an X server; returned from
7100%      XOpenDisplay.
7101%
7102%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7103%
7104%    o windows: Specifies a pointer to a XWindows structure.
7105%
7106%    o command: Specifies a command to perform.
7107%
7108%    o image: the image;  XMagickCommand may transform the image and return a
7109%      new image pointer.
7110%
7111%    o exception: return any errors or warnings in this structure.
7112%
7113*/
7114static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7115  XWindows *windows,const CommandType command,Image **image,
7116  ExceptionInfo *exception)
7117{
7118  char
7119    filename[MaxTextExtent],
7120    geometry[MaxTextExtent],
7121    modulate_factors[MaxTextExtent];
7122
7123  GeometryInfo
7124    geometry_info;
7125
7126  Image
7127    *nexus;
7128
7129  ImageInfo
7130    *image_info;
7131
7132  int
7133    x,
7134    y;
7135
7136  MagickStatusType
7137    flags,
7138    status;
7139
7140  QuantizeInfo
7141    quantize_info;
7142
7143  RectangleInfo
7144    page_geometry;
7145
7146  register int
7147    i;
7148
7149  static char
7150    color[MaxTextExtent] = "gray";
7151
7152  unsigned int
7153    height,
7154    width;
7155
7156  /*
7157    Process user command.
7158  */
7159  XCheckRefreshWindows(display,windows);
7160  XImageCache(display,resource_info,windows,command,image,exception);
7161  nexus=NewImageList();
7162  windows->image.window_changes.width=windows->image.ximage->width;
7163  windows->image.window_changes.height=windows->image.ximage->height;
7164  image_info=CloneImageInfo(resource_info->image_info);
7165  SetGeometryInfo(&geometry_info);
7166  GetQuantizeInfo(&quantize_info);
7167  switch (command)
7168  {
7169    case OpenCommand:
7170    {
7171      /*
7172        Load image.
7173      */
7174      nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7175      break;
7176    }
7177    case NextCommand:
7178    {
7179      /*
7180        Display next image.
7181      */
7182      for (i=0; i < resource_info->quantum; i++)
7183        XClientMessage(display,windows->image.id,windows->im_protocols,
7184          windows->im_next_image,CurrentTime);
7185      break;
7186    }
7187    case FormerCommand:
7188    {
7189      /*
7190        Display former image.
7191      */
7192      for (i=0; i < resource_info->quantum; i++)
7193        XClientMessage(display,windows->image.id,windows->im_protocols,
7194          windows->im_former_image,CurrentTime);
7195      break;
7196    }
7197    case SelectCommand:
7198    {
7199      int
7200        status;
7201
7202      /*
7203        Select image.
7204      */
7205      status=chdir(resource_info->home_directory);
7206      if (status == -1)
7207        (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
7208          "UnableToOpenFile","%s",resource_info->home_directory);
7209      nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7210      break;
7211    }
7212    case SaveCommand:
7213    {
7214      /*
7215        Save image.
7216      */
7217      status=XSaveImage(display,resource_info,windows,*image,exception);
7218      if (status == MagickFalse)
7219        {
7220          char
7221            message[MaxTextExtent];
7222
7223          (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7224            exception->reason != (char *) NULL ? exception->reason : "",
7225            exception->description != (char *) NULL ? exception->description :
7226            "");
7227          XNoticeWidget(display,windows,"Unable to save file:",message);
7228          break;
7229        }
7230      break;
7231    }
7232    case PrintCommand:
7233    {
7234      /*
7235        Print image.
7236      */
7237      status=XPrintImage(display,resource_info,windows,*image,exception);
7238      if (status == MagickFalse)
7239        {
7240          char
7241            message[MaxTextExtent];
7242
7243          (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7244            exception->reason != (char *) NULL ? exception->reason : "",
7245            exception->description != (char *) NULL ? exception->description :
7246            "");
7247          XNoticeWidget(display,windows,"Unable to print file:",message);
7248          break;
7249        }
7250      break;
7251    }
7252    case DeleteCommand:
7253    {
7254      static char
7255        filename[MaxTextExtent] = "\0";
7256
7257      /*
7258        Delete image file.
7259      */
7260      XFileBrowserWidget(display,windows,"Delete",filename);
7261      if (*filename == '\0')
7262        break;
7263      status=remove_utf8(filename) != 0 ? MagickTrue : MagickFalse;
7264      if (status != MagickFalse)
7265        XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7266      break;
7267    }
7268    case NewCommand:
7269    {
7270      int
7271        status;
7272
7273      static char
7274        color[MaxTextExtent] = "gray",
7275        geometry[MaxTextExtent] = "640x480";
7276
7277      static const char
7278        *format = "gradient";
7279
7280      /*
7281        Query user for canvas geometry.
7282      */
7283      status=XDialogWidget(display,windows,"New","Enter image geometry:",
7284        geometry);
7285      if (*geometry == '\0')
7286        break;
7287      if (status == 0)
7288        format="xc";
7289      XColorBrowserWidget(display,windows,"Select",color);
7290      if (*color == '\0')
7291        break;
7292      /*
7293        Create canvas.
7294      */
7295      (void) FormatLocaleString(image_info->filename,MaxTextExtent,
7296        "%s:%s",format,color);
7297      (void) CloneString(&image_info->size,geometry);
7298      nexus=ReadImage(image_info,exception);
7299      CatchException(exception);
7300      XClientMessage(display,windows->image.id,windows->im_protocols,
7301        windows->im_next_image,CurrentTime);
7302      break;
7303    }
7304    case VisualDirectoryCommand:
7305    {
7306      /*
7307        Visual Image directory.
7308      */
7309      nexus=XVisualDirectoryImage(display,resource_info,windows,exception);
7310      break;
7311    }
7312    case QuitCommand:
7313    {
7314      /*
7315        exit program.
7316      */
7317      if (resource_info->confirm_exit == MagickFalse)
7318        XClientMessage(display,windows->image.id,windows->im_protocols,
7319          windows->im_exit,CurrentTime);
7320      else
7321        {
7322          int
7323            status;
7324
7325          /*
7326            Confirm program exit.
7327          */
7328          status=XConfirmWidget(display,windows,"Do you really want to exit",
7329            resource_info->client_name);
7330          if (status > 0)
7331            XClientMessage(display,windows->image.id,windows->im_protocols,
7332              windows->im_exit,CurrentTime);
7333        }
7334      break;
7335    }
7336    case CutCommand:
7337    {
7338      /*
7339        Cut image.
7340      */
7341      (void) XCropImage(display,resource_info,windows,*image,CutMode,exception);
7342      break;
7343    }
7344    case CopyCommand:
7345    {
7346      /*
7347        Copy image.
7348      */
7349      (void) XCropImage(display,resource_info,windows,*image,CopyMode,
7350        exception);
7351      break;
7352    }
7353    case PasteCommand:
7354    {
7355      /*
7356        Paste image.
7357      */
7358      status=XPasteImage(display,resource_info,windows,*image,exception);
7359      if (status == MagickFalse)
7360        {
7361          XNoticeWidget(display,windows,"Unable to paste X image",
7362            (*image)->filename);
7363          break;
7364        }
7365      break;
7366    }
7367    case HalfSizeCommand:
7368    {
7369      /*
7370        Half image size.
7371      */
7372      windows->image.window_changes.width=windows->image.ximage->width/2;
7373      windows->image.window_changes.height=windows->image.ximage->height/2;
7374      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7375      break;
7376    }
7377    case OriginalSizeCommand:
7378    {
7379      /*
7380        Original image size.
7381      */
7382      windows->image.window_changes.width=(int) (*image)->columns;
7383      windows->image.window_changes.height=(int) (*image)->rows;
7384      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7385      break;
7386    }
7387    case DoubleSizeCommand:
7388    {
7389      /*
7390        Double the image size.
7391      */
7392      windows->image.window_changes.width=windows->image.ximage->width << 1;
7393      windows->image.window_changes.height=windows->image.ximage->height << 1;
7394      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7395      break;
7396    }
7397    case ResizeCommand:
7398    {
7399      int
7400        status;
7401
7402      size_t
7403        height,
7404        width;
7405
7406      ssize_t
7407        x,
7408        y;
7409
7410      /*
7411        Resize image.
7412      */
7413      width=(size_t) windows->image.ximage->width;
7414      height=(size_t) windows->image.ximage->height;
7415      x=0;
7416      y=0;
7417      (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g+0+0",
7418        (double) width,(double) height);
7419      status=XDialogWidget(display,windows,"Resize",
7420        "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7421      if (*geometry == '\0')
7422        break;
7423      if (status == 0)
7424        (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7425      (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7426      windows->image.window_changes.width=(int) width;
7427      windows->image.window_changes.height=(int) height;
7428      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7429      break;
7430    }
7431    case ApplyCommand:
7432    {
7433      char
7434        image_geometry[MaxTextExtent];
7435
7436      if ((windows->image.crop_geometry == (char *) NULL) &&
7437          ((int) (*image)->columns == windows->image.ximage->width) &&
7438          ((int) (*image)->rows == windows->image.ximage->height))
7439        break;
7440      /*
7441        Apply size transforms to image.
7442      */
7443      XSetCursorState(display,windows,MagickTrue);
7444      XCheckRefreshWindows(display,windows);
7445      /*
7446        Crop and/or scale displayed image.
7447      */
7448      (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
7449        windows->image.ximage->width,windows->image.ximage->height);
7450      (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
7451        exception);
7452      if (windows->image.crop_geometry != (char *) NULL)
7453        windows->image.crop_geometry=(char *) RelinquishMagickMemory(
7454          windows->image.crop_geometry);
7455      windows->image.x=0;
7456      windows->image.y=0;
7457      XConfigureImageColormap(display,resource_info,windows,*image);
7458      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7459      break;
7460    }
7461    case RefreshCommand:
7462    {
7463      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7464      break;
7465    }
7466    case RestoreCommand:
7467    {
7468      /*
7469        Restore Image window to its original size.
7470      */
7471      if ((windows->image.width == (unsigned int) (*image)->columns) &&
7472          (windows->image.height == (unsigned int) (*image)->rows) &&
7473          (windows->image.crop_geometry == (char *) NULL))
7474        {
7475          (void) XBell(display,0);
7476          break;
7477        }
7478      windows->image.window_changes.width=(int) (*image)->columns;
7479      windows->image.window_changes.height=(int) (*image)->rows;
7480      if (windows->image.crop_geometry != (char *) NULL)
7481        {
7482          windows->image.crop_geometry=(char *)
7483            RelinquishMagickMemory(windows->image.crop_geometry);
7484          windows->image.crop_geometry=(char *) NULL;
7485          windows->image.x=0;
7486          windows->image.y=0;
7487        }
7488      XConfigureImageColormap(display,resource_info,windows,*image);
7489      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7490      break;
7491    }
7492    case CropCommand:
7493    {
7494      /*
7495        Crop image.
7496      */
7497      (void) XCropImage(display,resource_info,windows,*image,CropMode,
7498        exception);
7499      break;
7500    }
7501    case ChopCommand:
7502    {
7503      /*
7504        Chop image.
7505      */
7506      status=XChopImage(display,resource_info,windows,image,exception);
7507      if (status == MagickFalse)
7508        {
7509          XNoticeWidget(display,windows,"Unable to cut X image",
7510            (*image)->filename);
7511          break;
7512        }
7513      break;
7514    }
7515    case FlopCommand:
7516    {
7517      Image
7518        *flop_image;
7519
7520      /*
7521        Flop image scanlines.
7522      */
7523      XSetCursorState(display,windows,MagickTrue);
7524      XCheckRefreshWindows(display,windows);
7525      flop_image=FlopImage(*image,exception);
7526      if (flop_image != (Image *) NULL)
7527        {
7528          *image=DestroyImage(*image);
7529          *image=flop_image;
7530        }
7531      CatchException(exception);
7532      XSetCursorState(display,windows,MagickFalse);
7533      if (windows->image.crop_geometry != (char *) NULL)
7534        {
7535          /*
7536            Flop crop geometry.
7537          */
7538          width=(unsigned int) (*image)->columns;
7539          height=(unsigned int) (*image)->rows;
7540          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7541            &width,&height);
7542          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7543            "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7544        }
7545      if (windows->image.orphan != MagickFalse)
7546        break;
7547      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7548      break;
7549    }
7550    case FlipCommand:
7551    {
7552      Image
7553        *flip_image;
7554
7555      /*
7556        Flip image scanlines.
7557      */
7558      XSetCursorState(display,windows,MagickTrue);
7559      XCheckRefreshWindows(display,windows);
7560      flip_image=FlipImage(*image,exception);
7561      if (flip_image != (Image *) NULL)
7562        {
7563          *image=DestroyImage(*image);
7564          *image=flip_image;
7565        }
7566      CatchException(exception);
7567      XSetCursorState(display,windows,MagickFalse);
7568      if (windows->image.crop_geometry != (char *) NULL)
7569        {
7570          /*
7571            Flip crop geometry.
7572          */
7573          width=(unsigned int) (*image)->columns;
7574          height=(unsigned int) (*image)->rows;
7575          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7576            &width,&height);
7577          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7578            "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7579        }
7580      if (windows->image.orphan != MagickFalse)
7581        break;
7582      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7583      break;
7584    }
7585    case RotateRightCommand:
7586    {
7587      /*
7588        Rotate image 90 degrees clockwise.
7589      */
7590      status=XRotateImage(display,resource_info,windows,90.0,image,exception);
7591      if (status == MagickFalse)
7592        {
7593          XNoticeWidget(display,windows,"Unable to rotate X image",
7594            (*image)->filename);
7595          break;
7596        }
7597      break;
7598    }
7599    case RotateLeftCommand:
7600    {
7601      /*
7602        Rotate image 90 degrees counter-clockwise.
7603      */
7604      status=XRotateImage(display,resource_info,windows,-90.0,image,exception);
7605      if (status == MagickFalse)
7606        {
7607          XNoticeWidget(display,windows,"Unable to rotate X image",
7608            (*image)->filename);
7609          break;
7610        }
7611      break;
7612    }
7613    case RotateCommand:
7614    {
7615      /*
7616        Rotate image.
7617      */
7618      status=XRotateImage(display,resource_info,windows,0.0,image,exception);
7619      if (status == MagickFalse)
7620        {
7621          XNoticeWidget(display,windows,"Unable to rotate X image",
7622            (*image)->filename);
7623          break;
7624        }
7625      break;
7626    }
7627    case ShearCommand:
7628    {
7629      Image
7630        *shear_image;
7631
7632      static char
7633        geometry[MaxTextExtent] = "45.0x45.0";
7634
7635      /*
7636        Query user for shear color and geometry.
7637      */
7638      XColorBrowserWidget(display,windows,"Select",color);
7639      if (*color == '\0')
7640        break;
7641      (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7642        geometry);
7643      if (*geometry == '\0')
7644        break;
7645      /*
7646        Shear image.
7647      */
7648      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7649        exception);
7650      XSetCursorState(display,windows,MagickTrue);
7651      XCheckRefreshWindows(display,windows);
7652      (void) QueryColorCompliance(color,AllCompliance,
7653        &(*image)->background_color,exception);
7654      flags=ParseGeometry(geometry,&geometry_info);
7655      if ((flags & SigmaValue) == 0)
7656        geometry_info.sigma=geometry_info.rho;
7657      shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7658        exception);
7659      if (shear_image != (Image *) NULL)
7660        {
7661          *image=DestroyImage(*image);
7662          *image=shear_image;
7663        }
7664      CatchException(exception);
7665      XSetCursorState(display,windows,MagickFalse);
7666      if (windows->image.orphan != MagickFalse)
7667        break;
7668      windows->image.window_changes.width=(int) (*image)->columns;
7669      windows->image.window_changes.height=(int) (*image)->rows;
7670      XConfigureImageColormap(display,resource_info,windows,*image);
7671      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7672      break;
7673    }
7674    case RollCommand:
7675    {
7676      Image
7677        *roll_image;
7678
7679      static char
7680        geometry[MaxTextExtent] = "+2+2";
7681
7682      /*
7683        Query user for the roll geometry.
7684      */
7685      (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7686        geometry);
7687      if (*geometry == '\0')
7688        break;
7689      /*
7690        Roll image.
7691      */
7692      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7693        exception);
7694      XSetCursorState(display,windows,MagickTrue);
7695      XCheckRefreshWindows(display,windows);
7696      (void) ParsePageGeometry(*image,geometry,&page_geometry,
7697        exception);
7698      roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7699        exception);
7700      if (roll_image != (Image *) NULL)
7701        {
7702          *image=DestroyImage(*image);
7703          *image=roll_image;
7704        }
7705      CatchException(exception);
7706      XSetCursorState(display,windows,MagickFalse);
7707      if (windows->image.orphan != MagickFalse)
7708        break;
7709      windows->image.window_changes.width=(int) (*image)->columns;
7710      windows->image.window_changes.height=(int) (*image)->rows;
7711      XConfigureImageColormap(display,resource_info,windows,*image);
7712      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7713      break;
7714    }
7715    case TrimCommand:
7716    {
7717      static char
7718        fuzz[MaxTextExtent];
7719
7720      /*
7721        Query user for the fuzz factor.
7722      */
7723      (void) FormatLocaleString(fuzz,MaxTextExtent,"%g%%",100.0*
7724        (*image)->fuzz/(QuantumRange+1.0));
7725      (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7726      if (*fuzz == '\0')
7727        break;
7728      (*image)->fuzz=SiPrefixToDouble(fuzz,(double) QuantumRange+1.0);
7729      /*
7730        Trim image.
7731      */
7732      status=XTrimImage(display,resource_info,windows,*image,exception);
7733      if (status == MagickFalse)
7734        {
7735          XNoticeWidget(display,windows,"Unable to trim X image",
7736            (*image)->filename);
7737          break;
7738        }
7739      break;
7740    }
7741    case HueCommand:
7742    {
7743      static char
7744        hue_percent[MaxTextExtent] = "110";
7745
7746      /*
7747        Query user for percent hue change.
7748      */
7749      (void) XDialogWidget(display,windows,"Apply",
7750        "Enter percent change in image hue (0-200):",hue_percent);
7751      if (*hue_percent == '\0')
7752        break;
7753      /*
7754        Vary the image hue.
7755      */
7756      XSetCursorState(display,windows,MagickTrue);
7757      XCheckRefreshWindows(display,windows);
7758      (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7759      (void) ConcatenateMagickString(modulate_factors,hue_percent,
7760        MaxTextExtent);
7761      (void) ModulateImage(*image,modulate_factors,exception);
7762      XSetCursorState(display,windows,MagickFalse);
7763      if (windows->image.orphan != MagickFalse)
7764        break;
7765      XConfigureImageColormap(display,resource_info,windows,*image);
7766      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7767      break;
7768    }
7769    case SaturationCommand:
7770    {
7771      static char
7772        saturation_percent[MaxTextExtent] = "110";
7773
7774      /*
7775        Query user for percent saturation change.
7776      */
7777      (void) XDialogWidget(display,windows,"Apply",
7778        "Enter percent change in color saturation (0-200):",saturation_percent);
7779      if (*saturation_percent == '\0')
7780        break;
7781      /*
7782        Vary color saturation.
7783      */
7784      XSetCursorState(display,windows,MagickTrue);
7785      XCheckRefreshWindows(display,windows);
7786      (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7787      (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7788        MaxTextExtent);
7789      (void) ModulateImage(*image,modulate_factors,exception);
7790      XSetCursorState(display,windows,MagickFalse);
7791      if (windows->image.orphan != MagickFalse)
7792        break;
7793      XConfigureImageColormap(display,resource_info,windows,*image);
7794      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7795      break;
7796    }
7797    case BrightnessCommand:
7798    {
7799      static char
7800        brightness_percent[MaxTextExtent] = "110";
7801
7802      /*
7803        Query user for percent brightness change.
7804      */
7805      (void) XDialogWidget(display,windows,"Apply",
7806        "Enter percent change in color brightness (0-200):",brightness_percent);
7807      if (*brightness_percent == '\0')
7808        break;
7809      /*
7810        Vary the color brightness.
7811      */
7812      XSetCursorState(display,windows,MagickTrue);
7813      XCheckRefreshWindows(display,windows);
7814      (void) CopyMagickString(modulate_factors,brightness_percent,
7815        MaxTextExtent);
7816      (void) ModulateImage(*image,modulate_factors,exception);
7817      XSetCursorState(display,windows,MagickFalse);
7818      if (windows->image.orphan != MagickFalse)
7819        break;
7820      XConfigureImageColormap(display,resource_info,windows,*image);
7821      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7822      break;
7823    }
7824    case GammaCommand:
7825    {
7826      static char
7827        factor[MaxTextExtent] = "1.6";
7828
7829      /*
7830        Query user for gamma value.
7831      */
7832      (void) XDialogWidget(display,windows,"Gamma",
7833        "Enter gamma value (e.g. 1.2):",factor);
7834      if (*factor == '\0')
7835        break;
7836      /*
7837        Gamma correct image.
7838      */
7839      XSetCursorState(display,windows,MagickTrue);
7840      XCheckRefreshWindows(display,windows);
7841      (void) GammaImage(*image,atof(factor),exception);
7842      XSetCursorState(display,windows,MagickFalse);
7843      if (windows->image.orphan != MagickFalse)
7844        break;
7845      XConfigureImageColormap(display,resource_info,windows,*image);
7846      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7847      break;
7848    }
7849    case SpiffCommand:
7850    {
7851      /*
7852        Sharpen the image contrast.
7853      */
7854      XSetCursorState(display,windows,MagickTrue);
7855      XCheckRefreshWindows(display,windows);
7856      (void) ContrastImage(*image,MagickTrue,exception);
7857      XSetCursorState(display,windows,MagickFalse);
7858      if (windows->image.orphan != MagickFalse)
7859        break;
7860      XConfigureImageColormap(display,resource_info,windows,*image);
7861      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7862      break;
7863    }
7864    case DullCommand:
7865    {
7866      /*
7867        Dull the image contrast.
7868      */
7869      XSetCursorState(display,windows,MagickTrue);
7870      XCheckRefreshWindows(display,windows);
7871      (void) ContrastImage(*image,MagickFalse,exception);
7872      XSetCursorState(display,windows,MagickFalse);
7873      if (windows->image.orphan != MagickFalse)
7874        break;
7875      XConfigureImageColormap(display,resource_info,windows,*image);
7876      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7877      break;
7878    }
7879    case ContrastStretchCommand:
7880    {
7881      double
7882        black_point,
7883        white_point;
7884
7885      static char
7886        levels[MaxTextExtent] = "1%";
7887
7888      /*
7889        Query user for gamma value.
7890      */
7891      (void) XDialogWidget(display,windows,"Contrast Stretch",
7892        "Enter black and white points:",levels);
7893      if (*levels == '\0')
7894        break;
7895      /*
7896        Contrast stretch image.
7897      */
7898      XSetCursorState(display,windows,MagickTrue);
7899      XCheckRefreshWindows(display,windows);
7900      flags=ParseGeometry(levels,&geometry_info);
7901      black_point=geometry_info.rho;
7902      white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7903      if ((flags & PercentValue) != 0)
7904        {
7905          black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7906          white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7907        }
7908      white_point=(MagickRealType) (*image)->columns*(*image)->rows-white_point;
7909      (void) ContrastStretchImage(*image,black_point,white_point,
7910        exception);
7911      XSetCursorState(display,windows,MagickFalse);
7912      if (windows->image.orphan != MagickFalse)
7913        break;
7914      XConfigureImageColormap(display,resource_info,windows,*image);
7915      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7916      break;
7917    }
7918    case SigmoidalContrastCommand:
7919    {
7920      GeometryInfo
7921        geometry_info;
7922
7923      MagickStatusType
7924        flags;
7925
7926      static char
7927        levels[MaxTextExtent] = "3x50%";
7928
7929      /*
7930        Query user for gamma value.
7931      */
7932      (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7933        "Enter contrast and midpoint:",levels);
7934      if (*levels == '\0')
7935        break;
7936      /*
7937        Contrast stretch image.
7938      */
7939      XSetCursorState(display,windows,MagickTrue);
7940      XCheckRefreshWindows(display,windows);
7941      flags=ParseGeometry(levels,&geometry_info);
7942      if ((flags & SigmaValue) == 0)
7943        geometry_info.sigma=1.0*QuantumRange/2.0;
7944      if ((flags & PercentValue) != 0)
7945        geometry_info.sigma=1.0*QuantumRange*geometry_info.sigma/100.0;
7946      (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
7947        geometry_info.sigma,exception);
7948      XSetCursorState(display,windows,MagickFalse);
7949      if (windows->image.orphan != MagickFalse)
7950        break;
7951      XConfigureImageColormap(display,resource_info,windows,*image);
7952      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7953      break;
7954    }
7955    case NormalizeCommand:
7956    {
7957      /*
7958        Perform histogram normalization on the image.
7959      */
7960      XSetCursorState(display,windows,MagickTrue);
7961      XCheckRefreshWindows(display,windows);
7962      (void) NormalizeImage(*image,exception);
7963      XSetCursorState(display,windows,MagickFalse);
7964      if (windows->image.orphan != MagickFalse)
7965        break;
7966      XConfigureImageColormap(display,resource_info,windows,*image);
7967      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7968      break;
7969    }
7970    case EqualizeCommand:
7971    {
7972      /*
7973        Perform histogram equalization on the image.
7974      */
7975      XSetCursorState(display,windows,MagickTrue);
7976      XCheckRefreshWindows(display,windows);
7977      (void) EqualizeImage(*image,exception);
7978      XSetCursorState(display,windows,MagickFalse);
7979      if (windows->image.orphan != MagickFalse)
7980        break;
7981      XConfigureImageColormap(display,resource_info,windows,*image);
7982      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7983      break;
7984    }
7985    case NegateCommand:
7986    {
7987      /*
7988        Negate colors in image.
7989      */
7990      XSetCursorState(display,windows,MagickTrue);
7991      XCheckRefreshWindows(display,windows);
7992      (void) NegateImage(*image,MagickFalse,exception);
7993      XSetCursorState(display,windows,MagickFalse);
7994      if (windows->image.orphan != MagickFalse)
7995        break;
7996      XConfigureImageColormap(display,resource_info,windows,*image);
7997      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7998      break;
7999    }
8000    case GrayscaleCommand:
8001    {
8002      /*
8003        Convert image to grayscale.
8004      */
8005      XSetCursorState(display,windows,MagickTrue);
8006      XCheckRefreshWindows(display,windows);
8007      (void) SetImageType(*image,(*image)->matte == MagickFalse ?
8008        GrayscaleType : GrayscaleMatteType,exception);
8009      XSetCursorState(display,windows,MagickFalse);
8010      if (windows->image.orphan != MagickFalse)
8011        break;
8012      XConfigureImageColormap(display,resource_info,windows,*image);
8013      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8014      break;
8015    }
8016    case MapCommand:
8017    {
8018      Image
8019        *affinity_image;
8020
8021      static char
8022        filename[MaxTextExtent] = "\0";
8023
8024      /*
8025        Request image file name from user.
8026      */
8027      XFileBrowserWidget(display,windows,"Map",filename);
8028      if (*filename == '\0')
8029        break;
8030      /*
8031        Map image.
8032      */
8033      XSetCursorState(display,windows,MagickTrue);
8034      XCheckRefreshWindows(display,windows);
8035      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
8036      affinity_image=ReadImage(image_info,exception);
8037      if (affinity_image != (Image *) NULL)
8038        {
8039          (void) RemapImage(&quantize_info,*image,affinity_image,exception);
8040          affinity_image=DestroyImage(affinity_image);
8041        }
8042      CatchException(exception);
8043      XSetCursorState(display,windows,MagickFalse);
8044      if (windows->image.orphan != MagickFalse)
8045        break;
8046      XConfigureImageColormap(display,resource_info,windows,*image);
8047      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8048      break;
8049    }
8050    case QuantizeCommand:
8051    {
8052      int
8053        status;
8054
8055      static char
8056        colors[MaxTextExtent] = "256";
8057
8058      /*
8059        Query user for maximum number of colors.
8060      */
8061      status=XDialogWidget(display,windows,"Quantize",
8062        "Maximum number of colors:",colors);
8063      if (*colors == '\0')
8064        break;
8065      /*
8066        Color reduce the image.
8067      */
8068      XSetCursorState(display,windows,MagickTrue);
8069      XCheckRefreshWindows(display,windows);
8070      quantize_info.number_colors=StringToUnsignedLong(colors);
8071      quantize_info.dither=status != 0 ? MagickTrue : MagickFalse;
8072      (void) QuantizeImage(&quantize_info,*image,exception);
8073      XSetCursorState(display,windows,MagickFalse);
8074      if (windows->image.orphan != MagickFalse)
8075        break;
8076      XConfigureImageColormap(display,resource_info,windows,*image);
8077      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8078      break;
8079    }
8080    case DespeckleCommand:
8081    {
8082      Image
8083        *despeckle_image;
8084
8085      /*
8086        Despeckle image.
8087      */
8088      XSetCursorState(display,windows,MagickTrue);
8089      XCheckRefreshWindows(display,windows);
8090      despeckle_image=DespeckleImage(*image,exception);
8091      if (despeckle_image != (Image *) NULL)
8092        {
8093          *image=DestroyImage(*image);
8094          *image=despeckle_image;
8095        }
8096      CatchException(exception);
8097      XSetCursorState(display,windows,MagickFalse);
8098      if (windows->image.orphan != MagickFalse)
8099        break;
8100      XConfigureImageColormap(display,resource_info,windows,*image);
8101      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8102      break;
8103    }
8104    case EmbossCommand:
8105    {
8106      Image
8107        *emboss_image;
8108
8109      static char
8110        radius[MaxTextExtent] = "0.0x1.0";
8111
8112      /*
8113        Query user for emboss radius.
8114      */
8115      (void) XDialogWidget(display,windows,"Emboss",
8116        "Enter the emboss radius and standard deviation:",radius);
8117      if (*radius == '\0')
8118        break;
8119      /*
8120        Reduce noise in the image.
8121      */
8122      XSetCursorState(display,windows,MagickTrue);
8123      XCheckRefreshWindows(display,windows);
8124      flags=ParseGeometry(radius,&geometry_info);
8125      if ((flags & SigmaValue) == 0)
8126        geometry_info.sigma=1.0;
8127      emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8128        exception);
8129      if (emboss_image != (Image *) NULL)
8130        {
8131          *image=DestroyImage(*image);
8132          *image=emboss_image;
8133        }
8134      CatchException(exception);
8135      XSetCursorState(display,windows,MagickFalse);
8136      if (windows->image.orphan != MagickFalse)
8137        break;
8138      XConfigureImageColormap(display,resource_info,windows,*image);
8139      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8140      break;
8141    }
8142    case ReduceNoiseCommand:
8143    {
8144      Image
8145        *noise_image;
8146
8147      static char
8148        radius[MaxTextExtent] = "0";
8149
8150      /*
8151        Query user for noise radius.
8152      */
8153      (void) XDialogWidget(display,windows,"Reduce Noise",
8154        "Enter the noise radius:",radius);
8155      if (*radius == '\0')
8156        break;
8157      /*
8158        Reduce noise in the image.
8159      */
8160      XSetCursorState(display,windows,MagickTrue);
8161      XCheckRefreshWindows(display,windows);
8162      flags=ParseGeometry(radius,&geometry_info);
8163      noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8164        geometry_info.rho,(size_t) geometry_info.rho,exception);
8165      if (noise_image != (Image *) NULL)
8166        {
8167          *image=DestroyImage(*image);
8168          *image=noise_image;
8169        }
8170      CatchException(exception);
8171      XSetCursorState(display,windows,MagickFalse);
8172      if (windows->image.orphan != MagickFalse)
8173        break;
8174      XConfigureImageColormap(display,resource_info,windows,*image);
8175      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8176      break;
8177    }
8178    case AddNoiseCommand:
8179    {
8180      char
8181        **noises;
8182
8183      Image
8184        *noise_image;
8185
8186      static char
8187        noise_type[MaxTextExtent] = "Gaussian";
8188
8189      /*
8190        Add noise to the image.
8191      */
8192      noises=GetCommandOptions(MagickNoiseOptions);
8193      if (noises == (char **) NULL)
8194        break;
8195      XListBrowserWidget(display,windows,&windows->widget,
8196        (const char **) noises,"Add Noise",
8197        "Select a type of noise to add to your image:",noise_type);
8198      noises=DestroyStringList(noises);
8199      if (*noise_type == '\0')
8200        break;
8201      XSetCursorState(display,windows,MagickTrue);
8202      XCheckRefreshWindows(display,windows);
8203      noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8204        MagickNoiseOptions,MagickFalse,noise_type),1.0,exception);
8205      if (noise_image != (Image *) NULL)
8206        {
8207          *image=DestroyImage(*image);
8208          *image=noise_image;
8209        }
8210      CatchException(exception);
8211      XSetCursorState(display,windows,MagickFalse);
8212      if (windows->image.orphan != MagickFalse)
8213        break;
8214      XConfigureImageColormap(display,resource_info,windows,*image);
8215      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8216      break;
8217    }
8218    case SharpenCommand:
8219    {
8220      Image
8221        *sharp_image;
8222
8223      static char
8224        radius[MaxTextExtent] = "0.0x1.0";
8225
8226      /*
8227        Query user for sharpen radius.
8228      */
8229      (void) XDialogWidget(display,windows,"Sharpen",
8230        "Enter the sharpen radius and standard deviation:",radius);
8231      if (*radius == '\0')
8232        break;
8233      /*
8234        Sharpen image scanlines.
8235      */
8236      XSetCursorState(display,windows,MagickTrue);
8237      XCheckRefreshWindows(display,windows);
8238      flags=ParseGeometry(radius,&geometry_info);
8239      sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8240        geometry_info.xi,exception);
8241      if (sharp_image != (Image *) NULL)
8242        {
8243          *image=DestroyImage(*image);
8244          *image=sharp_image;
8245        }
8246      CatchException(exception);
8247      XSetCursorState(display,windows,MagickFalse);
8248      if (windows->image.orphan != MagickFalse)
8249        break;
8250      XConfigureImageColormap(display,resource_info,windows,*image);
8251      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8252      break;
8253    }
8254    case BlurCommand:
8255    {
8256      Image
8257        *blur_image;
8258
8259      static char
8260        radius[MaxTextExtent] = "0.0x1.0";
8261
8262      /*
8263        Query user for blur radius.
8264      */
8265      (void) XDialogWidget(display,windows,"Blur",
8266        "Enter the blur radius and standard deviation:",radius);
8267      if (*radius == '\0')
8268        break;
8269      /*
8270        Blur an image.
8271      */
8272      XSetCursorState(display,windows,MagickTrue);
8273      XCheckRefreshWindows(display,windows);
8274      flags=ParseGeometry(radius,&geometry_info);
8275      blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8276        geometry_info.xi,exception);
8277      if (blur_image != (Image *) NULL)
8278        {
8279          *image=DestroyImage(*image);
8280          *image=blur_image;
8281        }
8282      CatchException(exception);
8283      XSetCursorState(display,windows,MagickFalse);
8284      if (windows->image.orphan != MagickFalse)
8285        break;
8286      XConfigureImageColormap(display,resource_info,windows,*image);
8287      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8288      break;
8289    }
8290    case ThresholdCommand:
8291    {
8292      double
8293        threshold;
8294
8295      static char
8296        factor[MaxTextExtent] = "128";
8297
8298      /*
8299        Query user for threshold value.
8300      */
8301      (void) XDialogWidget(display,windows,"Threshold",
8302        "Enter threshold value:",factor);
8303      if (*factor == '\0')
8304        break;
8305      /*
8306        Gamma correct image.
8307      */
8308      XSetCursorState(display,windows,MagickTrue);
8309      XCheckRefreshWindows(display,windows);
8310      threshold=SiPrefixToDouble(factor,QuantumRange);
8311      (void) BilevelImage(*image,threshold,exception);
8312      XSetCursorState(display,windows,MagickFalse);
8313      if (windows->image.orphan != MagickFalse)
8314        break;
8315      XConfigureImageColormap(display,resource_info,windows,*image);
8316      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8317      break;
8318    }
8319    case EdgeDetectCommand:
8320    {
8321      Image
8322        *edge_image;
8323
8324      static char
8325        radius[MaxTextExtent] = "0";
8326
8327      /*
8328        Query user for edge factor.
8329      */
8330      (void) XDialogWidget(display,windows,"Detect Edges",
8331        "Enter the edge detect radius:",radius);
8332      if (*radius == '\0')
8333        break;
8334      /*
8335        Detect edge in image.
8336      */
8337      XSetCursorState(display,windows,MagickTrue);
8338      XCheckRefreshWindows(display,windows);
8339      flags=ParseGeometry(radius,&geometry_info);
8340      edge_image=EdgeImage(*image,geometry_info.rho,geometry_info.sigma,
8341        exception);
8342      if (edge_image != (Image *) NULL)
8343        {
8344          *image=DestroyImage(*image);
8345          *image=edge_image;
8346        }
8347      CatchException(exception);
8348      XSetCursorState(display,windows,MagickFalse);
8349      if (windows->image.orphan != MagickFalse)
8350        break;
8351      XConfigureImageColormap(display,resource_info,windows,*image);
8352      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8353      break;
8354    }
8355    case SpreadCommand:
8356    {
8357      Image
8358        *spread_image;
8359
8360      static char
8361        amount[MaxTextExtent] = "2";
8362
8363      /*
8364        Query user for spread amount.
8365      */
8366      (void) XDialogWidget(display,windows,"Spread",
8367        "Enter the displacement amount:",amount);
8368      if (*amount == '\0')
8369        break;
8370      /*
8371        Displace image pixels by a random amount.
8372      */
8373      XSetCursorState(display,windows,MagickTrue);
8374      XCheckRefreshWindows(display,windows);
8375      flags=ParseGeometry(amount,&geometry_info);
8376      spread_image=EdgeImage(*image,geometry_info.rho,geometry_info.sigma,
8377        exception);
8378      if (spread_image != (Image *) NULL)
8379        {
8380          *image=DestroyImage(*image);
8381          *image=spread_image;
8382        }
8383      CatchException(exception);
8384      XSetCursorState(display,windows,MagickFalse);
8385      if (windows->image.orphan != MagickFalse)
8386        break;
8387      XConfigureImageColormap(display,resource_info,windows,*image);
8388      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8389      break;
8390    }
8391    case ShadeCommand:
8392    {
8393      Image
8394        *shade_image;
8395
8396      int
8397        status;
8398
8399      static char
8400        geometry[MaxTextExtent] = "30x30";
8401
8402      /*
8403        Query user for the shade geometry.
8404      */
8405      status=XDialogWidget(display,windows,"Shade",
8406        "Enter the azimuth and elevation of the light source:",geometry);
8407      if (*geometry == '\0')
8408        break;
8409      /*
8410        Shade image pixels.
8411      */
8412      XSetCursorState(display,windows,MagickTrue);
8413      XCheckRefreshWindows(display,windows);
8414      flags=ParseGeometry(geometry,&geometry_info);
8415      if ((flags & SigmaValue) == 0)
8416        geometry_info.sigma=1.0;
8417      shade_image=ShadeImage(*image,status != 0 ? MagickFalse : MagickTrue,
8418        geometry_info.rho,geometry_info.sigma,exception);
8419      if (shade_image != (Image *) NULL)
8420        {
8421          *image=DestroyImage(*image);
8422          *image=shade_image;
8423        }
8424      CatchException(exception);
8425      XSetCursorState(display,windows,MagickFalse);
8426      if (windows->image.orphan != MagickFalse)
8427        break;
8428      XConfigureImageColormap(display,resource_info,windows,*image);
8429      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8430      break;
8431    }
8432    case RaiseCommand:
8433    {
8434      static char
8435        bevel_width[MaxTextExtent] = "10";
8436
8437      /*
8438        Query user for bevel width.
8439      */
8440      (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8441      if (*bevel_width == '\0')
8442        break;
8443      /*
8444        Raise an image.
8445      */
8446      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8447        exception);
8448      XSetCursorState(display,windows,MagickTrue);
8449      XCheckRefreshWindows(display,windows);
8450      (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8451        exception);
8452      (void) RaiseImage(*image,&page_geometry,MagickTrue,exception);
8453      XSetCursorState(display,windows,MagickFalse);
8454      if (windows->image.orphan != MagickFalse)
8455        break;
8456      XConfigureImageColormap(display,resource_info,windows,*image);
8457      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8458      break;
8459    }
8460    case SegmentCommand:
8461    {
8462      static char
8463        threshold[MaxTextExtent] = "1.0x1.5";
8464
8465      /*
8466        Query user for smoothing threshold.
8467      */
8468      (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8469        threshold);
8470      if (*threshold == '\0')
8471        break;
8472      /*
8473        Segment an image.
8474      */
8475      XSetCursorState(display,windows,MagickTrue);
8476      XCheckRefreshWindows(display,windows);
8477      flags=ParseGeometry(threshold,&geometry_info);
8478      if ((flags & SigmaValue) == 0)
8479        geometry_info.sigma=1.0;
8480      (void) SegmentImage(*image,RGBColorspace,MagickFalse,geometry_info.rho,
8481        geometry_info.sigma,exception);
8482      XSetCursorState(display,windows,MagickFalse);
8483      if (windows->image.orphan != MagickFalse)
8484        break;
8485      XConfigureImageColormap(display,resource_info,windows,*image);
8486      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8487      break;
8488    }
8489    case SepiaToneCommand:
8490    {
8491      double
8492        threshold;
8493
8494      Image
8495        *sepia_image;
8496
8497      static char
8498        factor[MaxTextExtent] = "80%";
8499
8500      /*
8501        Query user for sepia-tone factor.
8502      */
8503      (void) XDialogWidget(display,windows,"Sepia Tone",
8504        "Enter the sepia tone factor (0 - 99.9%):",factor);
8505      if (*factor == '\0')
8506        break;
8507      /*
8508        Sepia tone image pixels.
8509      */
8510      XSetCursorState(display,windows,MagickTrue);
8511      XCheckRefreshWindows(display,windows);
8512      threshold=SiPrefixToDouble(factor,QuantumRange);
8513      sepia_image=SepiaToneImage(*image,threshold,exception);
8514      if (sepia_image != (Image *) NULL)
8515        {
8516          *image=DestroyImage(*image);
8517          *image=sepia_image;
8518        }
8519      CatchException(exception);
8520      XSetCursorState(display,windows,MagickFalse);
8521      if (windows->image.orphan != MagickFalse)
8522        break;
8523      XConfigureImageColormap(display,resource_info,windows,*image);
8524      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8525      break;
8526    }
8527    case SolarizeCommand:
8528    {
8529      double
8530        threshold;
8531
8532      static char
8533        factor[MaxTextExtent] = "60%";
8534
8535      /*
8536        Query user for solarize factor.
8537      */
8538      (void) XDialogWidget(display,windows,"Solarize",
8539        "Enter the solarize factor (0 - 99.9%):",factor);
8540      if (*factor == '\0')
8541        break;
8542      /*
8543        Solarize image pixels.
8544      */
8545      XSetCursorState(display,windows,MagickTrue);
8546      XCheckRefreshWindows(display,windows);
8547      threshold=SiPrefixToDouble(factor,QuantumRange);
8548      (void) SolarizeImage(*image,threshold,exception);
8549      XSetCursorState(display,windows,MagickFalse);
8550      if (windows->image.orphan != MagickFalse)
8551        break;
8552      XConfigureImageColormap(display,resource_info,windows,*image);
8553      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8554      break;
8555    }
8556    case SwirlCommand:
8557    {
8558      Image
8559        *swirl_image;
8560
8561      static char
8562        degrees[MaxTextExtent] = "60";
8563
8564      /*
8565        Query user for swirl angle.
8566      */
8567      (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8568        degrees);
8569      if (*degrees == '\0')
8570        break;
8571      /*
8572        Swirl image pixels about the center.
8573      */
8574      XSetCursorState(display,windows,MagickTrue);
8575      XCheckRefreshWindows(display,windows);
8576      flags=ParseGeometry(degrees,&geometry_info);
8577      swirl_image=SwirlImage(*image,geometry_info.rho,(*image)->interpolate,
8578        exception);
8579      if (swirl_image != (Image *) NULL)
8580        {
8581          *image=DestroyImage(*image);
8582          *image=swirl_image;
8583        }
8584      CatchException(exception);
8585      XSetCursorState(display,windows,MagickFalse);
8586      if (windows->image.orphan != MagickFalse)
8587        break;
8588      XConfigureImageColormap(display,resource_info,windows,*image);
8589      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8590      break;
8591    }
8592    case ImplodeCommand:
8593    {
8594      Image
8595        *implode_image;
8596
8597      static char
8598        factor[MaxTextExtent] = "0.3";
8599
8600      /*
8601        Query user for implode factor.
8602      */
8603      (void) XDialogWidget(display,windows,"Implode",
8604        "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8605      if (*factor == '\0')
8606        break;
8607      /*
8608        Implode image pixels about the center.
8609      */
8610      XSetCursorState(display,windows,MagickTrue);
8611      XCheckRefreshWindows(display,windows);
8612      flags=ParseGeometry(factor,&geometry_info);
8613      implode_image=ImplodeImage(*image,geometry_info.rho,(*image)->interpolate,
8614        exception);
8615      if (implode_image != (Image *) NULL)
8616        {
8617          *image=DestroyImage(*image);
8618          *image=implode_image;
8619        }
8620      CatchException(exception);
8621      XSetCursorState(display,windows,MagickFalse);
8622      if (windows->image.orphan != MagickFalse)
8623        break;
8624      XConfigureImageColormap(display,resource_info,windows,*image);
8625      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8626      break;
8627    }
8628    case VignetteCommand:
8629    {
8630      Image
8631        *vignette_image;
8632
8633      static char
8634        geometry[MaxTextExtent] = "0x20";
8635
8636      /*
8637        Query user for the vignette geometry.
8638      */
8639      (void) XDialogWidget(display,windows,"Vignette",
8640        "Enter the radius, sigma, and x and y offsets:",geometry);
8641      if (*geometry == '\0')
8642        break;
8643      /*
8644        Soften the edges of the image in vignette style
8645      */
8646      XSetCursorState(display,windows,MagickTrue);
8647      XCheckRefreshWindows(display,windows);
8648      flags=ParseGeometry(geometry,&geometry_info);
8649      if ((flags & SigmaValue) == 0)
8650        geometry_info.sigma=1.0;
8651      if ((flags & XiValue) == 0)
8652        geometry_info.xi=0.1*(*image)->columns;
8653      if ((flags & PsiValue) == 0)
8654        geometry_info.psi=0.1*(*image)->rows;
8655      vignette_image=VignetteImage(*image,geometry_info.rho,geometry_info.sigma,
8656        (ssize_t) ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-
8657        0.5),exception);
8658      if (vignette_image != (Image *) NULL)
8659        {
8660          *image=DestroyImage(*image);
8661          *image=vignette_image;
8662        }
8663      CatchException(exception);
8664      XSetCursorState(display,windows,MagickFalse);
8665      if (windows->image.orphan != MagickFalse)
8666        break;
8667      XConfigureImageColormap(display,resource_info,windows,*image);
8668      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8669      break;
8670    }
8671    case WaveCommand:
8672    {
8673      Image
8674        *wave_image;
8675
8676      static char
8677        geometry[MaxTextExtent] = "25x150";
8678
8679      /*
8680        Query user for the wave geometry.
8681      */
8682      (void) XDialogWidget(display,windows,"Wave",
8683        "Enter the amplitude and length of the wave:",geometry);
8684      if (*geometry == '\0')
8685        break;
8686      /*
8687        Alter an image along a sine wave.
8688      */
8689      XSetCursorState(display,windows,MagickTrue);
8690      XCheckRefreshWindows(display,windows);
8691      flags=ParseGeometry(geometry,&geometry_info);
8692      if ((flags & SigmaValue) == 0)
8693        geometry_info.sigma=1.0;
8694      wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8695        (*image)->interpolate,exception);
8696      if (wave_image != (Image *) NULL)
8697        {
8698          *image=DestroyImage(*image);
8699          *image=wave_image;
8700        }
8701      CatchException(exception);
8702      XSetCursorState(display,windows,MagickFalse);
8703      if (windows->image.orphan != MagickFalse)
8704        break;
8705      XConfigureImageColormap(display,resource_info,windows,*image);
8706      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8707      break;
8708    }
8709    case OilPaintCommand:
8710    {
8711      Image
8712        *paint_image;
8713
8714      static char
8715        radius[MaxTextExtent] = "0";
8716
8717      /*
8718        Query user for circular neighborhood radius.
8719      */
8720      (void) XDialogWidget(display,windows,"Oil Paint",
8721        "Enter the mask radius:",radius);
8722      if (*radius == '\0')
8723        break;
8724      /*
8725        OilPaint image scanlines.
8726      */
8727      XSetCursorState(display,windows,MagickTrue);
8728      XCheckRefreshWindows(display,windows);
8729      flags=ParseGeometry(radius,&geometry_info);
8730      paint_image=OilPaintImage(*image,geometry_info.rho,geometry_info.sigma,
8731        exception);
8732      if (paint_image != (Image *) NULL)
8733        {
8734          *image=DestroyImage(*image);
8735          *image=paint_image;
8736        }
8737      CatchException(exception);
8738      XSetCursorState(display,windows,MagickFalse);
8739      if (windows->image.orphan != MagickFalse)
8740        break;
8741      XConfigureImageColormap(display,resource_info,windows,*image);
8742      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8743      break;
8744    }
8745    case CharcoalDrawCommand:
8746    {
8747      Image
8748        *charcoal_image;
8749
8750      static char
8751        radius[MaxTextExtent] = "0x1";
8752
8753      /*
8754        Query user for charcoal radius.
8755      */
8756      (void) XDialogWidget(display,windows,"Charcoal Draw",
8757        "Enter the charcoal radius and sigma:",radius);
8758      if (*radius == '\0')
8759        break;
8760      /*
8761        Charcoal the image.
8762      */
8763      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8764        exception);
8765      XSetCursorState(display,windows,MagickTrue);
8766      XCheckRefreshWindows(display,windows);
8767      flags=ParseGeometry(radius,&geometry_info);
8768      if ((flags & SigmaValue) == 0)
8769        geometry_info.sigma=geometry_info.rho;
8770      charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8771        geometry_info.xi,exception);
8772      if (charcoal_image != (Image *) NULL)
8773        {
8774          *image=DestroyImage(*image);
8775          *image=charcoal_image;
8776        }
8777      CatchException(exception);
8778      XSetCursorState(display,windows,MagickFalse);
8779      if (windows->image.orphan != MagickFalse)
8780        break;
8781      XConfigureImageColormap(display,resource_info,windows,*image);
8782      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8783      break;
8784    }
8785    case AnnotateCommand:
8786    {
8787      /*
8788        Annotate the image with text.
8789      */
8790      status=XAnnotateEditImage(display,resource_info,windows,*image,exception);
8791      if (status == MagickFalse)
8792        {
8793          XNoticeWidget(display,windows,"Unable to annotate X image",
8794            (*image)->filename);
8795          break;
8796        }
8797      break;
8798    }
8799    case DrawCommand:
8800    {
8801      /*
8802        Draw image.
8803      */
8804      status=XDrawEditImage(display,resource_info,windows,image,exception);
8805      if (status == MagickFalse)
8806        {
8807          XNoticeWidget(display,windows,"Unable to draw on the X image",
8808            (*image)->filename);
8809          break;
8810        }
8811      break;
8812    }
8813    case ColorCommand:
8814    {
8815      /*
8816        Color edit.
8817      */
8818      status=XColorEditImage(display,resource_info,windows,image,exception);
8819      if (status == MagickFalse)
8820        {
8821          XNoticeWidget(display,windows,"Unable to pixel edit X image",
8822            (*image)->filename);
8823          break;
8824        }
8825      break;
8826    }
8827    case MatteCommand:
8828    {
8829      /*
8830        Matte edit.
8831      */
8832      status=XMatteEditImage(display,resource_info,windows,image,exception);
8833      if (status == MagickFalse)
8834        {
8835          XNoticeWidget(display,windows,"Unable to matte edit X image",
8836            (*image)->filename);
8837          break;
8838        }
8839      break;
8840    }
8841    case CompositeCommand:
8842    {
8843      /*
8844        Composite image.
8845      */
8846      status=XCompositeImage(display,resource_info,windows,*image,
8847        exception);
8848      if (status == MagickFalse)
8849        {
8850          XNoticeWidget(display,windows,"Unable to composite X image",
8851            (*image)->filename);
8852          break;
8853        }
8854      break;
8855    }
8856    case AddBorderCommand:
8857    {
8858      Image
8859        *border_image;
8860
8861      static char
8862        geometry[MaxTextExtent] = "6x6";
8863
8864      /*
8865        Query user for border color and geometry.
8866      */
8867      XColorBrowserWidget(display,windows,"Select",color);
8868      if (*color == '\0')
8869        break;
8870      (void) XDialogWidget(display,windows,"Add Border",
8871        "Enter border geometry:",geometry);
8872      if (*geometry == '\0')
8873        break;
8874      /*
8875        Add a border to the image.
8876      */
8877      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8878        exception);
8879      XSetCursorState(display,windows,MagickTrue);
8880      XCheckRefreshWindows(display,windows);
8881      (void) QueryColorCompliance(color,AllCompliance,&(*image)->border_color,
8882        exception);
8883      (void) ParsePageGeometry(*image,geometry,&page_geometry,
8884        exception);
8885      border_image=BorderImage(*image,&page_geometry,(*image)->compose,
8886        exception);
8887      if (border_image != (Image *) NULL)
8888        {
8889          *image=DestroyImage(*image);
8890          *image=border_image;
8891        }
8892      CatchException(exception);
8893      XSetCursorState(display,windows,MagickFalse);
8894      if (windows->image.orphan != MagickFalse)
8895        break;
8896      windows->image.window_changes.width=(int) (*image)->columns;
8897      windows->image.window_changes.height=(int) (*image)->rows;
8898      XConfigureImageColormap(display,resource_info,windows,*image);
8899      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8900      break;
8901    }
8902    case AddFrameCommand:
8903    {
8904      FrameInfo
8905        frame_info;
8906
8907      Image
8908        *frame_image;
8909
8910      static char
8911        geometry[MaxTextExtent] = "6x6";
8912
8913      /*
8914        Query user for frame color and geometry.
8915      */
8916      XColorBrowserWidget(display,windows,"Select",color);
8917      if (*color == '\0')
8918        break;
8919      (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8920        geometry);
8921      if (*geometry == '\0')
8922        break;
8923      /*
8924        Surround image with an ornamental border.
8925      */
8926      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8927        exception);
8928      XSetCursorState(display,windows,MagickTrue);
8929      XCheckRefreshWindows(display,windows);
8930      (void) QueryColorCompliance(color,AllCompliance,&(*image)->matte_color,
8931        exception);
8932      (void) ParsePageGeometry(*image,geometry,&page_geometry,
8933        exception);
8934      frame_info.width=page_geometry.width;
8935      frame_info.height=page_geometry.height;
8936      frame_info.outer_bevel=page_geometry.x;
8937      frame_info.inner_bevel=page_geometry.y;
8938      frame_info.x=(ssize_t) frame_info.width;
8939      frame_info.y=(ssize_t) frame_info.height;
8940      frame_info.width=(*image)->columns+2*frame_info.width;
8941      frame_info.height=(*image)->rows+2*frame_info.height;
8942      frame_image=FrameImage(*image,&frame_info,(*image)->compose,exception);
8943      if (frame_image != (Image *) NULL)
8944        {
8945          *image=DestroyImage(*image);
8946          *image=frame_image;
8947        }
8948      CatchException(exception);
8949      XSetCursorState(display,windows,MagickFalse);
8950      if (windows->image.orphan != MagickFalse)
8951        break;
8952      windows->image.window_changes.width=(int) (*image)->columns;
8953      windows->image.window_changes.height=(int) (*image)->rows;
8954      XConfigureImageColormap(display,resource_info,windows,*image);
8955      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8956      break;
8957    }
8958    case CommentCommand:
8959    {
8960      const char
8961        *value;
8962
8963      FILE
8964        *file;
8965
8966      int
8967        unique_file;
8968
8969      /*
8970        Edit image comment.
8971      */
8972      unique_file=AcquireUniqueFileResource(image_info->filename);
8973      if (unique_file == -1)
8974        XNoticeWidget(display,windows,"Unable to edit image comment",
8975          image_info->filename);
8976      value=GetImageProperty(*image,"comment",exception);
8977      if (value == (char *) NULL)
8978        unique_file=close(unique_file)-1;
8979      else
8980        {
8981          register const char
8982            *p;
8983
8984          file=fdopen(unique_file,"w");
8985          if (file == (FILE *) NULL)
8986            {
8987              XNoticeWidget(display,windows,"Unable to edit image comment",
8988                image_info->filename);
8989              break;
8990            }
8991          for (p=value; *p != '\0'; p++)
8992            (void) fputc((int) *p,file);
8993          (void) fputc('\n',file);
8994          (void) fclose(file);
8995        }
8996      XSetCursorState(display,windows,MagickTrue);
8997      XCheckRefreshWindows(display,windows);
8998      status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
8999        exception);
9000      if (status == MagickFalse)
9001        XNoticeWidget(display,windows,"Unable to edit image comment",
9002          (char *) NULL);
9003      else
9004        {
9005          char
9006            *comment;
9007
9008          comment=FileToString(image_info->filename,~0UL,exception);
9009          if (comment != (char *) NULL)
9010            {
9011              (void) SetImageProperty(*image,"comment",comment,exception);
9012              (*image)->taint=MagickTrue;
9013            }
9014        }
9015      (void) RelinquishUniqueFileResource(image_info->filename);
9016      XSetCursorState(display,windows,MagickFalse);
9017      break;
9018    }
9019    case LaunchCommand:
9020    {
9021      /*
9022        Launch program.
9023      */
9024      XSetCursorState(display,windows,MagickTrue);
9025      XCheckRefreshWindows(display,windows);
9026      (void) AcquireUniqueFilename(filename);
9027      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"launch:%s",
9028        filename);
9029      status=WriteImage(image_info,*image,exception);
9030      if (status == MagickFalse)
9031        XNoticeWidget(display,windows,"Unable to launch image editor",
9032          (char *) NULL);
9033      else
9034        {
9035          nexus=ReadImage(resource_info->image_info,exception);
9036          CatchException(exception);
9037          XClientMessage(display,windows->image.id,windows->im_protocols,
9038            windows->im_next_image,CurrentTime);
9039        }
9040      (void) RelinquishUniqueFileResource(filename);
9041      XSetCursorState(display,windows,MagickFalse);
9042      break;
9043    }
9044    case RegionofInterestCommand:
9045    {
9046      /*
9047        Apply an image processing technique to a region of interest.
9048      */
9049      (void) XROIImage(display,resource_info,windows,image,exception);
9050      break;
9051    }
9052    case InfoCommand:
9053      break;
9054    case ZoomCommand:
9055    {
9056      /*
9057        Zoom image.
9058      */
9059      if (windows->magnify.mapped != MagickFalse)
9060        (void) XRaiseWindow(display,windows->magnify.id);
9061      else
9062        {
9063          /*
9064            Make magnify image.
9065          */
9066          XSetCursorState(display,windows,MagickTrue);
9067          (void) XMapRaised(display,windows->magnify.id);
9068          XSetCursorState(display,windows,MagickFalse);
9069        }
9070      break;
9071    }
9072    case ShowPreviewCommand:
9073    {
9074      char
9075        **previews;
9076
9077      Image
9078        *preview_image;
9079
9080      static char
9081        preview_type[MaxTextExtent] = "Gamma";
9082
9083      /*
9084        Select preview type from menu.
9085      */
9086      previews=GetCommandOptions(MagickPreviewOptions);
9087      if (previews == (char **) NULL)
9088        break;
9089      XListBrowserWidget(display,windows,&windows->widget,
9090        (const char **) previews,"Preview",
9091        "Select an enhancement, effect, or F/X:",preview_type);
9092      previews=DestroyStringList(previews);
9093      if (*preview_type == '\0')
9094        break;
9095      /*
9096        Show image preview.
9097      */
9098      XSetCursorState(display,windows,MagickTrue);
9099      XCheckRefreshWindows(display,windows);
9100      image_info->preview_type=(PreviewType)
9101        ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
9102      image_info->group=(ssize_t) windows->image.id;
9103      (void) DeleteImageProperty(*image,"label");
9104      (void) SetImageProperty(*image,"label","Preview",exception);
9105      (void) AcquireUniqueFilename(filename);
9106      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"preview:%s",
9107        filename);
9108      status=WriteImage(image_info,*image,exception);
9109      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9110      preview_image=ReadImage(image_info,exception);
9111      (void) RelinquishUniqueFileResource(filename);
9112      if (preview_image == (Image *) NULL)
9113        break;
9114      (void) FormatLocaleString(preview_image->filename,MaxTextExtent,"show:%s",
9115        filename);
9116      status=WriteImage(image_info,preview_image,exception);
9117      preview_image=DestroyImage(preview_image);
9118      if (status == MagickFalse)
9119        XNoticeWidget(display,windows,"Unable to show image preview",
9120          (*image)->filename);
9121      XDelay(display,1500);
9122      XSetCursorState(display,windows,MagickFalse);
9123      break;
9124    }
9125    case ShowHistogramCommand:
9126    {
9127      Image
9128        *histogram_image;
9129
9130      /*
9131        Show image histogram.
9132      */
9133      XSetCursorState(display,windows,MagickTrue);
9134      XCheckRefreshWindows(display,windows);
9135      image_info->group=(ssize_t) windows->image.id;
9136      (void) DeleteImageProperty(*image,"label");
9137      (void) SetImageProperty(*image,"label","Histogram",exception);
9138      (void) AcquireUniqueFilename(filename);
9139      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"histogram:%s",
9140        filename);
9141      status=WriteImage(image_info,*image,exception);
9142      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9143      histogram_image=ReadImage(image_info,exception);
9144      (void) RelinquishUniqueFileResource(filename);
9145      if (histogram_image == (Image *) NULL)
9146        break;
9147      (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
9148        "show:%s",filename);
9149      status=WriteImage(image_info,histogram_image,exception);
9150      histogram_image=DestroyImage(histogram_image);
9151      if (status == MagickFalse)
9152        XNoticeWidget(display,windows,"Unable to show histogram",
9153          (*image)->filename);
9154      XDelay(display,1500);
9155      XSetCursorState(display,windows,MagickFalse);
9156      break;
9157    }
9158    case ShowMatteCommand:
9159    {
9160      Image
9161        *matte_image;
9162
9163      if ((*image)->matte == MagickFalse)
9164        {
9165          XNoticeWidget(display,windows,
9166            "Image does not have any matte information",(*image)->filename);
9167          break;
9168        }
9169      /*
9170        Show image matte.
9171      */
9172      XSetCursorState(display,windows,MagickTrue);
9173      XCheckRefreshWindows(display,windows);
9174      image_info->group=(ssize_t) windows->image.id;
9175      (void) DeleteImageProperty(*image,"label");
9176      (void) SetImageProperty(*image,"label","Matte",exception);
9177      (void) AcquireUniqueFilename(filename);
9178      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"matte:%s",
9179        filename);
9180      status=WriteImage(image_info,*image,exception);
9181      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9182      matte_image=ReadImage(image_info,exception);
9183      (void) RelinquishUniqueFileResource(filename);
9184      if (matte_image == (Image *) NULL)
9185        break;
9186      (void) FormatLocaleString(matte_image->filename,MaxTextExtent,"show:%s",
9187        filename);
9188      status=WriteImage(image_info,matte_image,exception);
9189      matte_image=DestroyImage(matte_image);
9190      if (status == MagickFalse)
9191        XNoticeWidget(display,windows,"Unable to show matte",
9192          (*image)->filename);
9193      XDelay(display,1500);
9194      XSetCursorState(display,windows,MagickFalse);
9195      break;
9196    }
9197    case BackgroundCommand:
9198    {
9199      /*
9200        Background image.
9201      */
9202      status=XBackgroundImage(display,resource_info,windows,image,exception);
9203      if (status == MagickFalse)
9204        break;
9205      nexus=CloneImage(*image,0,0,MagickTrue,exception);
9206      if (nexus != (Image *) NULL)
9207        XClientMessage(display,windows->image.id,windows->im_protocols,
9208          windows->im_next_image,CurrentTime);
9209      break;
9210    }
9211    case SlideShowCommand:
9212    {
9213      static char
9214        delay[MaxTextExtent] = "5";
9215
9216      /*
9217        Display next image after pausing.
9218      */
9219      (void) XDialogWidget(display,windows,"Slide Show",
9220        "Pause how many 1/100ths of a second between images:",delay);
9221      if (*delay == '\0')
9222        break;
9223      resource_info->delay=StringToUnsignedLong(delay);
9224      XClientMessage(display,windows->image.id,windows->im_protocols,
9225        windows->im_next_image,CurrentTime);
9226      break;
9227    }
9228    case PreferencesCommand:
9229    {
9230      /*
9231        Set user preferences.
9232      */
9233      status=XPreferencesWidget(display,resource_info,windows);
9234      if (status == MagickFalse)
9235        break;
9236      nexus=CloneImage(*image,0,0,MagickTrue,exception);
9237      if (nexus != (Image *) NULL)
9238        XClientMessage(display,windows->image.id,windows->im_protocols,
9239          windows->im_next_image,CurrentTime);
9240      break;
9241    }
9242    case HelpCommand:
9243    {
9244      /*
9245        User requested help.
9246      */
9247      XTextViewWidget(display,resource_info,windows,MagickFalse,
9248        "Help Viewer - Display",DisplayHelp);
9249      break;
9250    }
9251    case BrowseDocumentationCommand:
9252    {
9253      Atom
9254        mozilla_atom;
9255
9256      Window
9257        mozilla_window,
9258        root_window;
9259
9260      /*
9261        Browse the ImageMagick documentation.
9262      */
9263      root_window=XRootWindow(display,XDefaultScreen(display));
9264      mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9265      mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9266      if (mozilla_window != (Window) NULL)
9267        {
9268          char
9269            command[MaxTextExtent],
9270            *url;
9271
9272          /*
9273            Display documentation using Netscape remote control.
9274          */
9275          url=GetMagickHomeURL();
9276          (void) FormatLocaleString(command,MaxTextExtent,
9277            "openurl(%s,new-tab)",url);
9278          url=DestroyString(url);
9279          mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9280          (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9281            8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9282          XSetCursorState(display,windows,MagickFalse);
9283          break;
9284        }
9285      XSetCursorState(display,windows,MagickTrue);
9286      XCheckRefreshWindows(display,windows);
9287      status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9288        exception);
9289      if (status == MagickFalse)
9290        XNoticeWidget(display,windows,"Unable to browse documentation",
9291          (char *) NULL);
9292      XDelay(display,1500);
9293      XSetCursorState(display,windows,MagickFalse);
9294      break;
9295    }
9296    case VersionCommand:
9297    {
9298      XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9299        GetMagickCopyright());
9300      break;
9301    }
9302    case SaveToUndoBufferCommand:
9303      break;
9304    default:
9305    {
9306      (void) XBell(display,0);
9307      break;
9308    }
9309  }
9310  image_info=DestroyImageInfo(image_info);
9311  return(nexus);
9312}
9313
9314/*
9315%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9316%                                                                             %
9317%                                                                             %
9318%                                                                             %
9319+   X M a g n i f y I m a g e                                                 %
9320%                                                                             %
9321%                                                                             %
9322%                                                                             %
9323%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9324%
9325%  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9326%  The magnified portion is displayed in a separate window.
9327%
9328%  The format of the XMagnifyImage method is:
9329%
9330%      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9331%
9332%  A description of each parameter follows:
9333%
9334%    o display: Specifies a connection to an X server;  returned from
9335%      XOpenDisplay.
9336%
9337%    o windows: Specifies a pointer to a XWindows structure.
9338%
9339%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
9340%      the entire image is refreshed.
9341%
9342*/
9343static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9344{
9345  char
9346    text[MaxTextExtent];
9347
9348  register int
9349    x,
9350    y;
9351
9352  size_t
9353    state;
9354
9355  /*
9356    Update magnified image until the mouse button is released.
9357  */
9358  (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9359  state=DefaultState;
9360  x=event->xbutton.x;
9361  y=event->xbutton.y;
9362  windows->magnify.x=(int) windows->image.x+x;
9363  windows->magnify.y=(int) windows->image.y+y;
9364  do
9365  {
9366    /*
9367      Map and unmap Info widget as text cursor crosses its boundaries.
9368    */
9369    if (windows->info.mapped != MagickFalse)
9370      {
9371        if ((x < (int) (windows->info.x+windows->info.width)) &&
9372            (y < (int) (windows->info.y+windows->info.height)))
9373          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9374      }
9375    else
9376      if ((x > (int) (windows->info.x+windows->info.width)) ||
9377          (y > (int) (windows->info.y+windows->info.height)))
9378        (void) XMapWindow(display,windows->info.id);
9379    if (windows->info.mapped != MagickFalse)
9380      {
9381        /*
9382          Display pointer position.
9383        */
9384        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9385          windows->magnify.x,windows->magnify.y);
9386        XInfoWidget(display,windows,text);
9387      }
9388    /*
9389      Wait for next event.
9390    */
9391    XScreenEvent(display,windows,event);
9392    switch (event->type)
9393    {
9394      case ButtonPress:
9395        break;
9396      case ButtonRelease:
9397      {
9398        /*
9399          User has finished magnifying image.
9400        */
9401        x=event->xbutton.x;
9402        y=event->xbutton.y;
9403        state|=ExitState;
9404        break;
9405      }
9406      case Expose:
9407        break;
9408      case MotionNotify:
9409      {
9410        x=event->xmotion.x;
9411        y=event->xmotion.y;
9412        break;
9413      }
9414      default:
9415        break;
9416    }
9417    /*
9418      Check boundary conditions.
9419    */
9420    if (x < 0)
9421      x=0;
9422    else
9423      if (x >= (int) windows->image.width)
9424        x=(int) windows->image.width-1;
9425    if (y < 0)
9426      y=0;
9427    else
9428     if (y >= (int) windows->image.height)
9429       y=(int) windows->image.height-1;
9430  } while ((state & ExitState) == 0);
9431  /*
9432    Display magnified image.
9433  */
9434  XSetCursorState(display,windows,MagickFalse);
9435}
9436
9437/*
9438%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9439%                                                                             %
9440%                                                                             %
9441%                                                                             %
9442+   X M a g n i f y W i n d o w C o m m a n d                                 %
9443%                                                                             %
9444%                                                                             %
9445%                                                                             %
9446%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9447%
9448%  XMagnifyWindowCommand() moves the image within an Magnify window by one
9449%  pixel as specified by the key symbol.
9450%
9451%  The format of the XMagnifyWindowCommand method is:
9452%
9453%      void XMagnifyWindowCommand(Display *display,XWindows *windows,
9454%        const MagickStatusType state,const KeySym key_symbol)
9455%
9456%  A description of each parameter follows:
9457%
9458%    o display: Specifies a connection to an X server; returned from
9459%      XOpenDisplay.
9460%
9461%    o windows: Specifies a pointer to a XWindows structure.
9462%
9463%    o state: key mask.
9464%
9465%    o key_symbol: Specifies a KeySym which indicates which side of the image
9466%      to trim.
9467%
9468*/
9469static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9470  const MagickStatusType state,const KeySym key_symbol)
9471{
9472  unsigned int
9473    quantum;
9474
9475  /*
9476    User specified a magnify factor or position.
9477  */
9478  quantum=1;
9479  if ((state & Mod1Mask) != 0)
9480    quantum=10;
9481  switch ((int) key_symbol)
9482  {
9483    case QuitCommand:
9484    {
9485      (void) XWithdrawWindow(display,windows->magnify.id,
9486        windows->magnify.screen);
9487      break;
9488    }
9489    case XK_Home:
9490    case XK_KP_Home:
9491    {
9492      windows->magnify.x=(int) windows->image.width/2;
9493      windows->magnify.y=(int) windows->image.height/2;
9494      break;
9495    }
9496    case XK_Left:
9497    case XK_KP_Left:
9498    {
9499      if (windows->magnify.x > 0)
9500        windows->magnify.x-=quantum;
9501      break;
9502    }
9503    case XK_Up:
9504    case XK_KP_Up:
9505    {
9506      if (windows->magnify.y > 0)
9507        windows->magnify.y-=quantum;
9508      break;
9509    }
9510    case XK_Right:
9511    case XK_KP_Right:
9512    {
9513      if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9514        windows->magnify.x+=quantum;
9515      break;
9516    }
9517    case XK_Down:
9518    case XK_KP_Down:
9519    {
9520      if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9521        windows->magnify.y+=quantum;
9522      break;
9523    }
9524    case XK_0:
9525    case XK_1:
9526    case XK_2:
9527    case XK_3:
9528    case XK_4:
9529    case XK_5:
9530    case XK_6:
9531    case XK_7:
9532    case XK_8:
9533    case XK_9:
9534    {
9535      windows->magnify.data=(key_symbol-XK_0);
9536      break;
9537    }
9538    case XK_KP_0:
9539    case XK_KP_1:
9540    case XK_KP_2:
9541    case XK_KP_3:
9542    case XK_KP_4:
9543    case XK_KP_5:
9544    case XK_KP_6:
9545    case XK_KP_7:
9546    case XK_KP_8:
9547    case XK_KP_9:
9548    {
9549      windows->magnify.data=(key_symbol-XK_KP_0);
9550      break;
9551    }
9552    default:
9553      break;
9554  }
9555  XMakeMagnifyImage(display,windows);
9556}
9557
9558/*
9559%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9560%                                                                             %
9561%                                                                             %
9562%                                                                             %
9563+   X M a k e P a n I m a g e                                                 %
9564%                                                                             %
9565%                                                                             %
9566%                                                                             %
9567%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9568%
9569%  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9570%  icon window.
9571%
9572%  The format of the XMakePanImage method is:
9573%
9574%        void XMakePanImage(Display *display,XResourceInfo *resource_info,
9575%          XWindows *windows,Image *image,ExceptionInfo *exception)
9576%
9577%  A description of each parameter follows:
9578%
9579%    o display: Specifies a connection to an X server;  returned from
9580%      XOpenDisplay.
9581%
9582%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9583%
9584%    o windows: Specifies a pointer to a XWindows structure.
9585%
9586%    o image: the image.
9587%
9588%    o exception: return any errors or warnings in this structure.
9589%
9590*/
9591static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9592  XWindows *windows,Image *image,ExceptionInfo *exception)
9593{
9594  MagickStatusType
9595    status;
9596
9597  /*
9598    Create and display image for panning icon.
9599  */
9600  XSetCursorState(display,windows,MagickTrue);
9601  XCheckRefreshWindows(display,windows);
9602  windows->pan.x=(int) windows->image.x;
9603  windows->pan.y=(int) windows->image.y;
9604  status=XMakeImage(display,resource_info,&windows->pan,image,
9605    windows->pan.width,windows->pan.height,exception);
9606  if (status == MagickFalse)
9607    ThrowXWindowFatalException(ResourceLimitError,
9608     "MemoryAllocationFailed",image->filename);
9609  (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9610    windows->pan.pixmap);
9611  (void) XClearWindow(display,windows->pan.id);
9612  XDrawPanRectangle(display,windows);
9613  XSetCursorState(display,windows,MagickFalse);
9614}
9615
9616/*
9617%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9618%                                                                             %
9619%                                                                             %
9620%                                                                             %
9621+   X M a t t a E d i t I m a g e                                             %
9622%                                                                             %
9623%                                                                             %
9624%                                                                             %
9625%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9626%
9627%  XMatteEditImage() allows the user to interactively change the Matte channel
9628%  of an image.  If the image is PseudoClass it is promoted to DirectClass
9629%  before the matte information is stored.
9630%
9631%  The format of the XMatteEditImage method is:
9632%
9633%      MagickBooleanType XMatteEditImage(Display *display,
9634%        XResourceInfo *resource_info,XWindows *windows,Image **image,
9635%        ExceptionInfo *exception)
9636%
9637%  A description of each parameter follows:
9638%
9639%    o display: Specifies a connection to an X server;  returned from
9640%      XOpenDisplay.
9641%
9642%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9643%
9644%    o windows: Specifies a pointer to a XWindows structure.
9645%
9646%    o image: the image; returned from ReadImage.
9647%
9648%    o exception: return any errors or warnings in this structure.
9649%
9650*/
9651static MagickBooleanType XMatteEditImage(Display *display,
9652  XResourceInfo *resource_info,XWindows *windows,Image **image,
9653  ExceptionInfo *exception)
9654{
9655  static char
9656    matte[MaxTextExtent] = "0";
9657
9658  static const char
9659    *MatteEditMenu[] =
9660    {
9661      "Method",
9662      "Border Color",
9663      "Fuzz",
9664      "Matte Value",
9665      "Undo",
9666      "Help",
9667      "Dismiss",
9668      (char *) NULL
9669    };
9670
9671  static const ModeType
9672    MatteEditCommands[] =
9673    {
9674      MatteEditMethod,
9675      MatteEditBorderCommand,
9676      MatteEditFuzzCommand,
9677      MatteEditValueCommand,
9678      MatteEditUndoCommand,
9679      MatteEditHelpCommand,
9680      MatteEditDismissCommand
9681    };
9682
9683  static PaintMethod
9684    method = PointMethod;
9685
9686  static XColor
9687    border_color = { 0, 0, 0, 0, 0, 0 };
9688
9689  char
9690    command[MaxTextExtent],
9691    text[MaxTextExtent];
9692
9693  Cursor
9694    cursor;
9695
9696  int
9697    entry,
9698    id,
9699    x,
9700    x_offset,
9701    y,
9702    y_offset;
9703
9704  register int
9705    i;
9706
9707  register Quantum
9708    *q;
9709
9710  unsigned int
9711    height,
9712    width;
9713
9714  size_t
9715    state;
9716
9717  XEvent
9718    event;
9719
9720  /*
9721    Map Command widget.
9722  */
9723  (void) CloneString(&windows->command.name,"Matte Edit");
9724  windows->command.data=4;
9725  (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9726  (void) XMapRaised(display,windows->command.id);
9727  XClientMessage(display,windows->image.id,windows->im_protocols,
9728    windows->im_update_widget,CurrentTime);
9729  /*
9730    Make cursor.
9731  */
9732  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9733    resource_info->background_color,resource_info->foreground_color);
9734  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9735  /*
9736    Track pointer until button 1 is pressed.
9737  */
9738  XQueryPosition(display,windows->image.id,&x,&y);
9739  (void) XSelectInput(display,windows->image.id,
9740    windows->image.attributes.event_mask | PointerMotionMask);
9741  state=DefaultState;
9742  do
9743  {
9744    if (windows->info.mapped != MagickFalse)
9745      {
9746        /*
9747          Display pointer position.
9748        */
9749        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9750          x+windows->image.x,y+windows->image.y);
9751        XInfoWidget(display,windows,text);
9752      }
9753    /*
9754      Wait for next event.
9755    */
9756    XScreenEvent(display,windows,&event);
9757    if (event.xany.window == windows->command.id)
9758      {
9759        /*
9760          Select a command from the Command widget.
9761        */
9762        id=XCommandWidget(display,windows,MatteEditMenu,&event);
9763        if (id < 0)
9764          {
9765            (void) XCheckDefineCursor(display,windows->image.id,cursor);
9766            continue;
9767          }
9768        switch (MatteEditCommands[id])
9769        {
9770          case MatteEditMethod:
9771          {
9772            char
9773              **methods;
9774
9775            /*
9776              Select a method from the pop-up menu.
9777            */
9778            methods=GetCommandOptions(MagickMethodOptions);
9779            if (methods == (char **) NULL)
9780              break;
9781            entry=XMenuWidget(display,windows,MatteEditMenu[id],
9782              (const char **) methods,command);
9783            if (entry >= 0)
9784              method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9785                MagickFalse,methods[entry]);
9786            methods=DestroyStringList(methods);
9787            break;
9788          }
9789          case MatteEditBorderCommand:
9790          {
9791            const char
9792              *ColorMenu[MaxNumberPens];
9793
9794            int
9795              pen_number;
9796
9797            /*
9798              Initialize menu selections.
9799            */
9800            for (i=0; i < (int) (MaxNumberPens-2); i++)
9801              ColorMenu[i]=resource_info->pen_colors[i];
9802            ColorMenu[MaxNumberPens-2]="Browser...";
9803            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9804            /*
9805              Select a pen color from the pop-up menu.
9806            */
9807            pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9808              (const char **) ColorMenu,command);
9809            if (pen_number < 0)
9810              break;
9811            if (pen_number == (MaxNumberPens-2))
9812              {
9813                static char
9814                  color_name[MaxTextExtent] = "gray";
9815
9816                /*
9817                  Select a pen color from a dialog.
9818                */
9819                resource_info->pen_colors[pen_number]=color_name;
9820                XColorBrowserWidget(display,windows,"Select",color_name);
9821                if (*color_name == '\0')
9822                  break;
9823              }
9824            /*
9825              Set border color.
9826            */
9827            (void) XParseColor(display,windows->map_info->colormap,
9828              resource_info->pen_colors[pen_number],&border_color);
9829            break;
9830          }
9831          case MatteEditFuzzCommand:
9832          {
9833            static char
9834              fuzz[MaxTextExtent];
9835
9836            static const char
9837              *FuzzMenu[] =
9838              {
9839                "0%",
9840                "2%",
9841                "5%",
9842                "10%",
9843                "15%",
9844                "Dialog...",
9845                (char *) NULL,
9846              };
9847
9848            /*
9849              Select a command from the pop-up menu.
9850            */
9851            entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9852              command);
9853            if (entry < 0)
9854              break;
9855            if (entry != 5)
9856              {
9857                (*image)->fuzz=SiPrefixToDouble(FuzzMenu[entry],(double)
9858                  QuantumRange+1.0);
9859                break;
9860              }
9861            (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9862            (void) XDialogWidget(display,windows,"Ok",
9863              "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9864            if (*fuzz == '\0')
9865              break;
9866            (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
9867            (*image)->fuzz=SiPrefixToDouble(fuzz,(double) QuantumRange+1.0);
9868            break;
9869          }
9870          case MatteEditValueCommand:
9871          {
9872            static char
9873              message[MaxTextExtent];
9874
9875            static const char
9876              *MatteMenu[] =
9877              {
9878                "Opaque",
9879                "Transparent",
9880                "Dialog...",
9881                (char *) NULL,
9882              };
9883
9884            /*
9885              Select a command from the pop-up menu.
9886            */
9887            entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9888              command);
9889            if (entry < 0)
9890              break;
9891            if (entry != 2)
9892              {
9893                (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9894                  OpaqueAlpha);
9895                if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9896                  (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9897                    (Quantum) TransparentAlpha);
9898                break;
9899              }
9900            (void) FormatLocaleString(message,MaxTextExtent,
9901              "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9902              QuantumRange);
9903            (void) XDialogWidget(display,windows,"Matte",message,matte);
9904            if (*matte == '\0')
9905              break;
9906            break;
9907          }
9908          case MatteEditUndoCommand:
9909          {
9910            (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9911              image,exception);
9912            break;
9913          }
9914          case MatteEditHelpCommand:
9915          {
9916            XTextViewWidget(display,resource_info,windows,MagickFalse,
9917              "Help Viewer - Matte Edit",ImageMatteEditHelp);
9918            break;
9919          }
9920          case MatteEditDismissCommand:
9921          {
9922            /*
9923              Prematurely exit.
9924            */
9925            state|=EscapeState;
9926            state|=ExitState;
9927            break;
9928          }
9929          default:
9930            break;
9931        }
9932        (void) XCheckDefineCursor(display,windows->image.id,cursor);
9933        continue;
9934      }
9935    switch (event.type)
9936    {
9937      case ButtonPress:
9938      {
9939        if (event.xbutton.button != Button1)
9940          break;
9941        if ((event.xbutton.window != windows->image.id) &&
9942            (event.xbutton.window != windows->magnify.id))
9943          break;
9944        /*
9945          Update matte data.
9946        */
9947        x=event.xbutton.x;
9948        y=event.xbutton.y;
9949        (void) XMagickCommand(display,resource_info,windows,
9950          SaveToUndoBufferCommand,image,exception);
9951        state|=UpdateConfigurationState;
9952        break;
9953      }
9954      case ButtonRelease:
9955      {
9956        if (event.xbutton.button != Button1)
9957          break;
9958        if ((event.xbutton.window != windows->image.id) &&
9959            (event.xbutton.window != windows->magnify.id))
9960          break;
9961        /*
9962          Update colormap information.
9963        */
9964        x=event.xbutton.x;
9965        y=event.xbutton.y;
9966        XConfigureImageColormap(display,resource_info,windows,*image);
9967        (void) XConfigureImage(display,resource_info,windows,*image,exception);
9968        XInfoWidget(display,windows,text);
9969        (void) XCheckDefineCursor(display,windows->image.id,cursor);
9970        state&=(~UpdateConfigurationState);
9971        break;
9972      }
9973      case Expose:
9974        break;
9975      case KeyPress:
9976      {
9977        char
9978          command[MaxTextExtent];
9979
9980        KeySym
9981          key_symbol;
9982
9983        if (event.xkey.window == windows->magnify.id)
9984          {
9985            Window
9986              window;
9987
9988            window=windows->magnify.id;
9989            while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9990          }
9991        if (event.xkey.window != windows->image.id)
9992          break;
9993        /*
9994          Respond to a user key press.
9995        */
9996        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
9997          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
9998        switch ((int) key_symbol)
9999        {
10000          case XK_Escape:
10001          case XK_F20:
10002          {
10003            /*
10004              Prematurely exit.
10005            */
10006            state|=ExitState;
10007            break;
10008          }
10009          case XK_F1:
10010          case XK_Help:
10011          {
10012            XTextViewWidget(display,resource_info,windows,MagickFalse,
10013              "Help Viewer - Matte Edit",ImageMatteEditHelp);
10014            break;
10015          }
10016          default:
10017          {
10018            (void) XBell(display,0);
10019            break;
10020          }
10021        }
10022        break;
10023      }
10024      case MotionNotify:
10025      {
10026        /*
10027          Map and unmap Info widget as cursor crosses its boundaries.
10028        */
10029        x=event.xmotion.x;
10030        y=event.xmotion.y;
10031        if (windows->info.mapped != MagickFalse)
10032          {
10033            if ((x < (int) (windows->info.x+windows->info.width)) &&
10034                (y < (int) (windows->info.y+windows->info.height)))
10035              (void) XWithdrawWindow(display,windows->info.id,
10036                windows->info.screen);
10037          }
10038        else
10039          if ((x > (int) (windows->info.x+windows->info.width)) ||
10040              (y > (int) (windows->info.y+windows->info.height)))
10041            (void) XMapWindow(display,windows->info.id);
10042        break;
10043      }
10044      default:
10045        break;
10046    }
10047    if (event.xany.window == windows->magnify.id)
10048      {
10049        x=windows->magnify.x-windows->image.x;
10050        y=windows->magnify.y-windows->image.y;
10051      }
10052    x_offset=x;
10053    y_offset=y;
10054    if ((state & UpdateConfigurationState) != 0)
10055      {
10056        CacheView
10057          *image_view;
10058
10059        int
10060          x,
10061          y;
10062
10063        /*
10064          Matte edit is relative to image configuration.
10065        */
10066        (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
10067          MagickTrue);
10068        XPutPixel(windows->image.ximage,x_offset,y_offset,
10069          windows->pixel_info->background_color.pixel);
10070        width=(unsigned int) (*image)->columns;
10071        height=(unsigned int) (*image)->rows;
10072        x=0;
10073        y=0;
10074        if (windows->image.crop_geometry != (char *) NULL)
10075          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
10076            &height);
10077        x_offset=(int) (width*(windows->image.x+x_offset)/
10078          windows->image.ximage->width+x);
10079        y_offset=(int) (height*(windows->image.y+y_offset)/
10080          windows->image.ximage->height+y);
10081        if ((x_offset < 0) || (y_offset < 0))
10082          continue;
10083        if ((x_offset >= (int) (*image)->columns) ||
10084            (y_offset >= (int) (*image)->rows))
10085          continue;
10086        if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10087          return(MagickFalse);
10088        (*image)->matte=MagickTrue;
10089        image_view=AcquireCacheView(*image);
10090        switch (method)
10091        {
10092          case PointMethod:
10093          default:
10094          {
10095            /*
10096              Update matte information using point algorithm.
10097            */
10098            q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10099              (ssize_t) y_offset,1,1,exception);
10100            if (q == (Quantum *) NULL)
10101              break;
10102            SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10103            (void) SyncCacheViewAuthenticPixels(image_view,exception);
10104            break;
10105          }
10106          case ReplaceMethod:
10107          {
10108            PixelInfo
10109              pixel,
10110              target;
10111
10112            Quantum
10113              virtual_pixel[MaxPixelChannels];
10114
10115            /*
10116              Update matte information using replace algorithm.
10117            */
10118            (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
10119              (ssize_t) y_offset,virtual_pixel,exception);
10120            target.red=virtual_pixel[RedPixelChannel];
10121            target.green=virtual_pixel[GreenPixelChannel];
10122            target.blue=virtual_pixel[BluePixelChannel];
10123            target.alpha=virtual_pixel[AlphaPixelChannel];
10124            for (y=0; y < (int) (*image)->rows; y++)
10125            {
10126              q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10127                (*image)->columns,1,exception);
10128              if (q == (Quantum *) NULL)
10129                break;
10130              for (x=0; x < (int) (*image)->columns; x++)
10131              {
10132                GetPixelInfoPixel(*image,q,&pixel);
10133                if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
10134                  SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10135                q+=GetPixelChannels(*image);
10136              }
10137              if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10138                break;
10139            }
10140            break;
10141          }
10142          case FloodfillMethod:
10143          case FillToBorderMethod:
10144          {
10145            ChannelType
10146              channel_mask;
10147
10148            DrawInfo
10149              *draw_info;
10150
10151            PixelInfo
10152              target;
10153
10154            /*
10155              Update matte information using floodfill algorithm.
10156            */
10157            (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
10158              (ssize_t) y_offset,&target,exception);
10159            if (method == FillToBorderMethod)
10160              {
10161                target.red=(MagickRealType) ScaleShortToQuantum(
10162                  border_color.red);
10163                target.green=(MagickRealType) ScaleShortToQuantum(
10164                  border_color.green);
10165                target.blue=(MagickRealType) ScaleShortToQuantum(
10166                  border_color.blue);
10167              }
10168            draw_info=CloneDrawInfo(resource_info->image_info,
10169              (DrawInfo *) NULL);
10170            draw_info->fill.alpha=ClampToQuantum(InterpretLocaleValue(matte,
10171              (char **) NULL));
10172            channel_mask=SetPixelChannelMask(*image,AlphaChannel);
10173            (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10174              x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
10175              MagickFalse : MagickTrue,exception);
10176            (void) SetPixelChannelMap(*image,channel_mask);
10177            draw_info=DestroyDrawInfo(draw_info);
10178            break;
10179          }
10180          case ResetMethod:
10181          {
10182            /*
10183              Update matte information using reset algorithm.
10184            */
10185            if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10186              return(MagickFalse);
10187            for (y=0; y < (int) (*image)->rows; y++)
10188            {
10189              q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10190                (*image)->columns,1,exception);
10191              if (q == (Quantum *) NULL)
10192                break;
10193              for (x=0; x < (int) (*image)->columns; x++)
10194              {
10195                SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10196                q+=GetPixelChannels(*image);
10197              }
10198              if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10199                break;
10200            }
10201            if (StringToLong(matte) == (long) OpaqueAlpha)
10202              (*image)->matte=MagickFalse;
10203            break;
10204          }
10205        }
10206        image_view=DestroyCacheView(image_view);
10207        state&=(~UpdateConfigurationState);
10208      }
10209  } while ((state & ExitState) == 0);
10210  (void) XSelectInput(display,windows->image.id,
10211    windows->image.attributes.event_mask);
10212  XSetCursorState(display,windows,MagickFalse);
10213  (void) XFreeCursor(display,cursor);
10214  return(MagickTrue);
10215}
10216
10217/*
10218%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10219%                                                                             %
10220%                                                                             %
10221%                                                                             %
10222+   X O p e n I m a g e                                                       %
10223%                                                                             %
10224%                                                                             %
10225%                                                                             %
10226%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10227%
10228%  XOpenImage() loads an image from a file.
10229%
10230%  The format of the XOpenImage method is:
10231%
10232%     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10233%       XWindows *windows,const unsigned int command)
10234%
10235%  A description of each parameter follows:
10236%
10237%    o display: Specifies a connection to an X server; returned from
10238%      XOpenDisplay.
10239%
10240%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10241%
10242%    o windows: Specifies a pointer to a XWindows structure.
10243%
10244%    o command: A value other than zero indicates that the file is selected
10245%      from the command line argument list.
10246%
10247*/
10248static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10249  XWindows *windows,const MagickBooleanType command)
10250{
10251  const MagickInfo
10252    *magick_info;
10253
10254  ExceptionInfo
10255    *exception;
10256
10257  Image
10258    *nexus;
10259
10260  ImageInfo
10261    *image_info;
10262
10263  static char
10264    filename[MaxTextExtent] = "\0";
10265
10266  /*
10267    Request file name from user.
10268  */
10269  if (command == MagickFalse)
10270    XFileBrowserWidget(display,windows,"Open",filename);
10271  else
10272    {
10273      char
10274        **filelist,
10275        **files;
10276
10277      int
10278        count,
10279        status;
10280
10281      register int
10282        i,
10283        j;
10284
10285      /*
10286        Select next image from the command line.
10287      */
10288      status=XGetCommand(display,windows->image.id,&files,&count);
10289      if (status == 0)
10290        {
10291          ThrowXWindowFatalException(XServerError,"UnableToGetProperty","...");
10292          return((Image *) NULL);
10293        }
10294      filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10295      if (filelist == (char **) NULL)
10296        {
10297          ThrowXWindowFatalException(ResourceLimitError,
10298            "MemoryAllocationFailed","...");
10299          (void) XFreeStringList(files);
10300          return((Image *) NULL);
10301        }
10302      j=0;
10303      for (i=1; i < count; i++)
10304        if (*files[i] != '-')
10305          filelist[j++]=files[i];
10306      filelist[j]=(char *) NULL;
10307      XListBrowserWidget(display,windows,&windows->widget,
10308        (const char **) filelist,"Load","Select Image to Load:",filename);
10309      filelist=(char **) RelinquishMagickMemory(filelist);
10310      (void) XFreeStringList(files);
10311    }
10312  if (*filename == '\0')
10313    return((Image *) NULL);
10314  image_info=CloneImageInfo(resource_info->image_info);
10315  (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10316    (void *) NULL);
10317  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10318  exception=AcquireExceptionInfo();
10319  (void) SetImageInfo(image_info,0,exception);
10320  if (LocaleCompare(image_info->magick,"X") == 0)
10321    {
10322      char
10323        seconds[MaxTextExtent];
10324
10325      /*
10326        User may want to delay the X server screen grab.
10327      */
10328      (void) CopyMagickString(seconds,"0",MaxTextExtent);
10329      (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10330        seconds);
10331      if (*seconds == '\0')
10332        return((Image *) NULL);
10333      XDelay(display,(size_t) (1000*StringToLong(seconds)));
10334    }
10335  magick_info=GetMagickInfo(image_info->magick,exception);
10336  if ((magick_info != (const MagickInfo *) NULL) &&
10337      (magick_info->raw != MagickFalse))
10338    {
10339      char
10340        geometry[MaxTextExtent];
10341
10342      /*
10343        Request image size from the user.
10344      */
10345      (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10346      if (image_info->size != (char *) NULL)
10347        (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10348      (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10349        geometry);
10350      (void) CloneString(&image_info->size,geometry);
10351    }
10352  /*
10353    Load the image.
10354  */
10355  XSetCursorState(display,windows,MagickTrue);
10356  XCheckRefreshWindows(display,windows);
10357  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10358  nexus=ReadImage(image_info,exception);
10359  CatchException(exception);
10360  XSetCursorState(display,windows,MagickFalse);
10361  if (nexus != (Image *) NULL)
10362    XClientMessage(display,windows->image.id,windows->im_protocols,
10363      windows->im_next_image,CurrentTime);
10364  else
10365    {
10366      char
10367        *text,
10368        **textlist;
10369
10370      /*
10371        Unknown image format.
10372      */
10373      text=FileToString(filename,~0,exception);
10374      if (text == (char *) NULL)
10375        return((Image *) NULL);
10376      textlist=StringToList(text);
10377      if (textlist != (char **) NULL)
10378        {
10379          char
10380            title[MaxTextExtent];
10381
10382          register int
10383            i;
10384
10385          (void) FormatLocaleString(title,MaxTextExtent,
10386            "Unknown format: %s",filename);
10387          XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10388            (const char **) textlist);
10389          for (i=0; textlist[i] != (char *) NULL; i++)
10390            textlist[i]=DestroyString(textlist[i]);
10391          textlist=(char **) RelinquishMagickMemory(textlist);
10392        }
10393      text=DestroyString(text);
10394    }
10395  exception=DestroyExceptionInfo(exception);
10396  image_info=DestroyImageInfo(image_info);
10397  return(nexus);
10398}
10399
10400/*
10401%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10402%                                                                             %
10403%                                                                             %
10404%                                                                             %
10405+   X P a n I m a g e                                                         %
10406%                                                                             %
10407%                                                                             %
10408%                                                                             %
10409%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10410%
10411%  XPanImage() pans the image until the mouse button is released.
10412%
10413%  The format of the XPanImage method is:
10414%
10415%      void XPanImage(Display *display,XWindows *windows,XEvent *event)
10416%
10417%  A description of each parameter follows:
10418%
10419%    o display: Specifies a connection to an X server;  returned from
10420%      XOpenDisplay.
10421%
10422%    o windows: Specifies a pointer to a XWindows structure.
10423%
10424%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10425%      the entire image is refreshed.
10426%
10427*/
10428static void XPanImage(Display *display,XWindows *windows,XEvent *event)
10429{
10430  char
10431    text[MaxTextExtent];
10432
10433  Cursor
10434    cursor;
10435
10436  MagickRealType
10437    x_factor,
10438    y_factor;
10439
10440  RectangleInfo
10441    pan_info;
10442
10443  size_t
10444    state;
10445
10446  /*
10447    Define cursor.
10448  */
10449  if ((windows->image.ximage->width > (int) windows->image.width) &&
10450      (windows->image.ximage->height > (int) windows->image.height))
10451    cursor=XCreateFontCursor(display,XC_fleur);
10452  else
10453    if (windows->image.ximage->width > (int) windows->image.width)
10454      cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10455    else
10456      if (windows->image.ximage->height > (int) windows->image.height)
10457        cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10458      else
10459        cursor=XCreateFontCursor(display,XC_arrow);
10460  (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10461  /*
10462    Pan image as pointer moves until the mouse button is released.
10463  */
10464  x_factor=(MagickRealType) windows->image.ximage->width/windows->pan.width;
10465  y_factor=(MagickRealType) windows->image.ximage->height/windows->pan.height;
10466  pan_info.width=windows->pan.width*windows->image.width/
10467    windows->image.ximage->width;
10468  pan_info.height=windows->pan.height*windows->image.height/
10469    windows->image.ximage->height;
10470  pan_info.x=0;
10471  pan_info.y=0;
10472  state=UpdateConfigurationState;
10473  do
10474  {
10475    switch (event->type)
10476    {
10477      case ButtonPress:
10478      {
10479        /*
10480          User choose an initial pan location.
10481        */
10482        pan_info.x=(ssize_t) event->xbutton.x;
10483        pan_info.y=(ssize_t) event->xbutton.y;
10484        state|=UpdateConfigurationState;
10485        break;
10486      }
10487      case ButtonRelease:
10488      {
10489        /*
10490          User has finished panning the image.
10491        */
10492        pan_info.x=(ssize_t) event->xbutton.x;
10493        pan_info.y=(ssize_t) event->xbutton.y;
10494        state|=UpdateConfigurationState | ExitState;
10495        break;
10496      }
10497      case MotionNotify:
10498      {
10499        pan_info.x=(ssize_t) event->xmotion.x;
10500        pan_info.y=(ssize_t) event->xmotion.y;
10501        state|=UpdateConfigurationState;
10502      }
10503      default:
10504        break;
10505    }
10506    if ((state & UpdateConfigurationState) != 0)
10507      {
10508        /*
10509          Check boundary conditions.
10510        */
10511        if (pan_info.x < (ssize_t) (pan_info.width/2))
10512          pan_info.x=0;
10513        else
10514          pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10515        if (pan_info.x < 0)
10516          pan_info.x=0;
10517        else
10518          if ((int) (pan_info.x+windows->image.width) >
10519              windows->image.ximage->width)
10520            pan_info.x=(ssize_t)
10521              (windows->image.ximage->width-windows->image.width);
10522        if (pan_info.y < (ssize_t) (pan_info.height/2))
10523          pan_info.y=0;
10524        else
10525          pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10526        if (pan_info.y < 0)
10527          pan_info.y=0;
10528        else
10529          if ((int) (pan_info.y+windows->image.height) >
10530              windows->image.ximage->height)
10531            pan_info.y=(ssize_t)
10532              (windows->image.ximage->height-windows->image.height);
10533        if ((windows->image.x != (int) pan_info.x) ||
10534            (windows->image.y != (int) pan_info.y))
10535          {
10536            /*
10537              Display image pan offset.
10538            */
10539            windows->image.x=(int) pan_info.x;
10540            windows->image.y=(int) pan_info.y;
10541            (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
10542              windows->image.width,windows->image.height,windows->image.x,
10543              windows->image.y);
10544            XInfoWidget(display,windows,text);
10545            /*
10546              Refresh Image window.
10547            */
10548            XDrawPanRectangle(display,windows);
10549            XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10550          }
10551        state&=(~UpdateConfigurationState);
10552      }
10553    /*
10554      Wait for next event.
10555    */
10556    if ((state & ExitState) == 0)
10557      XScreenEvent(display,windows,event);
10558  } while ((state & ExitState) == 0);
10559  /*
10560    Restore cursor.
10561  */
10562  (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10563  (void) XFreeCursor(display,cursor);
10564  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10565}
10566
10567/*
10568%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10569%                                                                             %
10570%                                                                             %
10571%                                                                             %
10572+   X P a s t e I m a g e                                                     %
10573%                                                                             %
10574%                                                                             %
10575%                                                                             %
10576%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10577%
10578%  XPasteImage() pastes an image previously saved with XCropImage in the X
10579%  window image at a location the user chooses with the pointer.
10580%
10581%  The format of the XPasteImage method is:
10582%
10583%      MagickBooleanType XPasteImage(Display *display,
10584%        XResourceInfo *resource_info,XWindows *windows,Image *image,
10585%        ExceptionInfo *exception)
10586%
10587%  A description of each parameter follows:
10588%
10589%    o display: Specifies a connection to an X server;  returned from
10590%      XOpenDisplay.
10591%
10592%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10593%
10594%    o windows: Specifies a pointer to a XWindows structure.
10595%
10596%    o image: the image; returned from ReadImage.
10597%
10598%    o exception: return any errors or warnings in this structure.
10599%
10600*/
10601static MagickBooleanType XPasteImage(Display *display,
10602  XResourceInfo *resource_info,XWindows *windows,Image *image,
10603  ExceptionInfo *exception)
10604{
10605  static const char
10606    *PasteMenu[] =
10607    {
10608      "Operator",
10609      "Help",
10610      "Dismiss",
10611      (char *) NULL
10612    };
10613
10614  static const ModeType
10615    PasteCommands[] =
10616    {
10617      PasteOperatorsCommand,
10618      PasteHelpCommand,
10619      PasteDismissCommand
10620    };
10621
10622  static CompositeOperator
10623    compose = CopyCompositeOp;
10624
10625  char
10626    text[MaxTextExtent];
10627
10628  Cursor
10629    cursor;
10630
10631  Image
10632    *paste_image;
10633
10634  int
10635    entry,
10636    id,
10637    x,
10638    y;
10639
10640  MagickRealType
10641    scale_factor;
10642
10643  RectangleInfo
10644    highlight_info,
10645    paste_info;
10646
10647  unsigned int
10648    height,
10649    width;
10650
10651  size_t
10652    state;
10653
10654  XEvent
10655    event;
10656
10657  /*
10658    Copy image.
10659  */
10660  if (resource_info->copy_image == (Image *) NULL)
10661    return(MagickFalse);
10662  paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,exception);
10663  /*
10664    Map Command widget.
10665  */
10666  (void) CloneString(&windows->command.name,"Paste");
10667  windows->command.data=1;
10668  (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10669  (void) XMapRaised(display,windows->command.id);
10670  XClientMessage(display,windows->image.id,windows->im_protocols,
10671    windows->im_update_widget,CurrentTime);
10672  /*
10673    Track pointer until button 1 is pressed.
10674  */
10675  XSetCursorState(display,windows,MagickFalse);
10676  XQueryPosition(display,windows->image.id,&x,&y);
10677  (void) XSelectInput(display,windows->image.id,
10678    windows->image.attributes.event_mask | PointerMotionMask);
10679  paste_info.x=(ssize_t) windows->image.x+x;
10680  paste_info.y=(ssize_t) windows->image.y+y;
10681  paste_info.width=0;
10682  paste_info.height=0;
10683  cursor=XCreateFontCursor(display,XC_ul_angle);
10684  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10685  state=DefaultState;
10686  do
10687  {
10688    if (windows->info.mapped != MagickFalse)
10689      {
10690        /*
10691          Display pointer position.
10692        */
10693        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
10694          (long) paste_info.x,(long) paste_info.y);
10695        XInfoWidget(display,windows,text);
10696      }
10697    highlight_info=paste_info;
10698    highlight_info.x=paste_info.x-windows->image.x;
10699    highlight_info.y=paste_info.y-windows->image.y;
10700    XHighlightRectangle(display,windows->image.id,
10701      windows->image.highlight_context,&highlight_info);
10702    /*
10703      Wait for next event.
10704    */
10705    XScreenEvent(display,windows,&event);
10706    XHighlightRectangle(display,windows->image.id,
10707      windows->image.highlight_context,&highlight_info);
10708    if (event.xany.window == windows->command.id)
10709      {
10710        /*
10711          Select a command from the Command widget.
10712        */
10713        id=XCommandWidget(display,windows,PasteMenu,&event);
10714        if (id < 0)
10715          continue;
10716        switch (PasteCommands[id])
10717        {
10718          case PasteOperatorsCommand:
10719          {
10720            char
10721              command[MaxTextExtent],
10722              **operators;
10723
10724            /*
10725              Select a command from the pop-up menu.
10726            */
10727            operators=GetCommandOptions(MagickComposeOptions);
10728            if (operators == (char **) NULL)
10729              break;
10730            entry=XMenuWidget(display,windows,PasteMenu[id],
10731              (const char **) operators,command);
10732            if (entry >= 0)
10733              compose=(CompositeOperator) ParseCommandOption(
10734                MagickComposeOptions,MagickFalse,operators[entry]);
10735            operators=DestroyStringList(operators);
10736            break;
10737          }
10738          case PasteHelpCommand:
10739          {
10740            XTextViewWidget(display,resource_info,windows,MagickFalse,
10741              "Help Viewer - Image Composite",ImagePasteHelp);
10742            break;
10743          }
10744          case PasteDismissCommand:
10745          {
10746            /*
10747              Prematurely exit.
10748            */
10749            state|=EscapeState;
10750            state|=ExitState;
10751            break;
10752          }
10753          default:
10754            break;
10755        }
10756        continue;
10757      }
10758    switch (event.type)
10759    {
10760      case ButtonPress:
10761      {
10762        if (image->debug != MagickFalse)
10763          (void) LogMagickEvent(X11Event,GetMagickModule(),
10764            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10765            event.xbutton.button,event.xbutton.x,event.xbutton.y);
10766        if (event.xbutton.button != Button1)
10767          break;
10768        if (event.xbutton.window != windows->image.id)
10769          break;
10770        /*
10771          Paste rectangle is relative to image configuration.
10772        */
10773        width=(unsigned int) image->columns;
10774        height=(unsigned int) image->rows;
10775        x=0;
10776        y=0;
10777        if (windows->image.crop_geometry != (char *) NULL)
10778          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10779            &width,&height);
10780        scale_factor=(MagickRealType) windows->image.ximage->width/width;
10781        paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10782        scale_factor=(MagickRealType) windows->image.ximage->height/height;
10783        paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10784        (void) XCheckDefineCursor(display,windows->image.id,cursor);
10785        paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10786        paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10787        break;
10788      }
10789      case ButtonRelease:
10790      {
10791        if (image->debug != MagickFalse)
10792          (void) LogMagickEvent(X11Event,GetMagickModule(),
10793            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10794            event.xbutton.button,event.xbutton.x,event.xbutton.y);
10795        if (event.xbutton.button != Button1)
10796          break;
10797        if (event.xbutton.window != windows->image.id)
10798          break;
10799        if ((paste_info.width != 0) && (paste_info.height != 0))
10800          {
10801            /*
10802              User has selected the location of the paste image.
10803            */
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            state|=ExitState;
10807          }
10808        break;
10809      }
10810      case Expose:
10811        break;
10812      case KeyPress:
10813      {
10814        char
10815          command[MaxTextExtent];
10816
10817        KeySym
10818          key_symbol;
10819
10820        int
10821          length;
10822
10823        if (event.xkey.window != windows->image.id)
10824          break;
10825        /*
10826          Respond to a user key press.
10827        */
10828        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10829          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10830        *(command+length)='\0';
10831        if (image->debug != MagickFalse)
10832          (void) LogMagickEvent(X11Event,GetMagickModule(),
10833            "Key press: 0x%lx (%s)",(long) key_symbol,command);
10834        switch ((int) key_symbol)
10835        {
10836          case XK_Escape:
10837          case XK_F20:
10838          {
10839            /*
10840              Prematurely exit.
10841            */
10842            paste_image=DestroyImage(paste_image);
10843            state|=EscapeState;
10844            state|=ExitState;
10845            break;
10846          }
10847          case XK_F1:
10848          case XK_Help:
10849          {
10850            (void) XSetFunction(display,windows->image.highlight_context,
10851              GXcopy);
10852            XTextViewWidget(display,resource_info,windows,MagickFalse,
10853              "Help Viewer - Image Composite",ImagePasteHelp);
10854            (void) XSetFunction(display,windows->image.highlight_context,
10855              GXinvert);
10856            break;
10857          }
10858          default:
10859          {
10860            (void) XBell(display,0);
10861            break;
10862          }
10863        }
10864        break;
10865      }
10866      case MotionNotify:
10867      {
10868        /*
10869          Map and unmap Info widget as text cursor crosses its boundaries.
10870        */
10871        x=event.xmotion.x;
10872        y=event.xmotion.y;
10873        if (windows->info.mapped != MagickFalse)
10874          {
10875            if ((x < (int) (windows->info.x+windows->info.width)) &&
10876                (y < (int) (windows->info.y+windows->info.height)))
10877              (void) XWithdrawWindow(display,windows->info.id,
10878                windows->info.screen);
10879          }
10880        else
10881          if ((x > (int) (windows->info.x+windows->info.width)) ||
10882              (y > (int) (windows->info.y+windows->info.height)))
10883            (void) XMapWindow(display,windows->info.id);
10884        paste_info.x=(ssize_t) windows->image.x+x;
10885        paste_info.y=(ssize_t) windows->image.y+y;
10886        break;
10887      }
10888      default:
10889      {
10890        if (image->debug != MagickFalse)
10891          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10892            event.type);
10893        break;
10894      }
10895    }
10896  } while ((state & ExitState) == 0);
10897  (void) XSelectInput(display,windows->image.id,
10898    windows->image.attributes.event_mask);
10899  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10900  XSetCursorState(display,windows,MagickFalse);
10901  (void) XFreeCursor(display,cursor);
10902  if ((state & EscapeState) != 0)
10903    return(MagickTrue);
10904  /*
10905    Image pasting is relative to image configuration.
10906  */
10907  XSetCursorState(display,windows,MagickTrue);
10908  XCheckRefreshWindows(display,windows);
10909  width=(unsigned int) image->columns;
10910  height=(unsigned int) image->rows;
10911  x=0;
10912  y=0;
10913  if (windows->image.crop_geometry != (char *) NULL)
10914    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10915  scale_factor=(MagickRealType) width/windows->image.ximage->width;
10916  paste_info.x+=x;
10917  paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10918  paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10919  scale_factor=(MagickRealType) height/windows->image.ximage->height;
10920  paste_info.y+=y;
10921  paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10922  paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10923  /*
10924    Paste image with X Image window.
10925  */
10926  (void) CompositeImage(image,compose,paste_image,paste_info.x,paste_info.y,
10927    exception);
10928  paste_image=DestroyImage(paste_image);
10929  XSetCursorState(display,windows,MagickFalse);
10930  /*
10931    Update image colormap.
10932  */
10933  XConfigureImageColormap(display,resource_info,windows,image);
10934  (void) XConfigureImage(display,resource_info,windows,image,exception);
10935  return(MagickTrue);
10936}
10937
10938/*
10939%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10940%                                                                             %
10941%                                                                             %
10942%                                                                             %
10943+   X P r i n t I m a g e                                                     %
10944%                                                                             %
10945%                                                                             %
10946%                                                                             %
10947%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10948%
10949%  XPrintImage() prints an image to a Postscript printer.
10950%
10951%  The format of the XPrintImage method is:
10952%
10953%      MagickBooleanType XPrintImage(Display *display,
10954%        XResourceInfo *resource_info,XWindows *windows,Image *image,
10955%        ExceptionInfo *exception)
10956%
10957%  A description of each parameter follows:
10958%
10959%    o display: Specifies a connection to an X server; returned from
10960%      XOpenDisplay.
10961%
10962%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10963%
10964%    o windows: Specifies a pointer to a XWindows structure.
10965%
10966%    o image: the image.
10967%
10968%    o exception: return any errors or warnings in this structure.
10969%
10970*/
10971static MagickBooleanType XPrintImage(Display *display,
10972  XResourceInfo *resource_info,XWindows *windows,Image *image,
10973  ExceptionInfo *exception)
10974{
10975  char
10976    filename[MaxTextExtent],
10977    geometry[MaxTextExtent];
10978
10979  Image
10980    *print_image;
10981
10982  ImageInfo
10983    *image_info;
10984
10985  MagickStatusType
10986    status;
10987
10988  /*
10989    Request Postscript page geometry from user.
10990  */
10991  image_info=CloneImageInfo(resource_info->image_info);
10992  (void) FormatLocaleString(geometry,MaxTextExtent,"Letter");
10993  if (image_info->page != (char *) NULL)
10994    (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
10995  XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
10996    "Select Postscript Page Geometry:",geometry);
10997  if (*geometry == '\0')
10998    return(MagickTrue);
10999  image_info->page=GetPageGeometry(geometry);
11000  /*
11001    Apply image transforms.
11002  */
11003  XSetCursorState(display,windows,MagickTrue);
11004  XCheckRefreshWindows(display,windows);
11005  print_image=CloneImage(image,0,0,MagickTrue,exception);
11006  if (print_image == (Image *) NULL)
11007    return(MagickFalse);
11008  (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
11009    windows->image.ximage->width,windows->image.ximage->height);
11010  (void) TransformImage(&print_image,windows->image.crop_geometry,geometry,
11011    exception);
11012  /*
11013    Print image.
11014  */
11015  (void) AcquireUniqueFilename(filename);
11016  (void) FormatLocaleString(print_image->filename,MaxTextExtent,"print:%s",
11017    filename);
11018  status=WriteImage(image_info,print_image,exception);
11019  (void) RelinquishUniqueFileResource(filename);
11020  print_image=DestroyImage(print_image);
11021  image_info=DestroyImageInfo(image_info);
11022  XSetCursorState(display,windows,MagickFalse);
11023  return(status != 0 ? MagickTrue : MagickFalse);
11024}
11025
11026/*
11027%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11028%                                                                             %
11029%                                                                             %
11030%                                                                             %
11031+   X R O I I m a g e                                                         %
11032%                                                                             %
11033%                                                                             %
11034%                                                                             %
11035%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11036%
11037%  XROIImage() applies an image processing technique to a region of interest.
11038%
11039%  The format of the XROIImage method is:
11040%
11041%      MagickBooleanType XROIImage(Display *display,
11042%        XResourceInfo *resource_info,XWindows *windows,Image **image,
11043%        ExceptionInfo *exception)
11044%
11045%  A description of each parameter follows:
11046%
11047%    o display: Specifies a connection to an X server; returned from
11048%      XOpenDisplay.
11049%
11050%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11051%
11052%    o windows: Specifies a pointer to a XWindows structure.
11053%
11054%    o image: the image; returned from ReadImage.
11055%
11056%    o exception: return any errors or warnings in this structure.
11057%
11058*/
11059static MagickBooleanType XROIImage(Display *display,
11060  XResourceInfo *resource_info,XWindows *windows,Image **image,
11061  ExceptionInfo *exception)
11062{
11063#define ApplyMenus  7
11064
11065  static const char
11066    *ROIMenu[] =
11067    {
11068      "Help",
11069      "Dismiss",
11070      (char *) NULL
11071    },
11072    *ApplyMenu[] =
11073    {
11074      "File",
11075      "Edit",
11076      "Transform",
11077      "Enhance",
11078      "Effects",
11079      "F/X",
11080      "Miscellany",
11081      "Help",
11082      "Dismiss",
11083      (char *) NULL
11084    },
11085    *FileMenu[] =
11086    {
11087      "Save...",
11088      "Print...",
11089      (char *) NULL
11090    },
11091    *EditMenu[] =
11092    {
11093      "Undo",
11094      "Redo",
11095      (char *) NULL
11096    },
11097    *TransformMenu[] =
11098    {
11099      "Flop",
11100      "Flip",
11101      "Rotate Right",
11102      "Rotate Left",
11103      (char *) NULL
11104    },
11105    *EnhanceMenu[] =
11106    {
11107      "Hue...",
11108      "Saturation...",
11109      "Brightness...",
11110      "Gamma...",
11111      "Spiff",
11112      "Dull",
11113      "Contrast Stretch...",
11114      "Sigmoidal Contrast...",
11115      "Normalize",
11116      "Equalize",
11117      "Negate",
11118      "Grayscale",
11119      "Map...",
11120      "Quantize...",
11121      (char *) NULL
11122    },
11123    *EffectsMenu[] =
11124    {
11125      "Despeckle",
11126      "Emboss",
11127      "Reduce Noise",
11128      "Add Noise",
11129      "Sharpen...",
11130      "Blur...",
11131      "Threshold...",
11132      "Edge Detect...",
11133      "Spread...",
11134      "Shade...",
11135      "Raise...",
11136      "Segment...",
11137      (char *) NULL
11138    },
11139    *FXMenu[] =
11140    {
11141      "Solarize...",
11142      "Sepia Tone...",
11143      "Swirl...",
11144      "Implode...",
11145      "Vignette...",
11146      "Wave...",
11147      "Oil Paint...",
11148      "Charcoal Draw...",
11149      (char *) NULL
11150    },
11151    *MiscellanyMenu[] =
11152    {
11153      "Image Info",
11154      "Zoom Image",
11155      "Show Preview...",
11156      "Show Histogram",
11157      "Show Matte",
11158      (char *) NULL
11159    };
11160
11161  static const char
11162    **Menus[ApplyMenus] =
11163    {
11164      FileMenu,
11165      EditMenu,
11166      TransformMenu,
11167      EnhanceMenu,
11168      EffectsMenu,
11169      FXMenu,
11170      MiscellanyMenu
11171    };
11172
11173  static const CommandType
11174    ApplyCommands[] =
11175    {
11176      NullCommand,
11177      NullCommand,
11178      NullCommand,
11179      NullCommand,
11180      NullCommand,
11181      NullCommand,
11182      NullCommand,
11183      HelpCommand,
11184      QuitCommand
11185    },
11186    FileCommands[] =
11187    {
11188      SaveCommand,
11189      PrintCommand
11190    },
11191    EditCommands[] =
11192    {
11193      UndoCommand,
11194      RedoCommand
11195    },
11196    TransformCommands[] =
11197    {
11198      FlopCommand,
11199      FlipCommand,
11200      RotateRightCommand,
11201      RotateLeftCommand
11202    },
11203    EnhanceCommands[] =
11204    {
11205      HueCommand,
11206      SaturationCommand,
11207      BrightnessCommand,
11208      GammaCommand,
11209      SpiffCommand,
11210      DullCommand,
11211      ContrastStretchCommand,
11212      SigmoidalContrastCommand,
11213      NormalizeCommand,
11214      EqualizeCommand,
11215      NegateCommand,
11216      GrayscaleCommand,
11217      MapCommand,
11218      QuantizeCommand
11219    },
11220    EffectsCommands[] =
11221    {
11222      DespeckleCommand,
11223      EmbossCommand,
11224      ReduceNoiseCommand,
11225      AddNoiseCommand,
11226      SharpenCommand,
11227      BlurCommand,
11228      EdgeDetectCommand,
11229      SpreadCommand,
11230      ShadeCommand,
11231      RaiseCommand,
11232      SegmentCommand
11233    },
11234    FXCommands[] =
11235    {
11236      SolarizeCommand,
11237      SepiaToneCommand,
11238      SwirlCommand,
11239      ImplodeCommand,
11240      VignetteCommand,
11241      WaveCommand,
11242      OilPaintCommand,
11243      CharcoalDrawCommand
11244    },
11245    MiscellanyCommands[] =
11246    {
11247      InfoCommand,
11248      ZoomCommand,
11249      ShowPreviewCommand,
11250      ShowHistogramCommand,
11251      ShowMatteCommand
11252    },
11253    ROICommands[] =
11254    {
11255      ROIHelpCommand,
11256      ROIDismissCommand
11257    };
11258
11259  static const CommandType
11260    *Commands[ApplyMenus] =
11261    {
11262      FileCommands,
11263      EditCommands,
11264      TransformCommands,
11265      EnhanceCommands,
11266      EffectsCommands,
11267      FXCommands,
11268      MiscellanyCommands
11269    };
11270
11271  char
11272    command[MaxTextExtent],
11273    text[MaxTextExtent];
11274
11275  CommandType
11276    command_type;
11277
11278  Cursor
11279    cursor;
11280
11281  Image
11282    *roi_image;
11283
11284  int
11285    entry,
11286    id,
11287    x,
11288    y;
11289
11290  MagickRealType
11291    scale_factor;
11292
11293  MagickProgressMonitor
11294    progress_monitor;
11295
11296  RectangleInfo
11297    crop_info,
11298    highlight_info,
11299    roi_info;
11300
11301  unsigned int
11302    height,
11303    width;
11304
11305  size_t
11306    state;
11307
11308  XEvent
11309    event;
11310
11311  /*
11312    Map Command widget.
11313  */
11314  (void) CloneString(&windows->command.name,"ROI");
11315  windows->command.data=0;
11316  (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11317  (void) XMapRaised(display,windows->command.id);
11318  XClientMessage(display,windows->image.id,windows->im_protocols,
11319    windows->im_update_widget,CurrentTime);
11320  /*
11321    Track pointer until button 1 is pressed.
11322  */
11323  XQueryPosition(display,windows->image.id,&x,&y);
11324  (void) XSelectInput(display,windows->image.id,
11325    windows->image.attributes.event_mask | PointerMotionMask);
11326  roi_info.x=(ssize_t) windows->image.x+x;
11327  roi_info.y=(ssize_t) windows->image.y+y;
11328  roi_info.width=0;
11329  roi_info.height=0;
11330  cursor=XCreateFontCursor(display,XC_fleur);
11331  state=DefaultState;
11332  do
11333  {
11334    if (windows->info.mapped != MagickFalse)
11335      {
11336        /*
11337          Display pointer position.
11338        */
11339        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
11340          (long) roi_info.x,(long) roi_info.y);
11341        XInfoWidget(display,windows,text);
11342      }
11343    /*
11344      Wait for next event.
11345    */
11346    XScreenEvent(display,windows,&event);
11347    if (event.xany.window == windows->command.id)
11348      {
11349        /*
11350          Select a command from the Command widget.
11351        */
11352        id=XCommandWidget(display,windows,ROIMenu,&event);
11353        if (id < 0)
11354          continue;
11355        switch (ROICommands[id])
11356        {
11357          case ROIHelpCommand:
11358          {
11359            XTextViewWidget(display,resource_info,windows,MagickFalse,
11360              "Help Viewer - Region of Interest",ImageROIHelp);
11361            break;
11362          }
11363          case ROIDismissCommand:
11364          {
11365            /*
11366              Prematurely exit.
11367            */
11368            state|=EscapeState;
11369            state|=ExitState;
11370            break;
11371          }
11372          default:
11373            break;
11374        }
11375        continue;
11376      }
11377    switch (event.type)
11378    {
11379      case ButtonPress:
11380      {
11381        if (event.xbutton.button != Button1)
11382          break;
11383        if (event.xbutton.window != windows->image.id)
11384          break;
11385        /*
11386          Note first corner of region of interest rectangle-- exit loop.
11387        */
11388        (void) XCheckDefineCursor(display,windows->image.id,cursor);
11389        roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11390        roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11391        state|=ExitState;
11392        break;
11393      }
11394      case ButtonRelease:
11395        break;
11396      case Expose:
11397        break;
11398      case KeyPress:
11399      {
11400        KeySym
11401          key_symbol;
11402
11403        if (event.xkey.window != windows->image.id)
11404          break;
11405        /*
11406          Respond to a user key press.
11407        */
11408        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11409          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11410        switch ((int) key_symbol)
11411        {
11412          case XK_Escape:
11413          case XK_F20:
11414          {
11415            /*
11416              Prematurely exit.
11417            */
11418            state|=EscapeState;
11419            state|=ExitState;
11420            break;
11421          }
11422          case XK_F1:
11423          case XK_Help:
11424          {
11425            XTextViewWidget(display,resource_info,windows,MagickFalse,
11426              "Help Viewer - Region of Interest",ImageROIHelp);
11427            break;
11428          }
11429          default:
11430          {
11431            (void) XBell(display,0);
11432            break;
11433          }
11434        }
11435        break;
11436      }
11437      case MotionNotify:
11438      {
11439        /*
11440          Map and unmap Info widget as text cursor crosses its boundaries.
11441        */
11442        x=event.xmotion.x;
11443        y=event.xmotion.y;
11444        if (windows->info.mapped != MagickFalse)
11445          {
11446            if ((x < (int) (windows->info.x+windows->info.width)) &&
11447                (y < (int) (windows->info.y+windows->info.height)))
11448              (void) XWithdrawWindow(display,windows->info.id,
11449                windows->info.screen);
11450          }
11451        else
11452          if ((x > (int) (windows->info.x+windows->info.width)) ||
11453              (y > (int) (windows->info.y+windows->info.height)))
11454            (void) XMapWindow(display,windows->info.id);
11455        roi_info.x=(ssize_t) windows->image.x+x;
11456        roi_info.y=(ssize_t) windows->image.y+y;
11457        break;
11458      }
11459      default:
11460        break;
11461    }
11462  } while ((state & ExitState) == 0);
11463  (void) XSelectInput(display,windows->image.id,
11464    windows->image.attributes.event_mask);
11465  if ((state & EscapeState) != 0)
11466    {
11467      /*
11468        User want to exit without region of interest.
11469      */
11470      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11471      (void) XFreeCursor(display,cursor);
11472      return(MagickTrue);
11473    }
11474  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11475  do
11476  {
11477    /*
11478      Size rectangle as pointer moves until the mouse button is released.
11479    */
11480    x=(int) roi_info.x;
11481    y=(int) roi_info.y;
11482    roi_info.width=0;
11483    roi_info.height=0;
11484    state=DefaultState;
11485    do
11486    {
11487      highlight_info=roi_info;
11488      highlight_info.x=roi_info.x-windows->image.x;
11489      highlight_info.y=roi_info.y-windows->image.y;
11490      if ((highlight_info.width > 3) && (highlight_info.height > 3))
11491        {
11492          /*
11493            Display info and draw region of interest rectangle.
11494          */
11495          if (windows->info.mapped == MagickFalse)
11496            (void) XMapWindow(display,windows->info.id);
11497          (void) FormatLocaleString(text,MaxTextExtent,
11498            " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11499            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11500          XInfoWidget(display,windows,text);
11501          XHighlightRectangle(display,windows->image.id,
11502            windows->image.highlight_context,&highlight_info);
11503        }
11504      else
11505        if (windows->info.mapped != MagickFalse)
11506          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11507      /*
11508        Wait for next event.
11509      */
11510      XScreenEvent(display,windows,&event);
11511      if ((highlight_info.width > 3) && (highlight_info.height > 3))
11512        XHighlightRectangle(display,windows->image.id,
11513          windows->image.highlight_context,&highlight_info);
11514      switch (event.type)
11515      {
11516        case ButtonPress:
11517        {
11518          roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11519          roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11520          break;
11521        }
11522        case ButtonRelease:
11523        {
11524          /*
11525            User has committed to region of interest rectangle.
11526          */
11527          roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11528          roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11529          XSetCursorState(display,windows,MagickFalse);
11530          state|=ExitState;
11531          if (LocaleCompare(windows->command.name,"Apply") == 0)
11532            break;
11533          (void) CloneString(&windows->command.name,"Apply");
11534          windows->command.data=ApplyMenus;
11535          (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11536          break;
11537        }
11538        case Expose:
11539          break;
11540        case MotionNotify:
11541        {
11542          roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11543          roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11544        }
11545        default:
11546          break;
11547      }
11548      if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11549          ((state & ExitState) != 0))
11550        {
11551          /*
11552            Check boundary conditions.
11553          */
11554          if (roi_info.x < 0)
11555            roi_info.x=0;
11556          else
11557            if (roi_info.x > (ssize_t) windows->image.ximage->width)
11558              roi_info.x=(ssize_t) windows->image.ximage->width;
11559          if ((int) roi_info.x < x)
11560            roi_info.width=(unsigned int) (x-roi_info.x);
11561          else
11562            {
11563              roi_info.width=(unsigned int) (roi_info.x-x);
11564              roi_info.x=(ssize_t) x;
11565            }
11566          if (roi_info.y < 0)
11567            roi_info.y=0;
11568          else
11569            if (roi_info.y > (ssize_t) windows->image.ximage->height)
11570              roi_info.y=(ssize_t) windows->image.ximage->height;
11571          if ((int) roi_info.y < y)
11572            roi_info.height=(unsigned int) (y-roi_info.y);
11573          else
11574            {
11575              roi_info.height=(unsigned int) (roi_info.y-y);
11576              roi_info.y=(ssize_t) y;
11577            }
11578        }
11579    } while ((state & ExitState) == 0);
11580    /*
11581      Wait for user to grab a corner of the rectangle or press return.
11582    */
11583    state=DefaultState;
11584    command_type=NullCommand;
11585    (void) XMapWindow(display,windows->info.id);
11586    do
11587    {
11588      if (windows->info.mapped != MagickFalse)
11589        {
11590          /*
11591            Display pointer position.
11592          */
11593          (void) FormatLocaleString(text,MaxTextExtent,
11594            " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11595            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11596          XInfoWidget(display,windows,text);
11597        }
11598      highlight_info=roi_info;
11599      highlight_info.x=roi_info.x-windows->image.x;
11600      highlight_info.y=roi_info.y-windows->image.y;
11601      if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11602        {
11603          state|=EscapeState;
11604          state|=ExitState;
11605          break;
11606        }
11607      if ((state & UpdateRegionState) != 0)
11608        {
11609          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11610          switch (command_type)
11611          {
11612            case UndoCommand:
11613            case RedoCommand:
11614            {
11615              (void) XMagickCommand(display,resource_info,windows,command_type,
11616                image,exception);
11617              break;
11618            }
11619            default:
11620            {
11621              /*
11622                Region of interest is relative to image configuration.
11623              */
11624              progress_monitor=SetImageProgressMonitor(*image,
11625                (MagickProgressMonitor) NULL,(*image)->client_data);
11626              crop_info=roi_info;
11627              width=(unsigned int) (*image)->columns;
11628              height=(unsigned int) (*image)->rows;
11629              x=0;
11630              y=0;
11631              if (windows->image.crop_geometry != (char *) NULL)
11632                (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11633                  &width,&height);
11634              scale_factor=(MagickRealType) width/windows->image.ximage->width;
11635              crop_info.x+=x;
11636              crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11637              crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11638              scale_factor=(MagickRealType)
11639                height/windows->image.ximage->height;
11640              crop_info.y+=y;
11641              crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11642              crop_info.height=(unsigned int)
11643                (scale_factor*crop_info.height+0.5);
11644              roi_image=CropImage(*image,&crop_info,exception);
11645              (void) SetImageProgressMonitor(*image,progress_monitor,
11646                (*image)->client_data);
11647              if (roi_image == (Image *) NULL)
11648                continue;
11649              /*
11650                Apply image processing technique to the region of interest.
11651              */
11652              windows->image.orphan=MagickTrue;
11653              (void) XMagickCommand(display,resource_info,windows,command_type,
11654                &roi_image,exception);
11655              progress_monitor=SetImageProgressMonitor(*image,
11656                (MagickProgressMonitor) NULL,(*image)->client_data);
11657              (void) XMagickCommand(display,resource_info,windows,
11658                SaveToUndoBufferCommand,image,exception);
11659              windows->image.orphan=MagickFalse;
11660              (void) CompositeImage(*image,CopyCompositeOp,roi_image,
11661                crop_info.x,crop_info.y,exception);
11662              roi_image=DestroyImage(roi_image);
11663              (void) SetImageProgressMonitor(*image,progress_monitor,
11664                (*image)->client_data);
11665              break;
11666            }
11667          }
11668          if (command_type != InfoCommand)
11669            {
11670              XConfigureImageColormap(display,resource_info,windows,*image);
11671              (void) XConfigureImage(display,resource_info,windows,*image,exception);
11672            }
11673          XCheckRefreshWindows(display,windows);
11674          XInfoWidget(display,windows,text);
11675          (void) XSetFunction(display,windows->image.highlight_context,
11676            GXinvert);
11677          state&=(~UpdateRegionState);
11678        }
11679      XHighlightRectangle(display,windows->image.id,
11680        windows->image.highlight_context,&highlight_info);
11681      XScreenEvent(display,windows,&event);
11682      if (event.xany.window == windows->command.id)
11683        {
11684          /*
11685            Select a command from the Command widget.
11686          */
11687          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11688          command_type=NullCommand;
11689          id=XCommandWidget(display,windows,ApplyMenu,&event);
11690          if (id >= 0)
11691            {
11692              (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11693              command_type=ApplyCommands[id];
11694              if (id < ApplyMenus)
11695                {
11696                  /*
11697                    Select a command from a pop-up menu.
11698                  */
11699                  entry=XMenuWidget(display,windows,ApplyMenu[id],
11700                    (const char **) Menus[id],command);
11701                  if (entry >= 0)
11702                    {
11703                      (void) CopyMagickString(command,Menus[id][entry],
11704                        MaxTextExtent);
11705                      command_type=Commands[id][entry];
11706                    }
11707                }
11708            }
11709          (void) XSetFunction(display,windows->image.highlight_context,
11710            GXinvert);
11711          XHighlightRectangle(display,windows->image.id,
11712            windows->image.highlight_context,&highlight_info);
11713          if (command_type == HelpCommand)
11714            {
11715              (void) XSetFunction(display,windows->image.highlight_context,
11716                GXcopy);
11717              XTextViewWidget(display,resource_info,windows,MagickFalse,
11718                "Help Viewer - Region of Interest",ImageROIHelp);
11719              (void) XSetFunction(display,windows->image.highlight_context,
11720                GXinvert);
11721              continue;
11722            }
11723          if (command_type == QuitCommand)
11724            {
11725              /*
11726                exit.
11727              */
11728              state|=EscapeState;
11729              state|=ExitState;
11730              continue;
11731            }
11732          if (command_type != NullCommand)
11733            state|=UpdateRegionState;
11734          continue;
11735        }
11736      XHighlightRectangle(display,windows->image.id,
11737        windows->image.highlight_context,&highlight_info);
11738      switch (event.type)
11739      {
11740        case ButtonPress:
11741        {
11742          x=windows->image.x;
11743          y=windows->image.y;
11744          if (event.xbutton.button != Button1)
11745            break;
11746          if (event.xbutton.window != windows->image.id)
11747            break;
11748          x=windows->image.x+event.xbutton.x;
11749          y=windows->image.y+event.xbutton.y;
11750          if ((x < (int) (roi_info.x+RoiDelta)) &&
11751              (x > (int) (roi_info.x-RoiDelta)) &&
11752              (y < (int) (roi_info.y+RoiDelta)) &&
11753              (y > (int) (roi_info.y-RoiDelta)))
11754            {
11755              roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11756              roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11757              state|=UpdateConfigurationState;
11758              break;
11759            }
11760          if ((x < (int) (roi_info.x+RoiDelta)) &&
11761              (x > (int) (roi_info.x-RoiDelta)) &&
11762              (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11763              (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11764            {
11765              roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11766              state|=UpdateConfigurationState;
11767              break;
11768            }
11769          if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11770              (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11771              (y < (int) (roi_info.y+RoiDelta)) &&
11772              (y > (int) (roi_info.y-RoiDelta)))
11773            {
11774              roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11775              state|=UpdateConfigurationState;
11776              break;
11777            }
11778          if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11779              (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11780              (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11781              (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11782            {
11783              state|=UpdateConfigurationState;
11784              break;
11785            }
11786        }
11787        case ButtonRelease:
11788        {
11789          if (event.xbutton.window == windows->pan.id)
11790            if ((highlight_info.x != crop_info.x-windows->image.x) ||
11791                (highlight_info.y != crop_info.y-windows->image.y))
11792              XHighlightRectangle(display,windows->image.id,
11793                windows->image.highlight_context,&highlight_info);
11794          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11795            event.xbutton.time);
11796          break;
11797        }
11798        case Expose:
11799        {
11800          if (event.xexpose.window == windows->image.id)
11801            if (event.xexpose.count == 0)
11802              {
11803                event.xexpose.x=(int) highlight_info.x;
11804                event.xexpose.y=(int) highlight_info.y;
11805                event.xexpose.width=(int) highlight_info.width;
11806                event.xexpose.height=(int) highlight_info.height;
11807                XRefreshWindow(display,&windows->image,&event);
11808              }
11809          if (event.xexpose.window == windows->info.id)
11810            if (event.xexpose.count == 0)
11811              XInfoWidget(display,windows,text);
11812          break;
11813        }
11814        case KeyPress:
11815        {
11816          KeySym
11817            key_symbol;
11818
11819          if (event.xkey.window != windows->image.id)
11820            break;
11821          /*
11822            Respond to a user key press.
11823          */
11824          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11825            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11826          switch ((int) key_symbol)
11827          {
11828            case XK_Shift_L:
11829            case XK_Shift_R:
11830              break;
11831            case XK_Escape:
11832            case XK_F20:
11833              state|=EscapeState;
11834            case XK_Return:
11835            {
11836              state|=ExitState;
11837              break;
11838            }
11839            case XK_Home:
11840            case XK_KP_Home:
11841            {
11842              roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11843              roi_info.y=(ssize_t) (windows->image.height/2L-
11844                roi_info.height/2L);
11845              break;
11846            }
11847            case XK_Left:
11848            case XK_KP_Left:
11849            {
11850              roi_info.x--;
11851              break;
11852            }
11853            case XK_Up:
11854            case XK_KP_Up:
11855            case XK_Next:
11856            {
11857              roi_info.y--;
11858              break;
11859            }
11860            case XK_Right:
11861            case XK_KP_Right:
11862            {
11863              roi_info.x++;
11864              break;
11865            }
11866            case XK_Prior:
11867            case XK_Down:
11868            case XK_KP_Down:
11869            {
11870              roi_info.y++;
11871              break;
11872            }
11873            case XK_F1:
11874            case XK_Help:
11875            {
11876              (void) XSetFunction(display,windows->image.highlight_context,
11877                GXcopy);
11878              XTextViewWidget(display,resource_info,windows,MagickFalse,
11879                "Help Viewer - Region of Interest",ImageROIHelp);
11880              (void) XSetFunction(display,windows->image.highlight_context,
11881                GXinvert);
11882              break;
11883            }
11884            default:
11885            {
11886              command_type=XImageWindowCommand(display,resource_info,windows,
11887                event.xkey.state,key_symbol,image,exception);
11888              if (command_type != NullCommand)
11889                state|=UpdateRegionState;
11890              break;
11891            }
11892          }
11893          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11894            event.xkey.time);
11895          break;
11896        }
11897        case KeyRelease:
11898          break;
11899        case MotionNotify:
11900        {
11901          if (event.xbutton.window != windows->image.id)
11902            break;
11903          /*
11904            Map and unmap Info widget as text cursor crosses its boundaries.
11905          */
11906          x=event.xmotion.x;
11907          y=event.xmotion.y;
11908          if (windows->info.mapped != MagickFalse)
11909            {
11910              if ((x < (int) (windows->info.x+windows->info.width)) &&
11911                  (y < (int) (windows->info.y+windows->info.height)))
11912                (void) XWithdrawWindow(display,windows->info.id,
11913                  windows->info.screen);
11914            }
11915          else
11916            if ((x > (int) (windows->info.x+windows->info.width)) ||
11917                (y > (int) (windows->info.y+windows->info.height)))
11918              (void) XMapWindow(display,windows->info.id);
11919          roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11920          roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11921          break;
11922        }
11923        case SelectionRequest:
11924        {
11925          XSelectionEvent
11926            notify;
11927
11928          XSelectionRequestEvent
11929            *request;
11930
11931          /*
11932            Set primary selection.
11933          */
11934          (void) FormatLocaleString(text,MaxTextExtent,
11935            "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11936            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11937          request=(&(event.xselectionrequest));
11938          (void) XChangeProperty(request->display,request->requestor,
11939            request->property,request->target,8,PropModeReplace,
11940            (unsigned char *) text,(int) strlen(text));
11941          notify.type=SelectionNotify;
11942          notify.display=request->display;
11943          notify.requestor=request->requestor;
11944          notify.selection=request->selection;
11945          notify.target=request->target;
11946          notify.time=request->time;
11947          if (request->property == None)
11948            notify.property=request->target;
11949          else
11950            notify.property=request->property;
11951          (void) XSendEvent(request->display,request->requestor,False,0,
11952            (XEvent *) &notify);
11953        }
11954        default:
11955          break;
11956      }
11957      if ((state & UpdateConfigurationState) != 0)
11958        {
11959          (void) XPutBackEvent(display,&event);
11960          (void) XCheckDefineCursor(display,windows->image.id,cursor);
11961          break;
11962        }
11963    } while ((state & ExitState) == 0);
11964  } while ((state & ExitState) == 0);
11965  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11966  XSetCursorState(display,windows,MagickFalse);
11967  if ((state & EscapeState) != 0)
11968    return(MagickTrue);
11969  return(MagickTrue);
11970}
11971
11972/*
11973%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11974%                                                                             %
11975%                                                                             %
11976%                                                                             %
11977+   X R o t a t e I m a g e                                                   %
11978%                                                                             %
11979%                                                                             %
11980%                                                                             %
11981%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11982%
11983%  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
11984%  rotation angle is computed from the slope of a line drawn by the user.
11985%
11986%  The format of the XRotateImage method is:
11987%
11988%      MagickBooleanType XRotateImage(Display *display,
11989%        XResourceInfo *resource_info,XWindows *windows,double degrees,
11990%        Image **image,ExceptionInfo *exception)
11991%
11992%  A description of each parameter follows:
11993%
11994%    o display: Specifies a connection to an X server; returned from
11995%      XOpenDisplay.
11996%
11997%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11998%
11999%    o windows: Specifies a pointer to a XWindows structure.
12000%
12001%    o degrees: Specifies the number of degrees to rotate the image.
12002%
12003%    o image: the image.
12004%
12005%    o exception: return any errors or warnings in this structure.
12006%
12007*/
12008static MagickBooleanType XRotateImage(Display *display,
12009  XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
12010  ExceptionInfo *exception)
12011{
12012  static const char
12013    *RotateMenu[] =
12014    {
12015      "Pixel Color",
12016      "Direction",
12017      "Help",
12018      "Dismiss",
12019      (char *) NULL
12020    };
12021
12022  static ModeType
12023    direction = HorizontalRotateCommand;
12024
12025  static const ModeType
12026    DirectionCommands[] =
12027    {
12028      HorizontalRotateCommand,
12029      VerticalRotateCommand
12030    },
12031    RotateCommands[] =
12032    {
12033      RotateColorCommand,
12034      RotateDirectionCommand,
12035      RotateHelpCommand,
12036      RotateDismissCommand
12037    };
12038
12039  static unsigned int
12040    pen_id = 0;
12041
12042  char
12043    command[MaxTextExtent],
12044    text[MaxTextExtent];
12045
12046  Image
12047    *rotate_image;
12048
12049  int
12050    id,
12051    x,
12052    y;
12053
12054  MagickRealType
12055    normalized_degrees;
12056
12057  register int
12058    i;
12059
12060  unsigned int
12061    height,
12062    rotations,
12063    width;
12064
12065  if (degrees == 0.0)
12066    {
12067      unsigned int
12068        distance;
12069
12070      size_t
12071        state;
12072
12073      XEvent
12074        event;
12075
12076      XSegment
12077        rotate_info;
12078
12079      /*
12080        Map Command widget.
12081      */
12082      (void) CloneString(&windows->command.name,"Rotate");
12083      windows->command.data=2;
12084      (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12085      (void) XMapRaised(display,windows->command.id);
12086      XClientMessage(display,windows->image.id,windows->im_protocols,
12087        windows->im_update_widget,CurrentTime);
12088      /*
12089        Wait for first button press.
12090      */
12091      (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12092      XQueryPosition(display,windows->image.id,&x,&y);
12093      rotate_info.x1=x;
12094      rotate_info.y1=y;
12095      rotate_info.x2=x;
12096      rotate_info.y2=y;
12097      state=DefaultState;
12098      do
12099      {
12100        XHighlightLine(display,windows->image.id,
12101          windows->image.highlight_context,&rotate_info);
12102        /*
12103          Wait for next event.
12104        */
12105        XScreenEvent(display,windows,&event);
12106        XHighlightLine(display,windows->image.id,
12107          windows->image.highlight_context,&rotate_info);
12108        if (event.xany.window == windows->command.id)
12109          {
12110            /*
12111              Select a command from the Command widget.
12112            */
12113            id=XCommandWidget(display,windows,RotateMenu,&event);
12114            if (id < 0)
12115              continue;
12116            (void) XSetFunction(display,windows->image.highlight_context,
12117              GXcopy);
12118            switch (RotateCommands[id])
12119            {
12120              case RotateColorCommand:
12121              {
12122                const char
12123                  *ColorMenu[MaxNumberPens];
12124
12125                int
12126                  pen_number;
12127
12128                XColor
12129                  color;
12130
12131                /*
12132                  Initialize menu selections.
12133                */
12134                for (i=0; i < (int) (MaxNumberPens-2); i++)
12135                  ColorMenu[i]=resource_info->pen_colors[i];
12136                ColorMenu[MaxNumberPens-2]="Browser...";
12137                ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12138                /*
12139                  Select a pen color from the pop-up menu.
12140                */
12141                pen_number=XMenuWidget(display,windows,RotateMenu[id],
12142                  (const char **) ColorMenu,command);
12143                if (pen_number < 0)
12144                  break;
12145                if (pen_number == (MaxNumberPens-2))
12146                  {
12147                    static char
12148                      color_name[MaxTextExtent] = "gray";
12149
12150                    /*
12151                      Select a pen color from a dialog.
12152                    */
12153                    resource_info->pen_colors[pen_number]=color_name;
12154                    XColorBrowserWidget(display,windows,"Select",color_name);
12155                    if (*color_name == '\0')
12156                      break;
12157                  }
12158                /*
12159                  Set pen color.
12160                */
12161                (void) XParseColor(display,windows->map_info->colormap,
12162                  resource_info->pen_colors[pen_number],&color);
12163                XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12164                  (unsigned int) MaxColors,&color);
12165                windows->pixel_info->pen_colors[pen_number]=color;
12166                pen_id=(unsigned int) pen_number;
12167                break;
12168              }
12169              case RotateDirectionCommand:
12170              {
12171                static const char
12172                  *Directions[] =
12173                  {
12174                    "horizontal",
12175                    "vertical",
12176                    (char *) NULL,
12177                  };
12178
12179                /*
12180                  Select a command from the pop-up menu.
12181                */
12182                id=XMenuWidget(display,windows,RotateMenu[id],
12183                  Directions,command);
12184                if (id >= 0)
12185                  direction=DirectionCommands[id];
12186                break;
12187              }
12188              case RotateHelpCommand:
12189              {
12190                XTextViewWidget(display,resource_info,windows,MagickFalse,
12191                  "Help Viewer - Image Rotation",ImageRotateHelp);
12192                break;
12193              }
12194              case RotateDismissCommand:
12195              {
12196                /*
12197                  Prematurely exit.
12198                */
12199                state|=EscapeState;
12200                state|=ExitState;
12201                break;
12202              }
12203              default:
12204                break;
12205            }
12206            (void) XSetFunction(display,windows->image.highlight_context,
12207              GXinvert);
12208            continue;
12209          }
12210        switch (event.type)
12211        {
12212          case ButtonPress:
12213          {
12214            if (event.xbutton.button != Button1)
12215              break;
12216            if (event.xbutton.window != windows->image.id)
12217              break;
12218            /*
12219              exit loop.
12220            */
12221            (void) XSetFunction(display,windows->image.highlight_context,
12222              GXcopy);
12223            rotate_info.x1=event.xbutton.x;
12224            rotate_info.y1=event.xbutton.y;
12225            state|=ExitState;
12226            break;
12227          }
12228          case ButtonRelease:
12229            break;
12230          case Expose:
12231            break;
12232          case KeyPress:
12233          {
12234            char
12235              command[MaxTextExtent];
12236
12237            KeySym
12238              key_symbol;
12239
12240            if (event.xkey.window != windows->image.id)
12241              break;
12242            /*
12243              Respond to a user key press.
12244            */
12245            (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12246              sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12247            switch ((int) key_symbol)
12248            {
12249              case XK_Escape:
12250              case XK_F20:
12251              {
12252                /*
12253                  Prematurely exit.
12254                */
12255                state|=EscapeState;
12256                state|=ExitState;
12257                break;
12258              }
12259              case XK_F1:
12260              case XK_Help:
12261              {
12262                (void) XSetFunction(display,windows->image.highlight_context,
12263                  GXcopy);
12264                XTextViewWidget(display,resource_info,windows,MagickFalse,
12265                  "Help Viewer - Image Rotation",ImageRotateHelp);
12266                (void) XSetFunction(display,windows->image.highlight_context,
12267                  GXinvert);
12268                break;
12269              }
12270              default:
12271              {
12272                (void) XBell(display,0);
12273                break;
12274              }
12275            }
12276            break;
12277          }
12278          case MotionNotify:
12279          {
12280            rotate_info.x1=event.xmotion.x;
12281            rotate_info.y1=event.xmotion.y;
12282          }
12283        }
12284        rotate_info.x2=rotate_info.x1;
12285        rotate_info.y2=rotate_info.y1;
12286        if (direction == HorizontalRotateCommand)
12287          rotate_info.x2+=32;
12288        else
12289          rotate_info.y2-=32;
12290      } while ((state & ExitState) == 0);
12291      (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12292      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12293      if ((state & EscapeState) != 0)
12294        return(MagickTrue);
12295      /*
12296        Draw line as pointer moves until the mouse button is released.
12297      */
12298      distance=0;
12299      (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12300      state=DefaultState;
12301      do
12302      {
12303        if (distance > 9)
12304          {
12305            /*
12306              Display info and draw rotation line.
12307            */
12308            if (windows->info.mapped == MagickFalse)
12309              (void) XMapWindow(display,windows->info.id);
12310            (void) FormatLocaleString(text,MaxTextExtent," %g",
12311              direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12312            XInfoWidget(display,windows,text);
12313            XHighlightLine(display,windows->image.id,
12314              windows->image.highlight_context,&rotate_info);
12315          }
12316        else
12317          if (windows->info.mapped != MagickFalse)
12318            (void) XWithdrawWindow(display,windows->info.id,
12319              windows->info.screen);
12320        /*
12321          Wait for next event.
12322        */
12323        XScreenEvent(display,windows,&event);
12324        if (distance > 9)
12325          XHighlightLine(display,windows->image.id,
12326            windows->image.highlight_context,&rotate_info);
12327        switch (event.type)
12328        {
12329          case ButtonPress:
12330            break;
12331          case ButtonRelease:
12332          {
12333            /*
12334              User has committed to rotation line.
12335            */
12336            rotate_info.x2=event.xbutton.x;
12337            rotate_info.y2=event.xbutton.y;
12338            state|=ExitState;
12339            break;
12340          }
12341          case Expose:
12342            break;
12343          case MotionNotify:
12344          {
12345            rotate_info.x2=event.xmotion.x;
12346            rotate_info.y2=event.xmotion.y;
12347          }
12348          default:
12349            break;
12350        }
12351        /*
12352          Check boundary conditions.
12353        */
12354        if (rotate_info.x2 < 0)
12355          rotate_info.x2=0;
12356        else
12357          if (rotate_info.x2 > (int) windows->image.width)
12358            rotate_info.x2=(short) windows->image.width;
12359        if (rotate_info.y2 < 0)
12360          rotate_info.y2=0;
12361        else
12362          if (rotate_info.y2 > (int) windows->image.height)
12363            rotate_info.y2=(short) windows->image.height;
12364        /*
12365          Compute rotation angle from the slope of the line.
12366        */
12367        degrees=0.0;
12368        distance=(unsigned int)
12369          ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12370          ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12371        if (distance > 9)
12372          degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12373            rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12374      } while ((state & ExitState) == 0);
12375      (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12376      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12377      if (distance <= 9)
12378        return(MagickTrue);
12379    }
12380  if (direction == VerticalRotateCommand)
12381    degrees-=90.0;
12382  if (degrees == 0.0)
12383    return(MagickTrue);
12384  /*
12385    Rotate image.
12386  */
12387  normalized_degrees=degrees;
12388  while (normalized_degrees < -45.0)
12389    normalized_degrees+=360.0;
12390  for (rotations=0; normalized_degrees > 45.0; rotations++)
12391    normalized_degrees-=90.0;
12392  if (normalized_degrees != 0.0)
12393    (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12394      exception);
12395  XSetCursorState(display,windows,MagickTrue);
12396  XCheckRefreshWindows(display,windows);
12397  (*image)->background_color.red=ScaleShortToQuantum(
12398    windows->pixel_info->pen_colors[pen_id].red);
12399  (*image)->background_color.green=ScaleShortToQuantum(
12400    windows->pixel_info->pen_colors[pen_id].green);
12401  (*image)->background_color.blue=ScaleShortToQuantum(
12402    windows->pixel_info->pen_colors[pen_id].blue);
12403  rotate_image=RotateImage(*image,degrees,exception);
12404  XSetCursorState(display,windows,MagickFalse);
12405  if (rotate_image == (Image *) NULL)
12406    return(MagickFalse);
12407  *image=DestroyImage(*image);
12408  *image=rotate_image;
12409  if (windows->image.crop_geometry != (char *) NULL)
12410    {
12411      /*
12412        Rotate crop geometry.
12413      */
12414      width=(unsigned int) (*image)->columns;
12415      height=(unsigned int) (*image)->rows;
12416      (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12417      switch (rotations % 4)
12418      {
12419        default:
12420        case 0:
12421          break;
12422        case 1:
12423        {
12424          /*
12425            Rotate 90 degrees.
12426          */
12427          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12428            "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12429            (int) height-y,x);
12430          break;
12431        }
12432        case 2:
12433        {
12434          /*
12435            Rotate 180 degrees.
12436          */
12437          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12438            "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12439          break;
12440        }
12441        case 3:
12442        {
12443          /*
12444            Rotate 270 degrees.
12445          */
12446          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12447            "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12448          break;
12449        }
12450      }
12451    }
12452  if (windows->image.orphan != MagickFalse)
12453    return(MagickTrue);
12454  if (normalized_degrees != 0.0)
12455    {
12456      /*
12457        Update image colormap.
12458      */
12459      windows->image.window_changes.width=(int) (*image)->columns;
12460      windows->image.window_changes.height=(int) (*image)->rows;
12461      if (windows->image.crop_geometry != (char *) NULL)
12462        {
12463          /*
12464            Obtain dimensions of image from crop geometry.
12465          */
12466          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12467            &width,&height);
12468          windows->image.window_changes.width=(int) width;
12469          windows->image.window_changes.height=(int) height;
12470        }
12471      XConfigureImageColormap(display,resource_info,windows,*image);
12472    }
12473  else
12474    if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12475      {
12476        windows->image.window_changes.width=windows->image.ximage->height;
12477        windows->image.window_changes.height=windows->image.ximage->width;
12478      }
12479  /*
12480    Update image configuration.
12481  */
12482  (void) XConfigureImage(display,resource_info,windows,*image,exception);
12483  return(MagickTrue);
12484}
12485
12486/*
12487%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12488%                                                                             %
12489%                                                                             %
12490%                                                                             %
12491+   X S a v e I m a g e                                                       %
12492%                                                                             %
12493%                                                                             %
12494%                                                                             %
12495%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12496%
12497%  XSaveImage() saves an image to a file.
12498%
12499%  The format of the XSaveImage method is:
12500%
12501%      MagickBooleanType XSaveImage(Display *display,
12502%        XResourceInfo *resource_info,XWindows *windows,Image *image,
12503%        ExceptionInfo *exception)
12504%
12505%  A description of each parameter follows:
12506%
12507%    o display: Specifies a connection to an X server; returned from
12508%      XOpenDisplay.
12509%
12510%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12511%
12512%    o windows: Specifies a pointer to a XWindows structure.
12513%
12514%    o image: the image.
12515%
12516%    o exception: return any errors or warnings in this structure.
12517%
12518*/
12519static MagickBooleanType XSaveImage(Display *display,
12520  XResourceInfo *resource_info,XWindows *windows,Image *image,
12521  ExceptionInfo *exception)
12522{
12523  char
12524    filename[MaxTextExtent],
12525    geometry[MaxTextExtent];
12526
12527  Image
12528    *save_image;
12529
12530  ImageInfo
12531    *image_info;
12532
12533  MagickStatusType
12534    status;
12535
12536  /*
12537    Request file name from user.
12538  */
12539  if (resource_info->write_filename != (char *) NULL)
12540    (void) CopyMagickString(filename,resource_info->write_filename,
12541      MaxTextExtent);
12542  else
12543    {
12544      char
12545        path[MaxTextExtent];
12546
12547      int
12548        status;
12549
12550      GetPathComponent(image->filename,HeadPath,path);
12551      GetPathComponent(image->filename,TailPath,filename);
12552      if (*path != '\0')
12553        {
12554          status=chdir(path);
12555          if (status == -1)
12556            (void) ThrowMagickException(exception,GetMagickModule(),
12557              FileOpenError,"UnableToOpenFile","%s",path);
12558        }
12559    }
12560  XFileBrowserWidget(display,windows,"Save",filename);
12561  if (*filename == '\0')
12562    return(MagickTrue);
12563  if (IsPathAccessible(filename) != MagickFalse)
12564    {
12565      int
12566        status;
12567
12568      /*
12569        File exists-- seek user's permission before overwriting.
12570      */
12571      status=XConfirmWidget(display,windows,"Overwrite",filename);
12572      if (status <= 0)
12573        return(MagickTrue);
12574    }
12575  image_info=CloneImageInfo(resource_info->image_info);
12576  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12577  (void) SetImageInfo(image_info,1,exception);
12578  if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12579      (LocaleCompare(image_info->magick,"JPG") == 0))
12580    {
12581      char
12582        quality[MaxTextExtent];
12583
12584      int
12585        status;
12586
12587      /*
12588        Request JPEG quality from user.
12589      */
12590      (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
12591        image->quality);
12592      status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12593        quality);
12594      if (*quality == '\0')
12595        return(MagickTrue);
12596      image->quality=StringToUnsignedLong(quality);
12597      image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12598    }
12599  if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12600      (LocaleCompare(image_info->magick,"PDF") == 0) ||
12601      (LocaleCompare(image_info->magick,"PS") == 0) ||
12602      (LocaleCompare(image_info->magick,"PS2") == 0))
12603    {
12604      char
12605        geometry[MaxTextExtent];
12606
12607      /*
12608        Request page geometry from user.
12609      */
12610      (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12611      if (LocaleCompare(image_info->magick,"PDF") == 0)
12612        (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12613      if (image_info->page != (char *) NULL)
12614        (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12615      XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12616        "Select page geometry:",geometry);
12617      if (*geometry != '\0')
12618        image_info->page=GetPageGeometry(geometry);
12619    }
12620  /*
12621    Apply image transforms.
12622  */
12623  XSetCursorState(display,windows,MagickTrue);
12624  XCheckRefreshWindows(display,windows);
12625  save_image=CloneImage(image,0,0,MagickTrue,exception);
12626  if (save_image == (Image *) NULL)
12627    return(MagickFalse);
12628  (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
12629    windows->image.ximage->width,windows->image.ximage->height);
12630  (void) TransformImage(&save_image,windows->image.crop_geometry,geometry,
12631    exception);
12632  /*
12633    Write image.
12634  */
12635  (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12636  status=WriteImage(image_info,save_image,exception);
12637  if (status != MagickFalse)
12638    image->taint=MagickFalse;
12639  save_image=DestroyImage(save_image);
12640  image_info=DestroyImageInfo(image_info);
12641  XSetCursorState(display,windows,MagickFalse);
12642  return(status != 0 ? MagickTrue : MagickFalse);
12643}
12644
12645/*
12646%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12647%                                                                             %
12648%                                                                             %
12649%                                                                             %
12650+   X S c r e e n E v e n t                                                   %
12651%                                                                             %
12652%                                                                             %
12653%                                                                             %
12654%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12655%
12656%  XScreenEvent() handles global events associated with the Pan and Magnify
12657%  windows.
12658%
12659%  The format of the XScreenEvent function is:
12660%
12661%      void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12662%
12663%  A description of each parameter follows:
12664%
12665%    o display: Specifies a pointer to the Display structure;  returned from
12666%      XOpenDisplay.
12667%
12668%    o windows: Specifies a pointer to a XWindows structure.
12669%
12670%    o event: Specifies a pointer to a X11 XEvent structure.
12671%
12672%
12673*/
12674
12675#if defined(__cplusplus) || defined(c_plusplus)
12676extern "C" {
12677#endif
12678
12679static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12680{
12681  register XWindows
12682    *windows;
12683
12684  windows=(XWindows *) data;
12685  if ((event->type == ClientMessage) &&
12686      (event->xclient.window == windows->image.id))
12687    return(MagickFalse);
12688  return(MagickTrue);
12689}
12690
12691#if defined(__cplusplus) || defined(c_plusplus)
12692}
12693#endif
12694
12695static void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12696{
12697  register int
12698    x,
12699    y;
12700
12701  (void) XIfEvent(display,event,XPredicate,(char *) windows);
12702  if (event->xany.window == windows->command.id)
12703    return;
12704  switch (event->type)
12705  {
12706    case ButtonPress:
12707    case ButtonRelease:
12708    {
12709      if ((event->xbutton.button == Button3) &&
12710          (event->xbutton.state & Mod1Mask))
12711        {
12712          /*
12713            Convert Alt-Button3 to Button2.
12714          */
12715          event->xbutton.button=Button2;
12716          event->xbutton.state&=(~Mod1Mask);
12717        }
12718      if (event->xbutton.window == windows->backdrop.id)
12719        {
12720          (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12721            event->xbutton.time);
12722          break;
12723        }
12724      if (event->xbutton.window == windows->pan.id)
12725        {
12726          XPanImage(display,windows,event);
12727          break;
12728        }
12729      if (event->xbutton.window == windows->image.id)
12730        if (event->xbutton.button == Button2)
12731          {
12732            /*
12733              Update magnified image.
12734            */
12735            x=event->xbutton.x;
12736            y=event->xbutton.y;
12737            if (x < 0)
12738              x=0;
12739            else
12740              if (x >= (int) windows->image.width)
12741                x=(int) (windows->image.width-1);
12742            windows->magnify.x=(int) windows->image.x+x;
12743            if (y < 0)
12744              y=0;
12745            else
12746             if (y >= (int) windows->image.height)
12747               y=(int) (windows->image.height-1);
12748            windows->magnify.y=windows->image.y+y;
12749            if (windows->magnify.mapped == MagickFalse)
12750              (void) XMapRaised(display,windows->magnify.id);
12751            XMakeMagnifyImage(display,windows);
12752            if (event->type == ButtonRelease)
12753              (void) XWithdrawWindow(display,windows->info.id,
12754                windows->info.screen);
12755            break;
12756          }
12757      break;
12758    }
12759    case ClientMessage:
12760    {
12761      /*
12762        If client window delete message, exit.
12763      */
12764      if (event->xclient.message_type != windows->wm_protocols)
12765        break;
12766      if (*event->xclient.data.l != (long) windows->wm_delete_window)
12767        break;
12768      if (event->xclient.window == windows->magnify.id)
12769        {
12770          (void) XWithdrawWindow(display,windows->magnify.id,
12771            windows->magnify.screen);
12772          break;
12773        }
12774      break;
12775    }
12776    case ConfigureNotify:
12777    {
12778      if (event->xconfigure.window == windows->magnify.id)
12779        {
12780          unsigned int
12781            magnify;
12782
12783          /*
12784            Magnify window has a new configuration.
12785          */
12786          windows->magnify.width=(unsigned int) event->xconfigure.width;
12787          windows->magnify.height=(unsigned int) event->xconfigure.height;
12788          if (windows->magnify.mapped == MagickFalse)
12789            break;
12790          magnify=1;
12791          while ((int) magnify <= event->xconfigure.width)
12792            magnify<<=1;
12793          while ((int) magnify <= event->xconfigure.height)
12794            magnify<<=1;
12795          magnify>>=1;
12796          if (((int) magnify != event->xconfigure.width) ||
12797              ((int) magnify != event->xconfigure.height))
12798            {
12799              XWindowChanges
12800                window_changes;
12801
12802              window_changes.width=(int) magnify;
12803              window_changes.height=(int) magnify;
12804              (void) XReconfigureWMWindow(display,windows->magnify.id,
12805                windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12806                &window_changes);
12807              break;
12808            }
12809          XMakeMagnifyImage(display,windows);
12810          break;
12811        }
12812      break;
12813    }
12814    case Expose:
12815    {
12816      if (event->xexpose.window == windows->image.id)
12817        {
12818          XRefreshWindow(display,&windows->image,event);
12819          break;
12820        }
12821      if (event->xexpose.window == windows->pan.id)
12822        if (event->xexpose.count == 0)
12823          {
12824            XDrawPanRectangle(display,windows);
12825            break;
12826          }
12827      if (event->xexpose.window == windows->magnify.id)
12828        if (event->xexpose.count == 0)
12829          {
12830            XMakeMagnifyImage(display,windows);
12831            break;
12832          }
12833      break;
12834    }
12835    case KeyPress:
12836    {
12837      char
12838        command[MaxTextExtent];
12839
12840      KeySym
12841        key_symbol;
12842
12843      if (event->xkey.window != windows->magnify.id)
12844        break;
12845      /*
12846        Respond to a user key press.
12847      */
12848      (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12849        sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12850      XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol);
12851      break;
12852    }
12853    case MapNotify:
12854    {
12855      if (event->xmap.window == windows->magnify.id)
12856        {
12857          windows->magnify.mapped=MagickTrue;
12858          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12859          break;
12860        }
12861      if (event->xmap.window == windows->info.id)
12862        {
12863          windows->info.mapped=MagickTrue;
12864          break;
12865        }
12866      break;
12867    }
12868    case MotionNotify:
12869    {
12870      while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12871      if (event->xmotion.window == windows->image.id)
12872        if (windows->magnify.mapped != MagickFalse)
12873          {
12874            /*
12875              Update magnified image.
12876            */
12877            x=event->xmotion.x;
12878            y=event->xmotion.y;
12879            if (x < 0)
12880              x=0;
12881            else
12882              if (x >= (int) windows->image.width)
12883                x=(int) (windows->image.width-1);
12884            windows->magnify.x=(int) windows->image.x+x;
12885            if (y < 0)
12886              y=0;
12887            else
12888             if (y >= (int) windows->image.height)
12889               y=(int) (windows->image.height-1);
12890            windows->magnify.y=windows->image.y+y;
12891            XMakeMagnifyImage(display,windows);
12892          }
12893      break;
12894    }
12895    case UnmapNotify:
12896    {
12897      if (event->xunmap.window == windows->magnify.id)
12898        {
12899          windows->magnify.mapped=MagickFalse;
12900          break;
12901        }
12902      if (event->xunmap.window == windows->info.id)
12903        {
12904          windows->info.mapped=MagickFalse;
12905          break;
12906        }
12907      break;
12908    }
12909    default:
12910      break;
12911  }
12912}
12913
12914/*
12915%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12916%                                                                             %
12917%                                                                             %
12918%                                                                             %
12919+   X S e t C r o p G e o m e t r y                                           %
12920%                                                                             %
12921%                                                                             %
12922%                                                                             %
12923%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12924%
12925%  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12926%  and translates it to a cropping geometry relative to the image.
12927%
12928%  The format of the XSetCropGeometry method is:
12929%
12930%      void XSetCropGeometry(Display *display,XWindows *windows,
12931%        RectangleInfo *crop_info,Image *image)
12932%
12933%  A description of each parameter follows:
12934%
12935%    o display: Specifies a connection to an X server; returned from
12936%      XOpenDisplay.
12937%
12938%    o windows: Specifies a pointer to a XWindows structure.
12939%
12940%    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12941%      Image window to crop.
12942%
12943%    o image: the image.
12944%
12945*/
12946static void XSetCropGeometry(Display *display,XWindows *windows,
12947  RectangleInfo *crop_info,Image *image)
12948{
12949  char
12950    text[MaxTextExtent];
12951
12952  int
12953    x,
12954    y;
12955
12956  MagickRealType
12957    scale_factor;
12958
12959  unsigned int
12960    height,
12961    width;
12962
12963  if (windows->info.mapped != MagickFalse)
12964    {
12965      /*
12966        Display info on cropping rectangle.
12967      */
12968      (void) FormatLocaleString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
12969        (double) crop_info->width,(double) crop_info->height,(double)
12970        crop_info->x,(double) crop_info->y);
12971      XInfoWidget(display,windows,text);
12972    }
12973  /*
12974    Cropping geometry is relative to any previous crop geometry.
12975  */
12976  x=0;
12977  y=0;
12978  width=(unsigned int) image->columns;
12979  height=(unsigned int) image->rows;
12980  if (windows->image.crop_geometry != (char *) NULL)
12981    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12982  else
12983    windows->image.crop_geometry=AcquireString((char *) NULL);
12984  /*
12985    Define the crop geometry string from the cropping rectangle.
12986  */
12987  scale_factor=(MagickRealType) width/windows->image.ximage->width;
12988  if (crop_info->x > 0)
12989    x+=(int) (scale_factor*crop_info->x+0.5);
12990  width=(unsigned int) (scale_factor*crop_info->width+0.5);
12991  if (width == 0)
12992    width=1;
12993  scale_factor=(MagickRealType) height/windows->image.ximage->height;
12994  if (crop_info->y > 0)
12995    y+=(int) (scale_factor*crop_info->y+0.5);
12996  height=(unsigned int) (scale_factor*crop_info->height+0.5);
12997  if (height == 0)
12998    height=1;
12999  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
13000    "%ux%u%+d%+d",width,height,x,y);
13001}
13002
13003/*
13004%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13005%                                                                             %
13006%                                                                             %
13007%                                                                             %
13008+   X T i l e I m a g e                                                       %
13009%                                                                             %
13010%                                                                             %
13011%                                                                             %
13012%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13013%
13014%  XTileImage() loads or deletes a selected tile from a visual image directory.
13015%  The load or delete command is chosen from a menu.
13016%
13017%  The format of the XTileImage method is:
13018%
13019%      Image *XTileImage(Display *display,XResourceInfo *resource_info,
13020%        XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13021%
13022%  A description of each parameter follows:
13023%
13024%    o tile_image:  XTileImage reads or deletes the tile image
13025%      and returns it.  A null image is returned if an error occurs.
13026%
13027%    o display: Specifies a connection to an X server;  returned from
13028%      XOpenDisplay.
13029%
13030%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13031%
13032%    o windows: Specifies a pointer to a XWindows structure.
13033%
13034%    o image: the image; returned from ReadImage.
13035%
13036%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
13037%      the entire image is refreshed.
13038%
13039%    o exception: return any errors or warnings in this structure.
13040%
13041*/
13042static Image *XTileImage(Display *display,XResourceInfo *resource_info,
13043  XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13044{
13045  static const char
13046    *VerbMenu[] =
13047    {
13048      "Load",
13049      "Next",
13050      "Former",
13051      "Delete",
13052      "Update",
13053      (char *) NULL,
13054    };
13055
13056  static const ModeType
13057    TileCommands[] =
13058    {
13059      TileLoadCommand,
13060      TileNextCommand,
13061      TileFormerCommand,
13062      TileDeleteCommand,
13063      TileUpdateCommand
13064    };
13065
13066  char
13067    command[MaxTextExtent],
13068    filename[MaxTextExtent];
13069
13070  Image
13071    *tile_image;
13072
13073  int
13074    id,
13075    status,
13076    tile,
13077    x,
13078    y;
13079
13080  MagickRealType
13081    scale_factor;
13082
13083  register char
13084    *p,
13085    *q;
13086
13087  register int
13088    i;
13089
13090  unsigned int
13091    height,
13092    width;
13093
13094  /*
13095    Tile image is relative to montage image configuration.
13096  */
13097  x=0;
13098  y=0;
13099  width=(unsigned int) image->columns;
13100  height=(unsigned int) image->rows;
13101  if (windows->image.crop_geometry != (char *) NULL)
13102    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13103  scale_factor=(MagickRealType) width/windows->image.ximage->width;
13104  event->xbutton.x+=windows->image.x;
13105  event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13106  scale_factor=(MagickRealType) height/windows->image.ximage->height;
13107  event->xbutton.y+=windows->image.y;
13108  event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13109  /*
13110    Determine size and location of each tile in the visual image directory.
13111  */
13112  width=(unsigned int) image->columns;
13113  height=(unsigned int) image->rows;
13114  x=0;
13115  y=0;
13116  (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13117  tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13118    (event->xbutton.x-x)/width;
13119  if (tile < 0)
13120    {
13121      /*
13122        Button press is outside any tile.
13123      */
13124      (void) XBell(display,0);
13125      return((Image *) NULL);
13126    }
13127  /*
13128    Determine file name from the tile directory.
13129  */
13130  p=image->directory;
13131  for (i=tile; (i != 0) && (*p != '\0'); )
13132  {
13133    if (*p == '\n')
13134      i--;
13135    p++;
13136  }
13137  if (*p == '\0')
13138    {
13139      /*
13140        Button press is outside any tile.
13141      */
13142      (void) XBell(display,0);
13143      return((Image *) NULL);
13144    }
13145  /*
13146    Select a command from the pop-up menu.
13147  */
13148  id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13149  if (id < 0)
13150    return((Image *) NULL);
13151  q=p;
13152  while ((*q != '\n') && (*q != '\0'))
13153    q++;
13154  (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13155  /*
13156    Perform command for the selected tile.
13157  */
13158  XSetCursorState(display,windows,MagickTrue);
13159  XCheckRefreshWindows(display,windows);
13160  tile_image=NewImageList();
13161  switch (TileCommands[id])
13162  {
13163    case TileLoadCommand:
13164    {
13165      /*
13166        Load tile image.
13167      */
13168      XCheckRefreshWindows(display,windows);
13169      (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13170        MaxTextExtent);
13171      (void) CopyMagickString(resource_info->image_info->filename,filename,
13172        MaxTextExtent);
13173      tile_image=ReadImage(resource_info->image_info,exception);
13174      CatchException(exception);
13175      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13176      break;
13177    }
13178    case TileNextCommand:
13179    {
13180      /*
13181        Display next image.
13182      */
13183      XClientMessage(display,windows->image.id,windows->im_protocols,
13184        windows->im_next_image,CurrentTime);
13185      break;
13186    }
13187    case TileFormerCommand:
13188    {
13189      /*
13190        Display former image.
13191      */
13192      XClientMessage(display,windows->image.id,windows->im_protocols,
13193        windows->im_former_image,CurrentTime);
13194      break;
13195    }
13196    case TileDeleteCommand:
13197    {
13198      /*
13199        Delete tile image.
13200      */
13201      if (IsPathAccessible(filename) == MagickFalse)
13202        {
13203          XNoticeWidget(display,windows,"Image file does not exist:",filename);
13204          break;
13205        }
13206      status=XConfirmWidget(display,windows,"Really delete tile",filename);
13207      if (status <= 0)
13208        break;
13209      status=remove_utf8(filename) != 0 ? MagickTrue : MagickFalse;
13210      if (status != MagickFalse)
13211        {
13212          XNoticeWidget(display,windows,"Unable to delete image file:",
13213            filename);
13214          break;
13215        }
13216    }
13217    case TileUpdateCommand:
13218    {
13219      int
13220        x_offset,
13221        y_offset;
13222
13223      PixelInfo
13224        pixel;
13225
13226      Quantum
13227        virtual_pixel[MaxPixelChannels];
13228
13229      register int
13230        j;
13231
13232      register Quantum
13233        *s;
13234
13235      /*
13236        Ensure all the images exist.
13237      */
13238      tile=0;
13239      GetPixelInfo(image,&pixel);
13240      for (p=image->directory; *p != '\0'; p++)
13241      {
13242        CacheView
13243          *image_view;
13244
13245        q=p;
13246        while ((*q != '\n') && (*q != '\0'))
13247          q++;
13248        (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13249        p=q;
13250        if (IsPathAccessible(filename) != MagickFalse)
13251          {
13252            tile++;
13253            continue;
13254          }
13255        /*
13256          Overwrite tile with background color.
13257        */
13258        x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13259        y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13260        image_view=AcquireCacheView(image);
13261        (void) GetOneCacheViewVirtualPixel(image_view,0,0,virtual_pixel,
13262          exception);
13263        pixel.red=virtual_pixel[RedPixelChannel];
13264        pixel.green=virtual_pixel[GreenPixelChannel];
13265        pixel.blue=virtual_pixel[BluePixelChannel];
13266        pixel.alpha=virtual_pixel[AlphaPixelChannel];
13267        for (i=0; i < (int) height; i++)
13268        {
13269          s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13270            y_offset+i,width,1,exception);
13271          if (s == (Quantum *) NULL)
13272            break;
13273          for (j=0; j < (int) width; j++)
13274          {
13275            SetPixelPixelInfo(image,&pixel,s);
13276            s+=GetPixelChannels(image);
13277          }
13278          if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13279            break;
13280        }
13281        image_view=DestroyCacheView(image_view);
13282        tile++;
13283      }
13284      windows->image.window_changes.width=(int) image->columns;
13285      windows->image.window_changes.height=(int) image->rows;
13286      XConfigureImageColormap(display,resource_info,windows,image);
13287      (void) XConfigureImage(display,resource_info,windows,image,exception);
13288      break;
13289    }
13290    default:
13291      break;
13292  }
13293  XSetCursorState(display,windows,MagickFalse);
13294  return(tile_image);
13295}
13296
13297/*
13298%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13299%                                                                             %
13300%                                                                             %
13301%                                                                             %
13302+   X T r a n s l a t e I m a g e                                             %
13303%                                                                             %
13304%                                                                             %
13305%                                                                             %
13306%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13307%
13308%  XTranslateImage() translates the image within an Image window by one pixel
13309%  as specified by the key symbol.  If the image has a `montage string the
13310%  translation is respect to the width and height contained within the string.
13311%
13312%  The format of the XTranslateImage method is:
13313%
13314%      void XTranslateImage(Display *display,XWindows *windows,
13315%        Image *image,const KeySym key_symbol)
13316%
13317%  A description of each parameter follows:
13318%
13319%    o display: Specifies a connection to an X server; returned from
13320%      XOpenDisplay.
13321%
13322%    o windows: Specifies a pointer to a XWindows structure.
13323%
13324%    o image: the image.
13325%
13326%    o key_symbol: Specifies a KeySym which indicates which side of the image
13327%      to trim.
13328%
13329*/
13330static void XTranslateImage(Display *display,XWindows *windows,
13331  Image *image,const KeySym key_symbol)
13332{
13333  char
13334    text[MaxTextExtent];
13335
13336  int
13337    x,
13338    y;
13339
13340  unsigned int
13341    x_offset,
13342    y_offset;
13343
13344  /*
13345    User specified a pan position offset.
13346  */
13347  x_offset=windows->image.width;
13348  y_offset=windows->image.height;
13349  if (image->montage != (char *) NULL)
13350    (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13351  switch ((int) key_symbol)
13352  {
13353    case XK_Home:
13354    case XK_KP_Home:
13355    {
13356      windows->image.x=(int) windows->image.width/2;
13357      windows->image.y=(int) windows->image.height/2;
13358      break;
13359    }
13360    case XK_Left:
13361    case XK_KP_Left:
13362    {
13363      windows->image.x-=x_offset;
13364      break;
13365    }
13366    case XK_Next:
13367    case XK_Up:
13368    case XK_KP_Up:
13369    {
13370      windows->image.y-=y_offset;
13371      break;
13372    }
13373    case XK_Right:
13374    case XK_KP_Right:
13375    {
13376      windows->image.x+=x_offset;
13377      break;
13378    }
13379    case XK_Prior:
13380    case XK_Down:
13381    case XK_KP_Down:
13382    {
13383      windows->image.y+=y_offset;
13384      break;
13385    }
13386    default:
13387      return;
13388  }
13389  /*
13390    Check boundary conditions.
13391  */
13392  if (windows->image.x < 0)
13393    windows->image.x=0;
13394  else
13395    if ((int) (windows->image.x+windows->image.width) >
13396        windows->image.ximage->width)
13397      windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13398  if (windows->image.y < 0)
13399    windows->image.y=0;
13400  else
13401    if ((int) (windows->image.y+windows->image.height) >
13402        windows->image.ximage->height)
13403      windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13404  /*
13405    Refresh Image window.
13406  */
13407  (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
13408    windows->image.width,windows->image.height,windows->image.x,
13409    windows->image.y);
13410  XInfoWidget(display,windows,text);
13411  XCheckRefreshWindows(display,windows);
13412  XDrawPanRectangle(display,windows);
13413  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13414  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13415}
13416
13417/*
13418%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13419%                                                                             %
13420%                                                                             %
13421%                                                                             %
13422+   X T r i m I m a g e                                                       %
13423%                                                                             %
13424%                                                                             %
13425%                                                                             %
13426%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13427%
13428%  XTrimImage() trims the edges from the Image window.
13429%
13430%  The format of the XTrimImage method is:
13431%
13432%      MagickBooleanType XTrimImage(Display *display,
13433%        XResourceInfo *resource_info,XWindows *windows,Image *image,
13434%        ExceptionInfo *exception)
13435%
13436%  A description of each parameter follows:
13437%
13438%    o display: Specifies a connection to an X server; returned from
13439%      XOpenDisplay.
13440%
13441%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13442%
13443%    o windows: Specifies a pointer to a XWindows structure.
13444%
13445%    o image: the image.
13446%
13447%    o exception: return any errors or warnings in this structure.
13448%
13449*/
13450static MagickBooleanType XTrimImage(Display *display,
13451  XResourceInfo *resource_info,XWindows *windows,Image *image,
13452  ExceptionInfo *exception)
13453{
13454  RectangleInfo
13455    trim_info;
13456
13457  register int
13458    x,
13459    y;
13460
13461  size_t
13462    background,
13463    pixel;
13464
13465  /*
13466    Trim edges from image.
13467  */
13468  XSetCursorState(display,windows,MagickTrue);
13469  XCheckRefreshWindows(display,windows);
13470  /*
13471    Crop the left edge.
13472  */
13473  background=XGetPixel(windows->image.ximage,0,0);
13474  trim_info.width=(size_t) windows->image.ximage->width;
13475  for (x=0; x < windows->image.ximage->width; x++)
13476  {
13477    for (y=0; y < windows->image.ximage->height; y++)
13478    {
13479      pixel=XGetPixel(windows->image.ximage,x,y);
13480      if (pixel != background)
13481        break;
13482    }
13483    if (y < windows->image.ximage->height)
13484      break;
13485  }
13486  trim_info.x=(ssize_t) x;
13487  if (trim_info.x == (ssize_t) windows->image.ximage->width)
13488    {
13489      XSetCursorState(display,windows,MagickFalse);
13490      return(MagickFalse);
13491    }
13492  /*
13493    Crop the right edge.
13494  */
13495  background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13496  for (x=windows->image.ximage->width-1; x != 0; x--)
13497  {
13498    for (y=0; y < windows->image.ximage->height; y++)
13499    {
13500      pixel=XGetPixel(windows->image.ximage,x,y);
13501      if (pixel != background)
13502        break;
13503    }
13504    if (y < windows->image.ximage->height)
13505      break;
13506  }
13507  trim_info.width=(size_t) (x-trim_info.x+1);
13508  /*
13509    Crop the top edge.
13510  */
13511  background=XGetPixel(windows->image.ximage,0,0);
13512  trim_info.height=(size_t) windows->image.ximage->height;
13513  for (y=0; y < windows->image.ximage->height; y++)
13514  {
13515    for (x=0; x < windows->image.ximage->width; x++)
13516    {
13517      pixel=XGetPixel(windows->image.ximage,x,y);
13518      if (pixel != background)
13519        break;
13520    }
13521    if (x < windows->image.ximage->width)
13522      break;
13523  }
13524  trim_info.y=(ssize_t) y;
13525  /*
13526    Crop the bottom edge.
13527  */
13528  background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13529  for (y=windows->image.ximage->height-1; y != 0; y--)
13530  {
13531    for (x=0; x < windows->image.ximage->width; x++)
13532    {
13533      pixel=XGetPixel(windows->image.ximage,x,y);
13534      if (pixel != background)
13535        break;
13536    }
13537    if (x < windows->image.ximage->width)
13538      break;
13539  }
13540  trim_info.height=(size_t) y-trim_info.y+1;
13541  if (((unsigned int) trim_info.width != windows->image.width) ||
13542      ((unsigned int) trim_info.height != windows->image.height))
13543    {
13544      /*
13545        Reconfigure Image window as defined by the trimming rectangle.
13546      */
13547      XSetCropGeometry(display,windows,&trim_info,image);
13548      windows->image.window_changes.width=(int) trim_info.width;
13549      windows->image.window_changes.height=(int) trim_info.height;
13550      (void) XConfigureImage(display,resource_info,windows,image,exception);
13551    }
13552  XSetCursorState(display,windows,MagickFalse);
13553  return(MagickTrue);
13554}
13555
13556/*
13557%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13558%                                                                             %
13559%                                                                             %
13560%                                                                             %
13561+   X V i s u a l D i r e c t o r y I m a g e                                 %
13562%                                                                             %
13563%                                                                             %
13564%                                                                             %
13565%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13566%
13567%  XVisualDirectoryImage() creates a Visual Image Directory.
13568%
13569%  The format of the XVisualDirectoryImage method is:
13570%
13571%      Image *XVisualDirectoryImage(Display *display,
13572%        XResourceInfo *resource_info,XWindows *windows,
13573%        ExceptionInfo *exception)
13574%
13575%  A description of each parameter follows:
13576%
13577%    o display: Specifies a connection to an X server; returned from
13578%      XOpenDisplay.
13579%
13580%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13581%
13582%    o windows: Specifies a pointer to a XWindows structure.
13583%
13584%    o exception: return any errors or warnings in this structure.
13585%
13586*/
13587static Image *XVisualDirectoryImage(Display *display,
13588  XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
13589{
13590#define TileImageTag  "Scale/Image"
13591#define XClientName  "montage"
13592
13593  char
13594    **filelist;
13595
13596  Image
13597    *images,
13598    *montage_image,
13599    *next_image,
13600    *thumbnail_image;
13601
13602  ImageInfo
13603    *read_info;
13604
13605  int
13606    number_files;
13607
13608  MagickBooleanType
13609    backdrop;
13610
13611  MagickStatusType
13612    status;
13613
13614  MontageInfo
13615    *montage_info;
13616
13617  RectangleInfo
13618    geometry;
13619
13620  register int
13621    i;
13622
13623  static char
13624    filename[MaxTextExtent] = "\0",
13625    filenames[MaxTextExtent] = "*";
13626
13627  XResourceInfo
13628    background_resources;
13629
13630  /*
13631    Request file name from user.
13632  */
13633  XFileBrowserWidget(display,windows,"Directory",filenames);
13634  if (*filenames == '\0')
13635    return((Image *) NULL);
13636  /*
13637    Expand the filenames.
13638  */
13639  filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13640  if (filelist == (char **) NULL)
13641    {
13642      ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13643        filenames);
13644      return((Image *) NULL);
13645    }
13646  number_files=1;
13647  filelist[0]=filenames;
13648  status=ExpandFilenames(&number_files,&filelist);
13649  if ((status == MagickFalse) || (number_files == 0))
13650    {
13651      if (number_files == 0)
13652        ThrowXWindowFatalException(ImageError,"NoImagesWereFound",filenames)
13653      else
13654        ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13655          filenames);
13656      return((Image *) NULL);
13657    }
13658  /*
13659    Set image background resources.
13660  */
13661  background_resources=(*resource_info);
13662  background_resources.window_id=AcquireString("");
13663  (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
13664    "0x%lx",windows->image.id);
13665  background_resources.backdrop=MagickTrue;
13666  /*
13667    Read each image and convert them to a tile.
13668  */
13669  backdrop=(windows->visual_info->klass == TrueColor) ||
13670    (windows->visual_info->klass == DirectColor) ? MagickTrue : MagickFalse;
13671  read_info=CloneImageInfo(resource_info->image_info);
13672  (void) SetImageOption(read_info,"jpeg:size","120x120");
13673  (void) CloneString(&read_info->size,DefaultTileGeometry);
13674  (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13675    (void *) NULL);
13676  images=NewImageList();
13677  XSetCursorState(display,windows,MagickTrue);
13678  XCheckRefreshWindows(display,windows);
13679  for (i=0; i < (int) number_files; i++)
13680  {
13681    (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13682    filelist[i]=DestroyString(filelist[i]);
13683    *read_info->magick='\0';
13684    next_image=ReadImage(read_info,exception);
13685    CatchException(exception);
13686    if (next_image != (Image *) NULL)
13687      {
13688        (void) DeleteImageProperty(next_image,"label");
13689        (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13690          read_info,next_image,DefaultTileLabel,exception),exception);
13691        (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13692          exception);
13693        thumbnail_image=ThumbnailImage(next_image,geometry.width,
13694          geometry.height,exception);
13695        if (thumbnail_image != (Image *) NULL)
13696          {
13697            next_image=DestroyImage(next_image);
13698            next_image=thumbnail_image;
13699          }
13700        if (backdrop)
13701          {
13702            (void) XDisplayBackgroundImage(display,&background_resources,
13703              next_image,exception);
13704            XSetCursorState(display,windows,MagickTrue);
13705          }
13706        AppendImageToList(&images,next_image);
13707        if (images->progress_monitor != (MagickProgressMonitor) NULL)
13708          {
13709            MagickBooleanType
13710              proceed;
13711
13712            proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13713              (MagickSizeType) number_files);
13714            if (proceed == MagickFalse)
13715              break;
13716          }
13717      }
13718  }
13719  filelist=(char **) RelinquishMagickMemory(filelist);
13720  if (images == (Image *) NULL)
13721    {
13722      read_info=DestroyImageInfo(read_info);
13723      XSetCursorState(display,windows,MagickFalse);
13724      ThrowXWindowFatalException(ImageError,"NoImagesWereLoaded",filenames);
13725      return((Image *) NULL);
13726    }
13727  /*
13728    Create the Visual Image Directory.
13729  */
13730  montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13731  montage_info->pointsize=10;
13732  if (resource_info->font != (char *) NULL)
13733    (void) CloneString(&montage_info->font,resource_info->font);
13734  (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13735  montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13736    images),exception);
13737  images=DestroyImageList(images);
13738  montage_info=DestroyMontageInfo(montage_info);
13739  read_info=DestroyImageInfo(read_info);
13740  XSetCursorState(display,windows,MagickFalse);
13741  if (montage_image == (Image *) NULL)
13742    return(montage_image);
13743  XClientMessage(display,windows->image.id,windows->im_protocols,
13744    windows->im_next_image,CurrentTime);
13745  return(montage_image);
13746}
13747
13748/*
13749%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13750%                                                                             %
13751%                                                                             %
13752%                                                                             %
13753%   X D i s p l a y B a c k g r o u n d I m a g e                             %
13754%                                                                             %
13755%                                                                             %
13756%                                                                             %
13757%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13758%
13759%  XDisplayBackgroundImage() displays an image in the background of a window.
13760%
13761%  The format of the XDisplayBackgroundImage method is:
13762%
13763%      MagickBooleanType XDisplayBackgroundImage(Display *display,
13764%        XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13765%
13766%  A description of each parameter follows:
13767%
13768%    o display: Specifies a connection to an X server;  returned from
13769%      XOpenDisplay.
13770%
13771%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13772%
13773%    o image: the image.
13774%
13775%    o exception: return any errors or warnings in this structure.
13776%
13777*/
13778MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13779  XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13780{
13781  char
13782    geometry[MaxTextExtent],
13783    visual_type[MaxTextExtent];
13784
13785  int
13786    height,
13787    status,
13788    width;
13789
13790  RectangleInfo
13791    geometry_info;
13792
13793  static XPixelInfo
13794    pixel;
13795
13796  static XStandardColormap
13797    *map_info;
13798
13799  static XVisualInfo
13800    *visual_info = (XVisualInfo *) NULL;
13801
13802  static XWindowInfo
13803    window_info;
13804
13805  size_t
13806    delay;
13807
13808  Window
13809    root_window;
13810
13811  XGCValues
13812    context_values;
13813
13814  XResourceInfo
13815    resources;
13816
13817  XWindowAttributes
13818    window_attributes;
13819
13820  /*
13821    Determine target window.
13822  */
13823  assert(image != (Image *) NULL);
13824  assert(image->signature == MagickSignature);
13825  if (image->debug != MagickFalse)
13826    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13827  resources=(*resource_info);
13828  window_info.id=(Window) NULL;
13829  root_window=XRootWindow(display,XDefaultScreen(display));
13830  if (LocaleCompare(resources.window_id,"root") == 0)
13831    window_info.id=root_window;
13832  else
13833    {
13834      if (isdigit((unsigned char) *resources.window_id) != 0)
13835        window_info.id=XWindowByID(display,root_window,
13836          (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13837      if (window_info.id == (Window) NULL)
13838        window_info.id=XWindowByName(display,root_window,resources.window_id);
13839    }
13840  if (window_info.id == (Window) NULL)
13841    {
13842      ThrowXWindowFatalException(XServerError,"NoWindowWithSpecifiedIDExists",
13843        resources.window_id);
13844      return(MagickFalse);
13845    }
13846  /*
13847    Determine window visual id.
13848  */
13849  window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13850  window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13851  (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13852  status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13853  if (status != 0)
13854    (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
13855      XVisualIDFromVisual(window_attributes.visual));
13856  if (visual_info == (XVisualInfo *) NULL)
13857    {
13858      /*
13859        Allocate standard colormap.
13860      */
13861      map_info=XAllocStandardColormap();
13862      if (map_info == (XStandardColormap *) NULL)
13863        ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13864          image->filename);
13865      map_info->colormap=(Colormap) NULL;
13866      pixel.pixels=(unsigned long *) NULL;
13867      /*
13868        Initialize visual info.
13869      */
13870      resources.map_type=(char *) NULL;
13871      resources.visual_type=visual_type;
13872      visual_info=XBestVisualInfo(display,map_info,&resources);
13873      if (visual_info == (XVisualInfo *) NULL)
13874        ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13875          resources.visual_type);
13876      /*
13877        Initialize window info.
13878      */
13879      window_info.ximage=(XImage *) NULL;
13880      window_info.matte_image=(XImage *) NULL;
13881      window_info.pixmap=(Pixmap) NULL;
13882      window_info.matte_pixmap=(Pixmap) NULL;
13883    }
13884  /*
13885    Free previous root colors.
13886  */
13887  if (window_info.id == root_window)
13888    (void) XDestroyWindowColors(display,root_window);
13889  /*
13890    Initialize Standard Colormap.
13891  */
13892  resources.colormap=SharedColormap;
13893  XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel);
13894  /*
13895    Graphic context superclass.
13896  */
13897  context_values.background=pixel.background_color.pixel;
13898  context_values.foreground=pixel.foreground_color.pixel;
13899  pixel.annotate_context=XCreateGC(display,window_info.id,
13900    (size_t) (GCBackground | GCForeground),&context_values);
13901  if (pixel.annotate_context == (GC) NULL)
13902    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13903      image->filename);
13904  /*
13905    Initialize Image window attributes.
13906  */
13907  window_info.name=AcquireString("\0");
13908  window_info.icon_name=AcquireString("\0");
13909  XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13910    &resources,&window_info);
13911  /*
13912    Create the X image.
13913  */
13914  window_info.width=(unsigned int) image->columns;
13915  window_info.height=(unsigned int) image->rows;
13916  if ((image->columns != window_info.width) ||
13917      (image->rows != window_info.height))
13918    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13919      image->filename);
13920  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
13921    window_attributes.width,window_attributes.height);
13922  geometry_info.width=window_info.width;
13923  geometry_info.height=window_info.height;
13924  geometry_info.x=(ssize_t) window_info.x;
13925  geometry_info.y=(ssize_t) window_info.y;
13926  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13927    &geometry_info.width,&geometry_info.height);
13928  window_info.width=(unsigned int) geometry_info.width;
13929  window_info.height=(unsigned int) geometry_info.height;
13930  window_info.x=(int) geometry_info.x;
13931  window_info.y=(int) geometry_info.y;
13932  status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13933    window_info.height,exception);
13934  if (status == MagickFalse)
13935    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13936      image->filename);
13937  window_info.x=0;
13938  window_info.y=0;
13939  if (image->debug != MagickFalse)
13940    {
13941      (void) LogMagickEvent(X11Event,GetMagickModule(),
13942        "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13943        (double) image->columns,(double) image->rows);
13944      if (image->colors != 0)
13945        (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13946          image->colors);
13947      (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13948    }
13949  /*
13950    Adjust image dimensions as specified by backdrop or geometry options.
13951  */
13952  width=(int) window_info.width;
13953  height=(int) window_info.height;
13954  if (resources.backdrop != MagickFalse)
13955    {
13956      /*
13957        Center image on window.
13958      */
13959      window_info.x=(window_attributes.width/2)-
13960        (window_info.ximage->width/2);
13961      window_info.y=(window_attributes.height/2)-
13962        (window_info.ximage->height/2);
13963      width=window_attributes.width;
13964      height=window_attributes.height;
13965    }
13966  if ((resources.image_geometry != (char *) NULL) &&
13967      (*resources.image_geometry != '\0'))
13968    {
13969      char
13970        default_geometry[MaxTextExtent];
13971
13972      int
13973        flags,
13974        gravity;
13975
13976      XSizeHints
13977        *size_hints;
13978
13979      /*
13980        User specified geometry.
13981      */
13982      size_hints=XAllocSizeHints();
13983      if (size_hints == (XSizeHints *) NULL)
13984        ThrowXWindowFatalException(ResourceLimitFatalError,
13985          "MemoryAllocationFailed",image->filename);
13986      size_hints->flags=0L;
13987      (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
13988        width,height);
13989      flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
13990        default_geometry,window_info.border_width,size_hints,&window_info.x,
13991        &window_info.y,&width,&height,&gravity);
13992      if (flags & (XValue | YValue))
13993        {
13994          width=window_attributes.width;
13995          height=window_attributes.height;
13996        }
13997      (void) XFree((void *) size_hints);
13998    }
13999  /*
14000    Create the X pixmap.
14001  */
14002  window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
14003    (unsigned int) height,window_info.depth);
14004  if (window_info.pixmap == (Pixmap) NULL)
14005    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
14006      image->filename);
14007  /*
14008    Display pixmap on the window.
14009  */
14010  if (((unsigned int) width > window_info.width) ||
14011      ((unsigned int) height > window_info.height))
14012    (void) XFillRectangle(display,window_info.pixmap,
14013      window_info.annotate_context,0,0,(unsigned int) width,
14014      (unsigned int) height);
14015  (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
14016    window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
14017    window_info.width,(unsigned int) window_info.height);
14018  (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
14019  (void) XClearWindow(display,window_info.id);
14020  delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
14021  XDelay(display,delay == 0UL ? 10UL : delay);
14022  (void) XSync(display,MagickFalse);
14023  return(window_info.id == root_window ? MagickTrue : MagickFalse);
14024}
14025
14026/*
14027%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14028%                                                                             %
14029%                                                                             %
14030%                                                                             %
14031+   X D i s p l a y I m a g e                                                 %
14032%                                                                             %
14033%                                                                             %
14034%                                                                             %
14035%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14036%
14037%  XDisplayImage() displays an image via X11.  A new image is created and
14038%  returned if the user interactively transforms the displayed image.
14039%
14040%  The format of the XDisplayImage method is:
14041%
14042%      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14043%        char **argv,int argc,Image **image,size_t *state,
14044%        ExceptionInfo *exception)
14045%
14046%  A description of each parameter follows:
14047%
14048%    o nexus:  Method XDisplayImage returns an image when the
14049%      user chooses 'Open Image' from the command menu or picks a tile
14050%      from the image directory.  Otherwise a null image is returned.
14051%
14052%    o display: Specifies a connection to an X server;  returned from
14053%      XOpenDisplay.
14054%
14055%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14056%
14057%    o argv: Specifies the application's argument list.
14058%
14059%    o argc: Specifies the number of arguments.
14060%
14061%    o image: Specifies an address to an address of an Image structure;
14062%
14063%    o exception: return any errors or warnings in this structure.
14064%
14065*/
14066MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14067  char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
14068{
14069#define MagnifySize  256  /* must be a power of 2 */
14070#define MagickMenus  10
14071#define MagickTitle  "Commands"
14072
14073  static const char
14074    *CommandMenu[] =
14075    {
14076      "File",
14077      "Edit",
14078      "View",
14079      "Transform",
14080      "Enhance",
14081      "Effects",
14082      "F/X",
14083      "Image Edit",
14084      "Miscellany",
14085      "Help",
14086      (char *) NULL
14087    },
14088    *FileMenu[] =
14089    {
14090      "Open...",
14091      "Next",
14092      "Former",
14093      "Select...",
14094      "Save...",
14095      "Print...",
14096      "Delete...",
14097      "New...",
14098      "Visual Directory...",
14099      "Quit",
14100      (char *) NULL
14101    },
14102    *EditMenu[] =
14103    {
14104      "Undo",
14105      "Redo",
14106      "Cut",
14107      "Copy",
14108      "Paste",
14109      (char *) NULL
14110    },
14111    *ViewMenu[] =
14112    {
14113      "Half Size",
14114      "Original Size",
14115      "Double Size",
14116      "Resize...",
14117      "Apply",
14118      "Refresh",
14119      "Restore",
14120      (char *) NULL
14121    },
14122    *TransformMenu[] =
14123    {
14124      "Crop",
14125      "Chop",
14126      "Flop",
14127      "Flip",
14128      "Rotate Right",
14129      "Rotate Left",
14130      "Rotate...",
14131      "Shear...",
14132      "Roll...",
14133      "Trim Edges",
14134      (char *) NULL
14135    },
14136    *EnhanceMenu[] =
14137    {
14138      "Hue...",
14139      "Saturation...",
14140      "Brightness...",
14141      "Gamma...",
14142      "Spiff",
14143      "Dull",
14144      "Contrast Stretch...",
14145      "Sigmoidal Contrast...",
14146      "Normalize",
14147      "Equalize",
14148      "Negate",
14149      "Grayscale",
14150      "Map...",
14151      "Quantize...",
14152      (char *) NULL
14153    },
14154    *EffectsMenu[] =
14155    {
14156      "Despeckle",
14157      "Emboss",
14158      "Reduce Noise",
14159      "Add Noise...",
14160      "Sharpen...",
14161      "Blur...",
14162      "Threshold...",
14163      "Edge Detect...",
14164      "Spread...",
14165      "Shade...",
14166      "Raise...",
14167      "Segment...",
14168      (char *) NULL
14169    },
14170    *FXMenu[] =
14171    {
14172      "Solarize...",
14173      "Sepia Tone...",
14174      "Swirl...",
14175      "Implode...",
14176      "Vignette...",
14177      "Wave...",
14178      "Oil Paint...",
14179      "Charcoal Draw...",
14180      (char *) NULL
14181    },
14182    *ImageEditMenu[] =
14183    {
14184      "Annotate...",
14185      "Draw...",
14186      "Color...",
14187      "Matte...",
14188      "Composite...",
14189      "Add Border...",
14190      "Add Frame...",
14191      "Comment...",
14192      "Launch...",
14193      "Region of Interest...",
14194      (char *) NULL
14195    },
14196    *MiscellanyMenu[] =
14197    {
14198      "Image Info",
14199      "Zoom Image",
14200      "Show Preview...",
14201      "Show Histogram",
14202      "Show Matte",
14203      "Background...",
14204      "Slide Show...",
14205      "Preferences...",
14206      (char *) NULL
14207    },
14208    *HelpMenu[] =
14209    {
14210      "Overview",
14211      "Browse Documentation",
14212      "About Display",
14213      (char *) NULL
14214    },
14215    *ShortCutsMenu[] =
14216    {
14217      "Next",
14218      "Former",
14219      "Open...",
14220      "Save...",
14221      "Print...",
14222      "Undo",
14223      "Restore",
14224      "Image Info",
14225      "Quit",
14226      (char *) NULL
14227    },
14228    *VirtualMenu[] =
14229    {
14230      "Image Info",
14231      "Print",
14232      "Next",
14233      "Quit",
14234      (char *) NULL
14235    };
14236
14237  static const char
14238    **Menus[MagickMenus] =
14239    {
14240      FileMenu,
14241      EditMenu,
14242      ViewMenu,
14243      TransformMenu,
14244      EnhanceMenu,
14245      EffectsMenu,
14246      FXMenu,
14247      ImageEditMenu,
14248      MiscellanyMenu,
14249      HelpMenu
14250    };
14251
14252  static CommandType
14253    CommandMenus[] =
14254    {
14255      NullCommand,
14256      NullCommand,
14257      NullCommand,
14258      NullCommand,
14259      NullCommand,
14260      NullCommand,
14261      NullCommand,
14262      NullCommand,
14263      NullCommand,
14264      NullCommand,
14265    },
14266    FileCommands[] =
14267    {
14268      OpenCommand,
14269      NextCommand,
14270      FormerCommand,
14271      SelectCommand,
14272      SaveCommand,
14273      PrintCommand,
14274      DeleteCommand,
14275      NewCommand,
14276      VisualDirectoryCommand,
14277      QuitCommand
14278    },
14279    EditCommands[] =
14280    {
14281      UndoCommand,
14282      RedoCommand,
14283      CutCommand,
14284      CopyCommand,
14285      PasteCommand
14286    },
14287    ViewCommands[] =
14288    {
14289      HalfSizeCommand,
14290      OriginalSizeCommand,
14291      DoubleSizeCommand,
14292      ResizeCommand,
14293      ApplyCommand,
14294      RefreshCommand,
14295      RestoreCommand
14296    },
14297    TransformCommands[] =
14298    {
14299      CropCommand,
14300      ChopCommand,
14301      FlopCommand,
14302      FlipCommand,
14303      RotateRightCommand,
14304      RotateLeftCommand,
14305      RotateCommand,
14306      ShearCommand,
14307      RollCommand,
14308      TrimCommand
14309    },
14310    EnhanceCommands[] =
14311    {
14312      HueCommand,
14313      SaturationCommand,
14314      BrightnessCommand,
14315      GammaCommand,
14316      SpiffCommand,
14317      DullCommand,
14318      ContrastStretchCommand,
14319      SigmoidalContrastCommand,
14320      NormalizeCommand,
14321      EqualizeCommand,
14322      NegateCommand,
14323      GrayscaleCommand,
14324      MapCommand,
14325      QuantizeCommand
14326    },
14327    EffectsCommands[] =
14328    {
14329      DespeckleCommand,
14330      EmbossCommand,
14331      ReduceNoiseCommand,
14332      AddNoiseCommand,
14333      SharpenCommand,
14334      BlurCommand,
14335      ThresholdCommand,
14336      EdgeDetectCommand,
14337      SpreadCommand,
14338      ShadeCommand,
14339      RaiseCommand,
14340      SegmentCommand
14341    },
14342    FXCommands[] =
14343    {
14344      SolarizeCommand,
14345      SepiaToneCommand,
14346      SwirlCommand,
14347      ImplodeCommand,
14348      VignetteCommand,
14349      WaveCommand,
14350      OilPaintCommand,
14351      CharcoalDrawCommand
14352    },
14353    ImageEditCommands[] =
14354    {
14355      AnnotateCommand,
14356      DrawCommand,
14357      ColorCommand,
14358      MatteCommand,
14359      CompositeCommand,
14360      AddBorderCommand,
14361      AddFrameCommand,
14362      CommentCommand,
14363      LaunchCommand,
14364      RegionofInterestCommand
14365    },
14366    MiscellanyCommands[] =
14367    {
14368      InfoCommand,
14369      ZoomCommand,
14370      ShowPreviewCommand,
14371      ShowHistogramCommand,
14372      ShowMatteCommand,
14373      BackgroundCommand,
14374      SlideShowCommand,
14375      PreferencesCommand
14376    },
14377    HelpCommands[] =
14378    {
14379      HelpCommand,
14380      BrowseDocumentationCommand,
14381      VersionCommand
14382    },
14383    ShortCutsCommands[] =
14384    {
14385      NextCommand,
14386      FormerCommand,
14387      OpenCommand,
14388      SaveCommand,
14389      PrintCommand,
14390      UndoCommand,
14391      RestoreCommand,
14392      InfoCommand,
14393      QuitCommand
14394    },
14395    VirtualCommands[] =
14396    {
14397      InfoCommand,
14398      PrintCommand,
14399      NextCommand,
14400      QuitCommand
14401    };
14402
14403  static CommandType
14404    *Commands[MagickMenus] =
14405    {
14406      FileCommands,
14407      EditCommands,
14408      ViewCommands,
14409      TransformCommands,
14410      EnhanceCommands,
14411      EffectsCommands,
14412      FXCommands,
14413      ImageEditCommands,
14414      MiscellanyCommands,
14415      HelpCommands
14416    };
14417
14418  char
14419    command[MaxTextExtent],
14420    *directory,
14421    geometry[MaxTextExtent],
14422    resource_name[MaxTextExtent];
14423
14424  CommandType
14425    command_type;
14426
14427  Image
14428    *display_image,
14429    *nexus;
14430
14431  int
14432    entry,
14433    id;
14434
14435  KeySym
14436    key_symbol;
14437
14438  MagickStatusType
14439    context_mask,
14440    status;
14441
14442  RectangleInfo
14443    geometry_info;
14444
14445  register int
14446    i;
14447
14448  static char
14449    working_directory[MaxTextExtent];
14450
14451  static XPoint
14452    vid_info;
14453
14454  static XWindowInfo
14455    *magick_windows[MaxXWindows];
14456
14457  static unsigned int
14458    number_windows;
14459
14460  struct stat
14461    attributes;
14462
14463  time_t
14464    timer,
14465    timestamp,
14466    update_time;
14467
14468  unsigned int
14469    height,
14470    width;
14471
14472  size_t
14473    delay;
14474
14475  WarningHandler
14476    warning_handler;
14477
14478  Window
14479    root_window;
14480
14481  XClassHint
14482    *class_hints;
14483
14484  XEvent
14485    event;
14486
14487  XFontStruct
14488    *font_info;
14489
14490  XGCValues
14491    context_values;
14492
14493  XPixelInfo
14494    *icon_pixel,
14495    *pixel;
14496
14497  XResourceInfo
14498    *icon_resources;
14499
14500  XStandardColormap
14501    *icon_map,
14502    *map_info;
14503
14504  XVisualInfo
14505    *icon_visual,
14506    *visual_info;
14507
14508  XWindowChanges
14509    window_changes;
14510
14511  XWindows
14512    *windows;
14513
14514  XWMHints
14515    *manager_hints;
14516
14517  assert(image != (Image **) NULL);
14518  assert((*image)->signature == MagickSignature);
14519  if ((*image)->debug != MagickFalse)
14520    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14521  display_image=(*image);
14522  warning_handler=(WarningHandler) NULL;
14523  windows=XSetWindows((XWindows *) ~0);
14524  if (windows != (XWindows *) NULL)
14525    {
14526      int
14527        status;
14528
14529      status=chdir(working_directory);
14530      if (status == -1)
14531        (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14532          "UnableToOpenFile","%s",working_directory);
14533      warning_handler=resource_info->display_warnings ?
14534        SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14535      warning_handler=resource_info->display_warnings ?
14536        SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14537    }
14538  else
14539    {
14540      /*
14541        Allocate windows structure.
14542      */
14543      resource_info->colors=display_image->colors;
14544      windows=XSetWindows(XInitializeWindows(display,resource_info));
14545      if (windows == (XWindows *) NULL)
14546        ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14547          (*image)->filename);
14548      /*
14549        Initialize window id's.
14550      */
14551      number_windows=0;
14552      magick_windows[number_windows++]=(&windows->icon);
14553      magick_windows[number_windows++]=(&windows->backdrop);
14554      magick_windows[number_windows++]=(&windows->image);
14555      magick_windows[number_windows++]=(&windows->info);
14556      magick_windows[number_windows++]=(&windows->command);
14557      magick_windows[number_windows++]=(&windows->widget);
14558      magick_windows[number_windows++]=(&windows->popup);
14559      magick_windows[number_windows++]=(&windows->magnify);
14560      magick_windows[number_windows++]=(&windows->pan);
14561      for (i=0; i < (int) number_windows; i++)
14562        magick_windows[i]->id=(Window) NULL;
14563      vid_info.x=0;
14564      vid_info.y=0;
14565    }
14566  /*
14567    Initialize font info.
14568  */
14569  if (windows->font_info != (XFontStruct *) NULL)
14570    (void) XFreeFont(display,windows->font_info);
14571  windows->font_info=XBestFont(display,resource_info,MagickFalse);
14572  if (windows->font_info == (XFontStruct *) NULL)
14573    ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14574      resource_info->font);
14575  /*
14576    Initialize Standard Colormap.
14577  */
14578  map_info=windows->map_info;
14579  icon_map=windows->icon_map;
14580  visual_info=windows->visual_info;
14581  icon_visual=windows->icon_visual;
14582  pixel=windows->pixel_info;
14583  icon_pixel=windows->icon_pixel;
14584  font_info=windows->font_info;
14585  icon_resources=windows->icon_resources;
14586  class_hints=windows->class_hints;
14587  manager_hints=windows->manager_hints;
14588  root_window=XRootWindow(display,visual_info->screen);
14589  nexus=NewImageList();
14590  if (display_image->debug != MagickFalse)
14591    {
14592      (void) LogMagickEvent(X11Event,GetMagickModule(),
14593        "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14594        (double) display_image->scene,(double) display_image->columns,
14595        (double) display_image->rows);
14596      if (display_image->colors != 0)
14597        (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14598          display_image->colors);
14599      (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14600        display_image->magick);
14601    }
14602  XMakeStandardColormap(display,visual_info,resource_info,display_image,
14603    map_info,pixel);
14604  display_image->taint=MagickFalse;
14605  /*
14606    Initialize graphic context.
14607  */
14608  windows->context.id=(Window) NULL;
14609  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14610    resource_info,&windows->context);
14611  (void) CloneString(&class_hints->res_name,resource_info->client_name);
14612  (void) CloneString(&class_hints->res_class,resource_info->client_name);
14613  class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14614  manager_hints->flags=InputHint | StateHint;
14615  manager_hints->input=MagickFalse;
14616  manager_hints->initial_state=WithdrawnState;
14617  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14618    &windows->context);
14619  if (display_image->debug != MagickFalse)
14620    (void) LogMagickEvent(X11Event,GetMagickModule(),
14621      "Window id: 0x%lx (context)",windows->context.id);
14622  context_values.background=pixel->background_color.pixel;
14623  context_values.font=font_info->fid;
14624  context_values.foreground=pixel->foreground_color.pixel;
14625  context_values.graphics_exposures=MagickFalse;
14626  context_mask=(MagickStatusType)
14627    (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14628  if (pixel->annotate_context != (GC) NULL)
14629    (void) XFreeGC(display,pixel->annotate_context);
14630  pixel->annotate_context=XCreateGC(display,windows->context.id,
14631    context_mask,&context_values);
14632  if (pixel->annotate_context == (GC) NULL)
14633    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14634      display_image->filename);
14635  context_values.background=pixel->depth_color.pixel;
14636  if (pixel->widget_context != (GC) NULL)
14637    (void) XFreeGC(display,pixel->widget_context);
14638  pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14639    &context_values);
14640  if (pixel->widget_context == (GC) NULL)
14641    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14642      display_image->filename);
14643  context_values.background=pixel->foreground_color.pixel;
14644  context_values.foreground=pixel->background_color.pixel;
14645  context_values.plane_mask=context_values.background ^
14646    context_values.foreground;
14647  if (pixel->highlight_context != (GC) NULL)
14648    (void) XFreeGC(display,pixel->highlight_context);
14649  pixel->highlight_context=XCreateGC(display,windows->context.id,
14650    (size_t) (context_mask | GCPlaneMask),&context_values);
14651  if (pixel->highlight_context == (GC) NULL)
14652    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14653      display_image->filename);
14654  (void) XDestroyWindow(display,windows->context.id);
14655  /*
14656    Initialize icon window.
14657  */
14658  XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14659    icon_resources,&windows->icon);
14660  windows->icon.geometry=resource_info->icon_geometry;
14661  XBestIconSize(display,&windows->icon,display_image);
14662  windows->icon.attributes.colormap=XDefaultColormap(display,
14663    icon_visual->screen);
14664  windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14665  manager_hints->flags=InputHint | StateHint;
14666  manager_hints->input=MagickFalse;
14667  manager_hints->initial_state=IconicState;
14668  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14669    &windows->icon);
14670  if (display_image->debug != MagickFalse)
14671    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14672      windows->icon.id);
14673  /*
14674    Initialize graphic context for icon window.
14675  */
14676  if (icon_pixel->annotate_context != (GC) NULL)
14677    (void) XFreeGC(display,icon_pixel->annotate_context);
14678  context_values.background=icon_pixel->background_color.pixel;
14679  context_values.foreground=icon_pixel->foreground_color.pixel;
14680  icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14681    (size_t) (GCBackground | GCForeground),&context_values);
14682  if (icon_pixel->annotate_context == (GC) NULL)
14683    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14684      display_image->filename);
14685  windows->icon.annotate_context=icon_pixel->annotate_context;
14686  /*
14687    Initialize Image window.
14688  */
14689  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14690    &windows->image);
14691  windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14692  if (resource_info->use_shared_memory == MagickFalse)
14693    windows->image.shared_memory=MagickFalse;
14694  if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14695    {
14696      char
14697        *title;
14698
14699      title=InterpretImageProperties(resource_info->image_info,display_image,
14700        resource_info->title,exception);
14701      (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
14702      (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
14703      title=DestroyString(title);
14704    }
14705  else
14706    {
14707      char
14708        filename[MaxTextExtent];
14709
14710      /*
14711        Window name is the base of the filename.
14712      */
14713      GetPathComponent(display_image->magick_filename,TailPath,filename);
14714      if (display_image->scene == 0)
14715        (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14716          "%s: %s",MagickPackageName,filename);
14717      else
14718        (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14719          "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14720          (double) display_image->scene,(double) GetImageListLength(
14721          display_image));
14722      (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
14723    }
14724  if (resource_info->immutable)
14725    windows->image.immutable=MagickTrue;
14726  windows->image.use_pixmap=resource_info->use_pixmap;
14727  windows->image.geometry=resource_info->image_geometry;
14728  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14729    XDisplayWidth(display,visual_info->screen),
14730    XDisplayHeight(display,visual_info->screen));
14731  geometry_info.width=display_image->columns;
14732  geometry_info.height=display_image->rows;
14733  geometry_info.x=0;
14734  geometry_info.y=0;
14735  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14736    &geometry_info.width,&geometry_info.height);
14737  windows->image.width=(unsigned int) geometry_info.width;
14738  windows->image.height=(unsigned int) geometry_info.height;
14739  windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14740    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14741    KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14742    PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14743  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14744    resource_info,&windows->backdrop);
14745  if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14746    {
14747      /*
14748        Initialize backdrop window.
14749      */
14750      windows->backdrop.x=0;
14751      windows->backdrop.y=0;
14752      (void) CloneString(&windows->backdrop.name,"Backdrop");
14753      windows->backdrop.flags=(size_t) (USSize | USPosition);
14754      windows->backdrop.width=(unsigned int)
14755        XDisplayWidth(display,visual_info->screen);
14756      windows->backdrop.height=(unsigned int)
14757        XDisplayHeight(display,visual_info->screen);
14758      windows->backdrop.border_width=0;
14759      windows->backdrop.immutable=MagickTrue;
14760      windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14761        ButtonReleaseMask;
14762      windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14763        StructureNotifyMask;
14764      manager_hints->flags=IconWindowHint | InputHint | StateHint;
14765      manager_hints->icon_window=windows->icon.id;
14766      manager_hints->input=MagickTrue;
14767      manager_hints->initial_state=resource_info->iconic ? IconicState :
14768        NormalState;
14769      XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14770        &windows->backdrop);
14771      if (display_image->debug != MagickFalse)
14772        (void) LogMagickEvent(X11Event,GetMagickModule(),
14773          "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14774      (void) XMapWindow(display,windows->backdrop.id);
14775      (void) XClearWindow(display,windows->backdrop.id);
14776      if (windows->image.id != (Window) NULL)
14777        {
14778          (void) XDestroyWindow(display,windows->image.id);
14779          windows->image.id=(Window) NULL;
14780        }
14781      /*
14782        Position image in the center the backdrop.
14783      */
14784      windows->image.flags|=USPosition;
14785      windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14786        (windows->image.width/2);
14787      windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14788        (windows->image.height/2);
14789    }
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  if (windows->group_leader.id != (Window) NULL)
14796    {
14797      /*
14798        Follow the leader.
14799      */
14800      manager_hints->flags|=WindowGroupHint;
14801      manager_hints->window_group=windows->group_leader.id;
14802      (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14803      if (display_image->debug != MagickFalse)
14804        (void) LogMagickEvent(X11Event,GetMagickModule(),
14805          "Window id: 0x%lx (group leader)",windows->group_leader.id);
14806    }
14807  XMakeWindow(display,
14808    (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14809    argv,argc,class_hints,manager_hints,&windows->image);
14810  (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14811    XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14812  if (windows->group_leader.id != (Window) NULL)
14813    (void) XSetTransientForHint(display,windows->image.id,
14814      windows->group_leader.id);
14815  if (display_image->debug != MagickFalse)
14816    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14817      windows->image.id);
14818  /*
14819    Initialize Info widget.
14820  */
14821  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14822    &windows->info);
14823  (void) CloneString(&windows->info.name,"Info");
14824  (void) CloneString(&windows->info.icon_name,"Info");
14825  windows->info.border_width=1;
14826  windows->info.x=2;
14827  windows->info.y=2;
14828  windows->info.flags|=PPosition;
14829  windows->info.attributes.win_gravity=UnmapGravity;
14830  windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14831    StructureNotifyMask;
14832  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14833  manager_hints->input=MagickFalse;
14834  manager_hints->initial_state=NormalState;
14835  manager_hints->window_group=windows->image.id;
14836  XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14837    &windows->info);
14838  windows->info.highlight_stipple=XCreateBitmapFromData(display,
14839    windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14840  windows->info.shadow_stipple=XCreateBitmapFromData(display,
14841    windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14842  (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14843  if (windows->image.mapped != MagickFalse)
14844    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14845  if (display_image->debug != MagickFalse)
14846    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14847      windows->info.id);
14848  /*
14849    Initialize Command widget.
14850  */
14851  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14852    resource_info,&windows->command);
14853  windows->command.data=MagickMenus;
14854  (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14855  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
14856    resource_info->client_name);
14857  windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14858    resource_name,"geometry",(char *) NULL);
14859  (void) CloneString(&windows->command.name,MagickTitle);
14860  windows->command.border_width=0;
14861  windows->command.flags|=PPosition;
14862  windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14863    ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14864    OwnerGrabButtonMask | StructureNotifyMask;
14865  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14866  manager_hints->input=MagickTrue;
14867  manager_hints->initial_state=NormalState;
14868  manager_hints->window_group=windows->image.id;
14869  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14870    &windows->command);
14871  windows->command.highlight_stipple=XCreateBitmapFromData(display,
14872    windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14873    HighlightHeight);
14874  windows->command.shadow_stipple=XCreateBitmapFromData(display,
14875    windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14876  (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14877  if (windows->command.mapped != MagickFalse)
14878    (void) XMapRaised(display,windows->command.id);
14879  if (display_image->debug != MagickFalse)
14880    (void) LogMagickEvent(X11Event,GetMagickModule(),
14881      "Window id: 0x%lx (command)",windows->command.id);
14882  /*
14883    Initialize Widget window.
14884  */
14885  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14886    resource_info,&windows->widget);
14887  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
14888    resource_info->client_name);
14889  windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14890    resource_name,"geometry",(char *) NULL);
14891  windows->widget.border_width=0;
14892  windows->widget.flags|=PPosition;
14893  windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14894    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14895    KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14896    StructureNotifyMask;
14897  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14898  manager_hints->input=MagickTrue;
14899  manager_hints->initial_state=NormalState;
14900  manager_hints->window_group=windows->image.id;
14901  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14902    &windows->widget);
14903  windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14904    windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14905  windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14906    windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14907  (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14908  if (display_image->debug != MagickFalse)
14909    (void) LogMagickEvent(X11Event,GetMagickModule(),
14910      "Window id: 0x%lx (widget)",windows->widget.id);
14911  /*
14912    Initialize popup window.
14913  */
14914  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14915    resource_info,&windows->popup);
14916  windows->popup.border_width=0;
14917  windows->popup.flags|=PPosition;
14918  windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14919    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14920    KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14921  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14922  manager_hints->input=MagickTrue;
14923  manager_hints->initial_state=NormalState;
14924  manager_hints->window_group=windows->image.id;
14925  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14926    &windows->popup);
14927  windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14928    windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14929  windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14930    windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14931  (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14932  if (display_image->debug != MagickFalse)
14933    (void) LogMagickEvent(X11Event,GetMagickModule(),
14934      "Window id: 0x%lx (pop up)",windows->popup.id);
14935  /*
14936    Initialize Magnify window and cursor.
14937  */
14938  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14939    resource_info,&windows->magnify);
14940  if (resource_info->use_shared_memory == MagickFalse)
14941    windows->magnify.shared_memory=MagickFalse;
14942  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
14943    resource_info->client_name);
14944  windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14945    resource_name,"geometry",(char *) NULL);
14946  (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14947    resource_info->magnify);
14948  if (windows->magnify.cursor != (Cursor) NULL)
14949    (void) XFreeCursor(display,windows->magnify.cursor);
14950  windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14951    map_info->colormap,resource_info->background_color,
14952    resource_info->foreground_color);
14953  if (windows->magnify.cursor == (Cursor) NULL)
14954    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14955      display_image->filename);
14956  windows->magnify.width=MagnifySize;
14957  windows->magnify.height=MagnifySize;
14958  windows->magnify.flags|=PPosition;
14959  windows->magnify.min_width=MagnifySize;
14960  windows->magnify.min_height=MagnifySize;
14961  windows->magnify.width_inc=MagnifySize;
14962  windows->magnify.height_inc=MagnifySize;
14963  windows->magnify.data=resource_info->magnify;
14964  windows->magnify.attributes.cursor=windows->magnify.cursor;
14965  windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14966    ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14967    StructureNotifyMask;
14968  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14969  manager_hints->input=MagickTrue;
14970  manager_hints->initial_state=NormalState;
14971  manager_hints->window_group=windows->image.id;
14972  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14973    &windows->magnify);
14974  if (display_image->debug != MagickFalse)
14975    (void) LogMagickEvent(X11Event,GetMagickModule(),
14976      "Window id: 0x%lx (magnify)",windows->magnify.id);
14977  (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14978  /*
14979    Initialize panning window.
14980  */
14981  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14982    resource_info,&windows->pan);
14983  (void) CloneString(&windows->pan.name,"Pan Icon");
14984  windows->pan.width=windows->icon.width;
14985  windows->pan.height=windows->icon.height;
14986  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
14987    resource_info->client_name);
14988  windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
14989    resource_name,"geometry",(char *) NULL);
14990  (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
14991    &windows->pan.width,&windows->pan.height);
14992  windows->pan.flags|=PPosition;
14993  windows->pan.immutable=MagickTrue;
14994  windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14995    ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
14996    StructureNotifyMask;
14997  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14998  manager_hints->input=MagickFalse;
14999  manager_hints->initial_state=NormalState;
15000  manager_hints->window_group=windows->image.id;
15001  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
15002    &windows->pan);
15003  if (display_image->debug != MagickFalse)
15004    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
15005      windows->pan.id);
15006  (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
15007  if (windows->info.mapped != MagickFalse)
15008    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15009  if ((windows->image.mapped == MagickFalse) ||
15010      (windows->backdrop.id != (Window) NULL))
15011    (void) XMapWindow(display,windows->image.id);
15012  /*
15013    Set our progress monitor and warning handlers.
15014  */
15015  if (warning_handler == (WarningHandler) NULL)
15016    {
15017      warning_handler=resource_info->display_warnings ?
15018        SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
15019      warning_handler=resource_info->display_warnings ?
15020        SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
15021    }
15022  /*
15023    Initialize Image and Magnify X images.
15024  */
15025  windows->image.x=0;
15026  windows->image.y=0;
15027  windows->magnify.shape=MagickFalse;
15028  width=(unsigned int) display_image->columns;
15029  height=(unsigned int) display_image->rows;
15030  if ((display_image->columns != width) || (display_image->rows != height))
15031    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15032      display_image->filename);
15033  status=XMakeImage(display,resource_info,&windows->image,display_image,
15034    width,height,exception);
15035  if (status == MagickFalse)
15036    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15037      display_image->filename);
15038  status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
15039    windows->magnify.width,windows->magnify.height,exception);
15040  if (status == MagickFalse)
15041    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15042      display_image->filename);
15043  if (windows->magnify.mapped != MagickFalse)
15044    (void) XMapRaised(display,windows->magnify.id);
15045  if (windows->pan.mapped != MagickFalse)
15046    (void) XMapRaised(display,windows->pan.id);
15047  windows->image.window_changes.width=(int) display_image->columns;
15048  windows->image.window_changes.height=(int) display_image->rows;
15049  (void) XConfigureImage(display,resource_info,windows,display_image,exception);
15050  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15051  (void) XSync(display,MagickFalse);
15052  /*
15053    Respond to events.
15054  */
15055  delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
15056  timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15057  update_time=0;
15058  if (resource_info->update != MagickFalse)
15059    {
15060      MagickBooleanType
15061        status;
15062
15063      /*
15064        Determine when file data was last modified.
15065      */
15066      status=GetPathAttributes(display_image->filename,&attributes);
15067      if (status != MagickFalse)
15068        update_time=attributes.st_mtime;
15069    }
15070  *state&=(~FormerImageState);
15071  *state&=(~MontageImageState);
15072  *state&=(~NextImageState);
15073  do
15074  {
15075    /*
15076      Handle a window event.
15077    */
15078    if (windows->image.mapped != MagickFalse)
15079      if ((display_image->delay != 0) || (resource_info->update != 0))
15080        {
15081          if (timer < time((time_t *) NULL))
15082            {
15083              if (resource_info->update == MagickFalse)
15084                *state|=NextImageState | ExitState;
15085              else
15086                {
15087                  MagickBooleanType
15088                    status;
15089
15090                  /*
15091                    Determine if image file was modified.
15092                  */
15093                  status=GetPathAttributes(display_image->filename,&attributes);
15094                  if (status != MagickFalse)
15095                    if (update_time != attributes.st_mtime)
15096                      {
15097                        /*
15098                          Redisplay image.
15099                        */
15100                        (void) FormatLocaleString(
15101                          resource_info->image_info->filename,MaxTextExtent,
15102                          "%s:%s",display_image->magick,
15103                          display_image->filename);
15104                        nexus=ReadImage(resource_info->image_info,
15105                          &display_image->exception);
15106                        if (nexus != (Image *) NULL)
15107                          {
15108                            nexus=DestroyImage(nexus);
15109                            *state|=NextImageState | ExitState;
15110                          }
15111                      }
15112                  delay=display_image->delay/MagickMax(
15113                    display_image->ticks_per_second,1L);
15114                  timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15115                }
15116            }
15117          if (XEventsQueued(display,QueuedAfterFlush) == 0)
15118            {
15119              /*
15120                Do not block if delay > 0.
15121              */
15122              XDelay(display,SuspendTime << 2);
15123              continue;
15124            }
15125        }
15126    timestamp=time((time_t *) NULL);
15127    (void) XNextEvent(display,&event);
15128    if (windows->image.stasis == MagickFalse)
15129      windows->image.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15130        MagickTrue : MagickFalse;
15131    if (windows->magnify.stasis == MagickFalse)
15132      windows->magnify.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15133        MagickTrue : MagickFalse;
15134    if (event.xany.window == windows->command.id)
15135      {
15136        /*
15137          Select a command from the Command widget.
15138        */
15139        id=XCommandWidget(display,windows,CommandMenu,&event);
15140        if (id < 0)
15141          continue;
15142        (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15143        command_type=CommandMenus[id];
15144        if (id < MagickMenus)
15145          {
15146            /*
15147              Select a command from a pop-up menu.
15148            */
15149            entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15150              command);
15151            if (entry < 0)
15152              continue;
15153            (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15154            command_type=Commands[id][entry];
15155          }
15156        if (command_type != NullCommand)
15157          nexus=XMagickCommand(display,resource_info,windows,command_type,
15158            &display_image,exception);
15159        continue;
15160      }
15161    switch (event.type)
15162    {
15163      case ButtonPress:
15164      {
15165        if (display_image->debug != MagickFalse)
15166          (void) LogMagickEvent(X11Event,GetMagickModule(),
15167            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15168            event.xbutton.button,event.xbutton.x,event.xbutton.y);
15169        if ((event.xbutton.button == Button3) &&
15170            (event.xbutton.state & Mod1Mask))
15171          {
15172            /*
15173              Convert Alt-Button3 to Button2.
15174            */
15175            event.xbutton.button=Button2;
15176            event.xbutton.state&=(~Mod1Mask);
15177          }
15178        if (event.xbutton.window == windows->backdrop.id)
15179          {
15180            (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15181              event.xbutton.time);
15182            break;
15183          }
15184        if (event.xbutton.window == windows->image.id)
15185          {
15186            switch (event.xbutton.button)
15187            {
15188              case Button1:
15189              {
15190                if (resource_info->immutable)
15191                  {
15192                    /*
15193                      Select a command from the Virtual menu.
15194                    */
15195                    entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15196                      command);
15197                    if (entry >= 0)
15198                      nexus=XMagickCommand(display,resource_info,windows,
15199                        VirtualCommands[entry],&display_image,exception);
15200                    break;
15201                  }
15202                /*
15203                  Map/unmap Command widget.
15204                */
15205                if (windows->command.mapped != MagickFalse)
15206                  (void) XWithdrawWindow(display,windows->command.id,
15207                    windows->command.screen);
15208                else
15209                  {
15210                    (void) XCommandWidget(display,windows,CommandMenu,
15211                      (XEvent *) NULL);
15212                    (void) XMapRaised(display,windows->command.id);
15213                  }
15214                break;
15215              }
15216              case Button2:
15217              {
15218                /*
15219                  User pressed the image magnify button.
15220                */
15221                (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15222                  &display_image,exception);
15223                XMagnifyImage(display,windows,&event);
15224                break;
15225              }
15226              case Button3:
15227              {
15228                if (resource_info->immutable)
15229                  {
15230                    /*
15231                      Select a command from the Virtual menu.
15232                    */
15233                    entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15234                      command);
15235                    if (entry >= 0)
15236                      nexus=XMagickCommand(display,resource_info,windows,
15237                        VirtualCommands[entry],&display_image,exception);
15238                    break;
15239                  }
15240                if (display_image->montage != (char *) NULL)
15241                  {
15242                    /*
15243                      Open or delete a tile from a visual image directory.
15244                    */
15245                    nexus=XTileImage(display,resource_info,windows,
15246                      display_image,&event,exception);
15247                    if (nexus != (Image *) NULL)
15248                      *state|=MontageImageState | NextImageState | ExitState;
15249                    vid_info.x=(short int) windows->image.x;
15250                    vid_info.y=(short int) windows->image.y;
15251                    break;
15252                  }
15253                /*
15254                  Select a command from the Short Cuts menu.
15255                */
15256                entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15257                  command);
15258                if (entry >= 0)
15259                  nexus=XMagickCommand(display,resource_info,windows,
15260                    ShortCutsCommands[entry],&display_image,exception);
15261                break;
15262              }
15263              case Button4:
15264              {
15265                /*
15266                  Wheel up.
15267                */
15268                XTranslateImage(display,windows,*image,XK_Up);
15269                break;
15270              }
15271              case Button5:
15272              {
15273                /*
15274                  Wheel down.
15275                */
15276                XTranslateImage(display,windows,*image,XK_Down);
15277                break;
15278              }
15279              default:
15280                break;
15281            }
15282            break;
15283          }
15284        if (event.xbutton.window == windows->magnify.id)
15285          {
15286            int
15287              factor;
15288
15289            static const char
15290              *MagnifyMenu[] =
15291              {
15292                "2",
15293                "4",
15294                "5",
15295                "6",
15296                "7",
15297                "8",
15298                "9",
15299                "3",
15300                (char *) NULL,
15301              };
15302
15303            static KeySym
15304              MagnifyCommands[] =
15305              {
15306                XK_2,
15307                XK_4,
15308                XK_5,
15309                XK_6,
15310                XK_7,
15311                XK_8,
15312                XK_9,
15313                XK_3
15314              };
15315
15316            /*
15317              Select a magnify factor from the pop-up menu.
15318            */
15319            factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15320            if (factor >= 0)
15321              XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor]);
15322            break;
15323          }
15324        if (event.xbutton.window == windows->pan.id)
15325          {
15326            switch (event.xbutton.button)
15327            {
15328              case Button4:
15329              {
15330                /*
15331                  Wheel up.
15332                */
15333                XTranslateImage(display,windows,*image,XK_Up);
15334                break;
15335              }
15336              case Button5:
15337              {
15338                /*
15339                  Wheel down.
15340                */
15341                XTranslateImage(display,windows,*image,XK_Down);
15342                break;
15343              }
15344              default:
15345              {
15346                XPanImage(display,windows,&event);
15347                break;
15348              }
15349            }
15350            break;
15351          }
15352        delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15353          1L);
15354        timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15355        break;
15356      }
15357      case ButtonRelease:
15358      {
15359        if (display_image->debug != MagickFalse)
15360          (void) LogMagickEvent(X11Event,GetMagickModule(),
15361            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15362            event.xbutton.button,event.xbutton.x,event.xbutton.y);
15363        break;
15364      }
15365      case ClientMessage:
15366      {
15367        if (display_image->debug != MagickFalse)
15368          (void) LogMagickEvent(X11Event,GetMagickModule(),
15369            "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15370            event.xclient.message_type,event.xclient.format,(unsigned long)
15371            event.xclient.data.l[0]);
15372        if (event.xclient.message_type == windows->im_protocols)
15373          {
15374            if (*event.xclient.data.l == (long) windows->im_update_widget)
15375              {
15376                (void) CloneString(&windows->command.name,MagickTitle);
15377                windows->command.data=MagickMenus;
15378                (void) XCommandWidget(display,windows,CommandMenu,
15379                  (XEvent *) NULL);
15380                break;
15381              }
15382            if (*event.xclient.data.l == (long) windows->im_update_colormap)
15383              {
15384                /*
15385                  Update graphic context and window colormap.
15386                */
15387                for (i=0; i < (int) number_windows; i++)
15388                {
15389                  if (magick_windows[i]->id == windows->icon.id)
15390                    continue;
15391                  context_values.background=pixel->background_color.pixel;
15392                  context_values.foreground=pixel->foreground_color.pixel;
15393                  (void) XChangeGC(display,magick_windows[i]->annotate_context,
15394                    context_mask,&context_values);
15395                  (void) XChangeGC(display,magick_windows[i]->widget_context,
15396                    context_mask,&context_values);
15397                  context_values.background=pixel->foreground_color.pixel;
15398                  context_values.foreground=pixel->background_color.pixel;
15399                  context_values.plane_mask=context_values.background ^
15400                    context_values.foreground;
15401                  (void) XChangeGC(display,magick_windows[i]->highlight_context,
15402                    (size_t) (context_mask | GCPlaneMask),
15403                    &context_values);
15404                  magick_windows[i]->attributes.background_pixel=
15405                    pixel->background_color.pixel;
15406                  magick_windows[i]->attributes.border_pixel=
15407                    pixel->border_color.pixel;
15408                  magick_windows[i]->attributes.colormap=map_info->colormap;
15409                  (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15410                    (unsigned long) magick_windows[i]->mask,
15411                    &magick_windows[i]->attributes);
15412                }
15413                if (windows->pan.mapped != MagickFalse)
15414                  {
15415                    (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15416                      windows->pan.pixmap);
15417                    (void) XClearWindow(display,windows->pan.id);
15418                    XDrawPanRectangle(display,windows);
15419                  }
15420                if (windows->backdrop.id != (Window) NULL)
15421                  (void) XInstallColormap(display,map_info->colormap);
15422                break;
15423              }
15424            if (*event.xclient.data.l == (long) windows->im_former_image)
15425              {
15426                *state|=FormerImageState | ExitState;
15427                break;
15428              }
15429            if (*event.xclient.data.l == (long) windows->im_next_image)
15430              {
15431                *state|=NextImageState | ExitState;
15432                break;
15433              }
15434            if (*event.xclient.data.l == (long) windows->im_retain_colors)
15435              {
15436                *state|=RetainColorsState;
15437                break;
15438              }
15439            if (*event.xclient.data.l == (long) windows->im_exit)
15440              {
15441                *state|=ExitState;
15442                break;
15443              }
15444            break;
15445          }
15446        if (event.xclient.message_type == windows->dnd_protocols)
15447          {
15448            Atom
15449              selection,
15450              type;
15451
15452            int
15453              format,
15454              status;
15455
15456            unsigned char
15457              *data;
15458
15459            unsigned long
15460              after,
15461              length;
15462
15463            /*
15464              Display image named by the Drag-and-Drop selection.
15465            */
15466            if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15467              break;
15468            selection=XInternAtom(display,"DndSelection",MagickFalse);
15469            status=XGetWindowProperty(display,root_window,selection,0L,(long)
15470              MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15471              &length,&after,&data);
15472            if ((status != Success) || (length == 0))
15473              break;
15474            if (*event.xclient.data.l == 2)
15475              {
15476                /*
15477                  Offix DND.
15478                */
15479                (void) CopyMagickString(resource_info->image_info->filename,
15480                  (char *) data,MaxTextExtent);
15481              }
15482            else
15483              {
15484                /*
15485                  XDND.
15486                */
15487                if (strncmp((char *) data, "file:", 5) != 0)
15488                  {
15489                    (void) XFree((void *) data);
15490                    break;
15491                  }
15492                (void) CopyMagickString(resource_info->image_info->filename,
15493                  ((char *) data)+5,MaxTextExtent);
15494              }
15495            nexus=ReadImage(resource_info->image_info,
15496              &display_image->exception);
15497            CatchException(&display_image->exception);
15498            if (nexus != (Image *) NULL)
15499              *state|=NextImageState | ExitState;
15500            (void) XFree((void *) data);
15501            break;
15502          }
15503        /*
15504          If client window delete message, exit.
15505        */
15506        if (event.xclient.message_type != windows->wm_protocols)
15507          break;
15508        if (*event.xclient.data.l != (long) windows->wm_delete_window)
15509          break;
15510        (void) XWithdrawWindow(display,event.xclient.window,
15511          visual_info->screen);
15512        if (event.xclient.window == windows->image.id)
15513          {
15514            *state|=ExitState;
15515            break;
15516          }
15517        if (event.xclient.window == windows->pan.id)
15518          {
15519            /*
15520              Restore original image size when pan window is deleted.
15521            */
15522            windows->image.window_changes.width=windows->image.ximage->width;
15523            windows->image.window_changes.height=windows->image.ximage->height;
15524            (void) XConfigureImage(display,resource_info,windows,
15525              display_image,exception);
15526          }
15527        break;
15528      }
15529      case ConfigureNotify:
15530      {
15531        if (display_image->debug != MagickFalse)
15532          (void) LogMagickEvent(X11Event,GetMagickModule(),
15533            "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15534            event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15535            event.xconfigure.y,event.xconfigure.send_event);
15536        if (event.xconfigure.window == windows->image.id)
15537          {
15538            /*
15539              Image window has a new configuration.
15540            */
15541            if (event.xconfigure.send_event != 0)
15542              {
15543                XWindowChanges
15544                  window_changes;
15545
15546                /*
15547                  Position the transient windows relative of the Image window.
15548                */
15549                if (windows->command.geometry == (char *) NULL)
15550                  if (windows->command.mapped == MagickFalse)
15551                    {
15552                      windows->command.x=event.xconfigure.x-
15553                        windows->command.width-25;
15554                      windows->command.y=event.xconfigure.y;
15555                      XConstrainWindowPosition(display,&windows->command);
15556                      window_changes.x=windows->command.x;
15557                      window_changes.y=windows->command.y;
15558                      (void) XReconfigureWMWindow(display,windows->command.id,
15559                        windows->command.screen,(unsigned int) (CWX | CWY),
15560                        &window_changes);
15561                    }
15562                if (windows->widget.geometry == (char *) NULL)
15563                  if (windows->widget.mapped == MagickFalse)
15564                    {
15565                      windows->widget.x=event.xconfigure.x+
15566                        event.xconfigure.width/10;
15567                      windows->widget.y=event.xconfigure.y+
15568                        event.xconfigure.height/10;
15569                      XConstrainWindowPosition(display,&windows->widget);
15570                      window_changes.x=windows->widget.x;
15571                      window_changes.y=windows->widget.y;
15572                      (void) XReconfigureWMWindow(display,windows->widget.id,
15573                        windows->widget.screen,(unsigned int) (CWX | CWY),
15574                        &window_changes);
15575                    }
15576                if (windows->magnify.geometry == (char *) NULL)
15577                  if (windows->magnify.mapped == MagickFalse)
15578                    {
15579                      windows->magnify.x=event.xconfigure.x+
15580                        event.xconfigure.width+25;
15581                      windows->magnify.y=event.xconfigure.y;
15582                      XConstrainWindowPosition(display,&windows->magnify);
15583                      window_changes.x=windows->magnify.x;
15584                      window_changes.y=windows->magnify.y;
15585                      (void) XReconfigureWMWindow(display,windows->magnify.id,
15586                        windows->magnify.screen,(unsigned int) (CWX | CWY),
15587                        &window_changes);
15588                    }
15589                if (windows->pan.geometry == (char *) NULL)
15590                  if (windows->pan.mapped == MagickFalse)
15591                    {
15592                      windows->pan.x=event.xconfigure.x+
15593                        event.xconfigure.width+25;
15594                      windows->pan.y=event.xconfigure.y+
15595                        windows->magnify.height+50;
15596                      XConstrainWindowPosition(display,&windows->pan);
15597                      window_changes.x=windows->pan.x;
15598                      window_changes.y=windows->pan.y;
15599                      (void) XReconfigureWMWindow(display,windows->pan.id,
15600                        windows->pan.screen,(unsigned int) (CWX | CWY),
15601                        &window_changes);
15602                    }
15603              }
15604            if ((event.xconfigure.width == (int) windows->image.width) &&
15605                (event.xconfigure.height == (int) windows->image.height))
15606              break;
15607            windows->image.width=(unsigned int) event.xconfigure.width;
15608            windows->image.height=(unsigned int) event.xconfigure.height;
15609            windows->image.x=0;
15610            windows->image.y=0;
15611            if (display_image->montage != (char *) NULL)
15612              {
15613                windows->image.x=vid_info.x;
15614                windows->image.y=vid_info.y;
15615              }
15616            if ((windows->image.mapped != MagickFalse) &&
15617                (windows->image.stasis != MagickFalse))
15618              {
15619                /*
15620                  Update image window configuration.
15621                */
15622                windows->image.window_changes.width=event.xconfigure.width;
15623                windows->image.window_changes.height=event.xconfigure.height;
15624                (void) XConfigureImage(display,resource_info,windows,
15625                  display_image,exception);
15626              }
15627            /*
15628              Update pan window configuration.
15629            */
15630            if ((event.xconfigure.width < windows->image.ximage->width) ||
15631                (event.xconfigure.height < windows->image.ximage->height))
15632              {
15633                (void) XMapRaised(display,windows->pan.id);
15634                XDrawPanRectangle(display,windows);
15635              }
15636            else
15637              if (windows->pan.mapped != MagickFalse)
15638                (void) XWithdrawWindow(display,windows->pan.id,
15639                  windows->pan.screen);
15640            break;
15641          }
15642        if (event.xconfigure.window == windows->magnify.id)
15643          {
15644            unsigned int
15645              magnify;
15646
15647            /*
15648              Magnify window has a new configuration.
15649            */
15650            windows->magnify.width=(unsigned int) event.xconfigure.width;
15651            windows->magnify.height=(unsigned int) event.xconfigure.height;
15652            if (windows->magnify.mapped == MagickFalse)
15653              break;
15654            magnify=1;
15655            while ((int) magnify <= event.xconfigure.width)
15656              magnify<<=1;
15657            while ((int) magnify <= event.xconfigure.height)
15658              magnify<<=1;
15659            magnify>>=1;
15660            if (((int) magnify != event.xconfigure.width) ||
15661                ((int) magnify != event.xconfigure.height))
15662              {
15663                window_changes.width=(int) magnify;
15664                window_changes.height=(int) magnify;
15665                (void) XReconfigureWMWindow(display,windows->magnify.id,
15666                  windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15667                  &window_changes);
15668                break;
15669              }
15670            if ((windows->magnify.mapped != MagickFalse) &&
15671                (windows->magnify.stasis != MagickFalse))
15672              {
15673                status=XMakeImage(display,resource_info,&windows->magnify,
15674                  display_image,windows->magnify.width,windows->magnify.height,
15675                  exception);
15676                XMakeMagnifyImage(display,windows);
15677              }
15678            break;
15679          }
15680        if ((windows->magnify.mapped != MagickFalse) &&
15681            (event.xconfigure.window == windows->pan.id))
15682          {
15683            /*
15684              Pan icon window has a new configuration.
15685            */
15686            if (event.xconfigure.send_event != 0)
15687              {
15688                windows->pan.x=event.xconfigure.x;
15689                windows->pan.y=event.xconfigure.y;
15690              }
15691            windows->pan.width=(unsigned int) event.xconfigure.width;
15692            windows->pan.height=(unsigned int) event.xconfigure.height;
15693            break;
15694          }
15695        if (event.xconfigure.window == windows->icon.id)
15696          {
15697            /*
15698              Icon window has a new configuration.
15699            */
15700            windows->icon.width=(unsigned int) event.xconfigure.width;
15701            windows->icon.height=(unsigned int) event.xconfigure.height;
15702            break;
15703          }
15704        break;
15705      }
15706      case DestroyNotify:
15707      {
15708        /*
15709          Group leader has exited.
15710        */
15711        if (display_image->debug != MagickFalse)
15712          (void) LogMagickEvent(X11Event,GetMagickModule(),
15713            "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15714        if (event.xdestroywindow.window == windows->group_leader.id)
15715          {
15716            *state|=ExitState;
15717            break;
15718          }
15719        break;
15720      }
15721      case EnterNotify:
15722      {
15723        /*
15724          Selectively install colormap.
15725        */
15726        if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15727          if (event.xcrossing.mode != NotifyUngrab)
15728            XInstallColormap(display,map_info->colormap);
15729        break;
15730      }
15731      case Expose:
15732      {
15733        if (display_image->debug != MagickFalse)
15734          (void) LogMagickEvent(X11Event,GetMagickModule(),
15735            "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15736            event.xexpose.width,event.xexpose.height,event.xexpose.x,
15737            event.xexpose.y);
15738        /*
15739          Refresh windows that are now exposed.
15740        */
15741        if ((event.xexpose.window == windows->image.id) &&
15742            (windows->image.mapped != MagickFalse))
15743          {
15744            XRefreshWindow(display,&windows->image,&event);
15745            delay=display_image->delay/MagickMax(
15746              display_image->ticks_per_second,1L);
15747            timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15748            break;
15749          }
15750        if ((event.xexpose.window == windows->magnify.id) &&
15751            (windows->magnify.mapped != MagickFalse))
15752          {
15753            XMakeMagnifyImage(display,windows);
15754            break;
15755          }
15756        if (event.xexpose.window == windows->pan.id)
15757          {
15758            XDrawPanRectangle(display,windows);
15759            break;
15760          }
15761        if (event.xexpose.window == windows->icon.id)
15762          {
15763            XRefreshWindow(display,&windows->icon,&event);
15764            break;
15765          }
15766        break;
15767      }
15768      case KeyPress:
15769      {
15770        int
15771          length;
15772
15773        /*
15774          Respond to a user key press.
15775        */
15776        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15777          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15778        *(command+length)='\0';
15779        if (display_image->debug != MagickFalse)
15780          (void) LogMagickEvent(X11Event,GetMagickModule(),
15781            "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15782            key_symbol,command);
15783        if (event.xkey.window == windows->image.id)
15784          {
15785            command_type=XImageWindowCommand(display,resource_info,windows,
15786              event.xkey.state,key_symbol,&display_image,exception);
15787            if (command_type != NullCommand)
15788              nexus=XMagickCommand(display,resource_info,windows,command_type,
15789                &display_image,exception);
15790          }
15791        if (event.xkey.window == windows->magnify.id)
15792          XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol);
15793        if (event.xkey.window == windows->pan.id)
15794          {
15795            if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15796              (void) XWithdrawWindow(display,windows->pan.id,
15797                windows->pan.screen);
15798            else
15799              if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15800                XTextViewWidget(display,resource_info,windows,MagickFalse,
15801                  "Help Viewer - Image Pan",ImagePanHelp);
15802              else
15803                XTranslateImage(display,windows,*image,key_symbol);
15804          }
15805        delay=display_image->delay/MagickMax(
15806          display_image->ticks_per_second,1L);
15807        timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15808        break;
15809      }
15810      case KeyRelease:
15811      {
15812        /*
15813          Respond to a user key release.
15814        */
15815        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15816          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15817        if (display_image->debug != MagickFalse)
15818          (void) LogMagickEvent(X11Event,GetMagickModule(),
15819            "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15820        break;
15821      }
15822      case LeaveNotify:
15823      {
15824        /*
15825          Selectively uninstall colormap.
15826        */
15827        if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15828          if (event.xcrossing.mode != NotifyUngrab)
15829            XUninstallColormap(display,map_info->colormap);
15830        break;
15831      }
15832      case MapNotify:
15833      {
15834        if (display_image->debug != MagickFalse)
15835          (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15836            event.xmap.window);
15837        if (event.xmap.window == windows->backdrop.id)
15838          {
15839            (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15840              CurrentTime);
15841            windows->backdrop.mapped=MagickTrue;
15842            break;
15843          }
15844        if (event.xmap.window == windows->image.id)
15845          {
15846            if (windows->backdrop.id != (Window) NULL)
15847              (void) XInstallColormap(display,map_info->colormap);
15848            if (LocaleCompare(display_image->magick,"LOGO") == 0)
15849              {
15850                if (LocaleCompare(display_image->filename,"LOGO") == 0)
15851                  nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15852              }
15853            if (((int) windows->image.width < windows->image.ximage->width) ||
15854                ((int) windows->image.height < windows->image.ximage->height))
15855              (void) XMapRaised(display,windows->pan.id);
15856            windows->image.mapped=MagickTrue;
15857            break;
15858          }
15859        if (event.xmap.window == windows->magnify.id)
15860          {
15861            XMakeMagnifyImage(display,windows);
15862            windows->magnify.mapped=MagickTrue;
15863            (void) XWithdrawWindow(display,windows->info.id,
15864              windows->info.screen);
15865            break;
15866          }
15867        if (event.xmap.window == windows->pan.id)
15868          {
15869            XMakePanImage(display,resource_info,windows,display_image,
15870              exception);
15871            windows->pan.mapped=MagickTrue;
15872            break;
15873          }
15874        if (event.xmap.window == windows->info.id)
15875          {
15876            windows->info.mapped=MagickTrue;
15877            break;
15878          }
15879        if (event.xmap.window == windows->icon.id)
15880          {
15881            MagickBooleanType
15882              taint;
15883
15884            /*
15885              Create an icon image.
15886            */
15887            taint=display_image->taint;
15888            XMakeStandardColormap(display,icon_visual,icon_resources,
15889              display_image,icon_map,icon_pixel);
15890            (void) XMakeImage(display,icon_resources,&windows->icon,
15891              display_image,windows->icon.width,windows->icon.height,
15892              exception);
15893            display_image->taint=taint;
15894            (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15895              windows->icon.pixmap);
15896            (void) XClearWindow(display,windows->icon.id);
15897            (void) XWithdrawWindow(display,windows->info.id,
15898              windows->info.screen);
15899            windows->icon.mapped=MagickTrue;
15900            break;
15901          }
15902        if (event.xmap.window == windows->command.id)
15903          {
15904            windows->command.mapped=MagickTrue;
15905            break;
15906          }
15907        if (event.xmap.window == windows->popup.id)
15908          {
15909            windows->popup.mapped=MagickTrue;
15910            break;
15911          }
15912        if (event.xmap.window == windows->widget.id)
15913          {
15914            windows->widget.mapped=MagickTrue;
15915            break;
15916          }
15917        break;
15918      }
15919      case MappingNotify:
15920      {
15921        (void) XRefreshKeyboardMapping(&event.xmapping);
15922        break;
15923      }
15924      case NoExpose:
15925        break;
15926      case PropertyNotify:
15927      {
15928        Atom
15929          type;
15930
15931        int
15932          format,
15933          status;
15934
15935        unsigned char
15936          *data;
15937
15938        unsigned long
15939          after,
15940          length;
15941
15942        if (display_image->debug != MagickFalse)
15943          (void) LogMagickEvent(X11Event,GetMagickModule(),
15944            "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15945            event.xproperty.atom,event.xproperty.state);
15946        if (event.xproperty.atom != windows->im_remote_command)
15947          break;
15948        /*
15949          Display image named by the remote command protocol.
15950        */
15951        status=XGetWindowProperty(display,event.xproperty.window,
15952          event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15953          AnyPropertyType,&type,&format,&length,&after,&data);
15954        if ((status != Success) || (length == 0))
15955          break;
15956        if (LocaleCompare((char *) data,"-quit") == 0)
15957          {
15958            XClientMessage(display,windows->image.id,windows->im_protocols,
15959              windows->im_exit,CurrentTime);
15960            (void) XFree((void *) data);
15961            break;
15962          }
15963        (void) CopyMagickString(resource_info->image_info->filename,
15964          (char *) data,MaxTextExtent);
15965        (void) XFree((void *) data);
15966        nexus=ReadImage(resource_info->image_info,&display_image->exception);
15967        CatchException(&display_image->exception);
15968        if (nexus != (Image *) NULL)
15969          *state|=NextImageState | ExitState;
15970        break;
15971      }
15972      case ReparentNotify:
15973      {
15974        if (display_image->debug != MagickFalse)
15975          (void) LogMagickEvent(X11Event,GetMagickModule(),
15976            "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15977            event.xreparent.window);
15978        break;
15979      }
15980      case UnmapNotify:
15981      {
15982        if (display_image->debug != MagickFalse)
15983          (void) LogMagickEvent(X11Event,GetMagickModule(),
15984            "Unmap Notify: 0x%lx",event.xunmap.window);
15985        if (event.xunmap.window == windows->backdrop.id)
15986          {
15987            windows->backdrop.mapped=MagickFalse;
15988            break;
15989          }
15990        if (event.xunmap.window == windows->image.id)
15991          {
15992            windows->image.mapped=MagickFalse;
15993            break;
15994          }
15995        if (event.xunmap.window == windows->magnify.id)
15996          {
15997            windows->magnify.mapped=MagickFalse;
15998            break;
15999          }
16000        if (event.xunmap.window == windows->pan.id)
16001          {
16002            windows->pan.mapped=MagickFalse;
16003            break;
16004          }
16005        if (event.xunmap.window == windows->info.id)
16006          {
16007            windows->info.mapped=MagickFalse;
16008            break;
16009          }
16010        if (event.xunmap.window == windows->icon.id)
16011          {
16012            if (map_info->colormap == icon_map->colormap)
16013              XConfigureImageColormap(display,resource_info,windows,
16014                display_image);
16015            (void) XFreeStandardColormap(display,icon_visual,icon_map,
16016              icon_pixel);
16017            windows->icon.mapped=MagickFalse;
16018            break;
16019          }
16020        if (event.xunmap.window == windows->command.id)
16021          {
16022            windows->command.mapped=MagickFalse;
16023            break;
16024          }
16025        if (event.xunmap.window == windows->popup.id)
16026          {
16027            if (windows->backdrop.id != (Window) NULL)
16028              (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16029                CurrentTime);
16030            windows->popup.mapped=MagickFalse;
16031            break;
16032          }
16033        if (event.xunmap.window == windows->widget.id)
16034          {
16035            if (windows->backdrop.id != (Window) NULL)
16036              (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16037                CurrentTime);
16038            windows->widget.mapped=MagickFalse;
16039            break;
16040          }
16041        break;
16042      }
16043      default:
16044      {
16045        if (display_image->debug != MagickFalse)
16046          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
16047            event.type);
16048        break;
16049      }
16050    }
16051  } while (!(*state & ExitState));
16052  if ((*state & ExitState) == 0)
16053    (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
16054      &display_image,exception);
16055  else
16056    if (resource_info->confirm_edit != MagickFalse)
16057      {
16058        /*
16059          Query user if image has changed.
16060        */
16061        if ((resource_info->immutable == MagickFalse) &&
16062            (display_image->taint != MagickFalse))
16063          {
16064            int
16065              status;
16066
16067            status=XConfirmWidget(display,windows,"Your image changed.",
16068              "Do you want to save it");
16069            if (status == 0)
16070              *state&=(~ExitState);
16071            else
16072              if (status > 0)
16073                (void) XMagickCommand(display,resource_info,windows,SaveCommand,
16074                  &display_image,exception);
16075          }
16076      }
16077  if ((windows->visual_info->klass == GrayScale) ||
16078      (windows->visual_info->klass == PseudoColor) ||
16079      (windows->visual_info->klass == DirectColor))
16080    {
16081      /*
16082        Withdraw pan and Magnify window.
16083      */
16084      if (windows->info.mapped != MagickFalse)
16085        (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16086      if (windows->magnify.mapped != MagickFalse)
16087        (void) XWithdrawWindow(display,windows->magnify.id,
16088          windows->magnify.screen);
16089      if (windows->command.mapped != MagickFalse)
16090        (void) XWithdrawWindow(display,windows->command.id,
16091          windows->command.screen);
16092    }
16093  if (windows->pan.mapped != MagickFalse)
16094    (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16095  if (resource_info->backdrop == MagickFalse)
16096    if (windows->backdrop.mapped)
16097      {
16098        (void) XWithdrawWindow(display,windows->backdrop.id,
16099          windows->backdrop.screen);
16100        (void) XDestroyWindow(display,windows->backdrop.id);
16101        windows->backdrop.id=(Window) NULL;
16102        (void) XWithdrawWindow(display,windows->image.id,
16103          windows->image.screen);
16104        (void) XDestroyWindow(display,windows->image.id);
16105        windows->image.id=(Window) NULL;
16106      }
16107  XSetCursorState(display,windows,MagickTrue);
16108  XCheckRefreshWindows(display,windows);
16109  if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16110    *state&=(~ExitState);
16111  if (*state & ExitState)
16112    {
16113      /*
16114        Free Standard Colormap.
16115      */
16116      (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16117      if (resource_info->map_type == (char *) NULL)
16118        (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16119      /*
16120        Free X resources.
16121      */
16122      if (resource_info->copy_image != (Image *) NULL)
16123        {
16124          resource_info->copy_image=DestroyImage(resource_info->copy_image);
16125          resource_info->copy_image=NewImageList();
16126        }
16127      DestroyXResources();
16128    }
16129  (void) XSync(display,MagickFalse);
16130  /*
16131    Restore our progress monitor and warning handlers.
16132  */
16133  (void) SetErrorHandler(warning_handler);
16134  (void) SetWarningHandler(warning_handler);
16135  /*
16136    Change to home directory.
16137  */
16138  directory=getcwd(working_directory,MaxTextExtent);
16139  (void) directory;
16140  {
16141    int
16142      status;
16143
16144    status=chdir(resource_info->home_directory);
16145    if (status == -1)
16146      (void) ThrowMagickException(&display_image->exception,GetMagickModule(),
16147        FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
16148  }
16149  *image=display_image;
16150  return(nexus);
16151}
16152#else
16153
16154/*
16155%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16156%                                                                             %
16157%                                                                             %
16158%                                                                             %
16159+   D i s p l a y I m a g e s                                                 %
16160%                                                                             %
16161%                                                                             %
16162%                                                                             %
16163%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16164%
16165%  DisplayImages() displays an image sequence to any X window screen.  It
16166%  returns a value other than 0 if successful.  Check the exception member
16167%  of image to determine the reason for any failure.
16168%
16169%  The format of the DisplayImages method is:
16170%
16171%      MagickBooleanType DisplayImages(const ImageInfo *image_info,
16172%        Image *images,ExceptionInfo *exception)
16173%
16174%  A description of each parameter follows:
16175%
16176%    o image_info: the image info.
16177%
16178%    o image: the image.
16179%
16180%    o exception: return any errors or warnings in this structure.
16181%
16182*/
16183MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16184  Image *image,ExceptionInfo *exception)
16185{
16186  assert(image_info != (const ImageInfo *) NULL);
16187  assert(image_info->signature == MagickSignature);
16188  assert(image != (Image *) NULL);
16189  assert(image->signature == MagickSignature);
16190  if (image->debug != MagickFalse)
16191    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16192  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16193    "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image->filename);
16194  return(MagickFalse);
16195}
16196
16197/*
16198%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16199%                                                                             %
16200%                                                                             %
16201%                                                                             %
16202+   R e m o t e D i s p l a y C o m m a n d                                   %
16203%                                                                             %
16204%                                                                             %
16205%                                                                             %
16206%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16207%
16208%  RemoteDisplayCommand() encourages a remote display program to display the
16209%  specified image filename.
16210%
16211%  The format of the RemoteDisplayCommand method is:
16212%
16213%      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16214%        const char *window,const char *filename,ExceptionInfo *exception)
16215%
16216%  A description of each parameter follows:
16217%
16218%    o image_info: the image info.
16219%
16220%    o window: Specifies the name or id of an X window.
16221%
16222%    o filename: the name of the image filename to display.
16223%
16224%    o exception: return any errors or warnings in this structure.
16225%
16226*/
16227MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16228  const char *window,const char *filename,ExceptionInfo *exception)
16229{
16230  assert(image_info != (const ImageInfo *) NULL);
16231  assert(image_info->signature == MagickSignature);
16232  assert(filename != (char *) NULL);
16233  (void) window;
16234  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16235  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16236    "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image_info->filename);
16237  return(MagickFalse);
16238}
16239#endif
16240