display.c revision 8a5d7f45b2ba420c8ea1e51bec48d3172814def3
1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3%                                                                             %
4%                                                                             %
5%                                                                             %
6%               DDDD   IIIII  SSSSS  PPPP   L       AAA   Y   Y               %
7%               D   D    I    SS     P   P  L      A   A   Y Y                %
8%               D   D    I     SSS   PPPP   L      AAAAA    Y                 %
9%               D   D    I       SS  P      L      A   A    Y                 %
10%               DDDD   IIIII  SSSSS  P      LLLLL  A   A    Y                 %
11%                                                                             %
12%                                                                             %
13%        MagickCore Methods to Interactively Display and Edit an Image        %
14%                                                                             %
15%                             Software Design                                 %
16%                               John Cristy                                   %
17%                                July 1992                                    %
18%                                                                             %
19%                                                                             %
20%  Copyright 1999-2013 ImageMagick Studio LLC, a non-profit organization      %
21%  dedicated to making software imaging solutions freely available.           %
22%                                                                             %
23%  You may not use this file except in compliance with the License.  You may  %
24%  obtain a copy of the License at                                            %
25%                                                                             %
26%    http://www.imagemagick.org/script/license.php                            %
27%                                                                             %
28%  Unless required by applicable law or agreed to in writing, software        %
29%  distributed under the License is distributed on an "AS IS" BASIS,          %
30%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31%  See the License for the specific language governing permissions and        %
32%  limitations under the License.                                             %
33%                                                                             %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37*/
38
39/*
40  Include declarations.
41*/
42#include "MagickCore/studio.h"
43#include "MagickCore/artifact.h"
44#include "MagickCore/attribute.h"
45#include "MagickCore/blob.h"
46#include "MagickCore/cache.h"
47#include "MagickCore/cache-private.h"
48#include "MagickCore/client.h"
49#include "MagickCore/color.h"
50#include "MagickCore/colorspace.h"
51#include "MagickCore/composite.h"
52#include "MagickCore/constitute.h"
53#include "MagickCore/decorate.h"
54#include "MagickCore/delegate.h"
55#include "MagickCore/display.h"
56#include "MagickCore/display-private.h"
57#include "MagickCore/distort.h"
58#include "MagickCore/draw.h"
59#include "MagickCore/effect.h"
60#include "MagickCore/enhance.h"
61#include "MagickCore/exception.h"
62#include "MagickCore/exception-private.h"
63#include "MagickCore/fx.h"
64#include "MagickCore/geometry.h"
65#include "MagickCore/image.h"
66#include "MagickCore/image-private.h"
67#include "MagickCore/list.h"
68#include "MagickCore/log.h"
69#include "MagickCore/magick.h"
70#include "MagickCore/memory_.h"
71#include "MagickCore/monitor.h"
72#include "MagickCore/monitor-private.h"
73#include "MagickCore/montage.h"
74#include "MagickCore/option.h"
75#include "MagickCore/paint.h"
76#include "MagickCore/pixel.h"
77#include "MagickCore/pixel-accessor.h"
78#include "MagickCore/PreRvIcccm.h"
79#include "MagickCore/property.h"
80#include "MagickCore/quantum.h"
81#include "MagickCore/quantum-private.h"
82#include "MagickCore/resize.h"
83#include "MagickCore/resource_.h"
84#include "MagickCore/shear.h"
85#include "MagickCore/segment.h"
86#include "MagickCore/statistic.h"
87#include "MagickCore/string_.h"
88#include "MagickCore/string-private.h"
89#include "MagickCore/transform.h"
90#include "MagickCore/threshold.h"
91#include "MagickCore/utility.h"
92#include "MagickCore/utility-private.h"
93#include "MagickCore/version.h"
94#include "MagickCore/widget.h"
95#include "MagickCore/widget-private.h"
96#include "MagickCore/xwindow.h"
97#include "MagickCore/xwindow-private.h"
98
99#if defined(MAGICKCORE_X11_DELEGATE)
100/*
101  Define declarations.
102*/
103#define MaxColors  MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
104
105/*
106  Constant declarations.
107*/
108static const unsigned char
109  HighlightBitmap[8] =
110  {
111    0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
112  },
113  OpaqueBitmap[8] =
114  {
115    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
116  },
117  ShadowBitmap[8] =
118  {
119    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
120  };
121
122static const char
123  *PageSizes[] =
124  {
125    "Letter",
126    "Tabloid",
127    "Ledger",
128    "Legal",
129    "Statement",
130    "Executive",
131    "A3",
132    "A4",
133    "A5",
134    "B4",
135    "B5",
136    "Folio",
137    "Quarto",
138    "10x14",
139    (char *) NULL
140  };
141
142/*
143  Help widget declarations.
144*/
145static const char
146  *ImageAnnotateHelp[] =
147  {
148    "In annotate mode, the Command widget has these options:",
149    "",
150    "    Font Name",
151    "      fixed",
152    "      variable",
153    "      5x8",
154    "      6x10",
155    "      7x13bold",
156    "      8x13bold",
157    "      9x15bold",
158    "      10x20",
159    "      12x24",
160    "      Browser...",
161    "    Font Color",
162    "      black",
163    "      blue",
164    "      cyan",
165    "      green",
166    "      gray",
167    "      red",
168    "      magenta",
169    "      yellow",
170    "      white",
171    "      transparent",
172    "      Browser...",
173    "    Font Color",
174    "      black",
175    "      blue",
176    "      cyan",
177    "      green",
178    "      gray",
179    "      red",
180    "      magenta",
181    "      yellow",
182    "      white",
183    "      transparent",
184    "      Browser...",
185    "    Rotate Text",
186    "      -90",
187    "      -45",
188    "      -30",
189    "      0",
190    "      30",
191    "      45",
192    "      90",
193    "      180",
194    "      Dialog...",
195    "    Help",
196    "    Dismiss",
197    "",
198    "Choose a font name from the Font Name sub-menu.  Additional",
199    "font names can be specified with the font browser.  You can",
200    "change the menu names by setting the X resources font1",
201    "through font9.",
202    "",
203    "Choose a font color from the Font Color sub-menu.",
204    "Additional font colors can be specified with the color",
205    "browser.  You can change the menu colors by setting the X",
206    "resources pen1 through pen9.",
207    "",
208    "If you select the color browser and press Grab, you can",
209    "choose the font color by moving the pointer to the desired",
210    "color on the screen and press any button.",
211    "",
212    "If you choose to rotate the text, choose Rotate Text from the",
213    "menu and select an angle.  Typically you will only want to",
214    "rotate one line of text at a time.  Depending on the angle you",
215    "choose, subsequent lines may end up overwriting each other.",
216    "",
217    "Choosing a font and its color is optional.  The default font",
218    "is fixed and the default color is black.  However, you must",
219    "choose a location to begin entering text and press button 1.",
220    "An underscore character will appear at the location of the",
221    "pointer.  The cursor changes to a pencil to indicate you are",
222    "in text mode.  To exit immediately, press Dismiss.",
223    "",
224    "In text mode, any key presses will display the character at",
225    "the location of the underscore and advance the underscore",
226    "cursor.  Enter your text and once completed press Apply to",
227    "finish your image annotation.  To correct errors press BACK",
228    "SPACE.  To delete an entire line of text, press DELETE.  Any",
229    "text that exceeds the boundaries of the image window is",
230    "automagically continued onto the next line.",
231    "",
232    "The actual color you request for the font is saved in the",
233    "image.  However, the color that appears in your image window",
234    "may be different.  For example, on a monochrome screen the",
235    "text will appear black or white even if you choose the color",
236    "red as the font color.  However, the image saved to a file",
237    "with -write is written with red lettering.  To assure the",
238    "correct color text in the final image, any PseudoClass image",
239    "is promoted to DirectClass (see miff(5)).  To force a",
240    "PseudoClass image to remain PseudoClass, use -colors.",
241    (char *) NULL,
242  },
243  *ImageChopHelp[] =
244  {
245    "In chop mode, the Command widget has these options:",
246    "",
247    "    Direction",
248    "      horizontal",
249    "      vertical",
250    "    Help",
251    "    Dismiss",
252    "",
253    "If the you choose the horizontal direction (this the",
254    "default), the area of the image between the two horizontal",
255    "endpoints of the chop line is removed.  Otherwise, the area",
256    "of the image between the two vertical endpoints of the chop",
257    "line is removed.",
258    "",
259    "Select a location within the image window to begin your chop,",
260    "press and hold any button.  Next, move the pointer to",
261    "another location in the image.  As you move a line will",
262    "connect the initial location and the pointer.  When you",
263    "release the button, the area within the image to chop is",
264    "determined by which direction you choose from the Command",
265    "widget.",
266    "",
267    "To cancel the image chopping, move the pointer back to the",
268    "starting point of the line and release the button.",
269    (char *) NULL,
270  },
271  *ImageColorEditHelp[] =
272  {
273    "In color edit mode, the Command widget has these options:",
274    "",
275    "    Method",
276    "      point",
277    "      replace",
278    "      floodfill",
279    "      filltoborder",
280    "      reset",
281    "    Pixel Color",
282    "      black",
283    "      blue",
284    "      cyan",
285    "      green",
286    "      gray",
287    "      red",
288    "      magenta",
289    "      yellow",
290    "      white",
291    "      Browser...",
292    "    Border Color",
293    "      black",
294    "      blue",
295    "      cyan",
296    "      green",
297    "      gray",
298    "      red",
299    "      magenta",
300    "      yellow",
301    "      white",
302    "      Browser...",
303    "    Fuzz",
304    "      0%",
305    "      2%",
306    "      5%",
307    "      10%",
308    "      15%",
309    "      Dialog...",
310    "    Undo",
311    "    Help",
312    "    Dismiss",
313    "",
314    "Choose a color editing method from the Method sub-menu",
315    "of the Command widget.  The point method recolors any pixel",
316    "selected with the pointer until the button is released.  The",
317    "replace method recolors any pixel that matches the color of",
318    "the pixel you select with a button press.  Floodfill recolors",
319    "any pixel that matches the color of the pixel you select with",
320    "a button press and is a neighbor.  Whereas filltoborder recolors",
321    "any neighbor pixel that is not the border color.  Finally reset",
322    "changes the entire image to the designated color.",
323    "",
324    "Next, choose a pixel color from the Pixel Color sub-menu.",
325    "Additional pixel colors can be specified with the color",
326    "browser.  You can change the menu colors by setting the X",
327    "resources pen1 through pen9.",
328    "",
329    "Now press button 1 to select a pixel within the image window",
330    "to change its color.  Additional pixels may be recolored as",
331    "prescribed by the method you choose.",
332    "",
333    "If the Magnify widget is mapped, it can be helpful in positioning",
334    "your pointer within the image (refer to button 2).",
335    "",
336    "The actual color you request for the pixels is saved in the",
337    "image.  However, the color that appears in your image window",
338    "may be different.  For example, on a monochrome screen the",
339    "pixel will appear black or white even if you choose the",
340    "color red as the pixel color.  However, the image saved to a",
341    "file with -write is written with red pixels.  To assure the",
342    "correct color text in the final image, any PseudoClass image",
343    "is promoted to DirectClass (see miff(5)).  To force a",
344    "PseudoClass image to remain PseudoClass, use -colors.",
345    (char *) NULL,
346  },
347  *ImageCompositeHelp[] =
348  {
349    "First a widget window is displayed requesting you to enter an",
350    "image name. Press Composite, Grab or type a file name.",
351    "Press Cancel if you choose not to create a composite image.",
352    "When you choose Grab, move the pointer to the desired window",
353    "and press any button.",
354    "",
355    "If the Composite image does not have any matte information,",
356    "you are informed and the file browser is displayed again.",
357    "Enter the name of a mask image.  The image is typically",
358    "grayscale and the same size as the composite image.  If the",
359    "image is not grayscale, it is converted to grayscale and the",
360    "resulting intensities are used as matte information.",
361    "",
362    "A small window appears showing the location of the cursor in",
363    "the image window. You are now in composite mode.  To exit",
364    "immediately, press Dismiss.  In composite mode, the Command",
365    "widget has these options:",
366    "",
367    "    Operators",
368    "      Over",
369    "      In",
370    "      Out",
371    "      Atop",
372    "      Xor",
373    "      Plus",
374    "      Minus",
375    "      Add",
376    "      Subtract",
377    "      Difference",
378    "      Multiply",
379    "      Bumpmap",
380    "      Copy",
381    "      CopyRed",
382    "      CopyGreen",
383    "      CopyBlue",
384    "      CopyOpacity",
385    "      Clear",
386    "    Dissolve",
387    "    Displace",
388    "    Help",
389    "    Dismiss",
390    "",
391    "Choose a composite operation from the Operators sub-menu of",
392    "the Command widget.  How each operator behaves is described",
393    "below.  Image window is the image currently displayed on",
394    "your X server and image is the image obtained with the File",
395    "Browser widget.",
396    "",
397    "Over     The result is the union of the two image shapes,",
398    "         with image obscuring image window in the region of",
399    "         overlap.",
400    "",
401    "In       The result is simply image cut by the shape of",
402    "         image window.  None of the image data of image",
403    "         window is in the result.",
404    "",
405    "Out      The resulting image is image with the shape of",
406    "         image window cut out.",
407    "",
408    "Atop     The result is the same shape as image image window,",
409    "         with image obscuring image window where the image",
410    "         shapes overlap.  Note this differs from over",
411    "         because the portion of image outside image window's",
412    "         shape does not appear in the result.",
413    "",
414    "Xor      The result is the image data from both image and",
415    "         image window that is outside the overlap region.",
416    "         The overlap region is blank.",
417    "",
418    "Plus     The result is just the sum of the image data.",
419    "         Output values are cropped to QuantumRange (no overflow).",
420    "",
421    "Minus    The result of image - image window, with underflow",
422    "         cropped to zero.",
423    "",
424    "Add      The result of image + image window, with overflow",
425    "         wrapping around (mod 256).",
426    "",
427    "Subtract The result of image - image window, with underflow",
428    "         wrapping around (mod 256).  The add and subtract",
429    "         operators can be used to perform reversible",
430    "         transformations.",
431    "",
432    "Difference",
433    "         The result of abs(image - image window).  This",
434    "         useful for comparing two very similar images.",
435    "",
436    "Multiply",
437    "         The result of image * image window.  This",
438    "         useful for the creation of drop-shadows.",
439    "",
440    "Bumpmap  The result of surface normals from image * image",
441    "         window.",
442    "",
443    "Copy     The resulting image is image window replaced with",
444    "         image.  Here the matte information is ignored.",
445    "",
446    "CopyRed  The red layer of the image window is replace with",
447    "         the red layer of the image.  The other layers are",
448    "         untouched.",
449    "",
450    "CopyGreen",
451    "         The green layer of the image window is replace with",
452    "         the green layer of the image.  The other layers are",
453    "         untouched.",
454    "",
455    "CopyBlue The blue layer of the image window is replace with",
456    "         the blue layer of the image.  The other layers are",
457    "         untouched.",
458    "",
459    "CopyOpacity",
460    "         The matte layer of the image window is replace with",
461    "         the matte layer of the image.  The other layers are",
462    "         untouched.",
463    "",
464    "The image compositor requires a matte, or alpha channel in",
465    "the image for some operations.  This extra channel usually",
466    "defines a mask which represents a sort of a cookie-cutter",
467    "for the image.  This the case when matte is opaque (full",
468    "coverage) for pixels inside the shape, zero outside, and",
469    "between 0 and QuantumRange on the boundary.  If image does not",
470    "have a matte channel, it is initialized with 0 for any pixel",
471    "matching in color to pixel location (0,0), otherwise QuantumRange.",
472    "",
473    "If you choose Dissolve, the composite operator becomes Over.  The",
474    "image matte channel percent transparency is initialized to factor.",
475    "The image window is initialized to (100-factor). Where factor is the",
476    "value you specify in the Dialog widget.",
477    "",
478    "Displace shifts the image pixels as defined by a displacement",
479    "map.  With this option, image is used as a displacement map.",
480    "Black, within the displacement map, is a maximum positive",
481    "displacement.  White is a maximum negative displacement and",
482    "middle gray is neutral.  The displacement is scaled to determine",
483    "the pixel shift.  By default, the displacement applies in both the",
484    "horizontal and vertical directions.  However, if you specify a mask,",
485    "image is the horizontal X displacement and mask the vertical Y",
486    "displacement.",
487    "",
488    "Note that matte information for image window is not retained",
489    "for colormapped X server visuals (e.g. StaticColor,",
490    "StaticColor, GrayScale, PseudoColor).  Correct compositing",
491    "behavior may require a TrueColor or DirectColor visual or a",
492    "Standard Colormap.",
493    "",
494    "Choosing a composite operator is optional.  The default",
495    "operator is replace.  However, you must choose a location to",
496    "composite your image and press button 1.  Press and hold the",
497    "button before releasing and an outline of the image will",
498    "appear to help you identify your location.",
499    "",
500    "The actual colors of the composite image is saved.  However,",
501    "the color that appears in image window may be different.",
502    "For example, on a monochrome screen image window will appear",
503    "black or white even though your composited image may have",
504    "many colors.  If the image is saved to a file it is written",
505    "with the correct colors.  To assure the correct colors are",
506    "saved in the final image, any PseudoClass image is promoted",
507    "to DirectClass (see miff(5)).  To force a PseudoClass image",
508    "to remain PseudoClass, use -colors.",
509    (char *) NULL,
510  },
511  *ImageCutHelp[] =
512  {
513    "In cut mode, the Command widget has these options:",
514    "",
515    "    Help",
516    "    Dismiss",
517    "",
518    "To define a cut region, press button 1 and drag.  The",
519    "cut region is defined by a highlighted rectangle that",
520    "expands or contracts as it follows the pointer.  Once you",
521    "are satisfied with the cut region, release the button.",
522    "You are now in rectify mode.  In rectify mode, the Command",
523    "widget has these options:",
524    "",
525    "    Cut",
526    "    Help",
527    "    Dismiss",
528    "",
529    "You can make adjustments by moving the pointer to one of the",
530    "cut rectangle corners, pressing a button, and dragging.",
531    "Finally, press Cut to commit your copy region.  To",
532    "exit without cutting the image, press Dismiss.",
533    (char *) NULL,
534  },
535  *ImageCopyHelp[] =
536  {
537    "In copy mode, the Command widget has these options:",
538    "",
539    "    Help",
540    "    Dismiss",
541    "",
542    "To define a copy region, press button 1 and drag.  The",
543    "copy region is defined by a highlighted rectangle that",
544    "expands or contracts as it follows the pointer.  Once you",
545    "are satisfied with the copy region, release the button.",
546    "You are now in rectify mode.  In rectify mode, the Command",
547    "widget has these options:",
548    "",
549    "    Copy",
550    "    Help",
551    "    Dismiss",
552    "",
553    "You can make adjustments by moving the pointer to one of the",
554    "copy rectangle corners, pressing a button, and dragging.",
555    "Finally, press Copy to commit your copy region.  To",
556    "exit without copying the image, press Dismiss.",
557    (char *) NULL,
558  },
559  *ImageCropHelp[] =
560  {
561    "In crop mode, the Command widget has these options:",
562    "",
563    "    Help",
564    "    Dismiss",
565    "",
566    "To define a cropping region, press button 1 and drag.  The",
567    "cropping region is defined by a highlighted rectangle that",
568    "expands or contracts as it follows the pointer.  Once you",
569    "are satisfied with the cropping region, release the button.",
570    "You are now in rectify mode.  In rectify mode, the Command",
571    "widget has these options:",
572    "",
573    "    Crop",
574    "    Help",
575    "    Dismiss",
576    "",
577    "You can make adjustments by moving the pointer to one of the",
578    "cropping rectangle corners, pressing a button, and dragging.",
579    "Finally, press Crop to commit your cropping region.  To",
580    "exit without cropping the image, press Dismiss.",
581    (char *) NULL,
582  },
583  *ImageDrawHelp[] =
584  {
585    "The cursor changes to a crosshair to indicate you are in",
586    "draw mode.  To exit immediately, press Dismiss.  In draw mode,",
587    "the Command widget has these options:",
588    "",
589    "    Element",
590    "      point",
591    "      line",
592    "      rectangle",
593    "      fill rectangle",
594    "      circle",
595    "      fill circle",
596    "      ellipse",
597    "      fill ellipse",
598    "      polygon",
599    "      fill polygon",
600    "    Color",
601    "      black",
602    "      blue",
603    "      cyan",
604    "      green",
605    "      gray",
606    "      red",
607    "      magenta",
608    "      yellow",
609    "      white",
610    "      transparent",
611    "      Browser...",
612    "    Stipple",
613    "      Brick",
614    "      Diagonal",
615    "      Scales",
616    "      Vertical",
617    "      Wavy",
618    "      Translucent",
619    "      Opaque",
620    "      Open...",
621    "    Width",
622    "      1",
623    "      2",
624    "      4",
625    "      8",
626    "      16",
627    "      Dialog...",
628    "    Undo",
629    "    Help",
630    "    Dismiss",
631    "",
632    "Choose a drawing primitive from the Element sub-menu.",
633    "",
634    "Choose a color from the Color sub-menu.  Additional",
635    "colors can be specified with the color browser.",
636    "",
637    "If you choose the color browser and press Grab, you can",
638    "select the color by moving the pointer to the desired",
639    "color on the screen and press any button.  The transparent",
640    "color updates the image matte channel and is useful for",
641    "image compositing.",
642    "",
643    "Choose a stipple, if appropriate, from the Stipple sub-menu.",
644    "Additional stipples can be specified with the file browser.",
645    "Stipples obtained from the file browser must be on disk in the",
646    "X11 bitmap format.",
647    "",
648    "Choose a width, if appropriate, from the Width sub-menu.  To",
649    "choose a specific width select the Dialog widget.",
650    "",
651    "Choose a point in the Image window and press button 1 and",
652    "hold.  Next, move the pointer to another location in the",
653    "image.  As you move, a line connects the initial location and",
654    "the pointer.  When you release the button, the image is",
655    "updated with the primitive you just drew.  For polygons, the",
656    "image is updated when you press and release the button without",
657    "moving the pointer.",
658    "",
659    "To cancel image drawing, move the pointer back to the",
660    "starting point of the line and release the button.",
661    (char *) NULL,
662  },
663  *DisplayHelp[] =
664  {
665    "BUTTONS",
666    "  The effects of each button press is described below.  Three",
667    "  buttons are required.  If you have a two button mouse,",
668    "  button 1 and 3 are returned.  Press ALT and button 3 to",
669    "  simulate button 2.",
670    "",
671    "  1    Press this button to map or unmap the Command widget.",
672    "",
673    "  2    Press and drag to define a region of the image to",
674    "       magnify.",
675    "",
676    "  3    Press and drag to choose from a select set of commands.",
677    "       This button behaves differently if the image being",
678    "       displayed is a visual image directory.  Here, choose a",
679    "       particular tile of the directory and press this button and",
680    "       drag to select a command from a pop-up menu.  Choose from",
681    "       these menu items:",
682    "",
683    "           Open",
684    "           Next",
685    "           Former",
686    "           Delete",
687    "           Update",
688    "",
689    "       If you choose Open, the image represented by the tile is",
690    "       displayed.  To return to the visual image directory, choose",
691    "       Next from the Command widget.  Next and Former moves to the",
692    "       next or former image respectively.  Choose Delete to delete",
693    "       a particular image tile.  Finally, choose Update to",
694    "       synchronize all the image tiles with their respective",
695    "       images.",
696    "",
697    "COMMAND WIDGET",
698    "  The Command widget lists a number of sub-menus and commands.",
699    "  They are",
700    "",
701    "      File",
702    "        Open...",
703    "        Next",
704    "        Former",
705    "        Select...",
706    "        Save...",
707    "        Print...",
708    "        Delete...",
709    "        New...",
710    "        Visual Directory...",
711    "        Quit",
712    "      Edit",
713    "        Undo",
714    "        Redo",
715    "        Cut",
716    "        Copy",
717    "        Paste",
718    "      View",
719    "        Half Size",
720    "        Original Size",
721    "        Double Size",
722    "        Resize...",
723    "        Apply",
724    "        Refresh",
725    "        Restore",
726    "      Transform",
727    "        Crop",
728    "        Chop",
729    "        Flop",
730    "        Flip",
731    "        Rotate Right",
732    "        Rotate Left",
733    "        Rotate...",
734    "        Shear...",
735    "        Roll...",
736    "        Trim Edges",
737    "      Enhance",
738    "        Brightness...",
739    "        Saturation...",
740    "        Hue...",
741    "        Gamma...",
742    "        Sharpen...",
743    "        Dull",
744    "        Contrast Stretch...",
745    "        Sigmoidal Contrast...",
746    "        Normalize",
747    "        Equalize",
748    "        Negate",
749    "        Grayscale",
750    "        Map...",
751    "        Quantize...",
752    "      Effects",
753    "        Despeckle",
754    "        Emboss",
755    "        Reduce Noise",
756    "        Add Noise",
757    "        Sharpen...",
758    "        Blur...",
759    "        Threshold...",
760    "        Edge Detect...",
761    "        Spread...",
762    "        Shade...",
763    "        Painting...",
764    "        Segment...",
765    "      F/X",
766    "        Solarize...",
767    "        Sepia Tone...",
768    "        Swirl...",
769    "        Implode...",
770    "        Vignette...",
771    "        Wave...",
772    "        Oil Painting...",
773    "        Charcoal Drawing...",
774    "      Image Edit",
775    "        Annotate...",
776    "        Draw...",
777    "        Color...",
778    "        Matte...",
779    "        Composite...",
780    "        Add Border...",
781    "        Add Frame...",
782    "        Comment...",
783    "        Launch...",
784    "        Region of Interest...",
785    "      Miscellany",
786    "        Image Info",
787    "        Zoom Image",
788    "        Show Preview...",
789    "        Show Histogram",
790    "        Show Matte",
791    "        Background...",
792    "        Slide Show",
793    "        Preferences...",
794    "      Help",
795    "        Overview",
796    "        Browse Documentation",
797    "        About Display",
798    "",
799    "  Menu items with a indented triangle have a sub-menu.  They",
800    "  are represented above as the indented items.  To access a",
801    "  sub-menu item, move the pointer to the appropriate menu and",
802    "  press a button and drag.  When you find the desired sub-menu",
803    "  item, release the button and the command is executed.  Move",
804    "  the pointer away from the sub-menu if you decide not to",
805    "  execute a particular command.",
806    "",
807    "KEYBOARD ACCELERATORS",
808    "  Accelerators are one or two key presses that effect a",
809    "  particular command.  The keyboard accelerators that",
810    "  display(1) understands is:",
811    "",
812    "  Ctl+O     Press to open an image from a file.",
813    "",
814    "  space     Press to display the next image.",
815    "",
816    "            If the image is a multi-paged document such as a Postscript",
817    "            document, you can skip ahead several pages by preceding",
818    "            this command with a number.  For example to display the",
819    "            third page beyond the current page, press 3<space>.",
820    "",
821    "  backspace Press to display the former image.",
822    "",
823    "            If the image is a multi-paged document such as a Postscript",
824    "            document, you can skip behind several pages by preceding",
825    "            this command with a number.  For example to display the",
826    "            third page preceding the current page, press 3<backspace>.",
827    "",
828    "  Ctl+S     Press to write the image to a file.",
829    "",
830    "  Ctl+P     Press to print the image to a Postscript printer.",
831    "",
832    "  Ctl+D     Press to delete an image file.",
833    "",
834    "  Ctl+N     Press to create a blank canvas.",
835    "",
836    "  Ctl+Q     Press to discard all images and exit program.",
837    "",
838    "  Ctl+Z     Press to undo last image transformation.",
839    "",
840    "  Ctl+R     Press to redo last image transformation.",
841    "",
842    "  Ctl+X     Press to cut a region of the image.",
843    "",
844    "  Ctl+C     Press to copy a region of the image.",
845    "",
846    "  Ctl+V     Press to paste a region to the image.",
847    "",
848    "  <         Press to half the image size.",
849    "",
850    "  -         Press to return to the original image size.",
851    "",
852    "  >         Press to double the image size.",
853    "",
854    "  %         Press to resize the image to a width and height you",
855    "            specify.",
856    "",
857    "Cmd-A       Press to make any image transformations permanent."
858    "",
859    "            By default, any image size transformations are applied",
860    "            to the original image to create the image displayed on",
861    "            the X server.  However, the transformations are not",
862    "            permanent (i.e. the original image does not change",
863    "            size only the X image does).  For example, if you",
864    "            press > the X image will appear to double in size,",
865    "            but the original image will in fact remain the same size.",
866    "            To force the original image to double in size, press >",
867    "            followed by Cmd-A.",
868    "",
869    "  @         Press to refresh the image window.",
870    "",
871    "  C         Press to cut out a rectangular region of the image.",
872    "",
873    "  [         Press to chop the image.",
874    "",
875    "  H         Press to flop image in the horizontal direction.",
876    "",
877    "  V         Press to flip image in the vertical direction.",
878    "",
879    "  /         Press to rotate the image 90 degrees clockwise.",
880    "",
881    " \\         Press to rotate the image 90 degrees counter-clockwise.",
882    "",
883    "  *         Press to rotate the image the number of degrees you",
884    "            specify.",
885    "",
886    "  S         Press to shear the image the number of degrees you",
887    "            specify.",
888    "",
889    "  R         Press to roll the image.",
890    "",
891    "  T         Press to trim the image edges.",
892    "",
893    "  Shft-H    Press to vary the image hue.",
894    "",
895    "  Shft-S    Press to vary the color saturation.",
896    "",
897    "  Shft-L    Press to vary the color brightness.",
898    "",
899    "  Shft-G    Press to gamma correct the image.",
900    "",
901    "  Shft-C    Press to sharpen the image contrast.",
902    "",
903    "  Shft-Z    Press to dull the image contrast.",
904    "",
905    "  =         Press to perform histogram equalization on the image.",
906    "",
907    "  Shft-N    Press to perform histogram normalization on the image.",
908    "",
909    "  Shft-~    Press to negate the colors of the image.",
910    "",
911    "  .         Press to convert the image colors to gray.",
912    "",
913    "  Shft-#    Press to set the maximum number of unique colors in the",
914    "            image.",
915    "",
916    "  F2        Press to reduce the speckles in an image.",
917    "",
918    "  F3        Press to eliminate peak noise from an image.",
919    "",
920    "  F4        Press to add noise to an image.",
921    "",
922    "  F5        Press to sharpen an image.",
923    "",
924    "  F6        Press to delete an image file.",
925    "",
926    "  F7        Press to threshold the image.",
927    "",
928    "  F8        Press to detect edges within an image.",
929    "",
930    "  F9        Press to emboss an image.",
931    "",
932    "  F10       Press to displace pixels by a random amount.",
933    "",
934    "  F11       Press to negate all pixels above the threshold level.",
935    "",
936    "  F12       Press to shade the image using a distant light source.",
937    "",
938    "  F13       Press to lighten or darken image edges to create a 3-D effect.",
939    "",
940    "  F14       Press to segment the image by color.",
941    "",
942    "  Meta-S    Press to swirl image pixels about the center.",
943    "",
944    "  Meta-I    Press to implode image pixels about the center.",
945    "",
946    "  Meta-W    Press to alter an image along a sine wave.",
947    "",
948    "  Meta-P    Press to simulate an oil painting.",
949    "",
950    "  Meta-C    Press to simulate a charcoal drawing.",
951    "",
952    "  Alt-A     Press to annotate the image with text.",
953    "",
954    "  Alt-D     Press to draw on an image.",
955    "",
956    "  Alt-P     Press to edit an image pixel color.",
957    "",
958    "  Alt-M     Press to edit the image matte information.",
959    "",
960    "  Alt-V     Press to composite the image with another.",
961    "",
962    "  Alt-B     Press to add a border to the image.",
963    "",
964    "  Alt-F     Press to add an ornamental border to the image.",
965    "",
966    "  Alt-Shft-!",
967    "            Press to add an image comment.",
968    "",
969    "  Ctl-A     Press to apply image processing techniques to a region",
970    "            of interest.",
971    "",
972    "  Shft-?    Press to display information about the image.",
973    "",
974    "  Shft-+    Press to map the zoom image window.",
975    "",
976    "  Shft-P    Press to preview an image enhancement, effect, or f/x.",
977    "",
978    "  F1        Press to display helpful information about display(1).",
979    "",
980    "  Find      Press to browse documentation about ImageMagick.",
981    "",
982    "  1-9       Press to change the level of magnification.",
983    "",
984    "  Use the arrow keys to move the image one pixel up, down,",
985    "  left, or right within the magnify window.  Be sure to first",
986    "  map the magnify window by pressing button 2.",
987    "",
988    "  Press ALT and one of the arrow keys to trim off one pixel",
989    "  from any side of the image.",
990    (char *) NULL,
991  },
992  *ImageMatteEditHelp[] =
993  {
994    "Matte information within an image is useful for some",
995    "operations such as image compositing (See IMAGE",
996    "COMPOSITING).  This extra channel usually defines a mask",
997    "which represents a sort of a cookie-cutter for the image.",
998    "This the case when matte is opaque (full coverage) for",
999    "pixels inside the shape, zero outside, and between 0 and",
1000    "QuantumRange on the boundary.",
1001    "",
1002    "A small window appears showing the location of the cursor in",
1003    "the image window. You are now in matte edit mode.  To exit",
1004    "immediately, press Dismiss.  In matte edit mode, the Command",
1005    "widget has these options:",
1006    "",
1007    "    Method",
1008    "      point",
1009    "      replace",
1010    "      floodfill",
1011    "      filltoborder",
1012    "      reset",
1013    "    Border Color",
1014    "      black",
1015    "      blue",
1016    "      cyan",
1017    "      green",
1018    "      gray",
1019    "      red",
1020    "      magenta",
1021    "      yellow",
1022    "      white",
1023    "      Browser...",
1024    "    Fuzz",
1025    "      0%",
1026    "      2%",
1027    "      5%",
1028    "      10%",
1029    "      15%",
1030    "      Dialog...",
1031    "    Matte",
1032    "      Opaque",
1033    "      Transparent",
1034    "      Dialog...",
1035    "    Undo",
1036    "    Help",
1037    "    Dismiss",
1038    "",
1039    "Choose a matte editing method from the Method sub-menu of",
1040    "the Command widget.  The point method changes the matte value",
1041    "of any pixel selected with the pointer until the button is",
1042    "is released.  The replace method changes the matte value of",
1043    "any pixel that matches the color of the pixel you select with",
1044    "a button press.  Floodfill changes the matte value of any pixel",
1045    "that matches the color of the pixel you select with a button",
1046    "press and is a neighbor.  Whereas filltoborder changes the matte",
1047    "value any neighbor pixel that is not the border color.  Finally",
1048    "reset changes the entire image to the designated matte value.",
1049    "",
1050    "Choose Matte Value and pick Opaque or Transarent.  For other values",
1051    "select the Dialog entry.  Here a dialog appears requesting a matte",
1052    "value.  The value you select is assigned as the opacity value of the",
1053    "selected pixel or pixels.",
1054    "",
1055    "Now, press any button to select a pixel within the image",
1056    "window to change its matte value.",
1057    "",
1058    "If the Magnify widget is mapped, it can be helpful in positioning",
1059    "your pointer within the image (refer to button 2).",
1060    "",
1061    "Matte information is only valid in a DirectClass image.",
1062    "Therefore, any PseudoClass image is promoted to DirectClass",
1063    "(see miff(5)).  Note that matte information for PseudoClass",
1064    "is not retained for colormapped X server visuals (e.g.",
1065    "StaticColor, StaticColor, GrayScale, PseudoColor) unless you",
1066    "immediately save your image to a file (refer to Write).",
1067    "Correct matte editing behavior may require a TrueColor or",
1068    "DirectColor visual or a Standard Colormap.",
1069    (char *) NULL,
1070  },
1071  *ImagePanHelp[] =
1072  {
1073    "When an image exceeds the width or height of the X server",
1074    "screen, display maps a small panning icon.  The rectangle",
1075    "within the panning icon shows the area that is currently",
1076    "displayed in the image window.  To pan about the image,",
1077    "press any button and drag the pointer within the panning",
1078    "icon.  The pan rectangle moves with the pointer and the",
1079    "image window is updated to reflect the location of the",
1080    "rectangle within the panning icon.  When you have selected",
1081    "the area of the image you wish to view, release the button.",
1082    "",
1083    "Use the arrow keys to pan the image one pixel up, down,",
1084    "left, or right within the image window.",
1085    "",
1086    "The panning icon is withdrawn if the image becomes smaller",
1087    "than the dimensions of the X server screen.",
1088    (char *) NULL,
1089  },
1090  *ImagePasteHelp[] =
1091  {
1092    "A small window appears showing the location of the cursor in",
1093    "the image window. You are now in paste mode.  To exit",
1094    "immediately, press Dismiss.  In paste mode, the Command",
1095    "widget has these options:",
1096    "",
1097    "    Operators",
1098    "      over",
1099    "      in",
1100    "      out",
1101    "      atop",
1102    "      xor",
1103    "      plus",
1104    "      minus",
1105    "      add",
1106    "      subtract",
1107    "      difference",
1108    "      replace",
1109    "    Help",
1110    "    Dismiss",
1111    "",
1112    "Choose a composite operation from the Operators sub-menu of",
1113    "the Command widget.  How each operator behaves is described",
1114    "below.  Image window is the image currently displayed on",
1115    "your X server and image is the image obtained with the File",
1116    "Browser widget.",
1117    "",
1118    "Over     The result is the union of the two image shapes,",
1119    "         with image obscuring image window in the region of",
1120    "         overlap.",
1121    "",
1122    "In       The result is simply image cut by the shape of",
1123    "         image window.  None of the image data of image",
1124    "         window is in the result.",
1125    "",
1126    "Out      The resulting image is image with the shape of",
1127    "         image window cut out.",
1128    "",
1129    "Atop     The result is the same shape as image image window,",
1130    "         with image obscuring image window where the image",
1131    "         shapes overlap.  Note this differs from over",
1132    "         because the portion of image outside image window's",
1133    "         shape does not appear in the result.",
1134    "",
1135    "Xor      The result is the image data from both image and",
1136    "         image window that is outside the overlap region.",
1137    "         The overlap region is blank.",
1138    "",
1139    "Plus     The result is just the sum of the image data.",
1140    "         Output values are cropped to QuantumRange (no overflow).",
1141    "         This operation is independent of the matte",
1142    "         channels.",
1143    "",
1144    "Minus    The result of image - image window, with underflow",
1145    "         cropped to zero.",
1146    "",
1147    "Add      The result of image + image window, with overflow",
1148    "         wrapping around (mod 256).",
1149    "",
1150    "Subtract The result of image - image window, with underflow",
1151    "         wrapping around (mod 256).  The add and subtract",
1152    "         operators can be used to perform reversible",
1153    "         transformations.",
1154    "",
1155    "Difference",
1156    "         The result of abs(image - image window).  This",
1157    "         useful for comparing two very similar images.",
1158    "",
1159    "Copy     The resulting image is image window replaced with",
1160    "         image.  Here the matte information is ignored.",
1161    "",
1162    "CopyRed  The red layer of the image window is replace with",
1163    "         the red layer of the image.  The other layers are",
1164    "         untouched.",
1165    "",
1166    "CopyGreen",
1167    "         The green layer of the image window is replace with",
1168    "         the green layer of the image.  The other layers are",
1169    "         untouched.",
1170    "",
1171    "CopyBlue The blue layer of the image window is replace with",
1172    "         the blue layer of the image.  The other layers are",
1173    "         untouched.",
1174    "",
1175    "CopyOpacity",
1176    "         The matte layer of the image window is replace with",
1177    "         the matte layer of the image.  The other layers are",
1178    "         untouched.",
1179    "",
1180    "The image compositor requires a matte, or alpha channel in",
1181    "the image for some operations.  This extra channel usually",
1182    "defines a mask which represents a sort of a cookie-cutter",
1183    "for the image.  This the case when matte is opaque (full",
1184    "coverage) for pixels inside the shape, zero outside, and",
1185    "between 0 and QuantumRange on the boundary.  If image does not",
1186    "have a matte channel, it is initialized with 0 for any pixel",
1187    "matching in color to pixel location (0,0), otherwise QuantumRange.",
1188    "",
1189    "Note that matte information for image window is not retained",
1190    "for colormapped X server visuals (e.g. StaticColor,",
1191    "StaticColor, GrayScale, PseudoColor).  Correct compositing",
1192    "behavior may require a TrueColor or DirectColor visual or a",
1193    "Standard Colormap.",
1194    "",
1195    "Choosing a composite operator is optional.  The default",
1196    "operator is replace.  However, you must choose a location to",
1197    "paste your image and press button 1.  Press and hold the",
1198    "button before releasing and an outline of the image will",
1199    "appear to help you identify your location.",
1200    "",
1201    "The actual colors of the pasted image is saved.  However,",
1202    "the color that appears in image window may be different.",
1203    "For example, on a monochrome screen image window will appear",
1204    "black or white even though your pasted image may have",
1205    "many colors.  If the image is saved to a file it is written",
1206    "with the correct colors.  To assure the correct colors are",
1207    "saved in the final image, any PseudoClass image is promoted",
1208    "to DirectClass (see miff(5)).  To force a PseudoClass image",
1209    "to remain PseudoClass, use -colors.",
1210    (char *) NULL,
1211  },
1212  *ImageROIHelp[] =
1213  {
1214    "In region of interest mode, the Command widget has these",
1215    "options:",
1216    "",
1217    "    Help",
1218    "    Dismiss",
1219    "",
1220    "To define a region of interest, press button 1 and drag.",
1221    "The region of interest is defined by a highlighted rectangle",
1222    "that expands or contracts as it follows the pointer.  Once",
1223    "you are satisfied with the region of interest, release the",
1224    "button.  You are now in apply mode.  In apply mode the",
1225    "Command widget has these options:",
1226    "",
1227    "      File",
1228    "        Save...",
1229    "        Print...",
1230    "      Edit",
1231    "        Undo",
1232    "        Redo",
1233    "      Transform",
1234    "        Flop",
1235    "        Flip",
1236    "        Rotate Right",
1237    "        Rotate Left",
1238    "      Enhance",
1239    "        Hue...",
1240    "        Saturation...",
1241    "        Brightness...",
1242    "        Gamma...",
1243    "        Spiff",
1244    "        Dull",
1245    "        Contrast Stretch",
1246    "        Sigmoidal Contrast...",
1247    "        Normalize",
1248    "        Equalize",
1249    "        Negate",
1250    "        Grayscale",
1251    "        Map...",
1252    "        Quantize...",
1253    "      Effects",
1254    "        Despeckle",
1255    "        Emboss",
1256    "        Reduce Noise",
1257    "        Sharpen...",
1258    "        Blur...",
1259    "        Threshold...",
1260    "        Edge Detect...",
1261    "        Spread...",
1262    "        Shade...",
1263    "        Raise...",
1264    "        Segment...",
1265    "      F/X",
1266    "        Solarize...",
1267    "        Sepia Tone...",
1268    "        Swirl...",
1269    "        Implode...",
1270    "        Vignette...",
1271    "        Wave...",
1272    "        Oil Painting...",
1273    "        Charcoal Drawing...",
1274    "      Miscellany",
1275    "        Image Info",
1276    "        Zoom Image",
1277    "        Show Preview...",
1278    "        Show Histogram",
1279    "        Show Matte",
1280    "      Help",
1281    "      Dismiss",
1282    "",
1283    "You can make adjustments to the region of interest by moving",
1284    "the pointer to one of the rectangle corners, pressing a",
1285    "button, and dragging.  Finally, choose an image processing",
1286    "technique from the Command widget.  You can choose more than",
1287    "one image processing technique to apply to an area.",
1288    "Alternatively, you can move the region of interest before",
1289    "applying another image processing technique.  To exit, press",
1290    "Dismiss.",
1291    (char *) NULL,
1292  },
1293  *ImageRotateHelp[] =
1294  {
1295    "In rotate mode, the Command widget has these options:",
1296    "",
1297    "    Pixel Color",
1298    "      black",
1299    "      blue",
1300    "      cyan",
1301    "      green",
1302    "      gray",
1303    "      red",
1304    "      magenta",
1305    "      yellow",
1306    "      white",
1307    "      Browser...",
1308    "    Direction",
1309    "      horizontal",
1310    "      vertical",
1311    "    Help",
1312    "    Dismiss",
1313    "",
1314    "Choose a background color from the Pixel Color sub-menu.",
1315    "Additional background colors can be specified with the color",
1316    "browser.  You can change the menu colors by setting the X",
1317    "resources pen1 through pen9.",
1318    "",
1319    "If you choose the color browser and press Grab, you can",
1320    "select the background color by moving the pointer to the",
1321    "desired color on the screen and press any button.",
1322    "",
1323    "Choose a point in the image window and press this button and",
1324    "hold.  Next, move the pointer to another location in the",
1325    "image.  As you move a line connects the initial location and",
1326    "the pointer.  When you release the button, the degree of",
1327    "image rotation is determined by the slope of the line you",
1328    "just drew.  The slope is relative to the direction you",
1329    "choose from the Direction sub-menu of the Command widget.",
1330    "",
1331    "To cancel the image rotation, move the pointer back to the",
1332    "starting point of the line and release the button.",
1333    (char *) NULL,
1334  };
1335
1336/*
1337  Enumeration declarations.
1338*/
1339typedef enum
1340{
1341  CopyMode,
1342  CropMode,
1343  CutMode
1344} ClipboardMode;
1345
1346typedef enum
1347{
1348  OpenCommand,
1349  NextCommand,
1350  FormerCommand,
1351  SelectCommand,
1352  SaveCommand,
1353  PrintCommand,
1354  DeleteCommand,
1355  NewCommand,
1356  VisualDirectoryCommand,
1357  QuitCommand,
1358  UndoCommand,
1359  RedoCommand,
1360  CutCommand,
1361  CopyCommand,
1362  PasteCommand,
1363  HalfSizeCommand,
1364  OriginalSizeCommand,
1365  DoubleSizeCommand,
1366  ResizeCommand,
1367  ApplyCommand,
1368  RefreshCommand,
1369  RestoreCommand,
1370  CropCommand,
1371  ChopCommand,
1372  FlopCommand,
1373  FlipCommand,
1374  RotateRightCommand,
1375  RotateLeftCommand,
1376  RotateCommand,
1377  ShearCommand,
1378  RollCommand,
1379  TrimCommand,
1380  HueCommand,
1381  SaturationCommand,
1382  BrightnessCommand,
1383  GammaCommand,
1384  SpiffCommand,
1385  DullCommand,
1386  ContrastStretchCommand,
1387  SigmoidalContrastCommand,
1388  NormalizeCommand,
1389  EqualizeCommand,
1390  NegateCommand,
1391  GrayscaleCommand,
1392  MapCommand,
1393  QuantizeCommand,
1394  DespeckleCommand,
1395  EmbossCommand,
1396  ReduceNoiseCommand,
1397  AddNoiseCommand,
1398  SharpenCommand,
1399  BlurCommand,
1400  ThresholdCommand,
1401  EdgeDetectCommand,
1402  SpreadCommand,
1403  ShadeCommand,
1404  RaiseCommand,
1405  SegmentCommand,
1406  SolarizeCommand,
1407  SepiaToneCommand,
1408  SwirlCommand,
1409  ImplodeCommand,
1410  VignetteCommand,
1411  WaveCommand,
1412  OilPaintCommand,
1413  CharcoalDrawCommand,
1414  AnnotateCommand,
1415  DrawCommand,
1416  ColorCommand,
1417  MatteCommand,
1418  CompositeCommand,
1419  AddBorderCommand,
1420  AddFrameCommand,
1421  CommentCommand,
1422  LaunchCommand,
1423  RegionofInterestCommand,
1424  ROIHelpCommand,
1425  ROIDismissCommand,
1426  InfoCommand,
1427  ZoomCommand,
1428  ShowPreviewCommand,
1429  ShowHistogramCommand,
1430  ShowMatteCommand,
1431  BackgroundCommand,
1432  SlideShowCommand,
1433  PreferencesCommand,
1434  HelpCommand,
1435  BrowseDocumentationCommand,
1436  VersionCommand,
1437  SaveToUndoBufferCommand,
1438  FreeBuffersCommand,
1439  NullCommand
1440} CommandType;
1441
1442typedef enum
1443{
1444  AnnotateNameCommand,
1445  AnnotateFontColorCommand,
1446  AnnotateBackgroundColorCommand,
1447  AnnotateRotateCommand,
1448  AnnotateHelpCommand,
1449  AnnotateDismissCommand,
1450  TextHelpCommand,
1451  TextApplyCommand,
1452  ChopDirectionCommand,
1453  ChopHelpCommand,
1454  ChopDismissCommand,
1455  HorizontalChopCommand,
1456  VerticalChopCommand,
1457  ColorEditMethodCommand,
1458  ColorEditColorCommand,
1459  ColorEditBorderCommand,
1460  ColorEditFuzzCommand,
1461  ColorEditUndoCommand,
1462  ColorEditHelpCommand,
1463  ColorEditDismissCommand,
1464  CompositeOperatorsCommand,
1465  CompositeDissolveCommand,
1466  CompositeDisplaceCommand,
1467  CompositeHelpCommand,
1468  CompositeDismissCommand,
1469  CropHelpCommand,
1470  CropDismissCommand,
1471  RectifyCopyCommand,
1472  RectifyHelpCommand,
1473  RectifyDismissCommand,
1474  DrawElementCommand,
1475  DrawColorCommand,
1476  DrawStippleCommand,
1477  DrawWidthCommand,
1478  DrawUndoCommand,
1479  DrawHelpCommand,
1480  DrawDismissCommand,
1481  MatteEditMethod,
1482  MatteEditBorderCommand,
1483  MatteEditFuzzCommand,
1484  MatteEditValueCommand,
1485  MatteEditUndoCommand,
1486  MatteEditHelpCommand,
1487  MatteEditDismissCommand,
1488  PasteOperatorsCommand,
1489  PasteHelpCommand,
1490  PasteDismissCommand,
1491  RotateColorCommand,
1492  RotateDirectionCommand,
1493  RotateCropCommand,
1494  RotateSharpenCommand,
1495  RotateHelpCommand,
1496  RotateDismissCommand,
1497  HorizontalRotateCommand,
1498  VerticalRotateCommand,
1499  TileLoadCommand,
1500  TileNextCommand,
1501  TileFormerCommand,
1502  TileDeleteCommand,
1503  TileUpdateCommand
1504} ModeType;
1505
1506/*
1507  Stipples.
1508*/
1509#define BricksWidth  20
1510#define BricksHeight  20
1511#define DiagonalWidth  16
1512#define DiagonalHeight  16
1513#define HighlightWidth  8
1514#define HighlightHeight  8
1515#define OpaqueWidth  8
1516#define OpaqueHeight  8
1517#define ScalesWidth  16
1518#define ScalesHeight  16
1519#define ShadowWidth  8
1520#define ShadowHeight  8
1521#define VerticalWidth  16
1522#define VerticalHeight  16
1523#define WavyWidth  16
1524#define WavyHeight  16
1525
1526/*
1527  Constant declaration.
1528*/
1529static const int
1530  RoiDelta = 8;
1531
1532static const unsigned char
1533  BricksBitmap[] =
1534  {
1535    0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1536    0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1537    0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1538    0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1539    0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1540  },
1541  DiagonalBitmap[] =
1542  {
1543    0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1544    0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1545    0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1546  },
1547  ScalesBitmap[] =
1548  {
1549    0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1550    0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1551    0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1552  },
1553  VerticalBitmap[] =
1554  {
1555    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1556    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1557    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1558  },
1559  WavyBitmap[] =
1560  {
1561    0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1562    0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1563    0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1564  };
1565
1566/*
1567  Function prototypes.
1568*/
1569static CommandType
1570  XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1571    const MagickStatusType,KeySym,Image **,ExceptionInfo *);
1572
1573static Image
1574  *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
1575    Image **,ExceptionInfo *),
1576  *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1577  *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *,
1578    ExceptionInfo *),
1579  *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *,
1580    ExceptionInfo *);
1581
1582static MagickBooleanType
1583  XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *,
1584    ExceptionInfo *),
1585  XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **,
1586    ExceptionInfo *),
1587  XChopImage(Display *,XResourceInfo *,XWindows *,Image **,
1588    ExceptionInfo *),
1589  XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode,
1590    ExceptionInfo *),
1591  XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1592    ExceptionInfo *),
1593  XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *,
1594    ExceptionInfo *),
1595  XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1596  XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1597    ExceptionInfo *),
1598  XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1599    ExceptionInfo *),
1600  XPasteImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1601  XPrintImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1602  XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **,
1603    ExceptionInfo *),
1604  XROIImage(Display *,XResourceInfo *,XWindows *,Image **,ExceptionInfo *),
1605  XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1606  XTrimImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
1607
1608static void
1609  XDrawPanRectangle(Display *,XWindows *),
1610  XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **,
1611    ExceptionInfo *),
1612  XMagnifyImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1613  XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1614  XPanImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1615  XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1616    const KeySym,ExceptionInfo *),
1617  XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1618  XScreenEvent(Display *,XWindows *,XEvent *,ExceptionInfo *),
1619  XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1620
1621/*
1622%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1623%                                                                             %
1624%                                                                             %
1625%                                                                             %
1626%   D i s p l a y I m a g e s                                                 %
1627%                                                                             %
1628%                                                                             %
1629%                                                                             %
1630%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1631%
1632%  DisplayImages() displays an image sequence to any X window screen.  It
1633%  returns a value other than 0 if successful.  Check the exception member
1634%  of image to determine the reason for any failure.
1635%
1636%  The format of the DisplayImages method is:
1637%
1638%      MagickBooleanType DisplayImages(const ImageInfo *image_info,
1639%        Image *images,ExceptionInfo *exception)
1640%
1641%  A description of each parameter follows:
1642%
1643%    o image_info: the image info.
1644%
1645%    o image: the image.
1646%
1647%    o exception: return any errors or warnings in this structure.
1648%
1649*/
1650MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1651  Image *images,ExceptionInfo *exception)
1652{
1653  char
1654    *argv[1];
1655
1656  Display
1657    *display;
1658
1659  Image
1660    *image;
1661
1662  register ssize_t
1663    i;
1664
1665  size_t
1666    state;
1667
1668  XrmDatabase
1669    resource_database;
1670
1671  XResourceInfo
1672    resource_info;
1673
1674  assert(image_info != (const ImageInfo *) NULL);
1675  assert(image_info->signature == MagickSignature);
1676  assert(images != (Image *) NULL);
1677  assert(images->signature == MagickSignature);
1678  if( IfMagickTrue(images->debug) )
1679    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1680  display=XOpenDisplay(image_info->server_name);
1681  if (display == (Display *) NULL)
1682    {
1683      (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1684        "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1685      return(MagickFalse);
1686    }
1687  if (exception->severity != UndefinedException)
1688    CatchException(exception);
1689  (void) XSetErrorHandler(XError);
1690  resource_database=XGetResourceDatabase(display,GetClientName());
1691  (void) ResetMagickMemory(&resource_info,0,sizeof(resource_info));
1692  XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1693  if (image_info->page != (char *) NULL)
1694    resource_info.image_geometry=AcquireString(image_info->page);
1695  resource_info.immutable=MagickTrue;
1696  argv[0]=AcquireString(GetClientName());
1697  state=DefaultState;
1698  for (i=0; (state & ExitState) == 0; i++)
1699  {
1700    if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1701      break;
1702    image=GetImageFromList(images,i % GetImageListLength(images));
1703    (void) XDisplayImage(display,&resource_info,argv,1,&image,&state,exception);
1704  }
1705  (void) SetErrorHandler((ErrorHandler) NULL);
1706  (void) SetWarningHandler((WarningHandler) NULL);
1707  argv[0]=DestroyString(argv[0]);
1708  (void) XCloseDisplay(display);
1709  XDestroyResourceInfo(&resource_info);
1710  if (exception->severity != UndefinedException)
1711    return(MagickFalse);
1712  return(MagickTrue);
1713}
1714
1715/*
1716%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1717%                                                                             %
1718%                                                                             %
1719%                                                                             %
1720%   R e m o t e D i s p l a y C o m m a n d                                   %
1721%                                                                             %
1722%                                                                             %
1723%                                                                             %
1724%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1725%
1726%  RemoteDisplayCommand() encourages a remote display program to display the
1727%  specified image filename.
1728%
1729%  The format of the RemoteDisplayCommand method is:
1730%
1731%      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1732%        const char *window,const char *filename,ExceptionInfo *exception)
1733%
1734%  A description of each parameter follows:
1735%
1736%    o image_info: the image info.
1737%
1738%    o window: Specifies the name or id of an X window.
1739%
1740%    o filename: the name of the image filename to display.
1741%
1742%    o exception: return any errors or warnings in this structure.
1743%
1744*/
1745MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1746  const char *window,const char *filename,ExceptionInfo *exception)
1747{
1748  Display
1749    *display;
1750
1751  MagickStatusType
1752    status;
1753
1754  assert(image_info != (const ImageInfo *) NULL);
1755  assert(image_info->signature == MagickSignature);
1756  assert(filename != (char *) NULL);
1757  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1758  display=XOpenDisplay(image_info->server_name);
1759  if (display == (Display *) NULL)
1760    {
1761      (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1762        "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1763      return(MagickFalse);
1764    }
1765  (void) XSetErrorHandler(XError);
1766  status=XRemoteCommand(display,window,filename);
1767  (void) XCloseDisplay(display);
1768  return(IsMagickTrue(status));
1769}
1770
1771/*
1772%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1773%                                                                             %
1774%                                                                             %
1775%                                                                             %
1776+   X A n n o t a t e E d i t I m a g e                                       %
1777%                                                                             %
1778%                                                                             %
1779%                                                                             %
1780%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1781%
1782%  XAnnotateEditImage() annotates the image with text.
1783%
1784%  The format of the XAnnotateEditImage method is:
1785%
1786%      MagickBooleanType XAnnotateEditImage(Display *display,
1787%        XResourceInfo *resource_info,XWindows *windows,Image *image,
1788%        ExceptionInfo *exception)
1789%
1790%  A description of each parameter follows:
1791%
1792%    o display: Specifies a connection to an X server;  returned from
1793%      XOpenDisplay.
1794%
1795%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1796%
1797%    o windows: Specifies a pointer to a XWindows structure.
1798%
1799%    o image: the image; returned from ReadImage.
1800%
1801*/
1802
1803static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
1804{
1805  if (x > y)
1806    return(x);
1807  return(y);
1808}
1809
1810static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
1811{
1812  if (x < y)
1813    return(x);
1814  return(y);
1815}
1816
1817static MagickBooleanType XAnnotateEditImage(Display *display,
1818  XResourceInfo *resource_info,XWindows *windows,Image *image,
1819  ExceptionInfo *exception)
1820{
1821  static const char
1822    *AnnotateMenu[] =
1823    {
1824      "Font Name",
1825      "Font Color",
1826      "Box Color",
1827      "Rotate Text",
1828      "Help",
1829      "Dismiss",
1830      (char *) NULL
1831    },
1832    *TextMenu[] =
1833    {
1834      "Help",
1835      "Apply",
1836      (char *) NULL
1837    };
1838
1839  static const ModeType
1840    AnnotateCommands[] =
1841    {
1842      AnnotateNameCommand,
1843      AnnotateFontColorCommand,
1844      AnnotateBackgroundColorCommand,
1845      AnnotateRotateCommand,
1846      AnnotateHelpCommand,
1847      AnnotateDismissCommand
1848    },
1849    TextCommands[] =
1850    {
1851      TextHelpCommand,
1852      TextApplyCommand
1853    };
1854
1855  static MagickBooleanType
1856    transparent_box = MagickTrue,
1857    transparent_pen = MagickFalse;
1858
1859  static double
1860    degrees = 0.0;
1861
1862  static unsigned int
1863    box_id = MaxNumberPens-2,
1864    font_id = 0,
1865    pen_id = 0;
1866
1867  char
1868    command[MaxTextExtent],
1869    text[MaxTextExtent];
1870
1871  const char
1872    *ColorMenu[MaxNumberPens+1];
1873
1874  Cursor
1875    cursor;
1876
1877  GC
1878    annotate_context;
1879
1880  int
1881    id,
1882    pen_number,
1883    status,
1884    x,
1885    y;
1886
1887  KeySym
1888    key_symbol;
1889
1890  register char
1891    *p;
1892
1893  register ssize_t
1894    i;
1895
1896  unsigned int
1897    height,
1898    width;
1899
1900  size_t
1901    state;
1902
1903  XAnnotateInfo
1904    *annotate_info,
1905    *previous_info;
1906
1907  XColor
1908    color;
1909
1910  XFontStruct
1911    *font_info;
1912
1913  XEvent
1914    event,
1915    text_event;
1916
1917  /*
1918    Map Command widget.
1919  */
1920  (void) CloneString(&windows->command.name,"Annotate");
1921  windows->command.data=4;
1922  (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1923  (void) XMapRaised(display,windows->command.id);
1924  XClientMessage(display,windows->image.id,windows->im_protocols,
1925    windows->im_update_widget,CurrentTime);
1926  /*
1927    Track pointer until button 1 is pressed.
1928  */
1929  XQueryPosition(display,windows->image.id,&x,&y);
1930  (void) XSelectInput(display,windows->image.id,
1931    windows->image.attributes.event_mask | PointerMotionMask);
1932  cursor=XCreateFontCursor(display,XC_left_side);
1933  (void) XCheckDefineCursor(display,windows->image.id,cursor);
1934  state=DefaultState;
1935  do
1936  {
1937    if( IfMagickTrue(windows->info.mapped) )
1938      {
1939        /*
1940          Display pointer position.
1941        */
1942        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
1943          x+windows->image.x,y+windows->image.y);
1944        XInfoWidget(display,windows,text);
1945      }
1946    /*
1947      Wait for next event.
1948    */
1949    XScreenEvent(display,windows,&event,exception);
1950    if (event.xany.window == windows->command.id)
1951      {
1952        /*
1953          Select a command from the Command widget.
1954        */
1955        id=XCommandWidget(display,windows,AnnotateMenu,&event);
1956        (void) XCheckDefineCursor(display,windows->image.id,cursor);
1957        if (id < 0)
1958          continue;
1959        switch (AnnotateCommands[id])
1960        {
1961          case AnnotateNameCommand:
1962          {
1963            const char
1964              *FontMenu[MaxNumberFonts];
1965
1966            int
1967              font_number;
1968
1969            /*
1970              Initialize menu selections.
1971            */
1972            for (i=0; i < MaxNumberFonts; i++)
1973              FontMenu[i]=resource_info->font_name[i];
1974            FontMenu[MaxNumberFonts-2]="Browser...";
1975            FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1976            /*
1977              Select a font name from the pop-up menu.
1978            */
1979            font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1980              (const char **) FontMenu,command);
1981            if (font_number < 0)
1982              break;
1983            if (font_number == (MaxNumberFonts-2))
1984              {
1985                static char
1986                  font_name[MaxTextExtent] = "fixed";
1987
1988                /*
1989                  Select a font name from a browser.
1990                */
1991                resource_info->font_name[font_number]=font_name;
1992                XFontBrowserWidget(display,windows,"Select",font_name);
1993                if (*font_name == '\0')
1994                  break;
1995              }
1996            /*
1997              Initialize font info.
1998            */
1999            font_info=XLoadQueryFont(display,resource_info->font_name[
2000              font_number]);
2001            if (font_info == (XFontStruct *) NULL)
2002              {
2003                XNoticeWidget(display,windows,"Unable to load font:",
2004                  resource_info->font_name[font_number]);
2005                break;
2006              }
2007            font_id=(unsigned int) font_number;
2008            (void) XFreeFont(display,font_info);
2009            break;
2010          }
2011          case AnnotateFontColorCommand:
2012          {
2013            /*
2014              Initialize menu selections.
2015            */
2016            for (i=0; i < (int) (MaxNumberPens-2); i++)
2017              ColorMenu[i]=resource_info->pen_colors[i];
2018            ColorMenu[MaxNumberPens-2]="transparent";
2019            ColorMenu[MaxNumberPens-1]="Browser...";
2020            ColorMenu[MaxNumberPens]=(const char *) NULL;
2021            /*
2022              Select a pen color from the pop-up menu.
2023            */
2024            pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2025              (const char **) ColorMenu,command);
2026            if (pen_number < 0)
2027              break;
2028            transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
2029              MagickFalse;
2030            if( IfMagickTrue(transparent_pen) )
2031              break;
2032            if (pen_number == (MaxNumberPens-1))
2033              {
2034                static char
2035                  color_name[MaxTextExtent] = "gray";
2036
2037                /*
2038                  Select a pen color from a dialog.
2039                */
2040                resource_info->pen_colors[pen_number]=color_name;
2041                XColorBrowserWidget(display,windows,"Select",color_name);
2042                if (*color_name == '\0')
2043                  break;
2044              }
2045            /*
2046              Set pen color.
2047            */
2048            (void) XParseColor(display,windows->map_info->colormap,
2049              resource_info->pen_colors[pen_number],&color);
2050            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2051              (unsigned int) MaxColors,&color);
2052            windows->pixel_info->pen_colors[pen_number]=color;
2053            pen_id=(unsigned int) pen_number;
2054            break;
2055          }
2056          case AnnotateBackgroundColorCommand:
2057          {
2058            /*
2059              Initialize menu selections.
2060            */
2061            for (i=0; i < (int) (MaxNumberPens-2); i++)
2062              ColorMenu[i]=resource_info->pen_colors[i];
2063            ColorMenu[MaxNumberPens-2]="transparent";
2064            ColorMenu[MaxNumberPens-1]="Browser...";
2065            ColorMenu[MaxNumberPens]=(const char *) NULL;
2066            /*
2067              Select a pen color from the pop-up menu.
2068            */
2069            pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2070              (const char **) ColorMenu,command);
2071            if (pen_number < 0)
2072              break;
2073            transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2074              MagickFalse;
2075            if( IfMagickTrue(transparent_box) )
2076              break;
2077            if (pen_number == (MaxNumberPens-1))
2078              {
2079                static char
2080                  color_name[MaxTextExtent] = "gray";
2081
2082                /*
2083                  Select a pen color from a dialog.
2084                */
2085                resource_info->pen_colors[pen_number]=color_name;
2086                XColorBrowserWidget(display,windows,"Select",color_name);
2087                if (*color_name == '\0')
2088                  break;
2089              }
2090            /*
2091              Set pen color.
2092            */
2093            (void) XParseColor(display,windows->map_info->colormap,
2094              resource_info->pen_colors[pen_number],&color);
2095            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2096              (unsigned int) MaxColors,&color);
2097            windows->pixel_info->pen_colors[pen_number]=color;
2098            box_id=(unsigned int) pen_number;
2099            break;
2100          }
2101          case AnnotateRotateCommand:
2102          {
2103            int
2104              entry;
2105
2106            static char
2107              angle[MaxTextExtent] = "30.0";
2108
2109            static const char
2110              *RotateMenu[] =
2111              {
2112                "-90",
2113                "-45",
2114                "-30",
2115                "0",
2116                "30",
2117                "45",
2118                "90",
2119                "180",
2120                "Dialog...",
2121                (char *) NULL,
2122              };
2123
2124            /*
2125              Select a command from the pop-up menu.
2126            */
2127            entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2128              command);
2129            if (entry < 0)
2130              break;
2131            if (entry != 8)
2132              {
2133                degrees=StringToDouble(RotateMenu[entry],(char **) NULL);
2134                break;
2135              }
2136            (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2137              angle);
2138            if (*angle == '\0')
2139              break;
2140            degrees=StringToDouble(angle,(char **) NULL);
2141            break;
2142          }
2143          case AnnotateHelpCommand:
2144          {
2145            XTextViewWidget(display,resource_info,windows,MagickFalse,
2146              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2147            break;
2148          }
2149          case AnnotateDismissCommand:
2150          {
2151            /*
2152              Prematurely exit.
2153            */
2154            state|=EscapeState;
2155            state|=ExitState;
2156            break;
2157          }
2158          default:
2159            break;
2160        }
2161        continue;
2162      }
2163    switch (event.type)
2164    {
2165      case ButtonPress:
2166      {
2167        if (event.xbutton.button != Button1)
2168          break;
2169        if (event.xbutton.window != windows->image.id)
2170          break;
2171        /*
2172          Change to text entering mode.
2173        */
2174        x=event.xbutton.x;
2175        y=event.xbutton.y;
2176        state|=ExitState;
2177        break;
2178      }
2179      case ButtonRelease:
2180        break;
2181      case Expose:
2182        break;
2183      case KeyPress:
2184      {
2185        if (event.xkey.window != windows->image.id)
2186          break;
2187        /*
2188          Respond to a user key press.
2189        */
2190        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2191          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2192        switch ((int) key_symbol)
2193        {
2194          case XK_Escape:
2195          case XK_F20:
2196          {
2197            /*
2198              Prematurely exit.
2199            */
2200            state|=EscapeState;
2201            state|=ExitState;
2202            break;
2203          }
2204          case XK_F1:
2205          case XK_Help:
2206          {
2207            XTextViewWidget(display,resource_info,windows,MagickFalse,
2208              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2209            break;
2210          }
2211          default:
2212          {
2213            (void) XBell(display,0);
2214            break;
2215          }
2216        }
2217        break;
2218      }
2219      case MotionNotify:
2220      {
2221        /*
2222          Map and unmap Info widget as cursor crosses its boundaries.
2223        */
2224        x=event.xmotion.x;
2225        y=event.xmotion.y;
2226        if( IfMagickTrue(windows->info.mapped) )
2227          {
2228            if ((x < (int) (windows->info.x+windows->info.width)) &&
2229                (y < (int) (windows->info.y+windows->info.height)))
2230              (void) XWithdrawWindow(display,windows->info.id,
2231                windows->info.screen);
2232          }
2233        else
2234          if ((x > (int) (windows->info.x+windows->info.width)) ||
2235              (y > (int) (windows->info.y+windows->info.height)))
2236            (void) XMapWindow(display,windows->info.id);
2237        break;
2238      }
2239      default:
2240        break;
2241    }
2242  } while ((state & ExitState) == 0);
2243  (void) XSelectInput(display,windows->image.id,
2244    windows->image.attributes.event_mask);
2245  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2246  if ((state & EscapeState) != 0)
2247    return(MagickTrue);
2248  /*
2249    Set font info and check boundary conditions.
2250  */
2251  font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2252  if (font_info == (XFontStruct *) NULL)
2253    {
2254      XNoticeWidget(display,windows,"Unable to load font:",
2255        resource_info->font_name[font_id]);
2256      font_info=windows->font_info;
2257    }
2258  if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2259    x=(int) windows->image.width-font_info->max_bounds.width;
2260  if (y < (int) (font_info->ascent+font_info->descent))
2261    y=(int) font_info->ascent+font_info->descent;
2262  if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2263      ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2264    return(MagickFalse);
2265  /*
2266    Initialize annotate structure.
2267  */
2268  annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2269  if (annotate_info == (XAnnotateInfo *) NULL)
2270    return(MagickFalse);
2271  XGetAnnotateInfo(annotate_info);
2272  annotate_info->x=x;
2273  annotate_info->y=y;
2274  if( IfMagickFalse(transparent_box) && IfMagickFalse(transparent_pen))
2275    annotate_info->stencil=OpaqueStencil;
2276  else
2277    if( IfMagickFalse(transparent_box) )
2278      annotate_info->stencil=BackgroundStencil;
2279    else
2280      annotate_info->stencil=ForegroundStencil;
2281  annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2282  annotate_info->degrees=degrees;
2283  annotate_info->font_info=font_info;
2284  annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2285    windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
2286    sizeof(*annotate_info->text));
2287  if (annotate_info->text == (char *) NULL)
2288    return(MagickFalse);
2289  /*
2290    Create cursor and set graphic context.
2291  */
2292  cursor=XCreateFontCursor(display,XC_pencil);
2293  (void) XCheckDefineCursor(display,windows->image.id,cursor);
2294  annotate_context=windows->image.annotate_context;
2295  (void) XSetFont(display,annotate_context,font_info->fid);
2296  (void) XSetBackground(display,annotate_context,
2297    windows->pixel_info->pen_colors[box_id].pixel);
2298  (void) XSetForeground(display,annotate_context,
2299    windows->pixel_info->pen_colors[pen_id].pixel);
2300  /*
2301    Begin annotating the image with text.
2302  */
2303  (void) CloneString(&windows->command.name,"Text");
2304  windows->command.data=0;
2305  (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2306  state=DefaultState;
2307  (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2308  text_event.xexpose.width=(int) font_info->max_bounds.width;
2309  text_event.xexpose.height=font_info->max_bounds.ascent+
2310    font_info->max_bounds.descent;
2311  p=annotate_info->text;
2312  do
2313  {
2314    /*
2315      Display text cursor.
2316    */
2317    *p='\0';
2318    (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2319    /*
2320      Wait for next event.
2321    */
2322    XScreenEvent(display,windows,&event,exception);
2323    if (event.xany.window == windows->command.id)
2324      {
2325        /*
2326          Select a command from the Command widget.
2327        */
2328        (void) XSetBackground(display,annotate_context,
2329          windows->pixel_info->background_color.pixel);
2330        (void) XSetForeground(display,annotate_context,
2331          windows->pixel_info->foreground_color.pixel);
2332        id=XCommandWidget(display,windows,AnnotateMenu,&event);
2333        (void) XSetBackground(display,annotate_context,
2334          windows->pixel_info->pen_colors[box_id].pixel);
2335        (void) XSetForeground(display,annotate_context,
2336          windows->pixel_info->pen_colors[pen_id].pixel);
2337        if (id < 0)
2338          continue;
2339        switch (TextCommands[id])
2340        {
2341          case TextHelpCommand:
2342          {
2343            XTextViewWidget(display,resource_info,windows,MagickFalse,
2344              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2345            (void) XCheckDefineCursor(display,windows->image.id,cursor);
2346            break;
2347          }
2348          case TextApplyCommand:
2349          {
2350            /*
2351              Finished annotating.
2352            */
2353            annotate_info->width=(unsigned int) XTextWidth(font_info,
2354              annotate_info->text,(int) strlen(annotate_info->text));
2355            XRefreshWindow(display,&windows->image,&text_event);
2356            state|=ExitState;
2357            break;
2358          }
2359          default:
2360            break;
2361        }
2362        continue;
2363      }
2364    /*
2365      Erase text cursor.
2366    */
2367    text_event.xexpose.x=x;
2368    text_event.xexpose.y=y-font_info->max_bounds.ascent;
2369    (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2370      (unsigned int) text_event.xexpose.width,(unsigned int)
2371      text_event.xexpose.height,MagickFalse);
2372    XRefreshWindow(display,&windows->image,&text_event);
2373    switch (event.type)
2374    {
2375      case ButtonPress:
2376      {
2377        if (event.xbutton.window != windows->image.id)
2378          break;
2379        if (event.xbutton.button == Button2)
2380          {
2381            /*
2382              Request primary selection.
2383            */
2384            (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2385              windows->image.id,CurrentTime);
2386            break;
2387          }
2388        break;
2389      }
2390      case Expose:
2391      {
2392        if (event.xexpose.count == 0)
2393          {
2394            XAnnotateInfo
2395              *text_info;
2396
2397            /*
2398              Refresh Image window.
2399            */
2400            XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2401            text_info=annotate_info;
2402            while (text_info != (XAnnotateInfo *) NULL)
2403            {
2404              if (annotate_info->stencil == ForegroundStencil)
2405                (void) XDrawString(display,windows->image.id,annotate_context,
2406                  text_info->x,text_info->y,text_info->text,
2407                  (int) strlen(text_info->text));
2408              else
2409                (void) XDrawImageString(display,windows->image.id,
2410                  annotate_context,text_info->x,text_info->y,text_info->text,
2411                  (int) strlen(text_info->text));
2412              text_info=text_info->previous;
2413            }
2414            (void) XDrawString(display,windows->image.id,annotate_context,
2415              x,y,"_",1);
2416          }
2417        break;
2418      }
2419      case KeyPress:
2420      {
2421        int
2422          length;
2423
2424        if (event.xkey.window != windows->image.id)
2425          break;
2426        /*
2427          Respond to a user key press.
2428        */
2429        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2430          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2431        *(command+length)='\0';
2432        if (((event.xkey.state & ControlMask) != 0) ||
2433            ((event.xkey.state & Mod1Mask) != 0))
2434          state|=ModifierState;
2435        if ((state & ModifierState) != 0)
2436          switch ((int) key_symbol)
2437          {
2438            case XK_u:
2439            case XK_U:
2440            {
2441              key_symbol=DeleteCommand;
2442              break;
2443            }
2444            default:
2445              break;
2446          }
2447        switch ((int) key_symbol)
2448        {
2449          case XK_BackSpace:
2450          {
2451            /*
2452              Erase one character.
2453            */
2454            if (p == annotate_info->text)
2455              {
2456                if (annotate_info->previous == (XAnnotateInfo *) NULL)
2457                  break;
2458                else
2459                  {
2460                    /*
2461                      Go to end of the previous line of text.
2462                    */
2463                    annotate_info=annotate_info->previous;
2464                    p=annotate_info->text;
2465                    x=annotate_info->x+annotate_info->width;
2466                    y=annotate_info->y;
2467                    if (annotate_info->width != 0)
2468                      p+=strlen(annotate_info->text);
2469                    break;
2470                  }
2471              }
2472            p--;
2473            x-=XTextWidth(font_info,p,1);
2474            text_event.xexpose.x=x;
2475            text_event.xexpose.y=y-font_info->max_bounds.ascent;
2476            XRefreshWindow(display,&windows->image,&text_event);
2477            break;
2478          }
2479          case XK_bracketleft:
2480          {
2481            key_symbol=XK_Escape;
2482            break;
2483          }
2484          case DeleteCommand:
2485          {
2486            /*
2487              Erase the entire line of text.
2488            */
2489            while (p != annotate_info->text)
2490            {
2491              p--;
2492              x-=XTextWidth(font_info,p,1);
2493              text_event.xexpose.x=x;
2494              XRefreshWindow(display,&windows->image,&text_event);
2495            }
2496            break;
2497          }
2498          case XK_Escape:
2499          case XK_F20:
2500          {
2501            /*
2502              Finished annotating.
2503            */
2504            annotate_info->width=(unsigned int) XTextWidth(font_info,
2505              annotate_info->text,(int) strlen(annotate_info->text));
2506            XRefreshWindow(display,&windows->image,&text_event);
2507            state|=ExitState;
2508            break;
2509          }
2510          default:
2511          {
2512            /*
2513              Draw a single character on the Image window.
2514            */
2515            if ((state & ModifierState) != 0)
2516              break;
2517            if (*command == '\0')
2518              break;
2519            *p=(*command);
2520            if (annotate_info->stencil == ForegroundStencil)
2521              (void) XDrawString(display,windows->image.id,annotate_context,
2522                x,y,p,1);
2523            else
2524              (void) XDrawImageString(display,windows->image.id,
2525                annotate_context,x,y,p,1);
2526            x+=XTextWidth(font_info,p,1);
2527            p++;
2528            if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2529              break;
2530          }
2531          case XK_Return:
2532          case XK_KP_Enter:
2533          {
2534            /*
2535              Advance to the next line of text.
2536            */
2537            *p='\0';
2538            annotate_info->width=(unsigned int) XTextWidth(font_info,
2539              annotate_info->text,(int) strlen(annotate_info->text));
2540            if (annotate_info->next != (XAnnotateInfo *) NULL)
2541              {
2542                /*
2543                  Line of text already exists.
2544                */
2545                annotate_info=annotate_info->next;
2546                x=annotate_info->x;
2547                y=annotate_info->y;
2548                p=annotate_info->text;
2549                break;
2550              }
2551            annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2552              sizeof(*annotate_info->next));
2553            if (annotate_info->next == (XAnnotateInfo *) NULL)
2554              return(MagickFalse);
2555            *annotate_info->next=(*annotate_info);
2556            annotate_info->next->previous=annotate_info;
2557            annotate_info=annotate_info->next;
2558            annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2559              windows->image.width/MagickMax((ssize_t)
2560              font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2561            if (annotate_info->text == (char *) NULL)
2562              return(MagickFalse);
2563            annotate_info->y+=annotate_info->height;
2564            if (annotate_info->y > (int) windows->image.height)
2565              annotate_info->y=(int) annotate_info->height;
2566            annotate_info->next=(XAnnotateInfo *) NULL;
2567            x=annotate_info->x;
2568            y=annotate_info->y;
2569            p=annotate_info->text;
2570            break;
2571          }
2572        }
2573        break;
2574      }
2575      case KeyRelease:
2576      {
2577        /*
2578          Respond to a user key release.
2579        */
2580        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2581          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2582        state&=(~ModifierState);
2583        break;
2584      }
2585      case SelectionNotify:
2586      {
2587        Atom
2588          type;
2589
2590        int
2591          format;
2592
2593        unsigned char
2594          *data;
2595
2596        unsigned long
2597          after,
2598          length;
2599
2600        /*
2601          Obtain response from primary selection.
2602        */
2603        if (event.xselection.property == (Atom) None)
2604          break;
2605        status=XGetWindowProperty(display,event.xselection.requestor,
2606          event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
2607          &type,&format,&length,&after,&data);
2608        if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2609            (length == 0))
2610          break;
2611        /*
2612          Annotate Image window with primary selection.
2613        */
2614        for (i=0; i < (ssize_t) length; i++)
2615        {
2616          if ((char) data[i] != '\n')
2617            {
2618              /*
2619                Draw a single character on the Image window.
2620              */
2621              *p=(char) data[i];
2622              (void) XDrawString(display,windows->image.id,annotate_context,
2623                x,y,p,1);
2624              x+=XTextWidth(font_info,p,1);
2625              p++;
2626              if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2627                continue;
2628            }
2629          /*
2630            Advance to the next line of text.
2631          */
2632          *p='\0';
2633          annotate_info->width=(unsigned int) XTextWidth(font_info,
2634            annotate_info->text,(int) strlen(annotate_info->text));
2635          if (annotate_info->next != (XAnnotateInfo *) NULL)
2636            {
2637              /*
2638                Line of text already exists.
2639              */
2640              annotate_info=annotate_info->next;
2641              x=annotate_info->x;
2642              y=annotate_info->y;
2643              p=annotate_info->text;
2644              continue;
2645            }
2646          annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2647            sizeof(*annotate_info->next));
2648          if (annotate_info->next == (XAnnotateInfo *) NULL)
2649            return(MagickFalse);
2650          *annotate_info->next=(*annotate_info);
2651          annotate_info->next->previous=annotate_info;
2652          annotate_info=annotate_info->next;
2653          annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2654            windows->image.width/MagickMax((ssize_t)
2655            font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2656          if (annotate_info->text == (char *) NULL)
2657            return(MagickFalse);
2658          annotate_info->y+=annotate_info->height;
2659          if (annotate_info->y > (int) windows->image.height)
2660            annotate_info->y=(int) annotate_info->height;
2661          annotate_info->next=(XAnnotateInfo *) NULL;
2662          x=annotate_info->x;
2663          y=annotate_info->y;
2664          p=annotate_info->text;
2665        }
2666        (void) XFree((void *) data);
2667        break;
2668      }
2669      default:
2670        break;
2671    }
2672  } while ((state & ExitState) == 0);
2673  (void) XFreeCursor(display,cursor);
2674  /*
2675    Annotation is relative to image configuration.
2676  */
2677  width=(unsigned int) image->columns;
2678  height=(unsigned int) image->rows;
2679  x=0;
2680  y=0;
2681  if (windows->image.crop_geometry != (char *) NULL)
2682    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2683  /*
2684    Initialize annotated image.
2685  */
2686  XSetCursorState(display,windows,MagickTrue);
2687  XCheckRefreshWindows(display,windows);
2688  while (annotate_info != (XAnnotateInfo *) NULL)
2689  {
2690    if (annotate_info->width == 0)
2691      {
2692        /*
2693          No text on this line--  go to the next line of text.
2694        */
2695        previous_info=annotate_info->previous;
2696        annotate_info->text=(char *)
2697          RelinquishMagickMemory(annotate_info->text);
2698        annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2699        annotate_info=previous_info;
2700        continue;
2701      }
2702    /*
2703      Determine pixel index for box and pen color.
2704    */
2705    windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2706    if (windows->pixel_info->colors != 0)
2707      for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2708        if (windows->pixel_info->pixels[i] ==
2709            windows->pixel_info->pen_colors[box_id].pixel)
2710          {
2711            windows->pixel_info->box_index=(unsigned short) i;
2712            break;
2713          }
2714    windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2715    if (windows->pixel_info->colors != 0)
2716      for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2717        if (windows->pixel_info->pixels[i] ==
2718            windows->pixel_info->pen_colors[pen_id].pixel)
2719          {
2720            windows->pixel_info->pen_index=(unsigned short) i;
2721            break;
2722          }
2723    /*
2724      Define the annotate geometry string.
2725    */
2726    annotate_info->x=(int)
2727      width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2728    annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2729      windows->image.y)/windows->image.ximage->height;
2730    (void) FormatLocaleString(annotate_info->geometry,MaxTextExtent,
2731      "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2732      height*annotate_info->height/windows->image.ximage->height,
2733      annotate_info->x+x,annotate_info->y+y);
2734    /*
2735      Annotate image with text.
2736    */
2737    status=XAnnotateImage(display,windows->pixel_info,annotate_info,image,
2738      exception);
2739    if (status == 0)
2740      return(MagickFalse);
2741    /*
2742      Free up memory.
2743    */
2744    previous_info=annotate_info->previous;
2745    annotate_info->text=DestroyString(annotate_info->text);
2746    annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2747    annotate_info=previous_info;
2748  }
2749  (void) XSetForeground(display,annotate_context,
2750    windows->pixel_info->foreground_color.pixel);
2751  (void) XSetBackground(display,annotate_context,
2752    windows->pixel_info->background_color.pixel);
2753  (void) XSetFont(display,annotate_context,windows->font_info->fid);
2754  XSetCursorState(display,windows,MagickFalse);
2755  (void) XFreeFont(display,font_info);
2756  /*
2757    Update image configuration.
2758  */
2759  XConfigureImageColormap(display,resource_info,windows,image,exception);
2760  (void) XConfigureImage(display,resource_info,windows,image,exception);
2761  return(MagickTrue);
2762}
2763
2764/*
2765%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2766%                                                                             %
2767%                                                                             %
2768%                                                                             %
2769+   X B a c k g r o u n d I m a g e                                           %
2770%                                                                             %
2771%                                                                             %
2772%                                                                             %
2773%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2774%
2775%  XBackgroundImage() displays the image in the background of a window.
2776%
2777%  The format of the XBackgroundImage method is:
2778%
2779%      MagickBooleanType XBackgroundImage(Display *display,
2780%        XResourceInfo *resource_info,XWindows *windows,Image **image,
2781%        ExceptionInfo *exception)
2782%
2783%  A description of each parameter follows:
2784%
2785%    o display: Specifies a connection to an X server; returned from
2786%      XOpenDisplay.
2787%
2788%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2789%
2790%    o windows: Specifies a pointer to a XWindows structure.
2791%
2792%    o image: the image.
2793%
2794%    o exception: return any errors or warnings in this structure.
2795%
2796*/
2797static MagickBooleanType XBackgroundImage(Display *display,
2798  XResourceInfo *resource_info,XWindows *windows,Image **image,
2799  ExceptionInfo *exception)
2800{
2801#define BackgroundImageTag  "Background/Image"
2802
2803  int
2804    status;
2805
2806  static char
2807    window_id[MaxTextExtent] = "root";
2808
2809  XResourceInfo
2810    background_resources;
2811
2812  /*
2813    Put image in background.
2814  */
2815  status=XDialogWidget(display,windows,"Background",
2816    "Enter window id (id 0x00 selects window with pointer):",window_id);
2817  if (*window_id == '\0')
2818    return(MagickFalse);
2819  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
2820    exception);
2821  XInfoWidget(display,windows,BackgroundImageTag);
2822  XSetCursorState(display,windows,MagickTrue);
2823  XCheckRefreshWindows(display,windows);
2824  background_resources=(*resource_info);
2825  background_resources.window_id=window_id;
2826  background_resources.backdrop=IsMagickTrue(status);
2827  status=XDisplayBackgroundImage(display,&background_resources,*image,
2828    exception);
2829  if (IfMagickTrue(status))
2830    XClientMessage(display,windows->image.id,windows->im_protocols,
2831      windows->im_retain_colors,CurrentTime);
2832  XSetCursorState(display,windows,MagickFalse);
2833  (void) XMagickCommand(display,resource_info,windows,UndoCommand,image,
2834    exception);
2835  return(MagickTrue);
2836}
2837
2838/*
2839%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2840%                                                                             %
2841%                                                                             %
2842%                                                                             %
2843+   X C h o p I m a g e                                                       %
2844%                                                                             %
2845%                                                                             %
2846%                                                                             %
2847%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2848%
2849%  XChopImage() chops the X image.
2850%
2851%  The format of the XChopImage method is:
2852%
2853%    MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2854%      XWindows *windows,Image **image,ExceptionInfo *exception)
2855%
2856%  A description of each parameter follows:
2857%
2858%    o display: Specifies a connection to an X server; returned from
2859%      XOpenDisplay.
2860%
2861%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2862%
2863%    o windows: Specifies a pointer to a XWindows structure.
2864%
2865%    o image: the image.
2866%
2867%    o exception: return any errors or warnings in this structure.
2868%
2869*/
2870static MagickBooleanType XChopImage(Display *display,
2871  XResourceInfo *resource_info,XWindows *windows,Image **image,
2872  ExceptionInfo *exception)
2873{
2874  static const char
2875    *ChopMenu[] =
2876    {
2877      "Direction",
2878      "Help",
2879      "Dismiss",
2880      (char *) NULL
2881    };
2882
2883  static ModeType
2884    direction = HorizontalChopCommand;
2885
2886  static const ModeType
2887    ChopCommands[] =
2888    {
2889      ChopDirectionCommand,
2890      ChopHelpCommand,
2891      ChopDismissCommand
2892    },
2893    DirectionCommands[] =
2894    {
2895      HorizontalChopCommand,
2896      VerticalChopCommand
2897    };
2898
2899  char
2900    text[MaxTextExtent];
2901
2902  Image
2903    *chop_image;
2904
2905  int
2906    id,
2907    x,
2908    y;
2909
2910  double
2911    scale_factor;
2912
2913  RectangleInfo
2914    chop_info;
2915
2916  unsigned int
2917    distance,
2918    height,
2919    width;
2920
2921  size_t
2922    state;
2923
2924  XEvent
2925    event;
2926
2927  XSegment
2928    segment_info;
2929
2930  /*
2931    Map Command widget.
2932  */
2933  (void) CloneString(&windows->command.name,"Chop");
2934  windows->command.data=1;
2935  (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2936  (void) XMapRaised(display,windows->command.id);
2937  XClientMessage(display,windows->image.id,windows->im_protocols,
2938    windows->im_update_widget,CurrentTime);
2939  /*
2940    Track pointer until button 1 is pressed.
2941  */
2942  XQueryPosition(display,windows->image.id,&x,&y);
2943  (void) XSelectInput(display,windows->image.id,
2944    windows->image.attributes.event_mask | PointerMotionMask);
2945  state=DefaultState;
2946  do
2947  {
2948    if( IfMagickTrue(windows->info.mapped) )
2949      {
2950        /*
2951          Display pointer position.
2952        */
2953        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
2954          x+windows->image.x,y+windows->image.y);
2955        XInfoWidget(display,windows,text);
2956      }
2957    /*
2958      Wait for next event.
2959    */
2960    XScreenEvent(display,windows,&event,exception);
2961    if (event.xany.window == windows->command.id)
2962      {
2963        /*
2964          Select a command from the Command widget.
2965        */
2966        id=XCommandWidget(display,windows,ChopMenu,&event);
2967        if (id < 0)
2968          continue;
2969        switch (ChopCommands[id])
2970        {
2971          case ChopDirectionCommand:
2972          {
2973            char
2974              command[MaxTextExtent];
2975
2976            static const char
2977              *Directions[] =
2978              {
2979                "horizontal",
2980                "vertical",
2981                (char *) NULL,
2982              };
2983
2984            /*
2985              Select a command from the pop-up menu.
2986            */
2987            id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2988            if (id >= 0)
2989              direction=DirectionCommands[id];
2990            break;
2991          }
2992          case ChopHelpCommand:
2993          {
2994            XTextViewWidget(display,resource_info,windows,MagickFalse,
2995              "Help Viewer - Image Chop",ImageChopHelp);
2996            break;
2997          }
2998          case ChopDismissCommand:
2999          {
3000            /*
3001              Prematurely exit.
3002            */
3003            state|=EscapeState;
3004            state|=ExitState;
3005            break;
3006          }
3007          default:
3008            break;
3009        }
3010        continue;
3011      }
3012    switch (event.type)
3013    {
3014      case ButtonPress:
3015      {
3016        if (event.xbutton.button != Button1)
3017          break;
3018        if (event.xbutton.window != windows->image.id)
3019          break;
3020        /*
3021          User has committed to start point of chopping line.
3022        */
3023        segment_info.x1=(short int) event.xbutton.x;
3024        segment_info.x2=(short int) event.xbutton.x;
3025        segment_info.y1=(short int) event.xbutton.y;
3026        segment_info.y2=(short int) event.xbutton.y;
3027        state|=ExitState;
3028        break;
3029      }
3030      case ButtonRelease:
3031        break;
3032      case Expose:
3033        break;
3034      case KeyPress:
3035      {
3036        char
3037          command[MaxTextExtent];
3038
3039        KeySym
3040          key_symbol;
3041
3042        if (event.xkey.window != windows->image.id)
3043          break;
3044        /*
3045          Respond to a user key press.
3046        */
3047        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3048          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3049        switch ((int) key_symbol)
3050        {
3051          case XK_Escape:
3052          case XK_F20:
3053          {
3054            /*
3055              Prematurely exit.
3056            */
3057            state|=EscapeState;
3058            state|=ExitState;
3059            break;
3060          }
3061          case XK_F1:
3062          case XK_Help:
3063          {
3064            (void) XSetFunction(display,windows->image.highlight_context,
3065              GXcopy);
3066            XTextViewWidget(display,resource_info,windows,MagickFalse,
3067              "Help Viewer - Image Chop",ImageChopHelp);
3068            (void) XSetFunction(display,windows->image.highlight_context,
3069              GXinvert);
3070            break;
3071          }
3072          default:
3073          {
3074            (void) XBell(display,0);
3075            break;
3076          }
3077        }
3078        break;
3079      }
3080      case MotionNotify:
3081      {
3082        /*
3083          Map and unmap Info widget as text cursor crosses its boundaries.
3084        */
3085        x=event.xmotion.x;
3086        y=event.xmotion.y;
3087        if( IfMagickTrue(windows->info.mapped) )
3088          {
3089            if ((x < (int) (windows->info.x+windows->info.width)) &&
3090                (y < (int) (windows->info.y+windows->info.height)))
3091              (void) XWithdrawWindow(display,windows->info.id,
3092                windows->info.screen);
3093          }
3094        else
3095          if ((x > (int) (windows->info.x+windows->info.width)) ||
3096              (y > (int) (windows->info.y+windows->info.height)))
3097            (void) XMapWindow(display,windows->info.id);
3098      }
3099    }
3100  } while ((state & ExitState) == 0);
3101  (void) XSelectInput(display,windows->image.id,
3102    windows->image.attributes.event_mask);
3103  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3104  if ((state & EscapeState) != 0)
3105    return(MagickTrue);
3106  /*
3107    Draw line as pointer moves until the mouse button is released.
3108  */
3109  chop_info.width=0;
3110  chop_info.height=0;
3111  chop_info.x=0;
3112  chop_info.y=0;
3113  distance=0;
3114  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3115  state=DefaultState;
3116  do
3117  {
3118    if (distance > 9)
3119      {
3120        /*
3121          Display info and draw chopping line.
3122        */
3123        if( IfMagickFalse(windows->info.mapped) )
3124          (void) XMapWindow(display,windows->info.id);
3125        (void) FormatLocaleString(text,MaxTextExtent,
3126          " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3127          chop_info.height,(double) chop_info.x,(double) chop_info.y);
3128        XInfoWidget(display,windows,text);
3129        XHighlightLine(display,windows->image.id,
3130          windows->image.highlight_context,&segment_info);
3131      }
3132    else
3133      if( IfMagickTrue(windows->info.mapped) )
3134        (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3135    /*
3136      Wait for next event.
3137    */
3138    XScreenEvent(display,windows,&event,exception);
3139    if (distance > 9)
3140      XHighlightLine(display,windows->image.id,
3141        windows->image.highlight_context,&segment_info);
3142    switch (event.type)
3143    {
3144      case ButtonPress:
3145      {
3146        segment_info.x2=(short int) event.xmotion.x;
3147        segment_info.y2=(short int) event.xmotion.y;
3148        break;
3149      }
3150      case ButtonRelease:
3151      {
3152        /*
3153          User has committed to chopping line.
3154        */
3155        segment_info.x2=(short int) event.xbutton.x;
3156        segment_info.y2=(short int) event.xbutton.y;
3157        state|=ExitState;
3158        break;
3159      }
3160      case Expose:
3161        break;
3162      case MotionNotify:
3163      {
3164        segment_info.x2=(short int) event.xmotion.x;
3165        segment_info.y2=(short int) event.xmotion.y;
3166      }
3167      default:
3168        break;
3169    }
3170    /*
3171      Check boundary conditions.
3172    */
3173    if (segment_info.x2 < 0)
3174      segment_info.x2=0;
3175    else
3176      if (segment_info.x2 > windows->image.ximage->width)
3177        segment_info.x2=windows->image.ximage->width;
3178    if (segment_info.y2 < 0)
3179      segment_info.y2=0;
3180    else
3181      if (segment_info.y2 > windows->image.ximage->height)
3182        segment_info.y2=windows->image.ximage->height;
3183    distance=(unsigned int)
3184      (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3185       ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3186    /*
3187      Compute chopping geometry.
3188    */
3189    if (direction == HorizontalChopCommand)
3190      {
3191        chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3192        chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3193        chop_info.height=0;
3194        chop_info.y=0;
3195        if (segment_info.x1 > (int) segment_info.x2)
3196          {
3197            chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3198            chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3199          }
3200      }
3201    else
3202      {
3203        chop_info.width=0;
3204        chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3205        chop_info.x=0;
3206        chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3207        if (segment_info.y1 > segment_info.y2)
3208          {
3209            chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3210            chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3211          }
3212      }
3213  } while ((state & ExitState) == 0);
3214  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3215  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3216  if (distance <= 9)
3217    return(MagickTrue);
3218  /*
3219    Image chopping is relative to image configuration.
3220  */
3221  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
3222    exception);
3223  XSetCursorState(display,windows,MagickTrue);
3224  XCheckRefreshWindows(display,windows);
3225  windows->image.window_changes.width=windows->image.ximage->width-
3226    (unsigned int) chop_info.width;
3227  windows->image.window_changes.height=windows->image.ximage->height-
3228    (unsigned int) chop_info.height;
3229  width=(unsigned int) (*image)->columns;
3230  height=(unsigned int) (*image)->rows;
3231  x=0;
3232  y=0;
3233  if (windows->image.crop_geometry != (char *) NULL)
3234    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3235  scale_factor=(double) width/windows->image.ximage->width;
3236  chop_info.x+=x;
3237  chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3238  chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3239  scale_factor=(double) height/windows->image.ximage->height;
3240  chop_info.y+=y;
3241  chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3242  chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3243  /*
3244    Chop image.
3245  */
3246  chop_image=ChopImage(*image,&chop_info,exception);
3247  XSetCursorState(display,windows,MagickFalse);
3248  if (chop_image == (Image *) NULL)
3249    return(MagickFalse);
3250  *image=DestroyImage(*image);
3251  *image=chop_image;
3252  /*
3253    Update image configuration.
3254  */
3255  XConfigureImageColormap(display,resource_info,windows,*image,exception);
3256  (void) XConfigureImage(display,resource_info,windows,*image,exception);
3257  return(MagickTrue);
3258}
3259
3260/*
3261%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3262%                                                                             %
3263%                                                                             %
3264%                                                                             %
3265+   X C o l o r E d i t I m a g e                                             %
3266%                                                                             %
3267%                                                                             %
3268%                                                                             %
3269%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3270%
3271%  XColorEditImage() allows the user to interactively change the color of one
3272%  pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3273%
3274%  The format of the XColorEditImage method is:
3275%
3276%      MagickBooleanType XColorEditImage(Display *display,
3277%        XResourceInfo *resource_info,XWindows *windows,Image **image,
3278%          ExceptionInfo *exception)
3279%
3280%  A description of each parameter follows:
3281%
3282%    o display: Specifies a connection to an X server;  returned from
3283%      XOpenDisplay.
3284%
3285%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3286%
3287%    o windows: Specifies a pointer to a XWindows structure.
3288%
3289%    o image: the image; returned from ReadImage.
3290%
3291%    o exception: return any errors or warnings in this structure.
3292%
3293*/
3294static MagickBooleanType XColorEditImage(Display *display,
3295  XResourceInfo *resource_info,XWindows *windows,Image **image,
3296  ExceptionInfo *exception)
3297{
3298  static const char
3299    *ColorEditMenu[] =
3300    {
3301      "Method",
3302      "Pixel Color",
3303      "Border Color",
3304      "Fuzz",
3305      "Undo",
3306      "Help",
3307      "Dismiss",
3308      (char *) NULL
3309    };
3310
3311  static const ModeType
3312    ColorEditCommands[] =
3313    {
3314      ColorEditMethodCommand,
3315      ColorEditColorCommand,
3316      ColorEditBorderCommand,
3317      ColorEditFuzzCommand,
3318      ColorEditUndoCommand,
3319      ColorEditHelpCommand,
3320      ColorEditDismissCommand
3321    };
3322
3323  static PaintMethod
3324    method = PointMethod;
3325
3326  static unsigned int
3327    pen_id = 0;
3328
3329  static XColor
3330    border_color = { 0, 0, 0, 0, 0, 0 };
3331
3332  char
3333    command[MaxTextExtent],
3334    text[MaxTextExtent];
3335
3336  Cursor
3337    cursor;
3338
3339  int
3340    entry,
3341    id,
3342    x,
3343    x_offset,
3344    y,
3345    y_offset;
3346
3347  register Quantum
3348    *q;
3349
3350  register ssize_t
3351    i;
3352
3353  unsigned int
3354    height,
3355    width;
3356
3357  size_t
3358    state;
3359
3360  XColor
3361    color;
3362
3363  XEvent
3364    event;
3365
3366  /*
3367    Map Command widget.
3368  */
3369  (void) CloneString(&windows->command.name,"Color Edit");
3370  windows->command.data=4;
3371  (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3372  (void) XMapRaised(display,windows->command.id);
3373  XClientMessage(display,windows->image.id,windows->im_protocols,
3374    windows->im_update_widget,CurrentTime);
3375  /*
3376    Make cursor.
3377  */
3378  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3379    resource_info->background_color,resource_info->foreground_color);
3380  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3381  /*
3382    Track pointer until button 1 is pressed.
3383  */
3384  XQueryPosition(display,windows->image.id,&x,&y);
3385  (void) XSelectInput(display,windows->image.id,
3386    windows->image.attributes.event_mask | PointerMotionMask);
3387  state=DefaultState;
3388  do
3389  {
3390    if( IfMagickTrue(windows->info.mapped) )
3391      {
3392        /*
3393          Display pointer position.
3394        */
3395        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
3396          x+windows->image.x,y+windows->image.y);
3397        XInfoWidget(display,windows,text);
3398      }
3399    /*
3400      Wait for next event.
3401    */
3402    XScreenEvent(display,windows,&event,exception);
3403    if (event.xany.window == windows->command.id)
3404      {
3405        /*
3406          Select a command from the Command widget.
3407        */
3408        id=XCommandWidget(display,windows,ColorEditMenu,&event);
3409        if (id < 0)
3410          {
3411            (void) XCheckDefineCursor(display,windows->image.id,cursor);
3412            continue;
3413          }
3414        switch (ColorEditCommands[id])
3415        {
3416          case ColorEditMethodCommand:
3417          {
3418            char
3419              **methods;
3420
3421            /*
3422              Select a method from the pop-up menu.
3423            */
3424            methods=(char **) GetCommandOptions(MagickMethodOptions);
3425            if (methods == (char **) NULL)
3426              break;
3427            entry=XMenuWidget(display,windows,ColorEditMenu[id],
3428              (const char **) methods,command);
3429            if (entry >= 0)
3430              method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3431                MagickFalse,methods[entry]);
3432            methods=DestroyStringList(methods);
3433            break;
3434          }
3435          case ColorEditColorCommand:
3436          {
3437            const char
3438              *ColorMenu[MaxNumberPens];
3439
3440            int
3441              pen_number;
3442
3443            /*
3444              Initialize menu selections.
3445            */
3446            for (i=0; i < (int) (MaxNumberPens-2); i++)
3447              ColorMenu[i]=resource_info->pen_colors[i];
3448            ColorMenu[MaxNumberPens-2]="Browser...";
3449            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3450            /*
3451              Select a pen color from the pop-up menu.
3452            */
3453            pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3454              (const char **) ColorMenu,command);
3455            if (pen_number < 0)
3456              break;
3457            if (pen_number == (MaxNumberPens-2))
3458              {
3459                static char
3460                  color_name[MaxTextExtent] = "gray";
3461
3462                /*
3463                  Select a pen color from a dialog.
3464                */
3465                resource_info->pen_colors[pen_number]=color_name;
3466                XColorBrowserWidget(display,windows,"Select",color_name);
3467                if (*color_name == '\0')
3468                  break;
3469              }
3470            /*
3471              Set pen color.
3472            */
3473            (void) XParseColor(display,windows->map_info->colormap,
3474              resource_info->pen_colors[pen_number],&color);
3475            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3476              (unsigned int) MaxColors,&color);
3477            windows->pixel_info->pen_colors[pen_number]=color;
3478            pen_id=(unsigned int) pen_number;
3479            break;
3480          }
3481          case ColorEditBorderCommand:
3482          {
3483            const char
3484              *ColorMenu[MaxNumberPens];
3485
3486            int
3487              pen_number;
3488
3489            /*
3490              Initialize menu selections.
3491            */
3492            for (i=0; i < (int) (MaxNumberPens-2); i++)
3493              ColorMenu[i]=resource_info->pen_colors[i];
3494            ColorMenu[MaxNumberPens-2]="Browser...";
3495            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3496            /*
3497              Select a pen color from the pop-up menu.
3498            */
3499            pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3500              (const char **) ColorMenu,command);
3501            if (pen_number < 0)
3502              break;
3503            if (pen_number == (MaxNumberPens-2))
3504              {
3505                static char
3506                  color_name[MaxTextExtent] = "gray";
3507
3508                /*
3509                  Select a pen color from a dialog.
3510                */
3511                resource_info->pen_colors[pen_number]=color_name;
3512                XColorBrowserWidget(display,windows,"Select",color_name);
3513                if (*color_name == '\0')
3514                  break;
3515              }
3516            /*
3517              Set border color.
3518            */
3519            (void) XParseColor(display,windows->map_info->colormap,
3520              resource_info->pen_colors[pen_number],&border_color);
3521            break;
3522          }
3523          case ColorEditFuzzCommand:
3524          {
3525            static char
3526              fuzz[MaxTextExtent];
3527
3528            static const char
3529              *FuzzMenu[] =
3530              {
3531                "0%",
3532                "2%",
3533                "5%",
3534                "10%",
3535                "15%",
3536                "Dialog...",
3537                (char *) NULL,
3538              };
3539
3540            /*
3541              Select a command from the pop-up menu.
3542            */
3543            entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3544              command);
3545            if (entry < 0)
3546              break;
3547            if (entry != 5)
3548              {
3549                (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
3550                  QuantumRange+1.0);
3551                break;
3552              }
3553            (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
3554            (void) XDialogWidget(display,windows,"Ok",
3555              "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3556            if (*fuzz == '\0')
3557              break;
3558            (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
3559            (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
3560              1.0);
3561            break;
3562          }
3563          case ColorEditUndoCommand:
3564          {
3565            (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3566              image,exception);
3567            break;
3568          }
3569          case ColorEditHelpCommand:
3570          default:
3571          {
3572            XTextViewWidget(display,resource_info,windows,MagickFalse,
3573              "Help Viewer - Image Annotation",ImageColorEditHelp);
3574            break;
3575          }
3576          case ColorEditDismissCommand:
3577          {
3578            /*
3579              Prematurely exit.
3580            */
3581            state|=EscapeState;
3582            state|=ExitState;
3583            break;
3584          }
3585        }
3586        (void) XCheckDefineCursor(display,windows->image.id,cursor);
3587        continue;
3588      }
3589    switch (event.type)
3590    {
3591      case ButtonPress:
3592      {
3593        if (event.xbutton.button != Button1)
3594          break;
3595        if ((event.xbutton.window != windows->image.id) &&
3596            (event.xbutton.window != windows->magnify.id))
3597          break;
3598        /*
3599          exit loop.
3600        */
3601        x=event.xbutton.x;
3602        y=event.xbutton.y;
3603        (void) XMagickCommand(display,resource_info,windows,
3604          SaveToUndoBufferCommand,image,exception);
3605        state|=UpdateConfigurationState;
3606        break;
3607      }
3608      case ButtonRelease:
3609      {
3610        if (event.xbutton.button != Button1)
3611          break;
3612        if ((event.xbutton.window != windows->image.id) &&
3613            (event.xbutton.window != windows->magnify.id))
3614          break;
3615        /*
3616          Update colormap information.
3617        */
3618        x=event.xbutton.x;
3619        y=event.xbutton.y;
3620        XConfigureImageColormap(display,resource_info,windows,*image,exception);
3621        (void) XConfigureImage(display,resource_info,windows,*image,exception);
3622        XInfoWidget(display,windows,text);
3623        (void) XCheckDefineCursor(display,windows->image.id,cursor);
3624        state&=(~UpdateConfigurationState);
3625        break;
3626      }
3627      case Expose:
3628        break;
3629      case KeyPress:
3630      {
3631        KeySym
3632          key_symbol;
3633
3634        if (event.xkey.window == windows->magnify.id)
3635          {
3636            Window
3637              window;
3638
3639            window=windows->magnify.id;
3640            while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3641          }
3642        if (event.xkey.window != windows->image.id)
3643          break;
3644        /*
3645          Respond to a user key press.
3646        */
3647        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3648          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3649        switch ((int) key_symbol)
3650        {
3651          case XK_Escape:
3652          case XK_F20:
3653          {
3654            /*
3655              Prematurely exit.
3656            */
3657            state|=ExitState;
3658            break;
3659          }
3660          case XK_F1:
3661          case XK_Help:
3662          {
3663            XTextViewWidget(display,resource_info,windows,MagickFalse,
3664              "Help Viewer - Image Annotation",ImageColorEditHelp);
3665            break;
3666          }
3667          default:
3668          {
3669            (void) XBell(display,0);
3670            break;
3671          }
3672        }
3673        break;
3674      }
3675      case MotionNotify:
3676      {
3677        /*
3678          Map and unmap Info widget as cursor crosses its boundaries.
3679        */
3680        x=event.xmotion.x;
3681        y=event.xmotion.y;
3682        if( IfMagickTrue(windows->info.mapped) )
3683          {
3684            if ((x < (int) (windows->info.x+windows->info.width)) &&
3685                (y < (int) (windows->info.y+windows->info.height)))
3686              (void) XWithdrawWindow(display,windows->info.id,
3687                windows->info.screen);
3688          }
3689        else
3690          if ((x > (int) (windows->info.x+windows->info.width)) ||
3691              (y > (int) (windows->info.y+windows->info.height)))
3692            (void) XMapWindow(display,windows->info.id);
3693        break;
3694      }
3695      default:
3696        break;
3697    }
3698    if (event.xany.window == windows->magnify.id)
3699      {
3700        x=windows->magnify.x-windows->image.x;
3701        y=windows->magnify.y-windows->image.y;
3702      }
3703    x_offset=x;
3704    y_offset=y;
3705    if ((state & UpdateConfigurationState) != 0)
3706      {
3707        CacheView
3708          *image_view;
3709
3710        int
3711          x,
3712          y;
3713
3714        /*
3715          Pixel edit is relative to image configuration.
3716        */
3717        (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3718          MagickTrue);
3719        color=windows->pixel_info->pen_colors[pen_id];
3720        XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3721        width=(unsigned int) (*image)->columns;
3722        height=(unsigned int) (*image)->rows;
3723        x=0;
3724        y=0;
3725        if (windows->image.crop_geometry != (char *) NULL)
3726          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3727            &width,&height);
3728        x_offset=(int)
3729          (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3730        y_offset=(int)
3731          (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3732        if ((x_offset < 0) || (y_offset < 0))
3733          continue;
3734        if ((x_offset >= (int) (*image)->columns) ||
3735            (y_offset >= (int) (*image)->rows))
3736          continue;
3737        image_view=AcquireAuthenticCacheView(*image,exception);
3738        switch (method)
3739        {
3740          case PointMethod:
3741          default:
3742          {
3743            /*
3744              Update color information using point algorithm.
3745            */
3746            if( IfMagickFalse(SetImageStorageClass(*image,DirectClass,exception)) )
3747              return(MagickFalse);
3748            q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3749              (ssize_t) y_offset,1,1,exception);
3750            if (q == (Quantum *) NULL)
3751              break;
3752            SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3753            SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3754            SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3755            (void) SyncCacheViewAuthenticPixels(image_view,exception);
3756            break;
3757          }
3758          case ReplaceMethod:
3759          {
3760            PixelInfo
3761              pixel,
3762              target;
3763
3764            /*
3765              Update color information using replace algorithm.
3766            */
3767            (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
3768              x_offset,(ssize_t) y_offset,&target,exception);
3769            if ((*image)->storage_class == DirectClass)
3770              {
3771                for (y=0; y < (int) (*image)->rows; y++)
3772                {
3773                  q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3774                    (*image)->columns,1,exception);
3775                  if (q == (Quantum *) NULL)
3776                    break;
3777                  for (x=0; x < (int) (*image)->columns; x++)
3778                  {
3779                    GetPixelInfoPixel(*image,q,&pixel);
3780                    if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
3781                      {
3782                        SetPixelRed(*image,ScaleShortToQuantum(
3783                          color.red),q);
3784                        SetPixelGreen(*image,ScaleShortToQuantum(
3785                          color.green),q);
3786                        SetPixelBlue(*image,ScaleShortToQuantum(
3787                          color.blue),q);
3788                      }
3789                    q+=GetPixelChannels(*image);
3790                  }
3791                  if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
3792                    break;
3793                }
3794              }
3795            else
3796              {
3797                for (i=0; i < (ssize_t) (*image)->colors; i++)
3798                  if (IsFuzzyEquivalencePixelInfo((*image)->colormap+i,&target))
3799                    {
3800                      (*image)->colormap[i].red=(double) ScaleShortToQuantum(
3801                        color.red);
3802                      (*image)->colormap[i].green=(double) ScaleShortToQuantum(
3803                        color.green);
3804                      (*image)->colormap[i].blue=(double) ScaleShortToQuantum(
3805                        color.blue);
3806                    }
3807                (void) SyncImage(*image,exception);
3808              }
3809            break;
3810          }
3811          case FloodfillMethod:
3812          case FillToBorderMethod:
3813          {
3814            DrawInfo
3815              *draw_info;
3816
3817            PixelInfo
3818              target;
3819
3820            /*
3821              Update color information using floodfill algorithm.
3822            */
3823            (void) GetOneVirtualPixelInfo(*image,
3824              GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
3825              y_offset,&target,exception);
3826            if (method == FillToBorderMethod)
3827              {
3828                target.red=(double)
3829                  ScaleShortToQuantum(border_color.red);
3830                target.green=(double)
3831                  ScaleShortToQuantum(border_color.green);
3832                target.blue=(double)
3833                  ScaleShortToQuantum(border_color.blue);
3834              }
3835            draw_info=CloneDrawInfo(resource_info->image_info,
3836              (DrawInfo *) NULL);
3837            (void) QueryColorCompliance(resource_info->pen_colors[pen_id],
3838              AllCompliance,&draw_info->fill,exception);
3839            (void) FloodfillPaintImage(*image,draw_info,&target,
3840              (ssize_t)x_offset,(ssize_t)y_offset,
3841              IsMagickFalse(method == FloodfillMethod),exception);
3842            draw_info=DestroyDrawInfo(draw_info);
3843            break;
3844          }
3845          case ResetMethod:
3846          {
3847            /*
3848              Update color information using reset algorithm.
3849            */
3850            if( IfMagickFalse(SetImageStorageClass(*image,DirectClass,exception)) )
3851              return(MagickFalse);
3852            for (y=0; y < (int) (*image)->rows; y++)
3853            {
3854              q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3855                (*image)->columns,1,exception);
3856              if (q == (Quantum *) NULL)
3857                break;
3858              for (x=0; x < (int) (*image)->columns; x++)
3859              {
3860                SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3861                SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3862                SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3863                q+=GetPixelChannels(*image);
3864              }
3865              if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
3866                break;
3867            }
3868            break;
3869          }
3870        }
3871        image_view=DestroyCacheView(image_view);
3872        state&=(~UpdateConfigurationState);
3873      }
3874  } while ((state & ExitState) == 0);
3875  (void) XSelectInput(display,windows->image.id,
3876    windows->image.attributes.event_mask);
3877  XSetCursorState(display,windows,MagickFalse);
3878  (void) XFreeCursor(display,cursor);
3879  return(MagickTrue);
3880}
3881
3882/*
3883%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3884%                                                                             %
3885%                                                                             %
3886%                                                                             %
3887+   X C o m p o s i t e I m a g e                                             %
3888%                                                                             %
3889%                                                                             %
3890%                                                                             %
3891%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3892%
3893%  XCompositeImage() requests an image name from the user, reads the image and
3894%  composites it with the X window image at a location the user chooses with
3895%  the pointer.
3896%
3897%  The format of the XCompositeImage method is:
3898%
3899%      MagickBooleanType XCompositeImage(Display *display,
3900%        XResourceInfo *resource_info,XWindows *windows,Image *image,
3901%        ExceptionInfo *exception)
3902%
3903%  A description of each parameter follows:
3904%
3905%    o display: Specifies a connection to an X server;  returned from
3906%      XOpenDisplay.
3907%
3908%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3909%
3910%    o windows: Specifies a pointer to a XWindows structure.
3911%
3912%    o image: the image; returned from ReadImage.
3913%
3914%    o exception: return any errors or warnings in this structure.
3915%
3916*/
3917static MagickBooleanType XCompositeImage(Display *display,
3918  XResourceInfo *resource_info,XWindows *windows,Image *image,
3919  ExceptionInfo *exception)
3920{
3921  static char
3922    displacement_geometry[MaxTextExtent] = "30x30",
3923    filename[MaxTextExtent] = "\0";
3924
3925  static const char
3926    *CompositeMenu[] =
3927    {
3928      "Operators",
3929      "Dissolve",
3930      "Displace",
3931      "Help",
3932      "Dismiss",
3933      (char *) NULL
3934    };
3935
3936  static CompositeOperator
3937    compose = CopyCompositeOp;
3938
3939  static const ModeType
3940    CompositeCommands[] =
3941    {
3942      CompositeOperatorsCommand,
3943      CompositeDissolveCommand,
3944      CompositeDisplaceCommand,
3945      CompositeHelpCommand,
3946      CompositeDismissCommand
3947    };
3948
3949  char
3950    text[MaxTextExtent];
3951
3952  Cursor
3953    cursor;
3954
3955  Image
3956    *composite_image;
3957
3958  int
3959    entry,
3960    id,
3961    x,
3962    y;
3963
3964  double
3965    blend,
3966    scale_factor;
3967
3968  RectangleInfo
3969    highlight_info,
3970    composite_info;
3971
3972  unsigned int
3973    height,
3974    width;
3975
3976  size_t
3977    state;
3978
3979  XEvent
3980    event;
3981
3982  /*
3983    Request image file name from user.
3984  */
3985  XFileBrowserWidget(display,windows,"Composite",filename);
3986  if (*filename == '\0')
3987    return(MagickTrue);
3988  /*
3989    Read image.
3990  */
3991  XSetCursorState(display,windows,MagickTrue);
3992  XCheckRefreshWindows(display,windows);
3993  (void) CopyMagickString(resource_info->image_info->filename,filename,
3994    MaxTextExtent);
3995  composite_image=ReadImage(resource_info->image_info,exception);
3996  CatchException(exception);
3997  XSetCursorState(display,windows,MagickFalse);
3998  if (composite_image == (Image *) NULL)
3999    return(MagickFalse);
4000  /*
4001    Map Command widget.
4002  */
4003  (void) CloneString(&windows->command.name,"Composite");
4004  windows->command.data=1;
4005  (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
4006  (void) XMapRaised(display,windows->command.id);
4007  XClientMessage(display,windows->image.id,windows->im_protocols,
4008    windows->im_update_widget,CurrentTime);
4009  /*
4010    Track pointer until button 1 is pressed.
4011  */
4012  XQueryPosition(display,windows->image.id,&x,&y);
4013  (void) XSelectInput(display,windows->image.id,
4014    windows->image.attributes.event_mask | PointerMotionMask);
4015  composite_info.x=(ssize_t) windows->image.x+x;
4016  composite_info.y=(ssize_t) windows->image.y+y;
4017  composite_info.width=0;
4018  composite_info.height=0;
4019  cursor=XCreateFontCursor(display,XC_ul_angle);
4020  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4021  blend=0.0;
4022  state=DefaultState;
4023  do
4024  {
4025    if( IfMagickTrue(windows->info.mapped) )
4026      {
4027        /*
4028          Display pointer position.
4029        */
4030        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4031          (long) composite_info.x,(long) composite_info.y);
4032        XInfoWidget(display,windows,text);
4033      }
4034    highlight_info=composite_info;
4035    highlight_info.x=composite_info.x-windows->image.x;
4036    highlight_info.y=composite_info.y-windows->image.y;
4037    XHighlightRectangle(display,windows->image.id,
4038      windows->image.highlight_context,&highlight_info);
4039    /*
4040      Wait for next event.
4041    */
4042    XScreenEvent(display,windows,&event,exception);
4043    XHighlightRectangle(display,windows->image.id,
4044      windows->image.highlight_context,&highlight_info);
4045    if (event.xany.window == windows->command.id)
4046      {
4047        /*
4048          Select a command from the Command widget.
4049        */
4050        id=XCommandWidget(display,windows,CompositeMenu,&event);
4051        if (id < 0)
4052          continue;
4053        switch (CompositeCommands[id])
4054        {
4055          case CompositeOperatorsCommand:
4056          {
4057            char
4058              command[MaxTextExtent],
4059              **operators;
4060
4061            /*
4062              Select a command from the pop-up menu.
4063            */
4064            operators=GetCommandOptions(MagickComposeOptions);
4065            if (operators == (char **) NULL)
4066              break;
4067            entry=XMenuWidget(display,windows,CompositeMenu[id],
4068              (const char **) operators,command);
4069            if (entry >= 0)
4070              compose=(CompositeOperator) ParseCommandOption(
4071                MagickComposeOptions,MagickFalse,operators[entry]);
4072            operators=DestroyStringList(operators);
4073            break;
4074          }
4075          case CompositeDissolveCommand:
4076          {
4077            static char
4078              factor[MaxTextExtent] = "20.0";
4079
4080            /*
4081              Dissolve the two images a given percent.
4082            */
4083            (void) XSetFunction(display,windows->image.highlight_context,
4084              GXcopy);
4085            (void) XDialogWidget(display,windows,"Dissolve",
4086              "Enter the blend factor (0.0 - 99.9%):",factor);
4087            (void) XSetFunction(display,windows->image.highlight_context,
4088              GXinvert);
4089            if (*factor == '\0')
4090              break;
4091            blend=StringToDouble(factor,(char **) NULL);
4092            compose=DissolveCompositeOp;
4093            break;
4094          }
4095          case CompositeDisplaceCommand:
4096          {
4097            /*
4098              Get horizontal and vertical scale displacement geometry.
4099            */
4100            (void) XSetFunction(display,windows->image.highlight_context,
4101              GXcopy);
4102            (void) XDialogWidget(display,windows,"Displace",
4103              "Enter the horizontal and vertical scale:",displacement_geometry);
4104            (void) XSetFunction(display,windows->image.highlight_context,
4105              GXinvert);
4106            if (*displacement_geometry == '\0')
4107              break;
4108            compose=DisplaceCompositeOp;
4109            break;
4110          }
4111          case CompositeHelpCommand:
4112          {
4113            (void) XSetFunction(display,windows->image.highlight_context,
4114              GXcopy);
4115            XTextViewWidget(display,resource_info,windows,MagickFalse,
4116              "Help Viewer - Image Composite",ImageCompositeHelp);
4117            (void) XSetFunction(display,windows->image.highlight_context,
4118              GXinvert);
4119            break;
4120          }
4121          case CompositeDismissCommand:
4122          {
4123            /*
4124              Prematurely exit.
4125            */
4126            state|=EscapeState;
4127            state|=ExitState;
4128            break;
4129          }
4130          default:
4131            break;
4132        }
4133        continue;
4134      }
4135    switch (event.type)
4136    {
4137      case ButtonPress:
4138      {
4139        if( IfMagickTrue(image->debug) )
4140          (void) LogMagickEvent(X11Event,GetMagickModule(),
4141            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4142            event.xbutton.button,event.xbutton.x,event.xbutton.y);
4143        if (event.xbutton.button != Button1)
4144          break;
4145        if (event.xbutton.window != windows->image.id)
4146          break;
4147        /*
4148          Change cursor.
4149        */
4150        composite_info.width=composite_image->columns;
4151        composite_info.height=composite_image->rows;
4152        (void) XCheckDefineCursor(display,windows->image.id,cursor);
4153        composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4154        composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4155        break;
4156      }
4157      case ButtonRelease:
4158      {
4159        if( IfMagickTrue(image->debug) )
4160          (void) LogMagickEvent(X11Event,GetMagickModule(),
4161            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4162            event.xbutton.button,event.xbutton.x,event.xbutton.y);
4163        if (event.xbutton.button != Button1)
4164          break;
4165        if (event.xbutton.window != windows->image.id)
4166          break;
4167        if ((composite_info.width != 0) && (composite_info.height != 0))
4168          {
4169            /*
4170              User has selected the location of the composite image.
4171            */
4172            composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4173            composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4174            state|=ExitState;
4175          }
4176        break;
4177      }
4178      case Expose:
4179        break;
4180      case KeyPress:
4181      {
4182        char
4183          command[MaxTextExtent];
4184
4185        KeySym
4186          key_symbol;
4187
4188        int
4189          length;
4190
4191        if (event.xkey.window != windows->image.id)
4192          break;
4193        /*
4194          Respond to a user key press.
4195        */
4196        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4197          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4198        *(command+length)='\0';
4199        if( IfMagickTrue(image->debug) )
4200          (void) LogMagickEvent(X11Event,GetMagickModule(),
4201            "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4202        switch ((int) key_symbol)
4203        {
4204          case XK_Escape:
4205          case XK_F20:
4206          {
4207            /*
4208              Prematurely exit.
4209            */
4210            composite_image=DestroyImage(composite_image);
4211            state|=EscapeState;
4212            state|=ExitState;
4213            break;
4214          }
4215          case XK_F1:
4216          case XK_Help:
4217          {
4218            (void) XSetFunction(display,windows->image.highlight_context,
4219              GXcopy);
4220            XTextViewWidget(display,resource_info,windows,MagickFalse,
4221              "Help Viewer - Image Composite",ImageCompositeHelp);
4222            (void) XSetFunction(display,windows->image.highlight_context,
4223              GXinvert);
4224            break;
4225          }
4226          default:
4227          {
4228            (void) XBell(display,0);
4229            break;
4230          }
4231        }
4232        break;
4233      }
4234      case MotionNotify:
4235      {
4236        /*
4237          Map and unmap Info widget as text cursor crosses its boundaries.
4238        */
4239        x=event.xmotion.x;
4240        y=event.xmotion.y;
4241        if( IfMagickTrue(windows->info.mapped) )
4242          {
4243            if ((x < (int) (windows->info.x+windows->info.width)) &&
4244                (y < (int) (windows->info.y+windows->info.height)))
4245              (void) XWithdrawWindow(display,windows->info.id,
4246                windows->info.screen);
4247          }
4248        else
4249          if ((x > (int) (windows->info.x+windows->info.width)) ||
4250              (y > (int) (windows->info.y+windows->info.height)))
4251            (void) XMapWindow(display,windows->info.id);
4252        composite_info.x=(ssize_t) windows->image.x+x;
4253        composite_info.y=(ssize_t) windows->image.y+y;
4254        break;
4255      }
4256      default:
4257      {
4258        if( IfMagickTrue(image->debug) )
4259          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4260            event.type);
4261        break;
4262      }
4263    }
4264  } while ((state & ExitState) == 0);
4265  (void) XSelectInput(display,windows->image.id,
4266    windows->image.attributes.event_mask);
4267  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4268  XSetCursorState(display,windows,MagickFalse);
4269  (void) XFreeCursor(display,cursor);
4270  if ((state & EscapeState) != 0)
4271    return(MagickTrue);
4272  /*
4273    Image compositing is relative to image configuration.
4274  */
4275  XSetCursorState(display,windows,MagickTrue);
4276  XCheckRefreshWindows(display,windows);
4277  width=(unsigned int) image->columns;
4278  height=(unsigned int) image->rows;
4279  x=0;
4280  y=0;
4281  if (windows->image.crop_geometry != (char *) NULL)
4282    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4283  scale_factor=(double) width/windows->image.ximage->width;
4284  composite_info.x+=x;
4285  composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4286  composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4287  scale_factor=(double) height/windows->image.ximage->height;
4288  composite_info.y+=y;
4289  composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4290  composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4291  if ((composite_info.width != composite_image->columns) ||
4292      (composite_info.height != composite_image->rows))
4293    {
4294      Image
4295        *resize_image;
4296
4297      /*
4298        Scale composite image.
4299      */
4300      resize_image=ResizeImage(composite_image,composite_info.width,
4301        composite_info.height,composite_image->filter,exception);
4302      composite_image=DestroyImage(composite_image);
4303      if (resize_image == (Image *) NULL)
4304        {
4305          XSetCursorState(display,windows,MagickFalse);
4306          return(MagickFalse);
4307        }
4308      composite_image=resize_image;
4309    }
4310  if (compose == DisplaceCompositeOp)
4311    (void) SetImageArtifact(composite_image,"compose:args",
4312      displacement_geometry);
4313  if (blend != 0.0)
4314    {
4315      CacheView
4316        *image_view;
4317
4318      int
4319        y;
4320
4321      Quantum
4322        opacity;
4323
4324      register int
4325        x;
4326
4327      register Quantum
4328        *q;
4329
4330      /*
4331        Create mattes for blending.
4332      */
4333      (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,exception);
4334      opacity=(Quantum) (ScaleQuantumToChar(QuantumRange)-
4335        ((ssize_t) ScaleQuantumToChar(QuantumRange)*blend)/100);
4336      if( IfMagickFalse(SetImageStorageClass(image,DirectClass,exception)) )
4337        return(MagickFalse);
4338      image->alpha_trait=BlendPixelTrait;
4339      image_view=AcquireAuthenticCacheView(image,exception);
4340      for (y=0; y < (int) image->rows; y++)
4341      {
4342        q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4343          exception);
4344        if (q == (Quantum *) NULL)
4345          break;
4346        for (x=0; x < (int) image->columns; x++)
4347        {
4348          SetPixelAlpha(image,opacity,q);
4349          q+=GetPixelChannels(image);
4350        }
4351        if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
4352          break;
4353      }
4354      image_view=DestroyCacheView(image_view);
4355    }
4356  /*
4357    Composite image with X Image window.
4358  */
4359  (void) CompositeImage(image,composite_image,compose,MagickTrue,
4360    composite_info.x,composite_info.y,exception);
4361  composite_image=DestroyImage(composite_image);
4362  XSetCursorState(display,windows,MagickFalse);
4363  /*
4364    Update image configuration.
4365  */
4366  XConfigureImageColormap(display,resource_info,windows,image,exception);
4367  (void) XConfigureImage(display,resource_info,windows,image,exception);
4368  return(MagickTrue);
4369}
4370
4371/*
4372%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4373%                                                                             %
4374%                                                                             %
4375%                                                                             %
4376+   X C o n f i g u r e I m a g e                                             %
4377%                                                                             %
4378%                                                                             %
4379%                                                                             %
4380%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4381%
4382%  XConfigureImage() creates a new X image.  It also notifies the window
4383%  manager of the new image size and configures the transient widows.
4384%
4385%  The format of the XConfigureImage method is:
4386%
4387%      MagickBooleanType XConfigureImage(Display *display,
4388%        XResourceInfo *resource_info,XWindows *windows,Image *image,
4389%        ExceptionInfo *exception)
4390%
4391%  A description of each parameter follows:
4392%
4393%    o display: Specifies a connection to an X server; returned from
4394%      XOpenDisplay.
4395%
4396%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4397%
4398%    o windows: Specifies a pointer to a XWindows structure.
4399%
4400%    o image: the image.
4401%
4402%    o exception: return any errors or warnings in this structure.
4403%
4404%    o exception: return any errors or warnings in this structure.
4405%
4406*/
4407static MagickBooleanType XConfigureImage(Display *display,
4408  XResourceInfo *resource_info,XWindows *windows,Image *image,
4409  ExceptionInfo *exception)
4410{
4411  char
4412    geometry[MaxTextExtent];
4413
4414  MagickStatusType
4415    status;
4416
4417  size_t
4418    mask,
4419    height,
4420    width;
4421
4422  ssize_t
4423    x,
4424    y;
4425
4426  XSizeHints
4427    *size_hints;
4428
4429  XWindowChanges
4430    window_changes;
4431
4432  /*
4433    Dismiss if window dimensions are zero.
4434  */
4435  width=(unsigned int) windows->image.window_changes.width;
4436  height=(unsigned int) windows->image.window_changes.height;
4437  if( IfMagickTrue(image->debug) )
4438    (void) LogMagickEvent(X11Event,GetMagickModule(),
4439      "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4440      windows->image.ximage->height,(double) width,(double) height);
4441  if ((width*height) == 0)
4442    return(MagickTrue);
4443  x=0;
4444  y=0;
4445  /*
4446    Resize image to fit Image window dimensions.
4447  */
4448  XSetCursorState(display,windows,MagickTrue);
4449  (void) XFlush(display);
4450  if (((int) width != windows->image.ximage->width) ||
4451      ((int) height != windows->image.ximage->height))
4452    image->taint=MagickTrue;
4453  windows->magnify.x=(int)
4454    width*windows->magnify.x/windows->image.ximage->width;
4455  windows->magnify.y=(int)
4456    height*windows->magnify.y/windows->image.ximage->height;
4457  windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4458  windows->image.y=(int)
4459    (height*windows->image.y/windows->image.ximage->height);
4460  status=XMakeImage(display,resource_info,&windows->image,image,
4461    (unsigned int) width,(unsigned int) height,exception);
4462  if( IfMagickFalse(status) )
4463    XNoticeWidget(display,windows,"Unable to configure X image:",
4464      windows->image.name);
4465  /*
4466    Notify window manager of the new configuration.
4467  */
4468  if (resource_info->image_geometry != (char *) NULL)
4469    (void) FormatLocaleString(geometry,MaxTextExtent,"%s>!",
4470      resource_info->image_geometry);
4471  else
4472    (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
4473      XDisplayWidth(display,windows->image.screen),
4474      XDisplayHeight(display,windows->image.screen));
4475  (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4476  window_changes.width=(int) width;
4477  if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4478    window_changes.width=XDisplayWidth(display,windows->image.screen);
4479  window_changes.height=(int) height;
4480  if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4481    window_changes.height=XDisplayHeight(display,windows->image.screen);
4482  mask=(size_t) (CWWidth | CWHeight);
4483  if (resource_info->backdrop)
4484    {
4485      mask|=CWX | CWY;
4486      window_changes.x=(int)
4487        ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4488      window_changes.y=(int)
4489        ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4490    }
4491  (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4492    (unsigned int) mask,&window_changes);
4493  (void) XClearWindow(display,windows->image.id);
4494  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4495  /*
4496    Update Magnify window configuration.
4497  */
4498  if( IfMagickTrue(windows->magnify.mapped) )
4499    XMakeMagnifyImage(display,windows,exception);
4500  windows->pan.crop_geometry=windows->image.crop_geometry;
4501  XBestIconSize(display,&windows->pan,image);
4502  while (((windows->pan.width << 1) < MaxIconSize) &&
4503         ((windows->pan.height << 1) < MaxIconSize))
4504  {
4505    windows->pan.width<<=1;
4506    windows->pan.height<<=1;
4507  }
4508  if (windows->pan.geometry != (char *) NULL)
4509    (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4510      &windows->pan.width,&windows->pan.height);
4511  window_changes.width=(int) windows->pan.width;
4512  window_changes.height=(int) windows->pan.height;
4513  size_hints=XAllocSizeHints();
4514  if (size_hints != (XSizeHints *) NULL)
4515    {
4516      /*
4517        Set new size hints.
4518      */
4519      size_hints->flags=PSize | PMinSize | PMaxSize;
4520      size_hints->width=window_changes.width;
4521      size_hints->height=window_changes.height;
4522      size_hints->min_width=size_hints->width;
4523      size_hints->min_height=size_hints->height;
4524      size_hints->max_width=size_hints->width;
4525      size_hints->max_height=size_hints->height;
4526      (void) XSetNormalHints(display,windows->pan.id,size_hints);
4527      (void) XFree((void *) size_hints);
4528    }
4529  (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4530    (unsigned int) (CWWidth | CWHeight),&window_changes);
4531  /*
4532    Update icon window configuration.
4533  */
4534  windows->icon.crop_geometry=windows->image.crop_geometry;
4535  XBestIconSize(display,&windows->icon,image);
4536  window_changes.width=(int) windows->icon.width;
4537  window_changes.height=(int) windows->icon.height;
4538  (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4539    (unsigned int) (CWWidth | CWHeight),&window_changes);
4540  XSetCursorState(display,windows,MagickFalse);
4541  return(IsMagickTrue(status));
4542}
4543
4544/*
4545%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4546%                                                                             %
4547%                                                                             %
4548%                                                                             %
4549+   X C r o p I m a g e                                                       %
4550%                                                                             %
4551%                                                                             %
4552%                                                                             %
4553%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4554%
4555%  XCropImage() allows the user to select a region of the image and crop, copy,
4556%  or cut it.  For copy or cut, the image can subsequently be composited onto
4557%  the image with XPasteImage.
4558%
4559%  The format of the XCropImage method is:
4560%
4561%      MagickBooleanType XCropImage(Display *display,
4562%        XResourceInfo *resource_info,XWindows *windows,Image *image,
4563%        const ClipboardMode mode,ExceptionInfo *exception)
4564%
4565%  A description of each parameter follows:
4566%
4567%    o display: Specifies a connection to an X server; returned from
4568%      XOpenDisplay.
4569%
4570%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4571%
4572%    o windows: Specifies a pointer to a XWindows structure.
4573%
4574%    o image: the image; returned from ReadImage.
4575%
4576%    o mode: This unsigned value specified whether the image should be
4577%      cropped, copied, or cut.
4578%
4579%    o exception: return any errors or warnings in this structure.
4580%
4581*/
4582static MagickBooleanType XCropImage(Display *display,
4583  XResourceInfo *resource_info,XWindows *windows,Image *image,
4584  const ClipboardMode mode,ExceptionInfo *exception)
4585{
4586  static const char
4587    *CropModeMenu[] =
4588    {
4589      "Help",
4590      "Dismiss",
4591      (char *) NULL
4592    },
4593    *RectifyModeMenu[] =
4594    {
4595      "Crop",
4596      "Help",
4597      "Dismiss",
4598      (char *) NULL
4599    };
4600
4601  static const ModeType
4602    CropCommands[] =
4603    {
4604      CropHelpCommand,
4605      CropDismissCommand
4606    },
4607    RectifyCommands[] =
4608    {
4609      RectifyCopyCommand,
4610      RectifyHelpCommand,
4611      RectifyDismissCommand
4612    };
4613
4614  CacheView
4615    *image_view;
4616
4617  char
4618    command[MaxTextExtent],
4619    text[MaxTextExtent];
4620
4621  Cursor
4622    cursor;
4623
4624  int
4625    id,
4626    x,
4627    y;
4628
4629  KeySym
4630    key_symbol;
4631
4632  Image
4633    *crop_image;
4634
4635  double
4636    scale_factor;
4637
4638  RectangleInfo
4639    crop_info,
4640    highlight_info;
4641
4642  register Quantum
4643    *q;
4644
4645  unsigned int
4646    height,
4647    width;
4648
4649  size_t
4650    state;
4651
4652  XEvent
4653    event;
4654
4655  /*
4656    Map Command widget.
4657  */
4658  switch (mode)
4659  {
4660    case CopyMode:
4661    {
4662      (void) CloneString(&windows->command.name,"Copy");
4663      break;
4664    }
4665    case CropMode:
4666    {
4667      (void) CloneString(&windows->command.name,"Crop");
4668      break;
4669    }
4670    case CutMode:
4671    {
4672      (void) CloneString(&windows->command.name,"Cut");
4673      break;
4674    }
4675  }
4676  RectifyModeMenu[0]=windows->command.name;
4677  windows->command.data=0;
4678  (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4679  (void) XMapRaised(display,windows->command.id);
4680  XClientMessage(display,windows->image.id,windows->im_protocols,
4681    windows->im_update_widget,CurrentTime);
4682  /*
4683    Track pointer until button 1 is pressed.
4684  */
4685  XQueryPosition(display,windows->image.id,&x,&y);
4686  (void) XSelectInput(display,windows->image.id,
4687    windows->image.attributes.event_mask | PointerMotionMask);
4688  crop_info.x=(ssize_t) windows->image.x+x;
4689  crop_info.y=(ssize_t) windows->image.y+y;
4690  crop_info.width=0;
4691  crop_info.height=0;
4692  cursor=XCreateFontCursor(display,XC_fleur);
4693  state=DefaultState;
4694  do
4695  {
4696    if( IfMagickTrue(windows->info.mapped) )
4697      {
4698        /*
4699          Display pointer position.
4700        */
4701        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4702          (long) crop_info.x,(long) crop_info.y);
4703        XInfoWidget(display,windows,text);
4704      }
4705    /*
4706      Wait for next event.
4707    */
4708    XScreenEvent(display,windows,&event,exception);
4709    if (event.xany.window == windows->command.id)
4710      {
4711        /*
4712          Select a command from the Command widget.
4713        */
4714        id=XCommandWidget(display,windows,CropModeMenu,&event);
4715        if (id < 0)
4716          continue;
4717        switch (CropCommands[id])
4718        {
4719          case CropHelpCommand:
4720          {
4721            switch (mode)
4722            {
4723              case CopyMode:
4724              {
4725                XTextViewWidget(display,resource_info,windows,MagickFalse,
4726                  "Help Viewer - Image Copy",ImageCopyHelp);
4727                break;
4728              }
4729              case CropMode:
4730              {
4731                XTextViewWidget(display,resource_info,windows,MagickFalse,
4732                  "Help Viewer - Image Crop",ImageCropHelp);
4733                break;
4734              }
4735              case CutMode:
4736              {
4737                XTextViewWidget(display,resource_info,windows,MagickFalse,
4738                  "Help Viewer - Image Cut",ImageCutHelp);
4739                break;
4740              }
4741            }
4742            break;
4743          }
4744          case CropDismissCommand:
4745          {
4746            /*
4747              Prematurely exit.
4748            */
4749            state|=EscapeState;
4750            state|=ExitState;
4751            break;
4752          }
4753          default:
4754            break;
4755        }
4756        continue;
4757      }
4758    switch (event.type)
4759    {
4760      case ButtonPress:
4761      {
4762        if (event.xbutton.button != Button1)
4763          break;
4764        if (event.xbutton.window != windows->image.id)
4765          break;
4766        /*
4767          Note first corner of cropping rectangle-- exit loop.
4768        */
4769        (void) XCheckDefineCursor(display,windows->image.id,cursor);
4770        crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4771        crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4772        state|=ExitState;
4773        break;
4774      }
4775      case ButtonRelease:
4776        break;
4777      case Expose:
4778        break;
4779      case KeyPress:
4780      {
4781        if (event.xkey.window != windows->image.id)
4782          break;
4783        /*
4784          Respond to a user key press.
4785        */
4786        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4787          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4788        switch ((int) key_symbol)
4789        {
4790          case XK_Escape:
4791          case XK_F20:
4792          {
4793            /*
4794              Prematurely exit.
4795            */
4796            state|=EscapeState;
4797            state|=ExitState;
4798            break;
4799          }
4800          case XK_F1:
4801          case XK_Help:
4802          {
4803            switch (mode)
4804            {
4805              case CopyMode:
4806              {
4807                XTextViewWidget(display,resource_info,windows,MagickFalse,
4808                  "Help Viewer - Image Copy",ImageCopyHelp);
4809                break;
4810              }
4811              case CropMode:
4812              {
4813                XTextViewWidget(display,resource_info,windows,MagickFalse,
4814                  "Help Viewer - Image Crop",ImageCropHelp);
4815                break;
4816              }
4817              case CutMode:
4818              {
4819                XTextViewWidget(display,resource_info,windows,MagickFalse,
4820                  "Help Viewer - Image Cut",ImageCutHelp);
4821                break;
4822              }
4823            }
4824            break;
4825          }
4826          default:
4827          {
4828            (void) XBell(display,0);
4829            break;
4830          }
4831        }
4832        break;
4833      }
4834      case MotionNotify:
4835      {
4836        if (event.xmotion.window != windows->image.id)
4837          break;
4838        /*
4839          Map and unmap Info widget as text cursor crosses its boundaries.
4840        */
4841        x=event.xmotion.x;
4842        y=event.xmotion.y;
4843        if( IfMagickTrue(windows->info.mapped) )
4844          {
4845            if ((x < (int) (windows->info.x+windows->info.width)) &&
4846                (y < (int) (windows->info.y+windows->info.height)))
4847              (void) XWithdrawWindow(display,windows->info.id,
4848                windows->info.screen);
4849          }
4850        else
4851          if ((x > (int) (windows->info.x+windows->info.width)) ||
4852              (y > (int) (windows->info.y+windows->info.height)))
4853            (void) XMapWindow(display,windows->info.id);
4854        crop_info.x=(ssize_t) windows->image.x+x;
4855        crop_info.y=(ssize_t) windows->image.y+y;
4856        break;
4857      }
4858      default:
4859        break;
4860    }
4861  } while ((state & ExitState) == 0);
4862  (void) XSelectInput(display,windows->image.id,
4863    windows->image.attributes.event_mask);
4864  if ((state & EscapeState) != 0)
4865    {
4866      /*
4867        User want to exit without cropping.
4868      */
4869      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4870      (void) XFreeCursor(display,cursor);
4871      return(MagickTrue);
4872    }
4873  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4874  do
4875  {
4876    /*
4877      Size rectangle as pointer moves until the mouse button is released.
4878    */
4879    x=(int) crop_info.x;
4880    y=(int) crop_info.y;
4881    crop_info.width=0;
4882    crop_info.height=0;
4883    state=DefaultState;
4884    do
4885    {
4886      highlight_info=crop_info;
4887      highlight_info.x=crop_info.x-windows->image.x;
4888      highlight_info.y=crop_info.y-windows->image.y;
4889      if ((highlight_info.width > 3) && (highlight_info.height > 3))
4890        {
4891          /*
4892            Display info and draw cropping rectangle.
4893          */
4894          if( IfMagickFalse(windows->info.mapped) )
4895            (void) XMapWindow(display,windows->info.id);
4896          (void) FormatLocaleString(text,MaxTextExtent,
4897            " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4898            crop_info.height,(double) crop_info.x,(double) crop_info.y);
4899          XInfoWidget(display,windows,text);
4900          XHighlightRectangle(display,windows->image.id,
4901            windows->image.highlight_context,&highlight_info);
4902        }
4903      else
4904        if( IfMagickTrue(windows->info.mapped) )
4905          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4906      /*
4907        Wait for next event.
4908      */
4909      XScreenEvent(display,windows,&event,exception);
4910      if ((highlight_info.width > 3) && (highlight_info.height > 3))
4911        XHighlightRectangle(display,windows->image.id,
4912          windows->image.highlight_context,&highlight_info);
4913      switch (event.type)
4914      {
4915        case ButtonPress:
4916        {
4917          crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4918          crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4919          break;
4920        }
4921        case ButtonRelease:
4922        {
4923          /*
4924            User has committed to cropping rectangle.
4925          */
4926          crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4927          crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4928          XSetCursorState(display,windows,MagickFalse);
4929          state|=ExitState;
4930          windows->command.data=0;
4931          (void) XCommandWidget(display,windows,RectifyModeMenu,
4932            (XEvent *) NULL);
4933          break;
4934        }
4935        case Expose:
4936          break;
4937        case MotionNotify:
4938        {
4939          crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4940          crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4941        }
4942        default:
4943          break;
4944      }
4945      if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4946          ((state & ExitState) != 0))
4947        {
4948          /*
4949            Check boundary conditions.
4950          */
4951          if (crop_info.x < 0)
4952            crop_info.x=0;
4953          else
4954            if (crop_info.x > (ssize_t) windows->image.ximage->width)
4955              crop_info.x=(ssize_t) windows->image.ximage->width;
4956          if ((int) crop_info.x < x)
4957            crop_info.width=(unsigned int) (x-crop_info.x);
4958          else
4959            {
4960              crop_info.width=(unsigned int) (crop_info.x-x);
4961              crop_info.x=(ssize_t) x;
4962            }
4963          if (crop_info.y < 0)
4964            crop_info.y=0;
4965          else
4966            if (crop_info.y > (ssize_t) windows->image.ximage->height)
4967              crop_info.y=(ssize_t) windows->image.ximage->height;
4968          if ((int) crop_info.y < y)
4969            crop_info.height=(unsigned int) (y-crop_info.y);
4970          else
4971            {
4972              crop_info.height=(unsigned int) (crop_info.y-y);
4973              crop_info.y=(ssize_t) y;
4974            }
4975        }
4976    } while ((state & ExitState) == 0);
4977    /*
4978      Wait for user to grab a corner of the rectangle or press return.
4979    */
4980    state=DefaultState;
4981    (void) XMapWindow(display,windows->info.id);
4982    do
4983    {
4984      if( IfMagickTrue(windows->info.mapped) )
4985        {
4986          /*
4987            Display pointer position.
4988          */
4989          (void) FormatLocaleString(text,MaxTextExtent,
4990            " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4991            crop_info.height,(double) crop_info.x,(double) crop_info.y);
4992          XInfoWidget(display,windows,text);
4993        }
4994      highlight_info=crop_info;
4995      highlight_info.x=crop_info.x-windows->image.x;
4996      highlight_info.y=crop_info.y-windows->image.y;
4997      if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4998        {
4999          state|=EscapeState;
5000          state|=ExitState;
5001          break;
5002        }
5003      XHighlightRectangle(display,windows->image.id,
5004        windows->image.highlight_context,&highlight_info);
5005      XScreenEvent(display,windows,&event,exception);
5006      if (event.xany.window == windows->command.id)
5007        {
5008          /*
5009            Select a command from the Command widget.
5010          */
5011          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5012          id=XCommandWidget(display,windows,RectifyModeMenu,&event);
5013          (void) XSetFunction(display,windows->image.highlight_context,
5014            GXinvert);
5015          XHighlightRectangle(display,windows->image.id,
5016            windows->image.highlight_context,&highlight_info);
5017          if (id >= 0)
5018            switch (RectifyCommands[id])
5019            {
5020              case RectifyCopyCommand:
5021              {
5022                state|=ExitState;
5023                break;
5024              }
5025              case RectifyHelpCommand:
5026              {
5027                (void) XSetFunction(display,windows->image.highlight_context,
5028                  GXcopy);
5029                switch (mode)
5030                {
5031                  case CopyMode:
5032                  {
5033                    XTextViewWidget(display,resource_info,windows,MagickFalse,
5034                      "Help Viewer - Image Copy",ImageCopyHelp);
5035                    break;
5036                  }
5037                  case CropMode:
5038                  {
5039                    XTextViewWidget(display,resource_info,windows,MagickFalse,
5040                      "Help Viewer - Image Crop",ImageCropHelp);
5041                    break;
5042                  }
5043                  case CutMode:
5044                  {
5045                    XTextViewWidget(display,resource_info,windows,MagickFalse,
5046                      "Help Viewer - Image Cut",ImageCutHelp);
5047                    break;
5048                  }
5049                }
5050                (void) XSetFunction(display,windows->image.highlight_context,
5051                  GXinvert);
5052                break;
5053              }
5054              case RectifyDismissCommand:
5055              {
5056                /*
5057                  Prematurely exit.
5058                */
5059                state|=EscapeState;
5060                state|=ExitState;
5061                break;
5062              }
5063              default:
5064                break;
5065            }
5066          continue;
5067        }
5068      XHighlightRectangle(display,windows->image.id,
5069        windows->image.highlight_context,&highlight_info);
5070      switch (event.type)
5071      {
5072        case ButtonPress:
5073        {
5074          if (event.xbutton.button != Button1)
5075            break;
5076          if (event.xbutton.window != windows->image.id)
5077            break;
5078          x=windows->image.x+event.xbutton.x;
5079          y=windows->image.y+event.xbutton.y;
5080          if ((x < (int) (crop_info.x+RoiDelta)) &&
5081              (x > (int) (crop_info.x-RoiDelta)) &&
5082              (y < (int) (crop_info.y+RoiDelta)) &&
5083              (y > (int) (crop_info.y-RoiDelta)))
5084            {
5085              crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5086              crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5087              state|=UpdateConfigurationState;
5088              break;
5089            }
5090          if ((x < (int) (crop_info.x+RoiDelta)) &&
5091              (x > (int) (crop_info.x-RoiDelta)) &&
5092              (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5093              (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5094            {
5095              crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5096              state|=UpdateConfigurationState;
5097              break;
5098            }
5099          if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5100              (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5101              (y < (int) (crop_info.y+RoiDelta)) &&
5102              (y > (int) (crop_info.y-RoiDelta)))
5103            {
5104              crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5105              state|=UpdateConfigurationState;
5106              break;
5107            }
5108          if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5109              (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5110              (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5111              (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5112            {
5113              state|=UpdateConfigurationState;
5114              break;
5115            }
5116        }
5117        case ButtonRelease:
5118        {
5119          if (event.xbutton.window == windows->pan.id)
5120            if ((highlight_info.x != crop_info.x-windows->image.x) ||
5121                (highlight_info.y != crop_info.y-windows->image.y))
5122              XHighlightRectangle(display,windows->image.id,
5123                windows->image.highlight_context,&highlight_info);
5124          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5125            event.xbutton.time);
5126          break;
5127        }
5128        case Expose:
5129        {
5130          if (event.xexpose.window == windows->image.id)
5131            if (event.xexpose.count == 0)
5132              {
5133                event.xexpose.x=(int) highlight_info.x;
5134                event.xexpose.y=(int) highlight_info.y;
5135                event.xexpose.width=(int) highlight_info.width;
5136                event.xexpose.height=(int) highlight_info.height;
5137                XRefreshWindow(display,&windows->image,&event);
5138              }
5139          if (event.xexpose.window == windows->info.id)
5140            if (event.xexpose.count == 0)
5141              XInfoWidget(display,windows,text);
5142          break;
5143        }
5144        case KeyPress:
5145        {
5146          if (event.xkey.window != windows->image.id)
5147            break;
5148          /*
5149            Respond to a user key press.
5150          */
5151          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5152            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5153          switch ((int) key_symbol)
5154          {
5155            case XK_Escape:
5156            case XK_F20:
5157              state|=EscapeState;
5158            case XK_Return:
5159            {
5160              state|=ExitState;
5161              break;
5162            }
5163            case XK_Home:
5164            case XK_KP_Home:
5165            {
5166              crop_info.x=(ssize_t) (windows->image.width/2L-
5167                crop_info.width/2L);
5168              crop_info.y=(ssize_t) (windows->image.height/2L-
5169                crop_info.height/2L);
5170              break;
5171            }
5172            case XK_Left:
5173            case XK_KP_Left:
5174            {
5175              crop_info.x--;
5176              break;
5177            }
5178            case XK_Up:
5179            case XK_KP_Up:
5180            case XK_Next:
5181            {
5182              crop_info.y--;
5183              break;
5184            }
5185            case XK_Right:
5186            case XK_KP_Right:
5187            {
5188              crop_info.x++;
5189              break;
5190            }
5191            case XK_Prior:
5192            case XK_Down:
5193            case XK_KP_Down:
5194            {
5195              crop_info.y++;
5196              break;
5197            }
5198            case XK_F1:
5199            case XK_Help:
5200            {
5201              (void) XSetFunction(display,windows->image.highlight_context,
5202                GXcopy);
5203              switch (mode)
5204              {
5205                case CopyMode:
5206                {
5207                  XTextViewWidget(display,resource_info,windows,MagickFalse,
5208                    "Help Viewer - Image Copy",ImageCopyHelp);
5209                  break;
5210                }
5211                case CropMode:
5212                {
5213                  XTextViewWidget(display,resource_info,windows,MagickFalse,
5214                    "Help Viewer - Image Cropg",ImageCropHelp);
5215                  break;
5216                }
5217                case CutMode:
5218                {
5219                  XTextViewWidget(display,resource_info,windows,MagickFalse,
5220                    "Help Viewer - Image Cutg",ImageCutHelp);
5221                  break;
5222                }
5223              }
5224              (void) XSetFunction(display,windows->image.highlight_context,
5225                GXinvert);
5226              break;
5227            }
5228            default:
5229            {
5230              (void) XBell(display,0);
5231              break;
5232            }
5233          }
5234          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5235            event.xkey.time);
5236          break;
5237        }
5238        case KeyRelease:
5239          break;
5240        case MotionNotify:
5241        {
5242          if (event.xmotion.window != windows->image.id)
5243            break;
5244          /*
5245            Map and unmap Info widget as text cursor crosses its boundaries.
5246          */
5247          x=event.xmotion.x;
5248          y=event.xmotion.y;
5249          if( IfMagickTrue(windows->info.mapped) )
5250            {
5251              if ((x < (int) (windows->info.x+windows->info.width)) &&
5252                  (y < (int) (windows->info.y+windows->info.height)))
5253                (void) XWithdrawWindow(display,windows->info.id,
5254                  windows->info.screen);
5255            }
5256          else
5257            if ((x > (int) (windows->info.x+windows->info.width)) ||
5258                (y > (int) (windows->info.y+windows->info.height)))
5259              (void) XMapWindow(display,windows->info.id);
5260          crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5261          crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5262          break;
5263        }
5264        case SelectionRequest:
5265        {
5266          XSelectionEvent
5267            notify;
5268
5269          XSelectionRequestEvent
5270            *request;
5271
5272          /*
5273            Set primary selection.
5274          */
5275          (void) FormatLocaleString(text,MaxTextExtent,
5276            "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5277            crop_info.height,(double) crop_info.x,(double) crop_info.y);
5278          request=(&(event.xselectionrequest));
5279          (void) XChangeProperty(request->display,request->requestor,
5280            request->property,request->target,8,PropModeReplace,
5281            (unsigned char *) text,(int) strlen(text));
5282          notify.type=SelectionNotify;
5283          notify.display=request->display;
5284          notify.requestor=request->requestor;
5285          notify.selection=request->selection;
5286          notify.target=request->target;
5287          notify.time=request->time;
5288          if (request->property == None)
5289            notify.property=request->target;
5290          else
5291            notify.property=request->property;
5292          (void) XSendEvent(request->display,request->requestor,False,0,
5293            (XEvent *) &notify);
5294        }
5295        default:
5296          break;
5297      }
5298      if ((state & UpdateConfigurationState) != 0)
5299        {
5300          (void) XPutBackEvent(display,&event);
5301          (void) XCheckDefineCursor(display,windows->image.id,cursor);
5302          break;
5303        }
5304    } while ((state & ExitState) == 0);
5305  } while ((state & ExitState) == 0);
5306  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5307  XSetCursorState(display,windows,MagickFalse);
5308  if ((state & EscapeState) != 0)
5309    return(MagickTrue);
5310  if (mode == CropMode)
5311    if (((int) crop_info.width != windows->image.ximage->width) ||
5312        ((int) crop_info.height != windows->image.ximage->height))
5313      {
5314        /*
5315          Reconfigure Image window as defined by cropping rectangle.
5316        */
5317        XSetCropGeometry(display,windows,&crop_info,image);
5318        windows->image.window_changes.width=(int) crop_info.width;
5319        windows->image.window_changes.height=(int) crop_info.height;
5320        (void) XConfigureImage(display,resource_info,windows,image,exception);
5321        return(MagickTrue);
5322      }
5323  /*
5324    Copy image before applying image transforms.
5325  */
5326  XSetCursorState(display,windows,MagickTrue);
5327  XCheckRefreshWindows(display,windows);
5328  width=(unsigned int) image->columns;
5329  height=(unsigned int) image->rows;
5330  x=0;
5331  y=0;
5332  if (windows->image.crop_geometry != (char *) NULL)
5333    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5334  scale_factor=(double) width/windows->image.ximage->width;
5335  crop_info.x+=x;
5336  crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5337  crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5338  scale_factor=(double) height/windows->image.ximage->height;
5339  crop_info.y+=y;
5340  crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5341  crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5342  crop_image=CropImage(image,&crop_info,exception);
5343  XSetCursorState(display,windows,MagickFalse);
5344  if (crop_image == (Image *) NULL)
5345    return(MagickFalse);
5346  if (resource_info->copy_image != (Image *) NULL)
5347    resource_info->copy_image=DestroyImage(resource_info->copy_image);
5348  resource_info->copy_image=crop_image;
5349  if (mode == CopyMode)
5350    {
5351      (void) XConfigureImage(display,resource_info,windows,image,exception);
5352      return(MagickTrue);
5353    }
5354  /*
5355    Cut image.
5356  */
5357  if( IfMagickFalse(SetImageStorageClass(image,DirectClass,exception)) )
5358    return(MagickFalse);
5359  image->alpha_trait=BlendPixelTrait;
5360  image_view=AcquireAuthenticCacheView(image,exception);
5361  for (y=0; y < (int) crop_info.height; y++)
5362  {
5363    q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5364      crop_info.width,1,exception);
5365    if (q == (Quantum *) NULL)
5366      break;
5367    for (x=0; x < (int) crop_info.width; x++)
5368    {
5369      SetPixelAlpha(image,TransparentAlpha,q);
5370      q+=GetPixelChannels(image);
5371    }
5372    if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
5373      break;
5374  }
5375  image_view=DestroyCacheView(image_view);
5376  /*
5377    Update image configuration.
5378  */
5379  XConfigureImageColormap(display,resource_info,windows,image,exception);
5380  (void) XConfigureImage(display,resource_info,windows,image,exception);
5381  return(MagickTrue);
5382}
5383
5384/*
5385%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5386%                                                                             %
5387%                                                                             %
5388%                                                                             %
5389+   X D r a w I m a g e                                                       %
5390%                                                                             %
5391%                                                                             %
5392%                                                                             %
5393%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5394%
5395%  XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5396%  the image.
5397%
5398%  The format of the XDrawEditImage method is:
5399%
5400%      MagickBooleanType XDrawEditImage(Display *display,
5401%        XResourceInfo *resource_info,XWindows *windows,Image **image,
5402%        ExceptionInfo *exception)
5403%
5404%  A description of each parameter follows:
5405%
5406%    o display: Specifies a connection to an X server; returned from
5407%      XOpenDisplay.
5408%
5409%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5410%
5411%    o windows: Specifies a pointer to a XWindows structure.
5412%
5413%    o image: the image.
5414%
5415%    o exception: return any errors or warnings in this structure.
5416%
5417*/
5418static MagickBooleanType XDrawEditImage(Display *display,
5419  XResourceInfo *resource_info,XWindows *windows,Image **image,
5420  ExceptionInfo *exception)
5421{
5422  static const char
5423    *DrawMenu[] =
5424    {
5425      "Element",
5426      "Color",
5427      "Stipple",
5428      "Width",
5429      "Undo",
5430      "Help",
5431      "Dismiss",
5432      (char *) NULL
5433    };
5434
5435  static ElementType
5436    element = PointElement;
5437
5438  static const ModeType
5439    DrawCommands[] =
5440    {
5441      DrawElementCommand,
5442      DrawColorCommand,
5443      DrawStippleCommand,
5444      DrawWidthCommand,
5445      DrawUndoCommand,
5446      DrawHelpCommand,
5447      DrawDismissCommand
5448    };
5449
5450  static Pixmap
5451    stipple = (Pixmap) NULL;
5452
5453  static unsigned int
5454    pen_id = 0,
5455    line_width = 1;
5456
5457  char
5458    command[MaxTextExtent],
5459    text[MaxTextExtent];
5460
5461  Cursor
5462    cursor;
5463
5464  int
5465    entry,
5466    id,
5467    number_coordinates,
5468    x,
5469    y;
5470
5471  double
5472    degrees;
5473
5474  MagickStatusType
5475    status;
5476
5477  RectangleInfo
5478    rectangle_info;
5479
5480  register int
5481    i;
5482
5483  unsigned int
5484    distance,
5485    height,
5486    max_coordinates,
5487    width;
5488
5489  size_t
5490    state;
5491
5492  Window
5493    root_window;
5494
5495  XDrawInfo
5496    draw_info;
5497
5498  XEvent
5499    event;
5500
5501  XPoint
5502    *coordinate_info;
5503
5504  XSegment
5505    line_info;
5506
5507  /*
5508    Allocate polygon info.
5509  */
5510  max_coordinates=2048;
5511  coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5512    sizeof(*coordinate_info));
5513  if (coordinate_info == (XPoint *) NULL)
5514    {
5515      (void) ThrowMagickException(exception,GetMagickModule(),
5516        ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5517      return(MagickFalse);
5518    }
5519  /*
5520    Map Command widget.
5521  */
5522  (void) CloneString(&windows->command.name,"Draw");
5523  windows->command.data=4;
5524  (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5525  (void) XMapRaised(display,windows->command.id);
5526  XClientMessage(display,windows->image.id,windows->im_protocols,
5527    windows->im_update_widget,CurrentTime);
5528  /*
5529    Wait for first button press.
5530  */
5531  root_window=XRootWindow(display,XDefaultScreen(display));
5532  draw_info.stencil=OpaqueStencil;
5533  status=MagickTrue;
5534  cursor=XCreateFontCursor(display,XC_tcross);
5535  for ( ; ; )
5536  {
5537    XQueryPosition(display,windows->image.id,&x,&y);
5538    (void) XSelectInput(display,windows->image.id,
5539      windows->image.attributes.event_mask | PointerMotionMask);
5540    (void) XCheckDefineCursor(display,windows->image.id,cursor);
5541    state=DefaultState;
5542    do
5543    {
5544      if( IfMagickTrue(windows->info.mapped) )
5545        {
5546          /*
5547            Display pointer position.
5548          */
5549          (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
5550            x+windows->image.x,y+windows->image.y);
5551          XInfoWidget(display,windows,text);
5552        }
5553      /*
5554        Wait for next event.
5555      */
5556      XScreenEvent(display,windows,&event,exception);
5557      if (event.xany.window == windows->command.id)
5558        {
5559          /*
5560            Select a command from the Command widget.
5561          */
5562          id=XCommandWidget(display,windows,DrawMenu,&event);
5563          if (id < 0)
5564            continue;
5565          switch (DrawCommands[id])
5566          {
5567            case DrawElementCommand:
5568            {
5569              static const char
5570                *Elements[] =
5571                {
5572                  "point",
5573                  "line",
5574                  "rectangle",
5575                  "fill rectangle",
5576                  "circle",
5577                  "fill circle",
5578                  "ellipse",
5579                  "fill ellipse",
5580                  "polygon",
5581                  "fill polygon",
5582                  (char *) NULL,
5583                };
5584
5585              /*
5586                Select a command from the pop-up menu.
5587              */
5588              element=(ElementType) (XMenuWidget(display,windows,
5589                DrawMenu[id],Elements,command)+1);
5590              break;
5591            }
5592            case DrawColorCommand:
5593            {
5594              const char
5595                *ColorMenu[MaxNumberPens+1];
5596
5597              int
5598                pen_number;
5599
5600              MagickBooleanType
5601                transparent;
5602
5603              XColor
5604                color;
5605
5606              /*
5607                Initialize menu selections.
5608              */
5609              for (i=0; i < (int) (MaxNumberPens-2); i++)
5610                ColorMenu[i]=resource_info->pen_colors[i];
5611              ColorMenu[MaxNumberPens-2]="transparent";
5612              ColorMenu[MaxNumberPens-1]="Browser...";
5613              ColorMenu[MaxNumberPens]=(char *) NULL;
5614              /*
5615                Select a pen color from the pop-up menu.
5616              */
5617              pen_number=XMenuWidget(display,windows,DrawMenu[id],
5618                (const char **) ColorMenu,command);
5619              if (pen_number < 0)
5620                break;
5621              transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5622                MagickFalse;
5623              if( IfMagickTrue(transparent) )
5624                {
5625                  draw_info.stencil=TransparentStencil;
5626                  break;
5627                }
5628              if (pen_number == (MaxNumberPens-1))
5629                {
5630                  static char
5631                    color_name[MaxTextExtent] = "gray";
5632
5633                  /*
5634                    Select a pen color from a dialog.
5635                  */
5636                  resource_info->pen_colors[pen_number]=color_name;
5637                  XColorBrowserWidget(display,windows,"Select",color_name);
5638                  if (*color_name == '\0')
5639                    break;
5640                }
5641              /*
5642                Set pen color.
5643              */
5644              (void) XParseColor(display,windows->map_info->colormap,
5645                resource_info->pen_colors[pen_number],&color);
5646              XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5647                (unsigned int) MaxColors,&color);
5648              windows->pixel_info->pen_colors[pen_number]=color;
5649              pen_id=(unsigned int) pen_number;
5650              draw_info.stencil=OpaqueStencil;
5651              break;
5652            }
5653            case DrawStippleCommand:
5654            {
5655              Image
5656                *stipple_image;
5657
5658              ImageInfo
5659                *image_info;
5660
5661              int
5662                status;
5663
5664              static char
5665                filename[MaxTextExtent] = "\0";
5666
5667              static const char
5668                *StipplesMenu[] =
5669                {
5670                  "Brick",
5671                  "Diagonal",
5672                  "Scales",
5673                  "Vertical",
5674                  "Wavy",
5675                  "Translucent",
5676                  "Opaque",
5677                  (char *) NULL,
5678                  (char *) NULL,
5679                };
5680
5681              /*
5682                Select a command from the pop-up menu.
5683              */
5684              StipplesMenu[7]="Open...";
5685              entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5686                command);
5687              if (entry < 0)
5688                break;
5689              if (stipple != (Pixmap) NULL)
5690                (void) XFreePixmap(display,stipple);
5691              stipple=(Pixmap) NULL;
5692              if (entry != 7)
5693                {
5694                  switch (entry)
5695                  {
5696                    case 0:
5697                    {
5698                      stipple=XCreateBitmapFromData(display,root_window,
5699                        (char *) BricksBitmap,BricksWidth,BricksHeight);
5700                      break;
5701                    }
5702                    case 1:
5703                    {
5704                      stipple=XCreateBitmapFromData(display,root_window,
5705                        (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5706                      break;
5707                    }
5708                    case 2:
5709                    {
5710                      stipple=XCreateBitmapFromData(display,root_window,
5711                        (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5712                      break;
5713                    }
5714                    case 3:
5715                    {
5716                      stipple=XCreateBitmapFromData(display,root_window,
5717                        (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5718                      break;
5719                    }
5720                    case 4:
5721                    {
5722                      stipple=XCreateBitmapFromData(display,root_window,
5723                        (char *) WavyBitmap,WavyWidth,WavyHeight);
5724                      break;
5725                    }
5726                    case 5:
5727                    {
5728                      stipple=XCreateBitmapFromData(display,root_window,
5729                        (char *) HighlightBitmap,HighlightWidth,
5730                        HighlightHeight);
5731                      break;
5732                    }
5733                    case 6:
5734                    default:
5735                    {
5736                      stipple=XCreateBitmapFromData(display,root_window,
5737                        (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5738                      break;
5739                    }
5740                  }
5741                  break;
5742                }
5743              XFileBrowserWidget(display,windows,"Stipple",filename);
5744              if (*filename == '\0')
5745                break;
5746              /*
5747                Read image.
5748              */
5749              XSetCursorState(display,windows,MagickTrue);
5750              XCheckRefreshWindows(display,windows);
5751              image_info=AcquireImageInfo();
5752              (void) CopyMagickString(image_info->filename,filename,
5753                MaxTextExtent);
5754              stipple_image=ReadImage(image_info,exception);
5755              CatchException(exception);
5756              XSetCursorState(display,windows,MagickFalse);
5757              if (stipple_image == (Image *) NULL)
5758                break;
5759              (void) AcquireUniqueFileResource(filename);
5760              (void) FormatLocaleString(stipple_image->filename,MaxTextExtent,
5761                "xbm:%s",filename);
5762              (void) WriteImage(image_info,stipple_image,exception);
5763              stipple_image=DestroyImage(stipple_image);
5764              image_info=DestroyImageInfo(image_info);
5765              status=XReadBitmapFile(display,root_window,filename,&width,
5766                &height,&stipple,&x,&y);
5767              (void) RelinquishUniqueFileResource(filename);
5768              if ((status != BitmapSuccess) != 0)
5769                XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5770                  filename);
5771              break;
5772            }
5773            case DrawWidthCommand:
5774            {
5775              static char
5776                width[MaxTextExtent] = "0";
5777
5778              static const char
5779                *WidthsMenu[] =
5780                {
5781                  "1",
5782                  "2",
5783                  "4",
5784                  "8",
5785                  "16",
5786                  "Dialog...",
5787                  (char *) NULL,
5788                };
5789
5790              /*
5791                Select a command from the pop-up menu.
5792              */
5793              entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5794                command);
5795              if (entry < 0)
5796                break;
5797              if (entry != 5)
5798                {
5799                  line_width=(unsigned int) StringToUnsignedLong(
5800                    WidthsMenu[entry]);
5801                  break;
5802                }
5803              (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5804                width);
5805              if (*width == '\0')
5806                break;
5807              line_width=(unsigned int) StringToUnsignedLong(width);
5808              break;
5809            }
5810            case DrawUndoCommand:
5811            {
5812              (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5813                image,exception);
5814              break;
5815            }
5816            case DrawHelpCommand:
5817            {
5818              XTextViewWidget(display,resource_info,windows,MagickFalse,
5819                "Help Viewer - Image Rotation",ImageDrawHelp);
5820              (void) XCheckDefineCursor(display,windows->image.id,cursor);
5821              break;
5822            }
5823            case DrawDismissCommand:
5824            {
5825              /*
5826                Prematurely exit.
5827              */
5828              state|=EscapeState;
5829              state|=ExitState;
5830              break;
5831            }
5832            default:
5833              break;
5834          }
5835          (void) XCheckDefineCursor(display,windows->image.id,cursor);
5836          continue;
5837        }
5838      switch (event.type)
5839      {
5840        case ButtonPress:
5841        {
5842          if (event.xbutton.button != Button1)
5843            break;
5844          if (event.xbutton.window != windows->image.id)
5845            break;
5846          /*
5847            exit loop.
5848          */
5849          x=event.xbutton.x;
5850          y=event.xbutton.y;
5851          state|=ExitState;
5852          break;
5853        }
5854        case ButtonRelease:
5855          break;
5856        case Expose:
5857          break;
5858        case KeyPress:
5859        {
5860          KeySym
5861            key_symbol;
5862
5863          if (event.xkey.window != windows->image.id)
5864            break;
5865          /*
5866            Respond to a user key press.
5867          */
5868          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5869            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5870          switch ((int) key_symbol)
5871          {
5872            case XK_Escape:
5873            case XK_F20:
5874            {
5875              /*
5876                Prematurely exit.
5877              */
5878              state|=EscapeState;
5879              state|=ExitState;
5880              break;
5881            }
5882            case XK_F1:
5883            case XK_Help:
5884            {
5885              XTextViewWidget(display,resource_info,windows,MagickFalse,
5886                "Help Viewer - Image Rotation",ImageDrawHelp);
5887              break;
5888            }
5889            default:
5890            {
5891              (void) XBell(display,0);
5892              break;
5893            }
5894          }
5895          break;
5896        }
5897        case MotionNotify:
5898        {
5899          /*
5900            Map and unmap Info widget as text cursor crosses its boundaries.
5901          */
5902          x=event.xmotion.x;
5903          y=event.xmotion.y;
5904          if( IfMagickTrue(windows->info.mapped) )
5905            {
5906              if ((x < (int) (windows->info.x+windows->info.width)) &&
5907                  (y < (int) (windows->info.y+windows->info.height)))
5908                (void) XWithdrawWindow(display,windows->info.id,
5909                  windows->info.screen);
5910            }
5911          else
5912            if ((x > (int) (windows->info.x+windows->info.width)) ||
5913                (y > (int) (windows->info.y+windows->info.height)))
5914              (void) XMapWindow(display,windows->info.id);
5915          break;
5916        }
5917      }
5918    } while ((state & ExitState) == 0);
5919    (void) XSelectInput(display,windows->image.id,
5920      windows->image.attributes.event_mask);
5921    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5922    if ((state & EscapeState) != 0)
5923      break;
5924    /*
5925      Draw element as pointer moves until the button is released.
5926    */
5927    distance=0;
5928    degrees=0.0;
5929    line_info.x1=x;
5930    line_info.y1=y;
5931    line_info.x2=x;
5932    line_info.y2=y;
5933    rectangle_info.x=(ssize_t) x;
5934    rectangle_info.y=(ssize_t) y;
5935    rectangle_info.width=0;
5936    rectangle_info.height=0;
5937    number_coordinates=1;
5938    coordinate_info->x=x;
5939    coordinate_info->y=y;
5940    (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5941    state=DefaultState;
5942    do
5943    {
5944      switch (element)
5945      {
5946        case PointElement:
5947        default:
5948        {
5949          if (number_coordinates > 1)
5950            {
5951              (void) XDrawLines(display,windows->image.id,
5952                windows->image.highlight_context,coordinate_info,
5953                number_coordinates,CoordModeOrigin);
5954              (void) FormatLocaleString(text,MaxTextExtent," %+d%+d",
5955                coordinate_info[number_coordinates-1].x,
5956                coordinate_info[number_coordinates-1].y);
5957              XInfoWidget(display,windows,text);
5958            }
5959          break;
5960        }
5961        case LineElement:
5962        {
5963          if (distance > 9)
5964            {
5965              /*
5966                Display angle of the line.
5967              */
5968              degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5969                line_info.y1),(double) (line_info.x2-line_info.x1)));
5970              (void) FormatLocaleString(text,MaxTextExtent," %g",
5971                (double) degrees);
5972              XInfoWidget(display,windows,text);
5973              XHighlightLine(display,windows->image.id,
5974                windows->image.highlight_context,&line_info);
5975            }
5976          else
5977            if( IfMagickTrue(windows->info.mapped) )
5978              (void) XWithdrawWindow(display,windows->info.id,
5979                windows->info.screen);
5980          break;
5981        }
5982        case RectangleElement:
5983        case FillRectangleElement:
5984        {
5985          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5986            {
5987              /*
5988                Display info and draw drawing rectangle.
5989              */
5990              (void) FormatLocaleString(text,MaxTextExtent,
5991                " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5992                (double) rectangle_info.height,(double) rectangle_info.x,
5993                (double) rectangle_info.y);
5994              XInfoWidget(display,windows,text);
5995              XHighlightRectangle(display,windows->image.id,
5996                windows->image.highlight_context,&rectangle_info);
5997            }
5998          else
5999            if( IfMagickTrue(windows->info.mapped) )
6000              (void) XWithdrawWindow(display,windows->info.id,
6001                windows->info.screen);
6002          break;
6003        }
6004        case CircleElement:
6005        case FillCircleElement:
6006        case EllipseElement:
6007        case FillEllipseElement:
6008        {
6009          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6010            {
6011              /*
6012                Display info and draw drawing rectangle.
6013              */
6014              (void) FormatLocaleString(text,MaxTextExtent,
6015                " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
6016                (double) rectangle_info.height,(double) rectangle_info.x,
6017                (double) rectangle_info.y);
6018              XInfoWidget(display,windows,text);
6019              XHighlightEllipse(display,windows->image.id,
6020                windows->image.highlight_context,&rectangle_info);
6021            }
6022          else
6023            if( IfMagickTrue(windows->info.mapped) )
6024              (void) XWithdrawWindow(display,windows->info.id,
6025                windows->info.screen);
6026          break;
6027        }
6028        case PolygonElement:
6029        case FillPolygonElement:
6030        {
6031          if (number_coordinates > 1)
6032            (void) XDrawLines(display,windows->image.id,
6033              windows->image.highlight_context,coordinate_info,
6034              number_coordinates,CoordModeOrigin);
6035          if (distance > 9)
6036            {
6037              /*
6038                Display angle of the line.
6039              */
6040              degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
6041                line_info.y1),(double) (line_info.x2-line_info.x1)));
6042              (void) FormatLocaleString(text,MaxTextExtent," %g",
6043                (double) degrees);
6044              XInfoWidget(display,windows,text);
6045              XHighlightLine(display,windows->image.id,
6046                windows->image.highlight_context,&line_info);
6047            }
6048          else
6049            if( IfMagickTrue(windows->info.mapped) )
6050              (void) XWithdrawWindow(display,windows->info.id,
6051                windows->info.screen);
6052          break;
6053        }
6054      }
6055      /*
6056        Wait for next event.
6057      */
6058      XScreenEvent(display,windows,&event,exception);
6059      switch (element)
6060      {
6061        case PointElement:
6062        default:
6063        {
6064          if (number_coordinates > 1)
6065            (void) XDrawLines(display,windows->image.id,
6066              windows->image.highlight_context,coordinate_info,
6067              number_coordinates,CoordModeOrigin);
6068          break;
6069        }
6070        case LineElement:
6071        {
6072          if (distance > 9)
6073            XHighlightLine(display,windows->image.id,
6074              windows->image.highlight_context,&line_info);
6075          break;
6076        }
6077        case RectangleElement:
6078        case FillRectangleElement:
6079        {
6080          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6081            XHighlightRectangle(display,windows->image.id,
6082              windows->image.highlight_context,&rectangle_info);
6083          break;
6084        }
6085        case CircleElement:
6086        case FillCircleElement:
6087        case EllipseElement:
6088        case FillEllipseElement:
6089        {
6090          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6091            XHighlightEllipse(display,windows->image.id,
6092              windows->image.highlight_context,&rectangle_info);
6093          break;
6094        }
6095        case PolygonElement:
6096        case FillPolygonElement:
6097        {
6098          if (number_coordinates > 1)
6099            (void) XDrawLines(display,windows->image.id,
6100              windows->image.highlight_context,coordinate_info,
6101              number_coordinates,CoordModeOrigin);
6102          if (distance > 9)
6103            XHighlightLine(display,windows->image.id,
6104              windows->image.highlight_context,&line_info);
6105          break;
6106        }
6107      }
6108      switch (event.type)
6109      {
6110        case ButtonPress:
6111          break;
6112        case ButtonRelease:
6113        {
6114          /*
6115            User has committed to element.
6116          */
6117          line_info.x2=event.xbutton.x;
6118          line_info.y2=event.xbutton.y;
6119          rectangle_info.x=(ssize_t) event.xbutton.x;
6120          rectangle_info.y=(ssize_t) event.xbutton.y;
6121          coordinate_info[number_coordinates].x=event.xbutton.x;
6122          coordinate_info[number_coordinates].y=event.xbutton.y;
6123          if (((element != PolygonElement) &&
6124               (element != FillPolygonElement)) || (distance <= 9))
6125            {
6126              state|=ExitState;
6127              break;
6128            }
6129          number_coordinates++;
6130          if (number_coordinates < (int) max_coordinates)
6131            {
6132              line_info.x1=event.xbutton.x;
6133              line_info.y1=event.xbutton.y;
6134              break;
6135            }
6136          max_coordinates<<=1;
6137          coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6138            max_coordinates,sizeof(*coordinate_info));
6139          if (coordinate_info == (XPoint *) NULL)
6140            (void) ThrowMagickException(exception,GetMagickModule(),
6141              ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6142          break;
6143        }
6144        case Expose:
6145          break;
6146        case MotionNotify:
6147        {
6148          if (event.xmotion.window != windows->image.id)
6149            break;
6150          if (element != PointElement)
6151            {
6152              line_info.x2=event.xmotion.x;
6153              line_info.y2=event.xmotion.y;
6154              rectangle_info.x=(ssize_t) event.xmotion.x;
6155              rectangle_info.y=(ssize_t) event.xmotion.y;
6156              break;
6157            }
6158          coordinate_info[number_coordinates].x=event.xbutton.x;
6159          coordinate_info[number_coordinates].y=event.xbutton.y;
6160          number_coordinates++;
6161          if (number_coordinates < (int) max_coordinates)
6162            break;
6163          max_coordinates<<=1;
6164          coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6165            max_coordinates,sizeof(*coordinate_info));
6166          if (coordinate_info == (XPoint *) NULL)
6167            (void) ThrowMagickException(exception,GetMagickModule(),
6168              ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6169          break;
6170        }
6171        default:
6172          break;
6173      }
6174      /*
6175        Check boundary conditions.
6176      */
6177      if (line_info.x2 < 0)
6178        line_info.x2=0;
6179      else
6180        if (line_info.x2 > (int) windows->image.width)
6181          line_info.x2=(short) windows->image.width;
6182      if (line_info.y2 < 0)
6183        line_info.y2=0;
6184      else
6185        if (line_info.y2 > (int) windows->image.height)
6186          line_info.y2=(short) windows->image.height;
6187      distance=(unsigned int)
6188        (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6189         ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6190      if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6191          ((state & ExitState) != 0))
6192        {
6193          if (rectangle_info.x < 0)
6194            rectangle_info.x=0;
6195          else
6196            if (rectangle_info.x > (ssize_t) windows->image.width)
6197              rectangle_info.x=(ssize_t) windows->image.width;
6198          if ((int) rectangle_info.x < x)
6199            rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6200          else
6201            {
6202              rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6203              rectangle_info.x=(ssize_t) x;
6204            }
6205          if (rectangle_info.y < 0)
6206            rectangle_info.y=0;
6207          else
6208            if (rectangle_info.y > (ssize_t) windows->image.height)
6209              rectangle_info.y=(ssize_t) windows->image.height;
6210          if ((int) rectangle_info.y < y)
6211            rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6212          else
6213            {
6214              rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6215              rectangle_info.y=(ssize_t) y;
6216            }
6217        }
6218    } while ((state & ExitState) == 0);
6219    (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6220    if ((element == PointElement) || (element == PolygonElement) ||
6221        (element == FillPolygonElement))
6222      {
6223        /*
6224          Determine polygon bounding box.
6225        */
6226        rectangle_info.x=(ssize_t) coordinate_info->x;
6227        rectangle_info.y=(ssize_t) coordinate_info->y;
6228        x=coordinate_info->x;
6229        y=coordinate_info->y;
6230        for (i=1; i < number_coordinates; i++)
6231        {
6232          if (coordinate_info[i].x > x)
6233            x=coordinate_info[i].x;
6234          if (coordinate_info[i].y > y)
6235            y=coordinate_info[i].y;
6236          if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6237            rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6238          if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6239            rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6240        }
6241        rectangle_info.width=(size_t) (x-rectangle_info.x);
6242        rectangle_info.height=(size_t) (y-rectangle_info.y);
6243        for (i=0; i < number_coordinates; i++)
6244        {
6245          coordinate_info[i].x-=rectangle_info.x;
6246          coordinate_info[i].y-=rectangle_info.y;
6247        }
6248      }
6249    else
6250      if (distance <= 9)
6251        continue;
6252      else
6253        if ((element == RectangleElement) ||
6254            (element == CircleElement) || (element == EllipseElement))
6255          {
6256            rectangle_info.width--;
6257            rectangle_info.height--;
6258          }
6259    /*
6260      Drawing is relative to image configuration.
6261    */
6262    draw_info.x=(int) rectangle_info.x;
6263    draw_info.y=(int) rectangle_info.y;
6264    (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6265      image,exception);
6266    width=(unsigned int) (*image)->columns;
6267    height=(unsigned int) (*image)->rows;
6268    x=0;
6269    y=0;
6270    if (windows->image.crop_geometry != (char *) NULL)
6271      (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6272    draw_info.x+=windows->image.x-(line_width/2);
6273    if (draw_info.x < 0)
6274      draw_info.x=0;
6275    draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6276    draw_info.y+=windows->image.y-(line_width/2);
6277    if (draw_info.y < 0)
6278      draw_info.y=0;
6279    draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6280    draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6281    if (draw_info.width > (unsigned int) (*image)->columns)
6282      draw_info.width=(unsigned int) (*image)->columns;
6283    draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6284    if (draw_info.height > (unsigned int) (*image)->rows)
6285      draw_info.height=(unsigned int) (*image)->rows;
6286    (void) FormatLocaleString(draw_info.geometry,MaxTextExtent,"%ux%u%+d%+d",
6287      width*draw_info.width/windows->image.ximage->width,
6288      height*draw_info.height/windows->image.ximage->height,
6289      draw_info.x+x,draw_info.y+y);
6290    /*
6291      Initialize drawing attributes.
6292    */
6293    draw_info.degrees=0.0;
6294    draw_info.element=element;
6295    draw_info.stipple=stipple;
6296    draw_info.line_width=line_width;
6297    draw_info.line_info=line_info;
6298    if (line_info.x1 > (int) (line_width/2))
6299      draw_info.line_info.x1=(short) line_width/2;
6300    if (line_info.y1 > (int) (line_width/2))
6301      draw_info.line_info.y1=(short) line_width/2;
6302    draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6303    draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6304    if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6305      {
6306        draw_info.line_info.x2=(-draw_info.line_info.x2);
6307        draw_info.line_info.y2=(-draw_info.line_info.y2);
6308      }
6309    if (draw_info.line_info.x2 < 0)
6310      {
6311        draw_info.line_info.x2=(-draw_info.line_info.x2);
6312        Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6313      }
6314    if (draw_info.line_info.y2 < 0)
6315      {
6316        draw_info.line_info.y2=(-draw_info.line_info.y2);
6317        Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6318      }
6319    draw_info.rectangle_info=rectangle_info;
6320    if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6321      draw_info.rectangle_info.x=(ssize_t) line_width/2;
6322    if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6323      draw_info.rectangle_info.y=(ssize_t) line_width/2;
6324    draw_info.number_coordinates=(unsigned int) number_coordinates;
6325    draw_info.coordinate_info=coordinate_info;
6326    windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6327    /*
6328      Draw element on image.
6329    */
6330    XSetCursorState(display,windows,MagickTrue);
6331    XCheckRefreshWindows(display,windows);
6332    status=XDrawImage(display,windows->pixel_info,&draw_info,*image,exception);
6333    XSetCursorState(display,windows,MagickFalse);
6334    /*
6335      Update image colormap and return to image drawing.
6336    */
6337    XConfigureImageColormap(display,resource_info,windows,*image,exception);
6338    (void) XConfigureImage(display,resource_info,windows,*image,exception);
6339  }
6340  XSetCursorState(display,windows,MagickFalse);
6341  coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6342  return(IsMagickTrue(status));
6343}
6344
6345/*
6346%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6347%                                                                             %
6348%                                                                             %
6349%                                                                             %
6350+   X D r a w P a n R e c t a n g l e                                         %
6351%                                                                             %
6352%                                                                             %
6353%                                                                             %
6354%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6355%
6356%  XDrawPanRectangle() draws a rectangle in the pan window.  The pan window
6357%  displays a zoom image and the rectangle shows which portion of the image is
6358%  displayed in the Image window.
6359%
6360%  The format of the XDrawPanRectangle method is:
6361%
6362%      XDrawPanRectangle(Display *display,XWindows *windows)
6363%
6364%  A description of each parameter follows:
6365%
6366%    o display: Specifies a connection to an X server;  returned from
6367%      XOpenDisplay.
6368%
6369%    o windows: Specifies a pointer to a XWindows structure.
6370%
6371*/
6372static void XDrawPanRectangle(Display *display,XWindows *windows)
6373{
6374  double
6375    scale_factor;
6376
6377  RectangleInfo
6378    highlight_info;
6379
6380  /*
6381    Determine dimensions of the panning rectangle.
6382  */
6383  scale_factor=(double) windows->pan.width/windows->image.ximage->width;
6384  highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6385  highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6386  scale_factor=(double)
6387    windows->pan.height/windows->image.ximage->height;
6388  highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6389  highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6390  /*
6391    Display the panning rectangle.
6392  */
6393  (void) XClearWindow(display,windows->pan.id);
6394  XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6395    &highlight_info);
6396}
6397
6398/*
6399%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6400%                                                                             %
6401%                                                                             %
6402%                                                                             %
6403+   X I m a g e C a c h e                                                     %
6404%                                                                             %
6405%                                                                             %
6406%                                                                             %
6407%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6408%
6409%  XImageCache() handles the creation, manipulation, and destruction of the
6410%  image cache (undo and redo buffers).
6411%
6412%  The format of the XImageCache method is:
6413%
6414%      void XImageCache(Display *display,XResourceInfo *resource_info,
6415%        XWindows *windows,const CommandType command,Image **image,
6416%        ExceptionInfo *exception)
6417%
6418%  A description of each parameter follows:
6419%
6420%    o display: Specifies a connection to an X server; returned from
6421%      XOpenDisplay.
6422%
6423%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6424%
6425%    o windows: Specifies a pointer to a XWindows structure.
6426%
6427%    o command: Specifies a command to perform.
6428%
6429%    o image: the image;  XImageCache may transform the image and return a new
6430%      image pointer.
6431%
6432%    o exception: return any errors or warnings in this structure.
6433%
6434*/
6435static void XImageCache(Display *display,XResourceInfo *resource_info,
6436  XWindows *windows,const CommandType command,Image **image,
6437  ExceptionInfo *exception)
6438{
6439  Image
6440    *cache_image;
6441
6442  static Image
6443    *redo_image = (Image *) NULL,
6444    *undo_image = (Image *) NULL;
6445
6446  switch (command)
6447  {
6448    case FreeBuffersCommand:
6449    {
6450      /*
6451        Free memory from the undo and redo cache.
6452      */
6453      while (undo_image != (Image *) NULL)
6454      {
6455        cache_image=undo_image;
6456        undo_image=GetPreviousImageInList(undo_image);
6457        cache_image->list=DestroyImage(cache_image->list);
6458        cache_image=DestroyImage(cache_image);
6459      }
6460      undo_image=NewImageList();
6461      if (redo_image != (Image *) NULL)
6462        redo_image=DestroyImage(redo_image);
6463      redo_image=NewImageList();
6464      return;
6465    }
6466    case UndoCommand:
6467    {
6468      char
6469        image_geometry[MaxTextExtent];
6470
6471      /*
6472        Undo the last image transformation.
6473      */
6474      if (undo_image == (Image *) NULL)
6475        {
6476          (void) XBell(display,0);
6477          return;
6478        }
6479      cache_image=undo_image;
6480      undo_image=GetPreviousImageInList(undo_image);
6481      windows->image.window_changes.width=(int) cache_image->columns;
6482      windows->image.window_changes.height=(int) cache_image->rows;
6483      (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
6484        windows->image.ximage->width,windows->image.ximage->height);
6485      (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
6486        exception);
6487      if (windows->image.crop_geometry != (char *) NULL)
6488        windows->image.crop_geometry=(char *) RelinquishMagickMemory(
6489          windows->image.crop_geometry);
6490      windows->image.crop_geometry=cache_image->geometry;
6491      if (redo_image != (Image *) NULL)
6492        redo_image=DestroyImage(redo_image);
6493      redo_image=(*image);
6494      *image=cache_image->list;
6495      cache_image=DestroyImage(cache_image);
6496      if( IfMagickTrue(windows->image.orphan) )
6497        return;
6498      XConfigureImageColormap(display,resource_info,windows,*image,exception);
6499      (void) XConfigureImage(display,resource_info,windows,*image,exception);
6500      return;
6501    }
6502    case CutCommand:
6503    case PasteCommand:
6504    case ApplyCommand:
6505    case HalfSizeCommand:
6506    case OriginalSizeCommand:
6507    case DoubleSizeCommand:
6508    case ResizeCommand:
6509    case TrimCommand:
6510    case CropCommand:
6511    case ChopCommand:
6512    case FlipCommand:
6513    case FlopCommand:
6514    case RotateRightCommand:
6515    case RotateLeftCommand:
6516    case RotateCommand:
6517    case ShearCommand:
6518    case RollCommand:
6519    case NegateCommand:
6520    case ContrastStretchCommand:
6521    case SigmoidalContrastCommand:
6522    case NormalizeCommand:
6523    case EqualizeCommand:
6524    case HueCommand:
6525    case SaturationCommand:
6526    case BrightnessCommand:
6527    case GammaCommand:
6528    case SpiffCommand:
6529    case DullCommand:
6530    case GrayscaleCommand:
6531    case MapCommand:
6532    case QuantizeCommand:
6533    case DespeckleCommand:
6534    case EmbossCommand:
6535    case ReduceNoiseCommand:
6536    case AddNoiseCommand:
6537    case SharpenCommand:
6538    case BlurCommand:
6539    case ThresholdCommand:
6540    case EdgeDetectCommand:
6541    case SpreadCommand:
6542    case ShadeCommand:
6543    case RaiseCommand:
6544    case SegmentCommand:
6545    case SolarizeCommand:
6546    case SepiaToneCommand:
6547    case SwirlCommand:
6548    case ImplodeCommand:
6549    case VignetteCommand:
6550    case WaveCommand:
6551    case OilPaintCommand:
6552    case CharcoalDrawCommand:
6553    case AnnotateCommand:
6554    case AddBorderCommand:
6555    case AddFrameCommand:
6556    case CompositeCommand:
6557    case CommentCommand:
6558    case LaunchCommand:
6559    case RegionofInterestCommand:
6560    case SaveToUndoBufferCommand:
6561    case RedoCommand:
6562    {
6563      Image
6564        *previous_image;
6565
6566      ssize_t
6567        bytes;
6568
6569      bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelInfo));
6570      if (undo_image != (Image *) NULL)
6571        {
6572          /*
6573            Ensure the undo cache has enough memory available.
6574          */
6575          previous_image=undo_image;
6576          while (previous_image != (Image *) NULL)
6577          {
6578            bytes+=previous_image->list->columns*previous_image->list->rows*
6579              sizeof(PixelInfo);
6580            if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6581              {
6582                previous_image=GetPreviousImageInList(previous_image);
6583                continue;
6584              }
6585            bytes-=previous_image->list->columns*previous_image->list->rows*
6586              sizeof(PixelInfo);
6587            if (previous_image == undo_image)
6588              undo_image=NewImageList();
6589            else
6590              previous_image->next->previous=NewImageList();
6591            break;
6592          }
6593          while (previous_image != (Image *) NULL)
6594          {
6595            /*
6596              Delete any excess memory from undo cache.
6597            */
6598            cache_image=previous_image;
6599            previous_image=GetPreviousImageInList(previous_image);
6600            cache_image->list=DestroyImage(cache_image->list);
6601            cache_image=DestroyImage(cache_image);
6602          }
6603        }
6604      if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6605        break;
6606      /*
6607        Save image before transformations are applied.
6608      */
6609      cache_image=AcquireImage((ImageInfo *) NULL,exception);
6610      if (cache_image == (Image *) NULL)
6611        break;
6612      XSetCursorState(display,windows,MagickTrue);
6613      XCheckRefreshWindows(display,windows);
6614      cache_image->list=CloneImage(*image,0,0,MagickTrue,exception);
6615      XSetCursorState(display,windows,MagickFalse);
6616      if (cache_image->list == (Image *) NULL)
6617        {
6618          cache_image=DestroyImage(cache_image);
6619          break;
6620        }
6621      cache_image->columns=(size_t) windows->image.ximage->width;
6622      cache_image->rows=(size_t) windows->image.ximage->height;
6623      cache_image->geometry=windows->image.crop_geometry;
6624      if (windows->image.crop_geometry != (char *) NULL)
6625        {
6626          cache_image->geometry=AcquireString((char *) NULL);
6627          (void) CopyMagickString(cache_image->geometry,
6628            windows->image.crop_geometry,MaxTextExtent);
6629        }
6630      if (undo_image == (Image *) NULL)
6631        {
6632          undo_image=cache_image;
6633          break;
6634        }
6635      undo_image->next=cache_image;
6636      undo_image->next->previous=undo_image;
6637      undo_image=undo_image->next;
6638      break;
6639    }
6640    default:
6641      break;
6642  }
6643  if (command == RedoCommand)
6644    {
6645      /*
6646        Redo the last image transformation.
6647      */
6648      if (redo_image == (Image *) NULL)
6649        {
6650          (void) XBell(display,0);
6651          return;
6652        }
6653      windows->image.window_changes.width=(int) redo_image->columns;
6654      windows->image.window_changes.height=(int) redo_image->rows;
6655      if (windows->image.crop_geometry != (char *) NULL)
6656        windows->image.crop_geometry=(char *)
6657          RelinquishMagickMemory(windows->image.crop_geometry);
6658      windows->image.crop_geometry=redo_image->geometry;
6659      *image=DestroyImage(*image);
6660      *image=redo_image;
6661      redo_image=NewImageList();
6662      if( IfMagickTrue(windows->image.orphan) )
6663        return;
6664      XConfigureImageColormap(display,resource_info,windows,*image,exception);
6665      (void) XConfigureImage(display,resource_info,windows,*image,exception);
6666      return;
6667    }
6668  if (command != InfoCommand)
6669    return;
6670  /*
6671    Display image info.
6672  */
6673  XSetCursorState(display,windows,MagickTrue);
6674  XCheckRefreshWindows(display,windows);
6675  XDisplayImageInfo(display,resource_info,windows,undo_image,*image,exception);
6676  XSetCursorState(display,windows,MagickFalse);
6677}
6678
6679/*
6680%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6681%                                                                             %
6682%                                                                             %
6683%                                                                             %
6684+   X I m a g e W i n d o w C o m m a n d                                     %
6685%                                                                             %
6686%                                                                             %
6687%                                                                             %
6688%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6689%
6690%  XImageWindowCommand() makes a transform to the image or Image window as
6691%  specified by a user menu button or keyboard command.
6692%
6693%  The format of the XImageWindowCommand method is:
6694%
6695%      CommandType XImageWindowCommand(Display *display,
6696%        XResourceInfo *resource_info,XWindows *windows,
6697%        const MagickStatusType state,KeySym key_symbol,Image **image,
6698%        ExceptionInfo *exception)
6699%
6700%  A description of each parameter follows:
6701%
6702%    o nexus:  Method XImageWindowCommand returns an image when the
6703%      user chooses 'Open Image' from the command menu.  Otherwise a null
6704%      image is returned.
6705%
6706%    o display: Specifies a connection to an X server; returned from
6707%      XOpenDisplay.
6708%
6709%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6710%
6711%    o windows: Specifies a pointer to a XWindows structure.
6712%
6713%    o state: key mask.
6714%
6715%    o key_symbol: Specifies a command to perform.
6716%
6717%    o image: the image;  XImageWIndowCommand may transform the image and
6718%      return a new image pointer.
6719%
6720%    o exception: return any errors or warnings in this structure.
6721%
6722*/
6723static CommandType XImageWindowCommand(Display *display,
6724  XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6725  KeySym key_symbol,Image **image,ExceptionInfo *exception)
6726{
6727  static char
6728    delta[MaxTextExtent] = "";
6729
6730  static const char
6731    Digits[] = "01234567890";
6732
6733  static KeySym
6734    last_symbol = XK_0;
6735
6736  if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6737    {
6738      if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6739        {
6740          *delta='\0';
6741          resource_info->quantum=1;
6742        }
6743      last_symbol=key_symbol;
6744      delta[strlen(delta)+1]='\0';
6745      delta[strlen(delta)]=Digits[key_symbol-XK_0];
6746      resource_info->quantum=StringToLong(delta);
6747      return(NullCommand);
6748    }
6749  last_symbol=key_symbol;
6750  if (resource_info->immutable)
6751    {
6752      /*
6753        Virtual image window has a restricted command set.
6754      */
6755      switch (key_symbol)
6756      {
6757        case XK_question:
6758          return(InfoCommand);
6759        case XK_p:
6760        case XK_Print:
6761          return(PrintCommand);
6762        case XK_space:
6763          return(NextCommand);
6764        case XK_q:
6765        case XK_Escape:
6766          return(QuitCommand);
6767        default:
6768          break;
6769      }
6770      return(NullCommand);
6771    }
6772  switch ((int) key_symbol)
6773  {
6774    case XK_o:
6775    {
6776      if ((state & ControlMask) == 0)
6777        break;
6778      return(OpenCommand);
6779    }
6780    case XK_space:
6781      return(NextCommand);
6782    case XK_BackSpace:
6783      return(FormerCommand);
6784    case XK_s:
6785    {
6786      if ((state & Mod1Mask) != 0)
6787        return(SwirlCommand);
6788      if ((state & ControlMask) == 0)
6789        return(ShearCommand);
6790      return(SaveCommand);
6791    }
6792    case XK_p:
6793    case XK_Print:
6794    {
6795      if ((state & Mod1Mask) != 0)
6796        return(OilPaintCommand);
6797      if ((state & Mod4Mask) != 0)
6798        return(ColorCommand);
6799      if ((state & ControlMask) == 0)
6800        return(NullCommand);
6801      return(PrintCommand);
6802    }
6803    case XK_d:
6804    {
6805      if ((state & Mod4Mask) != 0)
6806        return(DrawCommand);
6807      if ((state & ControlMask) == 0)
6808        return(NullCommand);
6809      return(DeleteCommand);
6810    }
6811    case XK_Select:
6812    {
6813      if ((state & ControlMask) == 0)
6814        return(NullCommand);
6815      return(SelectCommand);
6816    }
6817    case XK_n:
6818    {
6819      if ((state & ControlMask) == 0)
6820        return(NullCommand);
6821      return(NewCommand);
6822    }
6823    case XK_q:
6824    case XK_Escape:
6825      return(QuitCommand);
6826    case XK_z:
6827    case XK_Undo:
6828    {
6829      if ((state & ControlMask) == 0)
6830        return(NullCommand);
6831      return(UndoCommand);
6832    }
6833    case XK_r:
6834    case XK_Redo:
6835    {
6836      if ((state & ControlMask) == 0)
6837        return(RollCommand);
6838      return(RedoCommand);
6839    }
6840    case XK_x:
6841    {
6842      if ((state & ControlMask) == 0)
6843        return(NullCommand);
6844      return(CutCommand);
6845    }
6846    case XK_c:
6847    {
6848      if ((state & Mod1Mask) != 0)
6849        return(CharcoalDrawCommand);
6850      if ((state & ControlMask) == 0)
6851        return(CropCommand);
6852      return(CopyCommand);
6853    }
6854    case XK_v:
6855    case XK_Insert:
6856    {
6857      if ((state & Mod4Mask) != 0)
6858        return(CompositeCommand);
6859      if ((state & ControlMask) == 0)
6860        return(FlipCommand);
6861      return(PasteCommand);
6862    }
6863    case XK_less:
6864      return(HalfSizeCommand);
6865    case XK_minus:
6866      return(OriginalSizeCommand);
6867    case XK_greater:
6868      return(DoubleSizeCommand);
6869    case XK_percent:
6870      return(ResizeCommand);
6871    case XK_at:
6872      return(RefreshCommand);
6873    case XK_bracketleft:
6874      return(ChopCommand);
6875    case XK_h:
6876      return(FlopCommand);
6877    case XK_slash:
6878      return(RotateRightCommand);
6879    case XK_backslash:
6880      return(RotateLeftCommand);
6881    case XK_asterisk:
6882      return(RotateCommand);
6883    case XK_t:
6884      return(TrimCommand);
6885    case XK_H:
6886      return(HueCommand);
6887    case XK_S:
6888      return(SaturationCommand);
6889    case XK_L:
6890      return(BrightnessCommand);
6891    case XK_G:
6892      return(GammaCommand);
6893    case XK_C:
6894      return(SpiffCommand);
6895    case XK_Z:
6896      return(DullCommand);
6897    case XK_N:
6898      return(NormalizeCommand);
6899    case XK_equal:
6900      return(EqualizeCommand);
6901    case XK_asciitilde:
6902      return(NegateCommand);
6903    case XK_period:
6904      return(GrayscaleCommand);
6905    case XK_numbersign:
6906      return(QuantizeCommand);
6907    case XK_F2:
6908      return(DespeckleCommand);
6909    case XK_F3:
6910      return(EmbossCommand);
6911    case XK_F4:
6912      return(ReduceNoiseCommand);
6913    case XK_F5:
6914      return(AddNoiseCommand);
6915    case XK_F6:
6916      return(SharpenCommand);
6917    case XK_F7:
6918      return(BlurCommand);
6919    case XK_F8:
6920      return(ThresholdCommand);
6921    case XK_F9:
6922      return(EdgeDetectCommand);
6923    case XK_F10:
6924      return(SpreadCommand);
6925    case XK_F11:
6926      return(ShadeCommand);
6927    case XK_F12:
6928      return(RaiseCommand);
6929    case XK_F13:
6930      return(SegmentCommand);
6931    case XK_i:
6932    {
6933      if ((state & Mod1Mask) == 0)
6934        return(NullCommand);
6935      return(ImplodeCommand);
6936    }
6937    case XK_w:
6938    {
6939      if ((state & Mod1Mask) == 0)
6940        return(NullCommand);
6941      return(WaveCommand);
6942    }
6943    case XK_m:
6944    {
6945      if ((state & Mod4Mask) == 0)
6946        return(NullCommand);
6947      return(MatteCommand);
6948    }
6949    case XK_b:
6950    {
6951      if ((state & Mod4Mask) == 0)
6952        return(NullCommand);
6953      return(AddBorderCommand);
6954    }
6955    case XK_f:
6956    {
6957      if ((state & Mod4Mask) == 0)
6958        return(NullCommand);
6959      return(AddFrameCommand);
6960    }
6961    case XK_exclam:
6962    {
6963      if ((state & Mod4Mask) == 0)
6964        return(NullCommand);
6965      return(CommentCommand);
6966    }
6967    case XK_a:
6968    {
6969      if ((state & Mod1Mask) != 0)
6970        return(ApplyCommand);
6971      if ((state & Mod4Mask) != 0)
6972        return(AnnotateCommand);
6973      if ((state & ControlMask) == 0)
6974        return(NullCommand);
6975      return(RegionofInterestCommand);
6976    }
6977    case XK_question:
6978      return(InfoCommand);
6979    case XK_plus:
6980      return(ZoomCommand);
6981    case XK_P:
6982    {
6983      if ((state & ShiftMask) == 0)
6984        return(NullCommand);
6985      return(ShowPreviewCommand);
6986    }
6987    case XK_Execute:
6988      return(LaunchCommand);
6989    case XK_F1:
6990      return(HelpCommand);
6991    case XK_Find:
6992      return(BrowseDocumentationCommand);
6993    case XK_Menu:
6994    {
6995      (void) XMapRaised(display,windows->command.id);
6996      return(NullCommand);
6997    }
6998    case XK_Next:
6999    case XK_Prior:
7000    case XK_Home:
7001    case XK_KP_Home:
7002    {
7003      XTranslateImage(display,windows,*image,key_symbol);
7004      return(NullCommand);
7005    }
7006    case XK_Up:
7007    case XK_KP_Up:
7008    case XK_Down:
7009    case XK_KP_Down:
7010    case XK_Left:
7011    case XK_KP_Left:
7012    case XK_Right:
7013    case XK_KP_Right:
7014    {
7015      if ((state & Mod1Mask) != 0)
7016        {
7017          RectangleInfo
7018            crop_info;
7019
7020          /*
7021            Trim one pixel from edge of image.
7022          */
7023          crop_info.x=0;
7024          crop_info.y=0;
7025          crop_info.width=(size_t) windows->image.ximage->width;
7026          crop_info.height=(size_t) windows->image.ximage->height;
7027          if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
7028            {
7029              if (resource_info->quantum >= (int) crop_info.height)
7030                resource_info->quantum=(int) crop_info.height-1;
7031              crop_info.height-=resource_info->quantum;
7032            }
7033          if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
7034            {
7035              if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
7036                resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
7037              crop_info.y+=resource_info->quantum;
7038              crop_info.height-=resource_info->quantum;
7039            }
7040          if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
7041            {
7042              if (resource_info->quantum >= (int) crop_info.width)
7043                resource_info->quantum=(int) crop_info.width-1;
7044              crop_info.width-=resource_info->quantum;
7045            }
7046          if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
7047            {
7048              if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
7049                resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
7050              crop_info.x+=resource_info->quantum;
7051              crop_info.width-=resource_info->quantum;
7052            }
7053          if ((int) (windows->image.x+windows->image.width) >
7054              (int) crop_info.width)
7055            windows->image.x=(int) (crop_info.width-windows->image.width);
7056          if ((int) (windows->image.y+windows->image.height) >
7057              (int) crop_info.height)
7058            windows->image.y=(int) (crop_info.height-windows->image.height);
7059          XSetCropGeometry(display,windows,&crop_info,*image);
7060          windows->image.window_changes.width=(int) crop_info.width;
7061          windows->image.window_changes.height=(int) crop_info.height;
7062          (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
7063          (void) XConfigureImage(display,resource_info,windows,*image,
7064            exception);
7065          return(NullCommand);
7066        }
7067      XTranslateImage(display,windows,*image,key_symbol);
7068      return(NullCommand);
7069    }
7070    default:
7071      return(NullCommand);
7072  }
7073  return(NullCommand);
7074}
7075
7076/*
7077%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7078%                                                                             %
7079%                                                                             %
7080%                                                                             %
7081+   X M a g i c k C o m m a n d                                               %
7082%                                                                             %
7083%                                                                             %
7084%                                                                             %
7085%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7086%
7087%  XMagickCommand() makes a transform to the image or Image window as
7088%  specified by a user menu button or keyboard command.
7089%
7090%  The format of the XMagickCommand method is:
7091%
7092%      Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7093%        XWindows *windows,const CommandType command,Image **image,
7094%        ExceptionInfo *exception)
7095%
7096%  A description of each parameter follows:
7097%
7098%    o display: Specifies a connection to an X server; returned from
7099%      XOpenDisplay.
7100%
7101%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7102%
7103%    o windows: Specifies a pointer to a XWindows structure.
7104%
7105%    o command: Specifies a command to perform.
7106%
7107%    o image: the image;  XMagickCommand may transform the image and return a
7108%      new image pointer.
7109%
7110%    o exception: return any errors or warnings in this structure.
7111%
7112*/
7113static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7114  XWindows *windows,const CommandType command,Image **image,
7115  ExceptionInfo *exception)
7116{
7117  char
7118    filename[MaxTextExtent],
7119    geometry[MaxTextExtent],
7120    modulate_factors[MaxTextExtent];
7121
7122  GeometryInfo
7123    geometry_info;
7124
7125  Image
7126    *nexus;
7127
7128  ImageInfo
7129    *image_info;
7130
7131  int
7132    x,
7133    y;
7134
7135  MagickStatusType
7136    flags,
7137    status;
7138
7139  QuantizeInfo
7140    quantize_info;
7141
7142  RectangleInfo
7143    page_geometry;
7144
7145  register int
7146    i;
7147
7148  static char
7149    color[MaxTextExtent] = "gray";
7150
7151  unsigned int
7152    height,
7153    width;
7154
7155  /*
7156    Process user command.
7157  */
7158  XCheckRefreshWindows(display,windows);
7159  XImageCache(display,resource_info,windows,command,image,exception);
7160  nexus=NewImageList();
7161  windows->image.window_changes.width=windows->image.ximage->width;
7162  windows->image.window_changes.height=windows->image.ximage->height;
7163  image_info=CloneImageInfo(resource_info->image_info);
7164  SetGeometryInfo(&geometry_info);
7165  GetQuantizeInfo(&quantize_info);
7166  switch (command)
7167  {
7168    case OpenCommand:
7169    {
7170      /*
7171        Load image.
7172      */
7173      nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7174      break;
7175    }
7176    case NextCommand:
7177    {
7178      /*
7179        Display next image.
7180      */
7181      for (i=0; i < resource_info->quantum; i++)
7182        XClientMessage(display,windows->image.id,windows->im_protocols,
7183          windows->im_next_image,CurrentTime);
7184      break;
7185    }
7186    case FormerCommand:
7187    {
7188      /*
7189        Display former image.
7190      */
7191      for (i=0; i < resource_info->quantum; i++)
7192        XClientMessage(display,windows->image.id,windows->im_protocols,
7193          windows->im_former_image,CurrentTime);
7194      break;
7195    }
7196    case SelectCommand:
7197    {
7198      int
7199        status;
7200
7201      /*
7202        Select image.
7203      */
7204      if (*resource_info->home_directory == '\0')
7205        (void) CopyMagickString(resource_info->home_directory,".",
7206          MaxTextExtent);
7207      status=chdir(resource_info->home_directory);
7208      if (status == -1)
7209        (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
7210          "UnableToOpenFile","%s",resource_info->home_directory);
7211      nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7212      break;
7213    }
7214    case SaveCommand:
7215    {
7216      /*
7217        Save image.
7218      */
7219      status=XSaveImage(display,resource_info,windows,*image,exception);
7220      if( IfMagickFalse(status) )
7221        {
7222          char
7223            message[MaxTextExtent];
7224
7225          (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7226            exception->reason != (char *) NULL ? exception->reason : "",
7227            exception->description != (char *) NULL ? exception->description :
7228            "");
7229          XNoticeWidget(display,windows,"Unable to save file:",message);
7230          break;
7231        }
7232      break;
7233    }
7234    case PrintCommand:
7235    {
7236      /*
7237        Print image.
7238      */
7239      status=XPrintImage(display,resource_info,windows,*image,exception);
7240      if( IfMagickFalse(status) )
7241        {
7242          char
7243            message[MaxTextExtent];
7244
7245          (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7246            exception->reason != (char *) NULL ? exception->reason : "",
7247            exception->description != (char *) NULL ? exception->description :
7248            "");
7249          XNoticeWidget(display,windows,"Unable to print file:",message);
7250          break;
7251        }
7252      break;
7253    }
7254    case DeleteCommand:
7255    {
7256      static char
7257        filename[MaxTextExtent] = "\0";
7258
7259      /*
7260        Delete image file.
7261      */
7262      XFileBrowserWidget(display,windows,"Delete",filename);
7263      if (*filename == '\0')
7264        break;
7265      status=IsMagickTrue(remove_utf8(filename));
7266      if( IfMagickTrue(status) )
7267        XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7268      break;
7269    }
7270    case NewCommand:
7271    {
7272      int
7273        status;
7274
7275      static char
7276        color[MaxTextExtent] = "gray",
7277        geometry[MaxTextExtent] = "640x480";
7278
7279      static const char
7280        *format = "gradient";
7281
7282      /*
7283        Query user for canvas geometry.
7284      */
7285      status=XDialogWidget(display,windows,"New","Enter image geometry:",
7286        geometry);
7287      if (*geometry == '\0')
7288        break;
7289      if (status == 0)
7290        format="xc";
7291      XColorBrowserWidget(display,windows,"Select",color);
7292      if (*color == '\0')
7293        break;
7294      /*
7295        Create canvas.
7296      */
7297      (void) FormatLocaleString(image_info->filename,MaxTextExtent,
7298        "%s:%s",format,color);
7299      (void) CloneString(&image_info->size,geometry);
7300      nexus=ReadImage(image_info,exception);
7301      CatchException(exception);
7302      XClientMessage(display,windows->image.id,windows->im_protocols,
7303        windows->im_next_image,CurrentTime);
7304      break;
7305    }
7306    case VisualDirectoryCommand:
7307    {
7308      /*
7309        Visual Image directory.
7310      */
7311      nexus=XVisualDirectoryImage(display,resource_info,windows,exception);
7312      break;
7313    }
7314    case QuitCommand:
7315    {
7316      /*
7317        exit program.
7318      */
7319      if( IfMagickFalse(resource_info->confirm_exit) )
7320        XClientMessage(display,windows->image.id,windows->im_protocols,
7321          windows->im_exit,CurrentTime);
7322      else
7323        {
7324          int
7325            status;
7326
7327          /*
7328            Confirm program exit.
7329          */
7330          status=XConfirmWidget(display,windows,"Do you really want to exit",
7331            resource_info->client_name);
7332          if (status > 0)
7333            XClientMessage(display,windows->image.id,windows->im_protocols,
7334              windows->im_exit,CurrentTime);
7335        }
7336      break;
7337    }
7338    case CutCommand:
7339    {
7340      /*
7341        Cut image.
7342      */
7343      (void) XCropImage(display,resource_info,windows,*image,CutMode,exception);
7344      break;
7345    }
7346    case CopyCommand:
7347    {
7348      /*
7349        Copy image.
7350      */
7351      (void) XCropImage(display,resource_info,windows,*image,CopyMode,
7352        exception);
7353      break;
7354    }
7355    case PasteCommand:
7356    {
7357      /*
7358        Paste image.
7359      */
7360      status=XPasteImage(display,resource_info,windows,*image,exception);
7361      if( IfMagickFalse(status) )
7362        {
7363          XNoticeWidget(display,windows,"Unable to paste X image",
7364            (*image)->filename);
7365          break;
7366        }
7367      break;
7368    }
7369    case HalfSizeCommand:
7370    {
7371      /*
7372        Half image size.
7373      */
7374      windows->image.window_changes.width=windows->image.ximage->width/2;
7375      windows->image.window_changes.height=windows->image.ximage->height/2;
7376      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7377      break;
7378    }
7379    case OriginalSizeCommand:
7380    {
7381      /*
7382        Original image size.
7383      */
7384      windows->image.window_changes.width=(int) (*image)->columns;
7385      windows->image.window_changes.height=(int) (*image)->rows;
7386      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7387      break;
7388    }
7389    case DoubleSizeCommand:
7390    {
7391      /*
7392        Double the image size.
7393      */
7394      windows->image.window_changes.width=windows->image.ximage->width << 1;
7395      windows->image.window_changes.height=windows->image.ximage->height << 1;
7396      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7397      break;
7398    }
7399    case ResizeCommand:
7400    {
7401      int
7402        status;
7403
7404      size_t
7405        height,
7406        width;
7407
7408      ssize_t
7409        x,
7410        y;
7411
7412      /*
7413        Resize image.
7414      */
7415      width=(size_t) windows->image.ximage->width;
7416      height=(size_t) windows->image.ximage->height;
7417      x=0;
7418      y=0;
7419      (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g+0+0",
7420        (double) width,(double) height);
7421      status=XDialogWidget(display,windows,"Resize",
7422        "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7423      if (*geometry == '\0')
7424        break;
7425      if (status == 0)
7426        (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7427      (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7428      windows->image.window_changes.width=(int) width;
7429      windows->image.window_changes.height=(int) height;
7430      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7431      break;
7432    }
7433    case ApplyCommand:
7434    {
7435      char
7436        image_geometry[MaxTextExtent];
7437
7438      if ((windows->image.crop_geometry == (char *) NULL) &&
7439          ((int) (*image)->columns == windows->image.ximage->width) &&
7440          ((int) (*image)->rows == windows->image.ximage->height))
7441        break;
7442      /*
7443        Apply size transforms to image.
7444      */
7445      XSetCursorState(display,windows,MagickTrue);
7446      XCheckRefreshWindows(display,windows);
7447      /*
7448        Crop and/or scale displayed image.
7449      */
7450      (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
7451        windows->image.ximage->width,windows->image.ximage->height);
7452      (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
7453        exception);
7454      if (windows->image.crop_geometry != (char *) NULL)
7455        windows->image.crop_geometry=(char *) RelinquishMagickMemory(
7456          windows->image.crop_geometry);
7457      windows->image.x=0;
7458      windows->image.y=0;
7459      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7460      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7461      break;
7462    }
7463    case RefreshCommand:
7464    {
7465      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7466      break;
7467    }
7468    case RestoreCommand:
7469    {
7470      /*
7471        Restore Image window to its original size.
7472      */
7473      if ((windows->image.width == (unsigned int) (*image)->columns) &&
7474          (windows->image.height == (unsigned int) (*image)->rows) &&
7475          (windows->image.crop_geometry == (char *) NULL))
7476        {
7477          (void) XBell(display,0);
7478          break;
7479        }
7480      windows->image.window_changes.width=(int) (*image)->columns;
7481      windows->image.window_changes.height=(int) (*image)->rows;
7482      if (windows->image.crop_geometry != (char *) NULL)
7483        {
7484          windows->image.crop_geometry=(char *)
7485            RelinquishMagickMemory(windows->image.crop_geometry);
7486          windows->image.crop_geometry=(char *) NULL;
7487          windows->image.x=0;
7488          windows->image.y=0;
7489        }
7490      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7491      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7492      break;
7493    }
7494    case CropCommand:
7495    {
7496      /*
7497        Crop image.
7498      */
7499      (void) XCropImage(display,resource_info,windows,*image,CropMode,
7500        exception);
7501      break;
7502    }
7503    case ChopCommand:
7504    {
7505      /*
7506        Chop image.
7507      */
7508      status=XChopImage(display,resource_info,windows,image,exception);
7509      if( IfMagickFalse(status) )
7510        {
7511          XNoticeWidget(display,windows,"Unable to cut X image",
7512            (*image)->filename);
7513          break;
7514        }
7515      break;
7516    }
7517    case FlopCommand:
7518    {
7519      Image
7520        *flop_image;
7521
7522      /*
7523        Flop image scanlines.
7524      */
7525      XSetCursorState(display,windows,MagickTrue);
7526      XCheckRefreshWindows(display,windows);
7527      flop_image=FlopImage(*image,exception);
7528      if (flop_image != (Image *) NULL)
7529        {
7530          *image=DestroyImage(*image);
7531          *image=flop_image;
7532        }
7533      CatchException(exception);
7534      XSetCursorState(display,windows,MagickFalse);
7535      if (windows->image.crop_geometry != (char *) NULL)
7536        {
7537          /*
7538            Flop crop geometry.
7539          */
7540          width=(unsigned int) (*image)->columns;
7541          height=(unsigned int) (*image)->rows;
7542          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7543            &width,&height);
7544          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7545            "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7546        }
7547      if( IfMagickTrue(windows->image.orphan) )
7548        break;
7549      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7550      break;
7551    }
7552    case FlipCommand:
7553    {
7554      Image
7555        *flip_image;
7556
7557      /*
7558        Flip image scanlines.
7559      */
7560      XSetCursorState(display,windows,MagickTrue);
7561      XCheckRefreshWindows(display,windows);
7562      flip_image=FlipImage(*image,exception);
7563      if (flip_image != (Image *) NULL)
7564        {
7565          *image=DestroyImage(*image);
7566          *image=flip_image;
7567        }
7568      CatchException(exception);
7569      XSetCursorState(display,windows,MagickFalse);
7570      if (windows->image.crop_geometry != (char *) NULL)
7571        {
7572          /*
7573            Flip crop geometry.
7574          */
7575          width=(unsigned int) (*image)->columns;
7576          height=(unsigned int) (*image)->rows;
7577          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7578            &width,&height);
7579          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7580            "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7581        }
7582      if( IfMagickTrue(windows->image.orphan) )
7583        break;
7584      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7585      break;
7586    }
7587    case RotateRightCommand:
7588    {
7589      /*
7590        Rotate image 90 degrees clockwise.
7591      */
7592      status=XRotateImage(display,resource_info,windows,90.0,image,exception);
7593      if( IfMagickFalse(status) )
7594        {
7595          XNoticeWidget(display,windows,"Unable to rotate X image",
7596            (*image)->filename);
7597          break;
7598        }
7599      break;
7600    }
7601    case RotateLeftCommand:
7602    {
7603      /*
7604        Rotate image 90 degrees counter-clockwise.
7605      */
7606      status=XRotateImage(display,resource_info,windows,-90.0,image,exception);
7607      if( IfMagickFalse(status) )
7608        {
7609          XNoticeWidget(display,windows,"Unable to rotate X image",
7610            (*image)->filename);
7611          break;
7612        }
7613      break;
7614    }
7615    case RotateCommand:
7616    {
7617      /*
7618        Rotate image.
7619      */
7620      status=XRotateImage(display,resource_info,windows,0.0,image,exception);
7621      if( IfMagickFalse(status) )
7622        {
7623          XNoticeWidget(display,windows,"Unable to rotate X image",
7624            (*image)->filename);
7625          break;
7626        }
7627      break;
7628    }
7629    case ShearCommand:
7630    {
7631      Image
7632        *shear_image;
7633
7634      static char
7635        geometry[MaxTextExtent] = "45.0x45.0";
7636
7637      /*
7638        Query user for shear color and geometry.
7639      */
7640      XColorBrowserWidget(display,windows,"Select",color);
7641      if (*color == '\0')
7642        break;
7643      (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7644        geometry);
7645      if (*geometry == '\0')
7646        break;
7647      /*
7648        Shear image.
7649      */
7650      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7651        exception);
7652      XSetCursorState(display,windows,MagickTrue);
7653      XCheckRefreshWindows(display,windows);
7654      (void) QueryColorCompliance(color,AllCompliance,
7655        &(*image)->background_color,exception);
7656      flags=ParseGeometry(geometry,&geometry_info);
7657      if ((flags & SigmaValue) == 0)
7658        geometry_info.sigma=geometry_info.rho;
7659      shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7660        exception);
7661      if (shear_image != (Image *) NULL)
7662        {
7663          *image=DestroyImage(*image);
7664          *image=shear_image;
7665        }
7666      CatchException(exception);
7667      XSetCursorState(display,windows,MagickFalse);
7668      if( IfMagickTrue(windows->image.orphan) )
7669        break;
7670      windows->image.window_changes.width=(int) (*image)->columns;
7671      windows->image.window_changes.height=(int) (*image)->rows;
7672      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7673      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7674      break;
7675    }
7676    case RollCommand:
7677    {
7678      Image
7679        *roll_image;
7680
7681      static char
7682        geometry[MaxTextExtent] = "+2+2";
7683
7684      /*
7685        Query user for the roll geometry.
7686      */
7687      (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7688        geometry);
7689      if (*geometry == '\0')
7690        break;
7691      /*
7692        Roll image.
7693      */
7694      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7695        exception);
7696      XSetCursorState(display,windows,MagickTrue);
7697      XCheckRefreshWindows(display,windows);
7698      (void) ParsePageGeometry(*image,geometry,&page_geometry,
7699        exception);
7700      roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7701        exception);
7702      if (roll_image != (Image *) NULL)
7703        {
7704          *image=DestroyImage(*image);
7705          *image=roll_image;
7706        }
7707      CatchException(exception);
7708      XSetCursorState(display,windows,MagickFalse);
7709      if( IfMagickTrue(windows->image.orphan) )
7710        break;
7711      windows->image.window_changes.width=(int) (*image)->columns;
7712      windows->image.window_changes.height=(int) (*image)->rows;
7713      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7714      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7715      break;
7716    }
7717    case TrimCommand:
7718    {
7719      static char
7720        fuzz[MaxTextExtent];
7721
7722      /*
7723        Query user for the fuzz factor.
7724      */
7725      (void) FormatLocaleString(fuzz,MaxTextExtent,"%g%%",100.0*
7726        (*image)->fuzz/(QuantumRange+1.0));
7727      (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7728      if (*fuzz == '\0')
7729        break;
7730      (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+1.0);
7731      /*
7732        Trim image.
7733      */
7734      status=XTrimImage(display,resource_info,windows,*image,exception);
7735      if( IfMagickFalse(status) )
7736        {
7737          XNoticeWidget(display,windows,"Unable to trim X image",
7738            (*image)->filename);
7739          break;
7740        }
7741      break;
7742    }
7743    case HueCommand:
7744    {
7745      static char
7746        hue_percent[MaxTextExtent] = "110";
7747
7748      /*
7749        Query user for percent hue change.
7750      */
7751      (void) XDialogWidget(display,windows,"Apply",
7752        "Enter percent change in image hue (0-200):",hue_percent);
7753      if (*hue_percent == '\0')
7754        break;
7755      /*
7756        Vary the image hue.
7757      */
7758      XSetCursorState(display,windows,MagickTrue);
7759      XCheckRefreshWindows(display,windows);
7760      (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7761      (void) ConcatenateMagickString(modulate_factors,hue_percent,
7762        MaxTextExtent);
7763      (void) ModulateImage(*image,modulate_factors,exception);
7764      XSetCursorState(display,windows,MagickFalse);
7765      if( IfMagickTrue(windows->image.orphan) )
7766        break;
7767      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7768      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7769      break;
7770    }
7771    case SaturationCommand:
7772    {
7773      static char
7774        saturation_percent[MaxTextExtent] = "110";
7775
7776      /*
7777        Query user for percent saturation change.
7778      */
7779      (void) XDialogWidget(display,windows,"Apply",
7780        "Enter percent change in color saturation (0-200):",saturation_percent);
7781      if (*saturation_percent == '\0')
7782        break;
7783      /*
7784        Vary color saturation.
7785      */
7786      XSetCursorState(display,windows,MagickTrue);
7787      XCheckRefreshWindows(display,windows);
7788      (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7789      (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7790        MaxTextExtent);
7791      (void) ModulateImage(*image,modulate_factors,exception);
7792      XSetCursorState(display,windows,MagickFalse);
7793      if( IfMagickTrue(windows->image.orphan) )
7794        break;
7795      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7796      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7797      break;
7798    }
7799    case BrightnessCommand:
7800    {
7801      static char
7802        brightness_percent[MaxTextExtent] = "110";
7803
7804      /*
7805        Query user for percent brightness change.
7806      */
7807      (void) XDialogWidget(display,windows,"Apply",
7808        "Enter percent change in color brightness (0-200):",brightness_percent);
7809      if (*brightness_percent == '\0')
7810        break;
7811      /*
7812        Vary the color brightness.
7813      */
7814      XSetCursorState(display,windows,MagickTrue);
7815      XCheckRefreshWindows(display,windows);
7816      (void) CopyMagickString(modulate_factors,brightness_percent,
7817        MaxTextExtent);
7818      (void) ModulateImage(*image,modulate_factors,exception);
7819      XSetCursorState(display,windows,MagickFalse);
7820      if( IfMagickTrue(windows->image.orphan) )
7821        break;
7822      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7823      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7824      break;
7825    }
7826    case GammaCommand:
7827    {
7828      static char
7829        factor[MaxTextExtent] = "1.6";
7830
7831      /*
7832        Query user for gamma value.
7833      */
7834      (void) XDialogWidget(display,windows,"Gamma",
7835        "Enter gamma value (e.g. 1.2):",factor);
7836      if (*factor == '\0')
7837        break;
7838      /*
7839        Gamma correct image.
7840      */
7841      XSetCursorState(display,windows,MagickTrue);
7842      XCheckRefreshWindows(display,windows);
7843      (void) GammaImage(*image,atof(factor),exception);
7844      XSetCursorState(display,windows,MagickFalse);
7845      if( IfMagickTrue(windows->image.orphan) )
7846        break;
7847      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7848      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7849      break;
7850    }
7851    case SpiffCommand:
7852    {
7853      /*
7854        Sharpen the image contrast.
7855      */
7856      XSetCursorState(display,windows,MagickTrue);
7857      XCheckRefreshWindows(display,windows);
7858      (void) ContrastImage(*image,MagickTrue,exception);
7859      XSetCursorState(display,windows,MagickFalse);
7860      if( IfMagickTrue(windows->image.orphan) )
7861        break;
7862      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7863      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7864      break;
7865    }
7866    case DullCommand:
7867    {
7868      /*
7869        Dull the image contrast.
7870      */
7871      XSetCursorState(display,windows,MagickTrue);
7872      XCheckRefreshWindows(display,windows);
7873      (void) ContrastImage(*image,MagickFalse,exception);
7874      XSetCursorState(display,windows,MagickFalse);
7875      if( IfMagickTrue(windows->image.orphan) )
7876        break;
7877      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7878      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7879      break;
7880    }
7881    case ContrastStretchCommand:
7882    {
7883      double
7884        black_point,
7885        white_point;
7886
7887      static char
7888        levels[MaxTextExtent] = "1%";
7889
7890      /*
7891        Query user for gamma value.
7892      */
7893      (void) XDialogWidget(display,windows,"Contrast Stretch",
7894        "Enter black and white points:",levels);
7895      if (*levels == '\0')
7896        break;
7897      /*
7898        Contrast stretch image.
7899      */
7900      XSetCursorState(display,windows,MagickTrue);
7901      XCheckRefreshWindows(display,windows);
7902      flags=ParseGeometry(levels,&geometry_info);
7903      black_point=geometry_info.rho;
7904      white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7905      if ((flags & PercentValue) != 0)
7906        {
7907          black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7908          white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7909        }
7910      white_point=(double) (*image)->columns*(*image)->rows-white_point;
7911      (void) ContrastStretchImage(*image,black_point,white_point,
7912        exception);
7913      XSetCursorState(display,windows,MagickFalse);
7914      if( IfMagickTrue(windows->image.orphan) )
7915        break;
7916      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7917      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7918      break;
7919    }
7920    case SigmoidalContrastCommand:
7921    {
7922      GeometryInfo
7923        geometry_info;
7924
7925      MagickStatusType
7926        flags;
7927
7928      static char
7929        levels[MaxTextExtent] = "3x50%";
7930
7931      /*
7932        Query user for gamma value.
7933      */
7934      (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7935        "Enter contrast and midpoint:",levels);
7936      if (*levels == '\0')
7937        break;
7938      /*
7939        Contrast stretch image.
7940      */
7941      XSetCursorState(display,windows,MagickTrue);
7942      XCheckRefreshWindows(display,windows);
7943      flags=ParseGeometry(levels,&geometry_info);
7944      if ((flags & SigmaValue) == 0)
7945        geometry_info.sigma=1.0*QuantumRange/2.0;
7946      if ((flags & PercentValue) != 0)
7947        geometry_info.sigma=1.0*QuantumRange*geometry_info.sigma/100.0;
7948      (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
7949        geometry_info.sigma,exception);
7950      XSetCursorState(display,windows,MagickFalse);
7951      if( IfMagickTrue(windows->image.orphan) )
7952        break;
7953      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7954      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7955      break;
7956    }
7957    case NormalizeCommand:
7958    {
7959      /*
7960        Perform histogram normalization on the image.
7961      */
7962      XSetCursorState(display,windows,MagickTrue);
7963      XCheckRefreshWindows(display,windows);
7964      (void) NormalizeImage(*image,exception);
7965      XSetCursorState(display,windows,MagickFalse);
7966      if( IfMagickTrue(windows->image.orphan) )
7967        break;
7968      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7969      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7970      break;
7971    }
7972    case EqualizeCommand:
7973    {
7974      /*
7975        Perform histogram equalization on the image.
7976      */
7977      XSetCursorState(display,windows,MagickTrue);
7978      XCheckRefreshWindows(display,windows);
7979      (void) EqualizeImage(*image,exception);
7980      XSetCursorState(display,windows,MagickFalse);
7981      if( IfMagickTrue(windows->image.orphan) )
7982        break;
7983      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7984      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7985      break;
7986    }
7987    case NegateCommand:
7988    {
7989      /*
7990        Negate colors in image.
7991      */
7992      XSetCursorState(display,windows,MagickTrue);
7993      XCheckRefreshWindows(display,windows);
7994      (void) NegateImage(*image,MagickFalse,exception);
7995      XSetCursorState(display,windows,MagickFalse);
7996      if( IfMagickTrue(windows->image.orphan) )
7997        break;
7998      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7999      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8000      break;
8001    }
8002    case GrayscaleCommand:
8003    {
8004      /*
8005        Convert image to grayscale.
8006      */
8007      XSetCursorState(display,windows,MagickTrue);
8008      XCheckRefreshWindows(display,windows);
8009      (void) SetImageType(*image,(*image)->alpha_trait != BlendPixelTrait ?
8010        GrayscaleType : GrayscaleMatteType,exception);
8011      XSetCursorState(display,windows,MagickFalse);
8012      if( IfMagickTrue(windows->image.orphan) )
8013        break;
8014      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8015      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8016      break;
8017    }
8018    case MapCommand:
8019    {
8020      Image
8021        *affinity_image;
8022
8023      static char
8024        filename[MaxTextExtent] = "\0";
8025
8026      /*
8027        Request image file name from user.
8028      */
8029      XFileBrowserWidget(display,windows,"Map",filename);
8030      if (*filename == '\0')
8031        break;
8032      /*
8033        Map image.
8034      */
8035      XSetCursorState(display,windows,MagickTrue);
8036      XCheckRefreshWindows(display,windows);
8037      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
8038      affinity_image=ReadImage(image_info,exception);
8039      if (affinity_image != (Image *) NULL)
8040        {
8041          (void) RemapImage(&quantize_info,*image,affinity_image,exception);
8042          affinity_image=DestroyImage(affinity_image);
8043        }
8044      CatchException(exception);
8045      XSetCursorState(display,windows,MagickFalse);
8046      if( IfMagickTrue(windows->image.orphan) )
8047        break;
8048      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8049      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8050      break;
8051    }
8052    case QuantizeCommand:
8053    {
8054      int
8055        status;
8056
8057      static char
8058        colors[MaxTextExtent] = "256";
8059
8060      /*
8061        Query user for maximum number of colors.
8062      */
8063      status=XDialogWidget(display,windows,"Quantize",
8064        "Maximum number of colors:",colors);
8065      if (*colors == '\0')
8066        break;
8067      /*
8068        Color reduce the image.
8069      */
8070      XSetCursorState(display,windows,MagickTrue);
8071      XCheckRefreshWindows(display,windows);
8072      quantize_info.number_colors=StringToUnsignedLong(colors);
8073      quantize_info.dither_method=status != 0 ? RiemersmaDitherMethod :
8074        NoDitherMethod;
8075      (void) QuantizeImage(&quantize_info,*image,exception);
8076      XSetCursorState(display,windows,MagickFalse);
8077      if( IfMagickTrue(windows->image.orphan) )
8078        break;
8079      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8080      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8081      break;
8082    }
8083    case DespeckleCommand:
8084    {
8085      Image
8086        *despeckle_image;
8087
8088      /*
8089        Despeckle image.
8090      */
8091      XSetCursorState(display,windows,MagickTrue);
8092      XCheckRefreshWindows(display,windows);
8093      despeckle_image=DespeckleImage(*image,exception);
8094      if (despeckle_image != (Image *) NULL)
8095        {
8096          *image=DestroyImage(*image);
8097          *image=despeckle_image;
8098        }
8099      CatchException(exception);
8100      XSetCursorState(display,windows,MagickFalse);
8101      if( IfMagickTrue(windows->image.orphan) )
8102        break;
8103      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8104      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8105      break;
8106    }
8107    case EmbossCommand:
8108    {
8109      Image
8110        *emboss_image;
8111
8112      static char
8113        radius[MaxTextExtent] = "0.0x1.0";
8114
8115      /*
8116        Query user for emboss radius.
8117      */
8118      (void) XDialogWidget(display,windows,"Emboss",
8119        "Enter the emboss radius and standard deviation:",radius);
8120      if (*radius == '\0')
8121        break;
8122      /*
8123        Reduce noise in the image.
8124      */
8125      XSetCursorState(display,windows,MagickTrue);
8126      XCheckRefreshWindows(display,windows);
8127      flags=ParseGeometry(radius,&geometry_info);
8128      if ((flags & SigmaValue) == 0)
8129        geometry_info.sigma=1.0;
8130      emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8131        exception);
8132      if (emboss_image != (Image *) NULL)
8133        {
8134          *image=DestroyImage(*image);
8135          *image=emboss_image;
8136        }
8137      CatchException(exception);
8138      XSetCursorState(display,windows,MagickFalse);
8139      if( IfMagickTrue(windows->image.orphan) )
8140        break;
8141      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8142      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8143      break;
8144    }
8145    case ReduceNoiseCommand:
8146    {
8147      Image
8148        *noise_image;
8149
8150      static char
8151        radius[MaxTextExtent] = "0";
8152
8153      /*
8154        Query user for noise radius.
8155      */
8156      (void) XDialogWidget(display,windows,"Reduce Noise",
8157        "Enter the noise radius:",radius);
8158      if (*radius == '\0')
8159        break;
8160      /*
8161        Reduce noise in the image.
8162      */
8163      XSetCursorState(display,windows,MagickTrue);
8164      XCheckRefreshWindows(display,windows);
8165      flags=ParseGeometry(radius,&geometry_info);
8166      noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8167        geometry_info.rho,(size_t) geometry_info.rho,exception);
8168      if (noise_image != (Image *) NULL)
8169        {
8170          *image=DestroyImage(*image);
8171          *image=noise_image;
8172        }
8173      CatchException(exception);
8174      XSetCursorState(display,windows,MagickFalse);
8175      if( IfMagickTrue(windows->image.orphan) )
8176        break;
8177      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8178      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8179      break;
8180    }
8181    case AddNoiseCommand:
8182    {
8183      char
8184        **noises;
8185
8186      Image
8187        *noise_image;
8188
8189      static char
8190        noise_type[MaxTextExtent] = "Gaussian";
8191
8192      /*
8193        Add noise to the image.
8194      */
8195      noises=GetCommandOptions(MagickNoiseOptions);
8196      if (noises == (char **) NULL)
8197        break;
8198      XListBrowserWidget(display,windows,&windows->widget,
8199        (const char **) noises,"Add Noise",
8200        "Select a type of noise to add to your image:",noise_type);
8201      noises=DestroyStringList(noises);
8202      if (*noise_type == '\0')
8203        break;
8204      XSetCursorState(display,windows,MagickTrue);
8205      XCheckRefreshWindows(display,windows);
8206      noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8207        MagickNoiseOptions,MagickFalse,noise_type),1.0,exception);
8208      if (noise_image != (Image *) NULL)
8209        {
8210          *image=DestroyImage(*image);
8211          *image=noise_image;
8212        }
8213      CatchException(exception);
8214      XSetCursorState(display,windows,MagickFalse);
8215      if( IfMagickTrue(windows->image.orphan) )
8216        break;
8217      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8218      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8219      break;
8220    }
8221    case SharpenCommand:
8222    {
8223      Image
8224        *sharp_image;
8225
8226      static char
8227        radius[MaxTextExtent] = "0.0x1.0";
8228
8229      /*
8230        Query user for sharpen radius.
8231      */
8232      (void) XDialogWidget(display,windows,"Sharpen",
8233        "Enter the sharpen radius and standard deviation:",radius);
8234      if (*radius == '\0')
8235        break;
8236      /*
8237        Sharpen image scanlines.
8238      */
8239      XSetCursorState(display,windows,MagickTrue);
8240      XCheckRefreshWindows(display,windows);
8241      flags=ParseGeometry(radius,&geometry_info);
8242      sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8243        exception);
8244      if (sharp_image != (Image *) NULL)
8245        {
8246          *image=DestroyImage(*image);
8247          *image=sharp_image;
8248        }
8249      CatchException(exception);
8250      XSetCursorState(display,windows,MagickFalse);
8251      if( IfMagickTrue(windows->image.orphan) )
8252        break;
8253      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8254      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8255      break;
8256    }
8257    case BlurCommand:
8258    {
8259      Image
8260        *blur_image;
8261
8262      static char
8263        radius[MaxTextExtent] = "0.0x1.0";
8264
8265      /*
8266        Query user for blur radius.
8267      */
8268      (void) XDialogWidget(display,windows,"Blur",
8269        "Enter the blur radius and standard deviation:",radius);
8270      if (*radius == '\0')
8271        break;
8272      /*
8273        Blur an image.
8274      */
8275      XSetCursorState(display,windows,MagickTrue);
8276      XCheckRefreshWindows(display,windows);
8277      flags=ParseGeometry(radius,&geometry_info);
8278      blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8279        exception);
8280      if (blur_image != (Image *) NULL)
8281        {
8282          *image=DestroyImage(*image);
8283          *image=blur_image;
8284        }
8285      CatchException(exception);
8286      XSetCursorState(display,windows,MagickFalse);
8287      if( IfMagickTrue(windows->image.orphan) )
8288        break;
8289      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8290      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8291      break;
8292    }
8293    case ThresholdCommand:
8294    {
8295      double
8296        threshold;
8297
8298      static char
8299        factor[MaxTextExtent] = "128";
8300
8301      /*
8302        Query user for threshold value.
8303      */
8304      (void) XDialogWidget(display,windows,"Threshold",
8305        "Enter threshold value:",factor);
8306      if (*factor == '\0')
8307        break;
8308      /*
8309        Gamma correct image.
8310      */
8311      XSetCursorState(display,windows,MagickTrue);
8312      XCheckRefreshWindows(display,windows);
8313      threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8314      (void) BilevelImage(*image,threshold,exception);
8315      XSetCursorState(display,windows,MagickFalse);
8316      if( IfMagickTrue(windows->image.orphan) )
8317        break;
8318      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8319      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8320      break;
8321    }
8322    case EdgeDetectCommand:
8323    {
8324      Image
8325        *edge_image;
8326
8327      static char
8328        radius[MaxTextExtent] = "0";
8329
8330      /*
8331        Query user for edge factor.
8332      */
8333      (void) XDialogWidget(display,windows,"Detect Edges",
8334        "Enter the edge detect radius:",radius);
8335      if (*radius == '\0')
8336        break;
8337      /*
8338        Detect edge in image.
8339      */
8340      XSetCursorState(display,windows,MagickTrue);
8341      XCheckRefreshWindows(display,windows);
8342      flags=ParseGeometry(radius,&geometry_info);
8343      edge_image=EdgeImage(*image,geometry_info.rho,geometry_info.sigma,
8344        exception);
8345      if (edge_image != (Image *) NULL)
8346        {
8347          *image=DestroyImage(*image);
8348          *image=edge_image;
8349        }
8350      CatchException(exception);
8351      XSetCursorState(display,windows,MagickFalse);
8352      if( IfMagickTrue(windows->image.orphan) )
8353        break;
8354      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8355      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8356      break;
8357    }
8358    case SpreadCommand:
8359    {
8360      Image
8361        *spread_image;
8362
8363      static char
8364        amount[MaxTextExtent] = "2";
8365
8366      /*
8367        Query user for spread amount.
8368      */
8369      (void) XDialogWidget(display,windows,"Spread",
8370        "Enter the displacement amount:",amount);
8371      if (*amount == '\0')
8372        break;
8373      /*
8374        Displace image pixels by a random amount.
8375      */
8376      XSetCursorState(display,windows,MagickTrue);
8377      XCheckRefreshWindows(display,windows);
8378      flags=ParseGeometry(amount,&geometry_info);
8379      spread_image=EdgeImage(*image,geometry_info.rho,geometry_info.sigma,
8380        exception);
8381      if (spread_image != (Image *) NULL)
8382        {
8383          *image=DestroyImage(*image);
8384          *image=spread_image;
8385        }
8386      CatchException(exception);
8387      XSetCursorState(display,windows,MagickFalse);
8388      if( IfMagickTrue(windows->image.orphan) )
8389        break;
8390      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8391      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8392      break;
8393    }
8394    case ShadeCommand:
8395    {
8396      Image
8397        *shade_image;
8398
8399      int
8400        status;
8401
8402      static char
8403        geometry[MaxTextExtent] = "30x30";
8404
8405      /*
8406        Query user for the shade geometry.
8407      */
8408      status=XDialogWidget(display,windows,"Shade",
8409        "Enter the azimuth and elevation of the light source:",geometry);
8410      if (*geometry == '\0')
8411        break;
8412      /*
8413        Shade image pixels.
8414      */
8415      XSetCursorState(display,windows,MagickTrue);
8416      XCheckRefreshWindows(display,windows);
8417      flags=ParseGeometry(geometry,&geometry_info);
8418      if ((flags & SigmaValue) == 0)
8419        geometry_info.sigma=1.0;
8420      shade_image=ShadeImage(*image,IsMagickTrue(status),
8421        geometry_info.rho,geometry_info.sigma,exception);
8422      if (shade_image != (Image *) NULL)
8423        {
8424          *image=DestroyImage(*image);
8425          *image=shade_image;
8426        }
8427      CatchException(exception);
8428      XSetCursorState(display,windows,MagickFalse);
8429      if( IfMagickTrue(windows->image.orphan) )
8430        break;
8431      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8432      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8433      break;
8434    }
8435    case RaiseCommand:
8436    {
8437      static char
8438        bevel_width[MaxTextExtent] = "10";
8439
8440      /*
8441        Query user for bevel width.
8442      */
8443      (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8444      if (*bevel_width == '\0')
8445        break;
8446      /*
8447        Raise an image.
8448      */
8449      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8450        exception);
8451      XSetCursorState(display,windows,MagickTrue);
8452      XCheckRefreshWindows(display,windows);
8453      (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8454        exception);
8455      (void) RaiseImage(*image,&page_geometry,MagickTrue,exception);
8456      XSetCursorState(display,windows,MagickFalse);
8457      if( IfMagickTrue(windows->image.orphan) )
8458        break;
8459      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8460      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8461      break;
8462    }
8463    case SegmentCommand:
8464    {
8465      static char
8466        threshold[MaxTextExtent] = "1.0x1.5";
8467
8468      /*
8469        Query user for smoothing threshold.
8470      */
8471      (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8472        threshold);
8473      if (*threshold == '\0')
8474        break;
8475      /*
8476        Segment an image.
8477      */
8478      XSetCursorState(display,windows,MagickTrue);
8479      XCheckRefreshWindows(display,windows);
8480      flags=ParseGeometry(threshold,&geometry_info);
8481      if ((flags & SigmaValue) == 0)
8482        geometry_info.sigma=1.0;
8483      (void) SegmentImage(*image,sRGBColorspace,MagickFalse,geometry_info.rho,
8484        geometry_info.sigma,exception);
8485      XSetCursorState(display,windows,MagickFalse);
8486      if( IfMagickTrue(windows->image.orphan) )
8487        break;
8488      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8489      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8490      break;
8491    }
8492    case SepiaToneCommand:
8493    {
8494      double
8495        threshold;
8496
8497      Image
8498        *sepia_image;
8499
8500      static char
8501        factor[MaxTextExtent] = "80%";
8502
8503      /*
8504        Query user for sepia-tone factor.
8505      */
8506      (void) XDialogWidget(display,windows,"Sepia Tone",
8507        "Enter the sepia tone factor (0 - 99.9%):",factor);
8508      if (*factor == '\0')
8509        break;
8510      /*
8511        Sepia tone image pixels.
8512      */
8513      XSetCursorState(display,windows,MagickTrue);
8514      XCheckRefreshWindows(display,windows);
8515      threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8516      sepia_image=SepiaToneImage(*image,threshold,exception);
8517      if (sepia_image != (Image *) NULL)
8518        {
8519          *image=DestroyImage(*image);
8520          *image=sepia_image;
8521        }
8522      CatchException(exception);
8523      XSetCursorState(display,windows,MagickFalse);
8524      if( IfMagickTrue(windows->image.orphan) )
8525        break;
8526      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8527      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8528      break;
8529    }
8530    case SolarizeCommand:
8531    {
8532      double
8533        threshold;
8534
8535      static char
8536        factor[MaxTextExtent] = "60%";
8537
8538      /*
8539        Query user for solarize factor.
8540      */
8541      (void) XDialogWidget(display,windows,"Solarize",
8542        "Enter the solarize factor (0 - 99.9%):",factor);
8543      if (*factor == '\0')
8544        break;
8545      /*
8546        Solarize image pixels.
8547      */
8548      XSetCursorState(display,windows,MagickTrue);
8549      XCheckRefreshWindows(display,windows);
8550      threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8551      (void) SolarizeImage(*image,threshold,exception);
8552      XSetCursorState(display,windows,MagickFalse);
8553      if( IfMagickTrue(windows->image.orphan) )
8554        break;
8555      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8556      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8557      break;
8558    }
8559    case SwirlCommand:
8560    {
8561      Image
8562        *swirl_image;
8563
8564      static char
8565        degrees[MaxTextExtent] = "60";
8566
8567      /*
8568        Query user for swirl angle.
8569      */
8570      (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8571        degrees);
8572      if (*degrees == '\0')
8573        break;
8574      /*
8575        Swirl image pixels about the center.
8576      */
8577      XSetCursorState(display,windows,MagickTrue);
8578      XCheckRefreshWindows(display,windows);
8579      flags=ParseGeometry(degrees,&geometry_info);
8580      swirl_image=SwirlImage(*image,geometry_info.rho,(*image)->interpolate,
8581        exception);
8582      if (swirl_image != (Image *) NULL)
8583        {
8584          *image=DestroyImage(*image);
8585          *image=swirl_image;
8586        }
8587      CatchException(exception);
8588      XSetCursorState(display,windows,MagickFalse);
8589      if( IfMagickTrue(windows->image.orphan) )
8590        break;
8591      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8592      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8593      break;
8594    }
8595    case ImplodeCommand:
8596    {
8597      Image
8598        *implode_image;
8599
8600      static char
8601        factor[MaxTextExtent] = "0.3";
8602
8603      /*
8604        Query user for implode factor.
8605      */
8606      (void) XDialogWidget(display,windows,"Implode",
8607        "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8608      if (*factor == '\0')
8609        break;
8610      /*
8611        Implode image pixels about the center.
8612      */
8613      XSetCursorState(display,windows,MagickTrue);
8614      XCheckRefreshWindows(display,windows);
8615      flags=ParseGeometry(factor,&geometry_info);
8616      implode_image=ImplodeImage(*image,geometry_info.rho,(*image)->interpolate,
8617        exception);
8618      if (implode_image != (Image *) NULL)
8619        {
8620          *image=DestroyImage(*image);
8621          *image=implode_image;
8622        }
8623      CatchException(exception);
8624      XSetCursorState(display,windows,MagickFalse);
8625      if( IfMagickTrue(windows->image.orphan) )
8626        break;
8627      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8628      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8629      break;
8630    }
8631    case VignetteCommand:
8632    {
8633      Image
8634        *vignette_image;
8635
8636      static char
8637        geometry[MaxTextExtent] = "0x20";
8638
8639      /*
8640        Query user for the vignette geometry.
8641      */
8642      (void) XDialogWidget(display,windows,"Vignette",
8643        "Enter the radius, sigma, and x and y offsets:",geometry);
8644      if (*geometry == '\0')
8645        break;
8646      /*
8647        Soften the edges of the image in vignette style
8648      */
8649      XSetCursorState(display,windows,MagickTrue);
8650      XCheckRefreshWindows(display,windows);
8651      flags=ParseGeometry(geometry,&geometry_info);
8652      if ((flags & SigmaValue) == 0)
8653        geometry_info.sigma=1.0;
8654      if ((flags & XiValue) == 0)
8655        geometry_info.xi=0.1*(*image)->columns;
8656      if ((flags & PsiValue) == 0)
8657        geometry_info.psi=0.1*(*image)->rows;
8658      vignette_image=VignetteImage(*image,geometry_info.rho,0.0,(ssize_t)
8659        ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-0.5),
8660        exception);
8661      if (vignette_image != (Image *) NULL)
8662        {
8663          *image=DestroyImage(*image);
8664          *image=vignette_image;
8665        }
8666      CatchException(exception);
8667      XSetCursorState(display,windows,MagickFalse);
8668      if( IfMagickTrue(windows->image.orphan) )
8669        break;
8670      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8671      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8672      break;
8673    }
8674    case WaveCommand:
8675    {
8676      Image
8677        *wave_image;
8678
8679      static char
8680        geometry[MaxTextExtent] = "25x150";
8681
8682      /*
8683        Query user for the wave geometry.
8684      */
8685      (void) XDialogWidget(display,windows,"Wave",
8686        "Enter the amplitude and length of the wave:",geometry);
8687      if (*geometry == '\0')
8688        break;
8689      /*
8690        Alter an image along a sine wave.
8691      */
8692      XSetCursorState(display,windows,MagickTrue);
8693      XCheckRefreshWindows(display,windows);
8694      flags=ParseGeometry(geometry,&geometry_info);
8695      if ((flags & SigmaValue) == 0)
8696        geometry_info.sigma=1.0;
8697      wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8698        (*image)->interpolate,exception);
8699      if (wave_image != (Image *) NULL)
8700        {
8701          *image=DestroyImage(*image);
8702          *image=wave_image;
8703        }
8704      CatchException(exception);
8705      XSetCursorState(display,windows,MagickFalse);
8706      if( IfMagickTrue(windows->image.orphan) )
8707        break;
8708      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8709      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8710      break;
8711    }
8712    case OilPaintCommand:
8713    {
8714      Image
8715        *paint_image;
8716
8717      static char
8718        radius[MaxTextExtent] = "0";
8719
8720      /*
8721        Query user for circular neighborhood radius.
8722      */
8723      (void) XDialogWidget(display,windows,"Oil Paint",
8724        "Enter the mask radius:",radius);
8725      if (*radius == '\0')
8726        break;
8727      /*
8728        OilPaint image scanlines.
8729      */
8730      XSetCursorState(display,windows,MagickTrue);
8731      XCheckRefreshWindows(display,windows);
8732      flags=ParseGeometry(radius,&geometry_info);
8733      paint_image=OilPaintImage(*image,geometry_info.rho,geometry_info.sigma,
8734        exception);
8735      if (paint_image != (Image *) NULL)
8736        {
8737          *image=DestroyImage(*image);
8738          *image=paint_image;
8739        }
8740      CatchException(exception);
8741      XSetCursorState(display,windows,MagickFalse);
8742      if( IfMagickTrue(windows->image.orphan) )
8743        break;
8744      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8745      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8746      break;
8747    }
8748    case CharcoalDrawCommand:
8749    {
8750      Image
8751        *charcoal_image;
8752
8753      static char
8754        radius[MaxTextExtent] = "0x1";
8755
8756      /*
8757        Query user for charcoal radius.
8758      */
8759      (void) XDialogWidget(display,windows,"Charcoal Draw",
8760        "Enter the charcoal radius and sigma:",radius);
8761      if (*radius == '\0')
8762        break;
8763      /*
8764        Charcoal the image.
8765      */
8766      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8767        exception);
8768      XSetCursorState(display,windows,MagickTrue);
8769      XCheckRefreshWindows(display,windows);
8770      flags=ParseGeometry(radius,&geometry_info);
8771      if ((flags & SigmaValue) == 0)
8772        geometry_info.sigma=geometry_info.rho;
8773      charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8774        exception);
8775      if (charcoal_image != (Image *) NULL)
8776        {
8777          *image=DestroyImage(*image);
8778          *image=charcoal_image;
8779        }
8780      CatchException(exception);
8781      XSetCursorState(display,windows,MagickFalse);
8782      if( IfMagickTrue(windows->image.orphan) )
8783        break;
8784      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8785      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8786      break;
8787    }
8788    case AnnotateCommand:
8789    {
8790      /*
8791        Annotate the image with text.
8792      */
8793      status=XAnnotateEditImage(display,resource_info,windows,*image,exception);
8794      if( IfMagickFalse(status) )
8795        {
8796          XNoticeWidget(display,windows,"Unable to annotate X image",
8797            (*image)->filename);
8798          break;
8799        }
8800      break;
8801    }
8802    case DrawCommand:
8803    {
8804      /*
8805        Draw image.
8806      */
8807      status=XDrawEditImage(display,resource_info,windows,image,exception);
8808      if( IfMagickFalse(status) )
8809        {
8810          XNoticeWidget(display,windows,"Unable to draw on the X image",
8811            (*image)->filename);
8812          break;
8813        }
8814      break;
8815    }
8816    case ColorCommand:
8817    {
8818      /*
8819        Color edit.
8820      */
8821      status=XColorEditImage(display,resource_info,windows,image,exception);
8822      if( IfMagickFalse(status) )
8823        {
8824          XNoticeWidget(display,windows,"Unable to pixel edit X image",
8825            (*image)->filename);
8826          break;
8827        }
8828      break;
8829    }
8830    case MatteCommand:
8831    {
8832      /*
8833        Matte edit.
8834      */
8835      status=XMatteEditImage(display,resource_info,windows,image,exception);
8836      if( IfMagickFalse(status) )
8837        {
8838          XNoticeWidget(display,windows,"Unable to matte edit X image",
8839            (*image)->filename);
8840          break;
8841        }
8842      break;
8843    }
8844    case CompositeCommand:
8845    {
8846      /*
8847        Composite image.
8848      */
8849      status=XCompositeImage(display,resource_info,windows,*image,
8850        exception);
8851      if( IfMagickFalse(status) )
8852        {
8853          XNoticeWidget(display,windows,"Unable to composite X image",
8854            (*image)->filename);
8855          break;
8856        }
8857      break;
8858    }
8859    case AddBorderCommand:
8860    {
8861      Image
8862        *border_image;
8863
8864      static char
8865        geometry[MaxTextExtent] = "6x6";
8866
8867      /*
8868        Query user for border color and geometry.
8869      */
8870      XColorBrowserWidget(display,windows,"Select",color);
8871      if (*color == '\0')
8872        break;
8873      (void) XDialogWidget(display,windows,"Add Border",
8874        "Enter border geometry:",geometry);
8875      if (*geometry == '\0')
8876        break;
8877      /*
8878        Add a border to the image.
8879      */
8880      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8881        exception);
8882      XSetCursorState(display,windows,MagickTrue);
8883      XCheckRefreshWindows(display,windows);
8884      (void) QueryColorCompliance(color,AllCompliance,&(*image)->border_color,
8885        exception);
8886      (void) ParsePageGeometry(*image,geometry,&page_geometry,
8887        exception);
8888      border_image=BorderImage(*image,&page_geometry,(*image)->compose,
8889        exception);
8890      if (border_image != (Image *) NULL)
8891        {
8892          *image=DestroyImage(*image);
8893          *image=border_image;
8894        }
8895      CatchException(exception);
8896      XSetCursorState(display,windows,MagickFalse);
8897      if( IfMagickTrue(windows->image.orphan) )
8898        break;
8899      windows->image.window_changes.width=(int) (*image)->columns;
8900      windows->image.window_changes.height=(int) (*image)->rows;
8901      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8902      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8903      break;
8904    }
8905    case AddFrameCommand:
8906    {
8907      FrameInfo
8908        frame_info;
8909
8910      Image
8911        *frame_image;
8912
8913      static char
8914        geometry[MaxTextExtent] = "6x6";
8915
8916      /*
8917        Query user for frame color and geometry.
8918      */
8919      XColorBrowserWidget(display,windows,"Select",color);
8920      if (*color == '\0')
8921        break;
8922      (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8923        geometry);
8924      if (*geometry == '\0')
8925        break;
8926      /*
8927        Surround image with an ornamental border.
8928      */
8929      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8930        exception);
8931      XSetCursorState(display,windows,MagickTrue);
8932      XCheckRefreshWindows(display,windows);
8933      (void) QueryColorCompliance(color,AllCompliance,&(*image)->matte_color,
8934        exception);
8935      (void) ParsePageGeometry(*image,geometry,&page_geometry,
8936        exception);
8937      frame_info.width=page_geometry.width;
8938      frame_info.height=page_geometry.height;
8939      frame_info.outer_bevel=page_geometry.x;
8940      frame_info.inner_bevel=page_geometry.y;
8941      frame_info.x=(ssize_t) frame_info.width;
8942      frame_info.y=(ssize_t) frame_info.height;
8943      frame_info.width=(*image)->columns+2*frame_info.width;
8944      frame_info.height=(*image)->rows+2*frame_info.height;
8945      frame_image=FrameImage(*image,&frame_info,(*image)->compose,exception);
8946      if (frame_image != (Image *) NULL)
8947        {
8948          *image=DestroyImage(*image);
8949          *image=frame_image;
8950        }
8951      CatchException(exception);
8952      XSetCursorState(display,windows,MagickFalse);
8953      if( IfMagickTrue(windows->image.orphan) )
8954        break;
8955      windows->image.window_changes.width=(int) (*image)->columns;
8956      windows->image.window_changes.height=(int) (*image)->rows;
8957      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8958      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8959      break;
8960    }
8961    case CommentCommand:
8962    {
8963      const char
8964        *value;
8965
8966      FILE
8967        *file;
8968
8969      int
8970        unique_file;
8971
8972      /*
8973        Edit image comment.
8974      */
8975      unique_file=AcquireUniqueFileResource(image_info->filename);
8976      if (unique_file == -1)
8977        XNoticeWidget(display,windows,"Unable to edit image comment",
8978          image_info->filename);
8979      value=GetImageProperty(*image,"comment",exception);
8980      if (value == (char *) NULL)
8981        unique_file=close(unique_file)-1;
8982      else
8983        {
8984          register const char
8985            *p;
8986
8987          file=fdopen(unique_file,"w");
8988          if (file == (FILE *) NULL)
8989            {
8990              XNoticeWidget(display,windows,"Unable to edit image comment",
8991                image_info->filename);
8992              break;
8993            }
8994          for (p=value; *p != '\0'; p++)
8995            (void) fputc((int) *p,file);
8996          (void) fputc('\n',file);
8997          (void) fclose(file);
8998        }
8999      XSetCursorState(display,windows,MagickTrue);
9000      XCheckRefreshWindows(display,windows);
9001      status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
9002        exception);
9003      if( IfMagickFalse(status) )
9004        XNoticeWidget(display,windows,"Unable to edit image comment",
9005          (char *) NULL);
9006      else
9007        {
9008          char
9009            *comment;
9010
9011          comment=FileToString(image_info->filename,~0UL,exception);
9012          if (comment != (char *) NULL)
9013            {
9014              (void) SetImageProperty(*image,"comment",comment,exception);
9015              (*image)->taint=MagickTrue;
9016            }
9017        }
9018      (void) RelinquishUniqueFileResource(image_info->filename);
9019      XSetCursorState(display,windows,MagickFalse);
9020      break;
9021    }
9022    case LaunchCommand:
9023    {
9024      /*
9025        Launch program.
9026      */
9027      XSetCursorState(display,windows,MagickTrue);
9028      XCheckRefreshWindows(display,windows);
9029      (void) AcquireUniqueFilename(filename);
9030      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"launch:%s",
9031        filename);
9032      status=WriteImage(image_info,*image,exception);
9033      if( IfMagickFalse(status) )
9034        XNoticeWidget(display,windows,"Unable to launch image editor",
9035          (char *) NULL);
9036      else
9037        {
9038          nexus=ReadImage(resource_info->image_info,exception);
9039          CatchException(exception);
9040          XClientMessage(display,windows->image.id,windows->im_protocols,
9041            windows->im_next_image,CurrentTime);
9042        }
9043      (void) RelinquishUniqueFileResource(filename);
9044      XSetCursorState(display,windows,MagickFalse);
9045      break;
9046    }
9047    case RegionofInterestCommand:
9048    {
9049      /*
9050        Apply an image processing technique to a region of interest.
9051      */
9052      (void) XROIImage(display,resource_info,windows,image,exception);
9053      break;
9054    }
9055    case InfoCommand:
9056      break;
9057    case ZoomCommand:
9058    {
9059      /*
9060        Zoom image.
9061      */
9062      if( IfMagickTrue(windows->magnify.mapped) )
9063        (void) XRaiseWindow(display,windows->magnify.id);
9064      else
9065        {
9066          /*
9067            Make magnify image.
9068          */
9069          XSetCursorState(display,windows,MagickTrue);
9070          (void) XMapRaised(display,windows->magnify.id);
9071          XSetCursorState(display,windows,MagickFalse);
9072        }
9073      break;
9074    }
9075    case ShowPreviewCommand:
9076    {
9077      char
9078        **previews;
9079
9080      Image
9081        *preview_image;
9082
9083      static char
9084        preview_type[MaxTextExtent] = "Gamma";
9085
9086      /*
9087        Select preview type from menu.
9088      */
9089      previews=GetCommandOptions(MagickPreviewOptions);
9090      if (previews == (char **) NULL)
9091        break;
9092      XListBrowserWidget(display,windows,&windows->widget,
9093        (const char **) previews,"Preview",
9094        "Select an enhancement, effect, or F/X:",preview_type);
9095      previews=DestroyStringList(previews);
9096      if (*preview_type == '\0')
9097        break;
9098      /*
9099        Show image preview.
9100      */
9101      XSetCursorState(display,windows,MagickTrue);
9102      XCheckRefreshWindows(display,windows);
9103      image_info->preview_type=(PreviewType)
9104        ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
9105      image_info->group=(ssize_t) windows->image.id;
9106      (void) DeleteImageProperty(*image,"label");
9107      (void) SetImageProperty(*image,"label","Preview",exception);
9108      (void) AcquireUniqueFilename(filename);
9109      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"preview:%s",
9110        filename);
9111      status=WriteImage(image_info,*image,exception);
9112      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9113      preview_image=ReadImage(image_info,exception);
9114      (void) RelinquishUniqueFileResource(filename);
9115      if (preview_image == (Image *) NULL)
9116        break;
9117      (void) FormatLocaleString(preview_image->filename,MaxTextExtent,"show:%s",
9118        filename);
9119      status=WriteImage(image_info,preview_image,exception);
9120      preview_image=DestroyImage(preview_image);
9121      if( IfMagickFalse(status) )
9122        XNoticeWidget(display,windows,"Unable to show image preview",
9123          (*image)->filename);
9124      XDelay(display,1500);
9125      XSetCursorState(display,windows,MagickFalse);
9126      break;
9127    }
9128    case ShowHistogramCommand:
9129    {
9130      Image
9131        *histogram_image;
9132
9133      /*
9134        Show image histogram.
9135      */
9136      XSetCursorState(display,windows,MagickTrue);
9137      XCheckRefreshWindows(display,windows);
9138      image_info->group=(ssize_t) windows->image.id;
9139      (void) DeleteImageProperty(*image,"label");
9140      (void) SetImageProperty(*image,"label","Histogram",exception);
9141      (void) AcquireUniqueFilename(filename);
9142      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"histogram:%s",
9143        filename);
9144      status=WriteImage(image_info,*image,exception);
9145      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9146      histogram_image=ReadImage(image_info,exception);
9147      (void) RelinquishUniqueFileResource(filename);
9148      if (histogram_image == (Image *) NULL)
9149        break;
9150      (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
9151        "show:%s",filename);
9152      status=WriteImage(image_info,histogram_image,exception);
9153      histogram_image=DestroyImage(histogram_image);
9154      if( IfMagickFalse(status) )
9155        XNoticeWidget(display,windows,"Unable to show histogram",
9156          (*image)->filename);
9157      XDelay(display,1500);
9158      XSetCursorState(display,windows,MagickFalse);
9159      break;
9160    }
9161    case ShowMatteCommand:
9162    {
9163      Image
9164        *matte_image;
9165
9166      if ((*image)->alpha_trait != BlendPixelTrait)
9167        {
9168          XNoticeWidget(display,windows,
9169            "Image does not have any matte information",(*image)->filename);
9170          break;
9171        }
9172      /*
9173        Show image matte.
9174      */
9175      XSetCursorState(display,windows,MagickTrue);
9176      XCheckRefreshWindows(display,windows);
9177      image_info->group=(ssize_t) windows->image.id;
9178      (void) DeleteImageProperty(*image,"label");
9179      (void) SetImageProperty(*image,"label","Matte",exception);
9180      (void) AcquireUniqueFilename(filename);
9181      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"matte:%s",
9182        filename);
9183      status=WriteImage(image_info,*image,exception);
9184      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9185      matte_image=ReadImage(image_info,exception);
9186      (void) RelinquishUniqueFileResource(filename);
9187      if (matte_image == (Image *) NULL)
9188        break;
9189      (void) FormatLocaleString(matte_image->filename,MaxTextExtent,"show:%s",
9190        filename);
9191      status=WriteImage(image_info,matte_image,exception);
9192      matte_image=DestroyImage(matte_image);
9193      if( IfMagickFalse(status) )
9194        XNoticeWidget(display,windows,"Unable to show matte",
9195          (*image)->filename);
9196      XDelay(display,1500);
9197      XSetCursorState(display,windows,MagickFalse);
9198      break;
9199    }
9200    case BackgroundCommand:
9201    {
9202      /*
9203        Background image.
9204      */
9205      status=XBackgroundImage(display,resource_info,windows,image,exception);
9206      if( IfMagickFalse(status) )
9207        break;
9208      nexus=CloneImage(*image,0,0,MagickTrue,exception);
9209      if (nexus != (Image *) NULL)
9210        XClientMessage(display,windows->image.id,windows->im_protocols,
9211          windows->im_next_image,CurrentTime);
9212      break;
9213    }
9214    case SlideShowCommand:
9215    {
9216      static char
9217        delay[MaxTextExtent] = "5";
9218
9219      /*
9220        Display next image after pausing.
9221      */
9222      (void) XDialogWidget(display,windows,"Slide Show",
9223        "Pause how many 1/100ths of a second between images:",delay);
9224      if (*delay == '\0')
9225        break;
9226      resource_info->delay=StringToUnsignedLong(delay);
9227      XClientMessage(display,windows->image.id,windows->im_protocols,
9228        windows->im_next_image,CurrentTime);
9229      break;
9230    }
9231    case PreferencesCommand:
9232    {
9233      /*
9234        Set user preferences.
9235      */
9236      status=XPreferencesWidget(display,resource_info,windows);
9237      if( IfMagickFalse(status) )
9238        break;
9239      nexus=CloneImage(*image,0,0,MagickTrue,exception);
9240      if (nexus != (Image *) NULL)
9241        XClientMessage(display,windows->image.id,windows->im_protocols,
9242          windows->im_next_image,CurrentTime);
9243      break;
9244    }
9245    case HelpCommand:
9246    {
9247      /*
9248        User requested help.
9249      */
9250      XTextViewWidget(display,resource_info,windows,MagickFalse,
9251        "Help Viewer - Display",DisplayHelp);
9252      break;
9253    }
9254    case BrowseDocumentationCommand:
9255    {
9256      Atom
9257        mozilla_atom;
9258
9259      Window
9260        mozilla_window,
9261        root_window;
9262
9263      /*
9264        Browse the ImageMagick documentation.
9265      */
9266      root_window=XRootWindow(display,XDefaultScreen(display));
9267      mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9268      mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9269      if (mozilla_window != (Window) NULL)
9270        {
9271          char
9272            command[MaxTextExtent],
9273            *url;
9274
9275          /*
9276            Display documentation using Netscape remote control.
9277          */
9278          url=GetMagickHomeURL();
9279          (void) FormatLocaleString(command,MaxTextExtent,
9280            "openurl(%s,new-tab)",url);
9281          url=DestroyString(url);
9282          mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9283          (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9284            8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9285          XSetCursorState(display,windows,MagickFalse);
9286          break;
9287        }
9288      XSetCursorState(display,windows,MagickTrue);
9289      XCheckRefreshWindows(display,windows);
9290      status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9291        exception);
9292      if( IfMagickFalse(status) )
9293        XNoticeWidget(display,windows,"Unable to browse documentation",
9294          (char *) NULL);
9295      XDelay(display,1500);
9296      XSetCursorState(display,windows,MagickFalse);
9297      break;
9298    }
9299    case VersionCommand:
9300    {
9301      XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9302        GetMagickCopyright());
9303      break;
9304    }
9305    case SaveToUndoBufferCommand:
9306      break;
9307    default:
9308    {
9309      (void) XBell(display,0);
9310      break;
9311    }
9312  }
9313  image_info=DestroyImageInfo(image_info);
9314  return(nexus);
9315}
9316
9317/*
9318%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9319%                                                                             %
9320%                                                                             %
9321%                                                                             %
9322+   X M a g n i f y I m a g e                                                 %
9323%                                                                             %
9324%                                                                             %
9325%                                                                             %
9326%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9327%
9328%  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9329%  The magnified portion is displayed in a separate window.
9330%
9331%  The format of the XMagnifyImage method is:
9332%
9333%      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9334%        ExceptionInfo *exception)
9335%
9336%  A description of each parameter follows:
9337%
9338%    o display: Specifies a connection to an X server;  returned from
9339%      XOpenDisplay.
9340%
9341%    o windows: Specifies a pointer to a XWindows structure.
9342%
9343%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
9344%      the entire image is refreshed.
9345%
9346%    o exception: return any errors or warnings in this structure.
9347%
9348*/
9349static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9350  ExceptionInfo *exception)
9351{
9352  char
9353    text[MaxTextExtent];
9354
9355  register int
9356    x,
9357    y;
9358
9359  size_t
9360    state;
9361
9362  /*
9363    Update magnified image until the mouse button is released.
9364  */
9365  (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9366  state=DefaultState;
9367  x=event->xbutton.x;
9368  y=event->xbutton.y;
9369  windows->magnify.x=(int) windows->image.x+x;
9370  windows->magnify.y=(int) windows->image.y+y;
9371  do
9372  {
9373    /*
9374      Map and unmap Info widget as text cursor crosses its boundaries.
9375    */
9376    if( IfMagickTrue(windows->info.mapped) )
9377      {
9378        if ((x < (int) (windows->info.x+windows->info.width)) &&
9379            (y < (int) (windows->info.y+windows->info.height)))
9380          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9381      }
9382    else
9383      if ((x > (int) (windows->info.x+windows->info.width)) ||
9384          (y > (int) (windows->info.y+windows->info.height)))
9385        (void) XMapWindow(display,windows->info.id);
9386    if( IfMagickTrue(windows->info.mapped) )
9387      {
9388        /*
9389          Display pointer position.
9390        */
9391        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9392          windows->magnify.x,windows->magnify.y);
9393        XInfoWidget(display,windows,text);
9394      }
9395    /*
9396      Wait for next event.
9397    */
9398    XScreenEvent(display,windows,event,exception);
9399    switch (event->type)
9400    {
9401      case ButtonPress:
9402        break;
9403      case ButtonRelease:
9404      {
9405        /*
9406          User has finished magnifying image.
9407        */
9408        x=event->xbutton.x;
9409        y=event->xbutton.y;
9410        state|=ExitState;
9411        break;
9412      }
9413      case Expose:
9414        break;
9415      case MotionNotify:
9416      {
9417        x=event->xmotion.x;
9418        y=event->xmotion.y;
9419        break;
9420      }
9421      default:
9422        break;
9423    }
9424    /*
9425      Check boundary conditions.
9426    */
9427    if (x < 0)
9428      x=0;
9429    else
9430      if (x >= (int) windows->image.width)
9431        x=(int) windows->image.width-1;
9432    if (y < 0)
9433      y=0;
9434    else
9435     if (y >= (int) windows->image.height)
9436       y=(int) windows->image.height-1;
9437  } while ((state & ExitState) == 0);
9438  /*
9439    Display magnified image.
9440  */
9441  XSetCursorState(display,windows,MagickFalse);
9442}
9443
9444/*
9445%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9446%                                                                             %
9447%                                                                             %
9448%                                                                             %
9449+   X M a g n i f y W i n d o w C o m m a n d                                 %
9450%                                                                             %
9451%                                                                             %
9452%                                                                             %
9453%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9454%
9455%  XMagnifyWindowCommand() moves the image within an Magnify window by one
9456%  pixel as specified by the key symbol.
9457%
9458%  The format of the XMagnifyWindowCommand method is:
9459%
9460%      void XMagnifyWindowCommand(Display *display,XWindows *windows,
9461%        const MagickStatusType state,const KeySym key_symbol,
9462%        ExceptionInfo *exception)
9463%
9464%  A description of each parameter follows:
9465%
9466%    o display: Specifies a connection to an X server; returned from
9467%      XOpenDisplay.
9468%
9469%    o windows: Specifies a pointer to a XWindows structure.
9470%
9471%    o state: key mask.
9472%
9473%    o key_symbol: Specifies a KeySym which indicates which side of the image
9474%      to trim.
9475%
9476%    o exception: return any errors or warnings in this structure.
9477%
9478*/
9479static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9480  const MagickStatusType state,const KeySym key_symbol,ExceptionInfo *exception)
9481{
9482  unsigned int
9483    quantum;
9484
9485  /*
9486    User specified a magnify factor or position.
9487  */
9488  quantum=1;
9489  if ((state & Mod1Mask) != 0)
9490    quantum=10;
9491  switch ((int) key_symbol)
9492  {
9493    case QuitCommand:
9494    {
9495      (void) XWithdrawWindow(display,windows->magnify.id,
9496        windows->magnify.screen);
9497      break;
9498    }
9499    case XK_Home:
9500    case XK_KP_Home:
9501    {
9502      windows->magnify.x=(int) windows->image.width/2;
9503      windows->magnify.y=(int) windows->image.height/2;
9504      break;
9505    }
9506    case XK_Left:
9507    case XK_KP_Left:
9508    {
9509      if (windows->magnify.x > 0)
9510        windows->magnify.x-=quantum;
9511      break;
9512    }
9513    case XK_Up:
9514    case XK_KP_Up:
9515    {
9516      if (windows->magnify.y > 0)
9517        windows->magnify.y-=quantum;
9518      break;
9519    }
9520    case XK_Right:
9521    case XK_KP_Right:
9522    {
9523      if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9524        windows->magnify.x+=quantum;
9525      break;
9526    }
9527    case XK_Down:
9528    case XK_KP_Down:
9529    {
9530      if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9531        windows->magnify.y+=quantum;
9532      break;
9533    }
9534    case XK_0:
9535    case XK_1:
9536    case XK_2:
9537    case XK_3:
9538    case XK_4:
9539    case XK_5:
9540    case XK_6:
9541    case XK_7:
9542    case XK_8:
9543    case XK_9:
9544    {
9545      windows->magnify.data=(key_symbol-XK_0);
9546      break;
9547    }
9548    case XK_KP_0:
9549    case XK_KP_1:
9550    case XK_KP_2:
9551    case XK_KP_3:
9552    case XK_KP_4:
9553    case XK_KP_5:
9554    case XK_KP_6:
9555    case XK_KP_7:
9556    case XK_KP_8:
9557    case XK_KP_9:
9558    {
9559      windows->magnify.data=(key_symbol-XK_KP_0);
9560      break;
9561    }
9562    default:
9563      break;
9564  }
9565  XMakeMagnifyImage(display,windows,exception);
9566}
9567
9568/*
9569%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9570%                                                                             %
9571%                                                                             %
9572%                                                                             %
9573+   X M a k e P a n I m a g e                                                 %
9574%                                                                             %
9575%                                                                             %
9576%                                                                             %
9577%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9578%
9579%  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9580%  icon window.
9581%
9582%  The format of the XMakePanImage method is:
9583%
9584%        void XMakePanImage(Display *display,XResourceInfo *resource_info,
9585%          XWindows *windows,Image *image,ExceptionInfo *exception)
9586%
9587%  A description of each parameter follows:
9588%
9589%    o display: Specifies a connection to an X server;  returned from
9590%      XOpenDisplay.
9591%
9592%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9593%
9594%    o windows: Specifies a pointer to a XWindows structure.
9595%
9596%    o image: the image.
9597%
9598%    o exception: return any errors or warnings in this structure.
9599%
9600*/
9601static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9602  XWindows *windows,Image *image,ExceptionInfo *exception)
9603{
9604  MagickStatusType
9605    status;
9606
9607  /*
9608    Create and display image for panning icon.
9609  */
9610  XSetCursorState(display,windows,MagickTrue);
9611  XCheckRefreshWindows(display,windows);
9612  windows->pan.x=(int) windows->image.x;
9613  windows->pan.y=(int) windows->image.y;
9614  status=XMakeImage(display,resource_info,&windows->pan,image,
9615    windows->pan.width,windows->pan.height,exception);
9616  if( IfMagickFalse(status) )
9617    ThrowXWindowFatalException(ResourceLimitError,
9618     "MemoryAllocationFailed",image->filename);
9619  (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9620    windows->pan.pixmap);
9621  (void) XClearWindow(display,windows->pan.id);
9622  XDrawPanRectangle(display,windows);
9623  XSetCursorState(display,windows,MagickFalse);
9624}
9625
9626/*
9627%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9628%                                                                             %
9629%                                                                             %
9630%                                                                             %
9631+   X M a t t a E d i t I m a g e                                             %
9632%                                                                             %
9633%                                                                             %
9634%                                                                             %
9635%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9636%
9637%  XMatteEditImage() allows the user to interactively change the Matte channel
9638%  of an image.  If the image is PseudoClass it is promoted to DirectClass
9639%  before the matte information is stored.
9640%
9641%  The format of the XMatteEditImage method is:
9642%
9643%      MagickBooleanType XMatteEditImage(Display *display,
9644%        XResourceInfo *resource_info,XWindows *windows,Image **image,
9645%        ExceptionInfo *exception)
9646%
9647%  A description of each parameter follows:
9648%
9649%    o display: Specifies a connection to an X server;  returned from
9650%      XOpenDisplay.
9651%
9652%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9653%
9654%    o windows: Specifies a pointer to a XWindows structure.
9655%
9656%    o image: the image; returned from ReadImage.
9657%
9658%    o exception: return any errors or warnings in this structure.
9659%
9660*/
9661static MagickBooleanType XMatteEditImage(Display *display,
9662  XResourceInfo *resource_info,XWindows *windows,Image **image,
9663  ExceptionInfo *exception)
9664{
9665  static char
9666    matte[MaxTextExtent] = "0";
9667
9668  static const char
9669    *MatteEditMenu[] =
9670    {
9671      "Method",
9672      "Border Color",
9673      "Fuzz",
9674      "Matte Value",
9675      "Undo",
9676      "Help",
9677      "Dismiss",
9678      (char *) NULL
9679    };
9680
9681  static const ModeType
9682    MatteEditCommands[] =
9683    {
9684      MatteEditMethod,
9685      MatteEditBorderCommand,
9686      MatteEditFuzzCommand,
9687      MatteEditValueCommand,
9688      MatteEditUndoCommand,
9689      MatteEditHelpCommand,
9690      MatteEditDismissCommand
9691    };
9692
9693  static PaintMethod
9694    method = PointMethod;
9695
9696  static XColor
9697    border_color = { 0, 0, 0, 0, 0, 0 };
9698
9699  char
9700    command[MaxTextExtent],
9701    text[MaxTextExtent];
9702
9703  Cursor
9704    cursor;
9705
9706  int
9707    entry,
9708    id,
9709    x,
9710    x_offset,
9711    y,
9712    y_offset;
9713
9714  register int
9715    i;
9716
9717  register Quantum
9718    *q;
9719
9720  unsigned int
9721    height,
9722    width;
9723
9724  size_t
9725    state;
9726
9727  XEvent
9728    event;
9729
9730  /*
9731    Map Command widget.
9732  */
9733  (void) CloneString(&windows->command.name,"Matte Edit");
9734  windows->command.data=4;
9735  (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9736  (void) XMapRaised(display,windows->command.id);
9737  XClientMessage(display,windows->image.id,windows->im_protocols,
9738    windows->im_update_widget,CurrentTime);
9739  /*
9740    Make cursor.
9741  */
9742  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9743    resource_info->background_color,resource_info->foreground_color);
9744  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9745  /*
9746    Track pointer until button 1 is pressed.
9747  */
9748  XQueryPosition(display,windows->image.id,&x,&y);
9749  (void) XSelectInput(display,windows->image.id,
9750    windows->image.attributes.event_mask | PointerMotionMask);
9751  state=DefaultState;
9752  do
9753  {
9754    if( IfMagickTrue(windows->info.mapped) )
9755      {
9756        /*
9757          Display pointer position.
9758        */
9759        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9760          x+windows->image.x,y+windows->image.y);
9761        XInfoWidget(display,windows,text);
9762      }
9763    /*
9764      Wait for next event.
9765    */
9766    XScreenEvent(display,windows,&event,exception);
9767    if (event.xany.window == windows->command.id)
9768      {
9769        /*
9770          Select a command from the Command widget.
9771        */
9772        id=XCommandWidget(display,windows,MatteEditMenu,&event);
9773        if (id < 0)
9774          {
9775            (void) XCheckDefineCursor(display,windows->image.id,cursor);
9776            continue;
9777          }
9778        switch (MatteEditCommands[id])
9779        {
9780          case MatteEditMethod:
9781          {
9782            char
9783              **methods;
9784
9785            /*
9786              Select a method from the pop-up menu.
9787            */
9788            methods=GetCommandOptions(MagickMethodOptions);
9789            if (methods == (char **) NULL)
9790              break;
9791            entry=XMenuWidget(display,windows,MatteEditMenu[id],
9792              (const char **) methods,command);
9793            if (entry >= 0)
9794              method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9795                MagickFalse,methods[entry]);
9796            methods=DestroyStringList(methods);
9797            break;
9798          }
9799          case MatteEditBorderCommand:
9800          {
9801            const char
9802              *ColorMenu[MaxNumberPens];
9803
9804            int
9805              pen_number;
9806
9807            /*
9808              Initialize menu selections.
9809            */
9810            for (i=0; i < (int) (MaxNumberPens-2); i++)
9811              ColorMenu[i]=resource_info->pen_colors[i];
9812            ColorMenu[MaxNumberPens-2]="Browser...";
9813            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9814            /*
9815              Select a pen color from the pop-up menu.
9816            */
9817            pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9818              (const char **) ColorMenu,command);
9819            if (pen_number < 0)
9820              break;
9821            if (pen_number == (MaxNumberPens-2))
9822              {
9823                static char
9824                  color_name[MaxTextExtent] = "gray";
9825
9826                /*
9827                  Select a pen color from a dialog.
9828                */
9829                resource_info->pen_colors[pen_number]=color_name;
9830                XColorBrowserWidget(display,windows,"Select",color_name);
9831                if (*color_name == '\0')
9832                  break;
9833              }
9834            /*
9835              Set border color.
9836            */
9837            (void) XParseColor(display,windows->map_info->colormap,
9838              resource_info->pen_colors[pen_number],&border_color);
9839            break;
9840          }
9841          case MatteEditFuzzCommand:
9842          {
9843            static char
9844              fuzz[MaxTextExtent];
9845
9846            static const char
9847              *FuzzMenu[] =
9848              {
9849                "0%",
9850                "2%",
9851                "5%",
9852                "10%",
9853                "15%",
9854                "Dialog...",
9855                (char *) NULL,
9856              };
9857
9858            /*
9859              Select a command from the pop-up menu.
9860            */
9861            entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9862              command);
9863            if (entry < 0)
9864              break;
9865            if (entry != 5)
9866              {
9867                (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
9868                  QuantumRange+1.0);
9869                break;
9870              }
9871            (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9872            (void) XDialogWidget(display,windows,"Ok",
9873              "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9874            if (*fuzz == '\0')
9875              break;
9876            (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
9877            (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
9878              1.0);
9879            break;
9880          }
9881          case MatteEditValueCommand:
9882          {
9883            static char
9884              message[MaxTextExtent];
9885
9886            static const char
9887              *MatteMenu[] =
9888              {
9889                "Opaque",
9890                "Transparent",
9891                "Dialog...",
9892                (char *) NULL,
9893              };
9894
9895            /*
9896              Select a command from the pop-up menu.
9897            */
9898            entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9899              command);
9900            if (entry < 0)
9901              break;
9902            if (entry != 2)
9903              {
9904                (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9905                  OpaqueAlpha);
9906                if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9907                  (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9908                    (Quantum) TransparentAlpha);
9909                break;
9910              }
9911            (void) FormatLocaleString(message,MaxTextExtent,
9912              "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9913              QuantumRange);
9914            (void) XDialogWidget(display,windows,"Matte",message,matte);
9915            if (*matte == '\0')
9916              break;
9917            break;
9918          }
9919          case MatteEditUndoCommand:
9920          {
9921            (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9922              image,exception);
9923            break;
9924          }
9925          case MatteEditHelpCommand:
9926          {
9927            XTextViewWidget(display,resource_info,windows,MagickFalse,
9928              "Help Viewer - Matte Edit",ImageMatteEditHelp);
9929            break;
9930          }
9931          case MatteEditDismissCommand:
9932          {
9933            /*
9934              Prematurely exit.
9935            */
9936            state|=EscapeState;
9937            state|=ExitState;
9938            break;
9939          }
9940          default:
9941            break;
9942        }
9943        (void) XCheckDefineCursor(display,windows->image.id,cursor);
9944        continue;
9945      }
9946    switch (event.type)
9947    {
9948      case ButtonPress:
9949      {
9950        if (event.xbutton.button != Button1)
9951          break;
9952        if ((event.xbutton.window != windows->image.id) &&
9953            (event.xbutton.window != windows->magnify.id))
9954          break;
9955        /*
9956          Update matte data.
9957        */
9958        x=event.xbutton.x;
9959        y=event.xbutton.y;
9960        (void) XMagickCommand(display,resource_info,windows,
9961          SaveToUndoBufferCommand,image,exception);
9962        state|=UpdateConfigurationState;
9963        break;
9964      }
9965      case ButtonRelease:
9966      {
9967        if (event.xbutton.button != Button1)
9968          break;
9969        if ((event.xbutton.window != windows->image.id) &&
9970            (event.xbutton.window != windows->magnify.id))
9971          break;
9972        /*
9973          Update colormap information.
9974        */
9975        x=event.xbutton.x;
9976        y=event.xbutton.y;
9977        XConfigureImageColormap(display,resource_info,windows,*image,exception);
9978        (void) XConfigureImage(display,resource_info,windows,*image,exception);
9979        XInfoWidget(display,windows,text);
9980        (void) XCheckDefineCursor(display,windows->image.id,cursor);
9981        state&=(~UpdateConfigurationState);
9982        break;
9983      }
9984      case Expose:
9985        break;
9986      case KeyPress:
9987      {
9988        char
9989          command[MaxTextExtent];
9990
9991        KeySym
9992          key_symbol;
9993
9994        if (event.xkey.window == windows->magnify.id)
9995          {
9996            Window
9997              window;
9998
9999            window=windows->magnify.id;
10000            while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
10001          }
10002        if (event.xkey.window != windows->image.id)
10003          break;
10004        /*
10005          Respond to a user key press.
10006        */
10007        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
10008          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10009        switch ((int) key_symbol)
10010        {
10011          case XK_Escape:
10012          case XK_F20:
10013          {
10014            /*
10015              Prematurely exit.
10016            */
10017            state|=ExitState;
10018            break;
10019          }
10020          case XK_F1:
10021          case XK_Help:
10022          {
10023            XTextViewWidget(display,resource_info,windows,MagickFalse,
10024              "Help Viewer - Matte Edit",ImageMatteEditHelp);
10025            break;
10026          }
10027          default:
10028          {
10029            (void) XBell(display,0);
10030            break;
10031          }
10032        }
10033        break;
10034      }
10035      case MotionNotify:
10036      {
10037        /*
10038          Map and unmap Info widget as cursor crosses its boundaries.
10039        */
10040        x=event.xmotion.x;
10041        y=event.xmotion.y;
10042        if( IfMagickTrue(windows->info.mapped) )
10043          {
10044            if ((x < (int) (windows->info.x+windows->info.width)) &&
10045                (y < (int) (windows->info.y+windows->info.height)))
10046              (void) XWithdrawWindow(display,windows->info.id,
10047                windows->info.screen);
10048          }
10049        else
10050          if ((x > (int) (windows->info.x+windows->info.width)) ||
10051              (y > (int) (windows->info.y+windows->info.height)))
10052            (void) XMapWindow(display,windows->info.id);
10053        break;
10054      }
10055      default:
10056        break;
10057    }
10058    if (event.xany.window == windows->magnify.id)
10059      {
10060        x=windows->magnify.x-windows->image.x;
10061        y=windows->magnify.y-windows->image.y;
10062      }
10063    x_offset=x;
10064    y_offset=y;
10065    if ((state & UpdateConfigurationState) != 0)
10066      {
10067        CacheView
10068          *image_view;
10069
10070        int
10071          x,
10072          y;
10073
10074        /*
10075          Matte edit is relative to image configuration.
10076        */
10077        (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
10078          MagickTrue);
10079        XPutPixel(windows->image.ximage,x_offset,y_offset,
10080          windows->pixel_info->background_color.pixel);
10081        width=(unsigned int) (*image)->columns;
10082        height=(unsigned int) (*image)->rows;
10083        x=0;
10084        y=0;
10085        if (windows->image.crop_geometry != (char *) NULL)
10086          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
10087            &height);
10088        x_offset=(int) (width*(windows->image.x+x_offset)/
10089          windows->image.ximage->width+x);
10090        y_offset=(int) (height*(windows->image.y+y_offset)/
10091          windows->image.ximage->height+y);
10092        if ((x_offset < 0) || (y_offset < 0))
10093          continue;
10094        if ((x_offset >= (int) (*image)->columns) ||
10095            (y_offset >= (int) (*image)->rows))
10096          continue;
10097        if( IfMagickFalse(SetImageStorageClass(*image,DirectClass,exception)) )
10098          return(MagickFalse);
10099        if ((*image)->alpha_trait != BlendPixelTrait)
10100          (void) SetImageAlphaChannel(*image,OpaqueAlphaChannel,exception);
10101        image_view=AcquireAuthenticCacheView(*image,exception);
10102        switch (method)
10103        {
10104          case PointMethod:
10105          default:
10106          {
10107            /*
10108              Update matte information using point algorithm.
10109            */
10110            q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10111              (ssize_t) y_offset,1,1,exception);
10112            if (q == (Quantum *) NULL)
10113              break;
10114            SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10115            (void) SyncCacheViewAuthenticPixels(image_view,exception);
10116            break;
10117          }
10118          case ReplaceMethod:
10119          {
10120            PixelInfo
10121              pixel,
10122              target;
10123
10124            /*
10125              Update matte information using replace algorithm.
10126            */
10127            (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
10128              x_offset,(ssize_t) y_offset,&target,exception);
10129            for (y=0; y < (int) (*image)->rows; y++)
10130            {
10131              q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10132                (*image)->columns,1,exception);
10133              if (q == (Quantum *) NULL)
10134                break;
10135              for (x=0; x < (int) (*image)->columns; x++)
10136              {
10137                GetPixelInfoPixel(*image,q,&pixel);
10138                if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
10139                  SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10140                q+=GetPixelChannels(*image);
10141              }
10142              if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
10143                break;
10144            }
10145            break;
10146          }
10147          case FloodfillMethod:
10148          case FillToBorderMethod:
10149          {
10150            ChannelType
10151              channel_mask;
10152
10153            DrawInfo
10154              *draw_info;
10155
10156            PixelInfo
10157              target;
10158
10159            /*
10160              Update matte information using floodfill algorithm.
10161            */
10162            (void) GetOneVirtualPixelInfo(*image,
10163              GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
10164              y_offset,&target,exception);
10165            if (method == FillToBorderMethod)
10166              {
10167                target.red=(double) ScaleShortToQuantum(
10168                  border_color.red);
10169                target.green=(double) ScaleShortToQuantum(
10170                  border_color.green);
10171                target.blue=(double) ScaleShortToQuantum(
10172                  border_color.blue);
10173              }
10174            draw_info=CloneDrawInfo(resource_info->image_info,
10175              (DrawInfo *) NULL);
10176            draw_info->fill.alpha=(double) ClampToQuantum(
10177              StringToDouble(matte,(char **) NULL));
10178            channel_mask=SetImageChannelMask(*image,AlphaChannel);
10179            (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10180              x_offset,(ssize_t) y_offset,
10181              IsMagickFalse(method == FloodfillMethod),exception);
10182            (void) SetPixelChannelMask(*image,channel_mask);
10183            draw_info=DestroyDrawInfo(draw_info);
10184            break;
10185          }
10186          case ResetMethod:
10187          {
10188            /*
10189              Update matte information using reset algorithm.
10190            */
10191            if( IfMagickFalse(SetImageStorageClass(*image,DirectClass,exception)) )
10192              return(MagickFalse);
10193            for (y=0; y < (int) (*image)->rows; y++)
10194            {
10195              q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10196                (*image)->columns,1,exception);
10197              if (q == (Quantum *) NULL)
10198                break;
10199              for (x=0; x < (int) (*image)->columns; x++)
10200              {
10201                SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10202                q+=GetPixelChannels(*image);
10203              }
10204              if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
10205                break;
10206            }
10207            if (StringToLong(matte) == (long) OpaqueAlpha)
10208              (*image)->alpha_trait=UndefinedPixelTrait;
10209            break;
10210          }
10211        }
10212        image_view=DestroyCacheView(image_view);
10213        state&=(~UpdateConfigurationState);
10214      }
10215  } while ((state & ExitState) == 0);
10216  (void) XSelectInput(display,windows->image.id,
10217    windows->image.attributes.event_mask);
10218  XSetCursorState(display,windows,MagickFalse);
10219  (void) XFreeCursor(display,cursor);
10220  return(MagickTrue);
10221}
10222
10223/*
10224%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10225%                                                                             %
10226%                                                                             %
10227%                                                                             %
10228+   X O p e n I m a g e                                                       %
10229%                                                                             %
10230%                                                                             %
10231%                                                                             %
10232%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10233%
10234%  XOpenImage() loads an image from a file.
10235%
10236%  The format of the XOpenImage method is:
10237%
10238%     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10239%       XWindows *windows,const unsigned int command)
10240%
10241%  A description of each parameter follows:
10242%
10243%    o display: Specifies a connection to an X server; returned from
10244%      XOpenDisplay.
10245%
10246%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10247%
10248%    o windows: Specifies a pointer to a XWindows structure.
10249%
10250%    o command: A value other than zero indicates that the file is selected
10251%      from the command line argument list.
10252%
10253*/
10254static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10255  XWindows *windows,const MagickBooleanType command)
10256{
10257  const MagickInfo
10258    *magick_info;
10259
10260  ExceptionInfo
10261    *exception;
10262
10263  Image
10264    *nexus;
10265
10266  ImageInfo
10267    *image_info;
10268
10269  static char
10270    filename[MaxTextExtent] = "\0";
10271
10272  /*
10273    Request file name from user.
10274  */
10275  if( IfMagickFalse(command) )
10276    XFileBrowserWidget(display,windows,"Open",filename);
10277  else
10278    {
10279      char
10280        **filelist,
10281        **files;
10282
10283      int
10284        count,
10285        status;
10286
10287      register int
10288        i,
10289        j;
10290
10291      /*
10292        Select next image from the command line.
10293      */
10294      status=XGetCommand(display,windows->image.id,&files,&count);
10295      if (status == 0)
10296        {
10297          ThrowXWindowFatalException(XServerError,"UnableToGetProperty","...");
10298          return((Image *) NULL);
10299        }
10300      filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10301      if (filelist == (char **) NULL)
10302        {
10303          ThrowXWindowFatalException(ResourceLimitError,
10304            "MemoryAllocationFailed","...");
10305          (void) XFreeStringList(files);
10306          return((Image *) NULL);
10307        }
10308      j=0;
10309      for (i=1; i < count; i++)
10310        if (*files[i] != '-')
10311          filelist[j++]=files[i];
10312      filelist[j]=(char *) NULL;
10313      XListBrowserWidget(display,windows,&windows->widget,
10314        (const char **) filelist,"Load","Select Image to Load:",filename);
10315      filelist=(char **) RelinquishMagickMemory(filelist);
10316      (void) XFreeStringList(files);
10317    }
10318  if (*filename == '\0')
10319    return((Image *) NULL);
10320  image_info=CloneImageInfo(resource_info->image_info);
10321  (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10322    (void *) NULL);
10323  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10324  exception=AcquireExceptionInfo();
10325  (void) SetImageInfo(image_info,0,exception);
10326  if (LocaleCompare(image_info->magick,"X") == 0)
10327    {
10328      char
10329        seconds[MaxTextExtent];
10330
10331      /*
10332        User may want to delay the X server screen grab.
10333      */
10334      (void) CopyMagickString(seconds,"0",MaxTextExtent);
10335      (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10336        seconds);
10337      if (*seconds == '\0')
10338        return((Image *) NULL);
10339      XDelay(display,(size_t) (1000*StringToLong(seconds)));
10340    }
10341  magick_info=GetMagickInfo(image_info->magick,exception);
10342  if ((magick_info != (const MagickInfo *) NULL) &&
10343      IfMagickTrue(magick_info->raw))
10344    {
10345      char
10346        geometry[MaxTextExtent];
10347
10348      /*
10349        Request image size from the user.
10350      */
10351      (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10352      if (image_info->size != (char *) NULL)
10353        (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10354      (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10355        geometry);
10356      (void) CloneString(&image_info->size,geometry);
10357    }
10358  /*
10359    Load the image.
10360  */
10361  XSetCursorState(display,windows,MagickTrue);
10362  XCheckRefreshWindows(display,windows);
10363  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10364  nexus=ReadImage(image_info,exception);
10365  CatchException(exception);
10366  XSetCursorState(display,windows,MagickFalse);
10367  if (nexus != (Image *) NULL)
10368    XClientMessage(display,windows->image.id,windows->im_protocols,
10369      windows->im_next_image,CurrentTime);
10370  else
10371    {
10372      char
10373        *text,
10374        **textlist;
10375
10376      /*
10377        Unknown image format.
10378      */
10379      text=FileToString(filename,~0,exception);
10380      if (text == (char *) NULL)
10381        return((Image *) NULL);
10382      textlist=StringToList(text);
10383      if (textlist != (char **) NULL)
10384        {
10385          char
10386            title[MaxTextExtent];
10387
10388          register int
10389            i;
10390
10391          (void) FormatLocaleString(title,MaxTextExtent,
10392            "Unknown format: %s",filename);
10393          XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10394            (const char **) textlist);
10395          for (i=0; textlist[i] != (char *) NULL; i++)
10396            textlist[i]=DestroyString(textlist[i]);
10397          textlist=(char **) RelinquishMagickMemory(textlist);
10398        }
10399      text=DestroyString(text);
10400    }
10401  exception=DestroyExceptionInfo(exception);
10402  image_info=DestroyImageInfo(image_info);
10403  return(nexus);
10404}
10405
10406/*
10407%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10408%                                                                             %
10409%                                                                             %
10410%                                                                             %
10411+   X P a n I m a g e                                                         %
10412%                                                                             %
10413%                                                                             %
10414%                                                                             %
10415%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10416%
10417%  XPanImage() pans the image until the mouse button is released.
10418%
10419%  The format of the XPanImage method is:
10420%
10421%      void XPanImage(Display *display,XWindows *windows,XEvent *event,
10422%        ExceptionInfo *exception)
10423%
10424%  A description of each parameter follows:
10425%
10426%    o display: Specifies a connection to an X server;  returned from
10427%      XOpenDisplay.
10428%
10429%    o windows: Specifies a pointer to a XWindows structure.
10430%
10431%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10432%      the entire image is refreshed.
10433%
10434%    o exception: return any errors or warnings in this structure.
10435%
10436*/
10437static void XPanImage(Display *display,XWindows *windows,XEvent *event,
10438  ExceptionInfo *exception)
10439{
10440  char
10441    text[MaxTextExtent];
10442
10443  Cursor
10444    cursor;
10445
10446  double
10447    x_factor,
10448    y_factor;
10449
10450  RectangleInfo
10451    pan_info;
10452
10453  size_t
10454    state;
10455
10456  /*
10457    Define cursor.
10458  */
10459  if ((windows->image.ximage->width > (int) windows->image.width) &&
10460      (windows->image.ximage->height > (int) windows->image.height))
10461    cursor=XCreateFontCursor(display,XC_fleur);
10462  else
10463    if (windows->image.ximage->width > (int) windows->image.width)
10464      cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10465    else
10466      if (windows->image.ximage->height > (int) windows->image.height)
10467        cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10468      else
10469        cursor=XCreateFontCursor(display,XC_arrow);
10470  (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10471  /*
10472    Pan image as pointer moves until the mouse button is released.
10473  */
10474  x_factor=(double) windows->image.ximage->width/windows->pan.width;
10475  y_factor=(double) windows->image.ximage->height/windows->pan.height;
10476  pan_info.width=windows->pan.width*windows->image.width/
10477    windows->image.ximage->width;
10478  pan_info.height=windows->pan.height*windows->image.height/
10479    windows->image.ximage->height;
10480  pan_info.x=0;
10481  pan_info.y=0;
10482  state=UpdateConfigurationState;
10483  do
10484  {
10485    switch (event->type)
10486    {
10487      case ButtonPress:
10488      {
10489        /*
10490          User choose an initial pan location.
10491        */
10492        pan_info.x=(ssize_t) event->xbutton.x;
10493        pan_info.y=(ssize_t) event->xbutton.y;
10494        state|=UpdateConfigurationState;
10495        break;
10496      }
10497      case ButtonRelease:
10498      {
10499        /*
10500          User has finished panning the image.
10501        */
10502        pan_info.x=(ssize_t) event->xbutton.x;
10503        pan_info.y=(ssize_t) event->xbutton.y;
10504        state|=UpdateConfigurationState | ExitState;
10505        break;
10506      }
10507      case MotionNotify:
10508      {
10509        pan_info.x=(ssize_t) event->xmotion.x;
10510        pan_info.y=(ssize_t) event->xmotion.y;
10511        state|=UpdateConfigurationState;
10512      }
10513      default:
10514        break;
10515    }
10516    if ((state & UpdateConfigurationState) != 0)
10517      {
10518        /*
10519          Check boundary conditions.
10520        */
10521        if (pan_info.x < (ssize_t) (pan_info.width/2))
10522          pan_info.x=0;
10523        else
10524          pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10525        if (pan_info.x < 0)
10526          pan_info.x=0;
10527        else
10528          if ((int) (pan_info.x+windows->image.width) >
10529              windows->image.ximage->width)
10530            pan_info.x=(ssize_t)
10531              (windows->image.ximage->width-windows->image.width);
10532        if (pan_info.y < (ssize_t) (pan_info.height/2))
10533          pan_info.y=0;
10534        else
10535          pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10536        if (pan_info.y < 0)
10537          pan_info.y=0;
10538        else
10539          if ((int) (pan_info.y+windows->image.height) >
10540              windows->image.ximage->height)
10541            pan_info.y=(ssize_t)
10542              (windows->image.ximage->height-windows->image.height);
10543        if ((windows->image.x != (int) pan_info.x) ||
10544            (windows->image.y != (int) pan_info.y))
10545          {
10546            /*
10547              Display image pan offset.
10548            */
10549            windows->image.x=(int) pan_info.x;
10550            windows->image.y=(int) pan_info.y;
10551            (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
10552              windows->image.width,windows->image.height,windows->image.x,
10553              windows->image.y);
10554            XInfoWidget(display,windows,text);
10555            /*
10556              Refresh Image window.
10557            */
10558            XDrawPanRectangle(display,windows);
10559            XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10560          }
10561        state&=(~UpdateConfigurationState);
10562      }
10563    /*
10564      Wait for next event.
10565    */
10566    if ((state & ExitState) == 0)
10567      XScreenEvent(display,windows,event,exception);
10568  } while ((state & ExitState) == 0);
10569  /*
10570    Restore cursor.
10571  */
10572  (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10573  (void) XFreeCursor(display,cursor);
10574  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10575}
10576
10577/*
10578%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10579%                                                                             %
10580%                                                                             %
10581%                                                                             %
10582+   X P a s t e I m a g e                                                     %
10583%                                                                             %
10584%                                                                             %
10585%                                                                             %
10586%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10587%
10588%  XPasteImage() pastes an image previously saved with XCropImage in the X
10589%  window image at a location the user chooses with the pointer.
10590%
10591%  The format of the XPasteImage method is:
10592%
10593%      MagickBooleanType XPasteImage(Display *display,
10594%        XResourceInfo *resource_info,XWindows *windows,Image *image,
10595%        ExceptionInfo *exception)
10596%
10597%  A description of each parameter follows:
10598%
10599%    o display: Specifies a connection to an X server;  returned from
10600%      XOpenDisplay.
10601%
10602%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10603%
10604%    o windows: Specifies a pointer to a XWindows structure.
10605%
10606%    o image: the image; returned from ReadImage.
10607%
10608%    o exception: return any errors or warnings in this structure.
10609%
10610*/
10611static MagickBooleanType XPasteImage(Display *display,
10612  XResourceInfo *resource_info,XWindows *windows,Image *image,
10613  ExceptionInfo *exception)
10614{
10615  static const char
10616    *PasteMenu[] =
10617    {
10618      "Operator",
10619      "Help",
10620      "Dismiss",
10621      (char *) NULL
10622    };
10623
10624  static const ModeType
10625    PasteCommands[] =
10626    {
10627      PasteOperatorsCommand,
10628      PasteHelpCommand,
10629      PasteDismissCommand
10630    };
10631
10632  static CompositeOperator
10633    compose = CopyCompositeOp;
10634
10635  char
10636    text[MaxTextExtent];
10637
10638  Cursor
10639    cursor;
10640
10641  Image
10642    *paste_image;
10643
10644  int
10645    entry,
10646    id,
10647    x,
10648    y;
10649
10650  double
10651    scale_factor;
10652
10653  RectangleInfo
10654    highlight_info,
10655    paste_info;
10656
10657  unsigned int
10658    height,
10659    width;
10660
10661  size_t
10662    state;
10663
10664  XEvent
10665    event;
10666
10667  /*
10668    Copy image.
10669  */
10670  if (resource_info->copy_image == (Image *) NULL)
10671    return(MagickFalse);
10672  paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,exception);
10673  /*
10674    Map Command widget.
10675  */
10676  (void) CloneString(&windows->command.name,"Paste");
10677  windows->command.data=1;
10678  (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10679  (void) XMapRaised(display,windows->command.id);
10680  XClientMessage(display,windows->image.id,windows->im_protocols,
10681    windows->im_update_widget,CurrentTime);
10682  /*
10683    Track pointer until button 1 is pressed.
10684  */
10685  XSetCursorState(display,windows,MagickFalse);
10686  XQueryPosition(display,windows->image.id,&x,&y);
10687  (void) XSelectInput(display,windows->image.id,
10688    windows->image.attributes.event_mask | PointerMotionMask);
10689  paste_info.x=(ssize_t) windows->image.x+x;
10690  paste_info.y=(ssize_t) windows->image.y+y;
10691  paste_info.width=0;
10692  paste_info.height=0;
10693  cursor=XCreateFontCursor(display,XC_ul_angle);
10694  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10695  state=DefaultState;
10696  do
10697  {
10698    if( IfMagickTrue(windows->info.mapped) )
10699      {
10700        /*
10701          Display pointer position.
10702        */
10703        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
10704          (long) paste_info.x,(long) paste_info.y);
10705        XInfoWidget(display,windows,text);
10706      }
10707    highlight_info=paste_info;
10708    highlight_info.x=paste_info.x-windows->image.x;
10709    highlight_info.y=paste_info.y-windows->image.y;
10710    XHighlightRectangle(display,windows->image.id,
10711      windows->image.highlight_context,&highlight_info);
10712    /*
10713      Wait for next event.
10714    */
10715    XScreenEvent(display,windows,&event,exception);
10716    XHighlightRectangle(display,windows->image.id,
10717      windows->image.highlight_context,&highlight_info);
10718    if (event.xany.window == windows->command.id)
10719      {
10720        /*
10721          Select a command from the Command widget.
10722        */
10723        id=XCommandWidget(display,windows,PasteMenu,&event);
10724        if (id < 0)
10725          continue;
10726        switch (PasteCommands[id])
10727        {
10728          case PasteOperatorsCommand:
10729          {
10730            char
10731              command[MaxTextExtent],
10732              **operators;
10733
10734            /*
10735              Select a command from the pop-up menu.
10736            */
10737            operators=GetCommandOptions(MagickComposeOptions);
10738            if (operators == (char **) NULL)
10739              break;
10740            entry=XMenuWidget(display,windows,PasteMenu[id],
10741              (const char **) operators,command);
10742            if (entry >= 0)
10743              compose=(CompositeOperator) ParseCommandOption(
10744                MagickComposeOptions,MagickFalse,operators[entry]);
10745            operators=DestroyStringList(operators);
10746            break;
10747          }
10748          case PasteHelpCommand:
10749          {
10750            XTextViewWidget(display,resource_info,windows,MagickFalse,
10751              "Help Viewer - Image Composite",ImagePasteHelp);
10752            break;
10753          }
10754          case PasteDismissCommand:
10755          {
10756            /*
10757              Prematurely exit.
10758            */
10759            state|=EscapeState;
10760            state|=ExitState;
10761            break;
10762          }
10763          default:
10764            break;
10765        }
10766        continue;
10767      }
10768    switch (event.type)
10769    {
10770      case ButtonPress:
10771      {
10772        if( IfMagickTrue(image->debug) )
10773          (void) LogMagickEvent(X11Event,GetMagickModule(),
10774            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10775            event.xbutton.button,event.xbutton.x,event.xbutton.y);
10776        if (event.xbutton.button != Button1)
10777          break;
10778        if (event.xbutton.window != windows->image.id)
10779          break;
10780        /*
10781          Paste rectangle is relative to image configuration.
10782        */
10783        width=(unsigned int) image->columns;
10784        height=(unsigned int) image->rows;
10785        x=0;
10786        y=0;
10787        if (windows->image.crop_geometry != (char *) NULL)
10788          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10789            &width,&height);
10790        scale_factor=(double) windows->image.ximage->width/width;
10791        paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10792        scale_factor=(double) windows->image.ximage->height/height;
10793        paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10794        (void) XCheckDefineCursor(display,windows->image.id,cursor);
10795        paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10796        paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10797        break;
10798      }
10799      case ButtonRelease:
10800      {
10801        if( IfMagickTrue(image->debug) )
10802          (void) LogMagickEvent(X11Event,GetMagickModule(),
10803            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10804            event.xbutton.button,event.xbutton.x,event.xbutton.y);
10805        if (event.xbutton.button != Button1)
10806          break;
10807        if (event.xbutton.window != windows->image.id)
10808          break;
10809        if ((paste_info.width != 0) && (paste_info.height != 0))
10810          {
10811            /*
10812              User has selected the location of the paste image.
10813            */
10814            paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10815            paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10816            state|=ExitState;
10817          }
10818        break;
10819      }
10820      case Expose:
10821        break;
10822      case KeyPress:
10823      {
10824        char
10825          command[MaxTextExtent];
10826
10827        KeySym
10828          key_symbol;
10829
10830        int
10831          length;
10832
10833        if (event.xkey.window != windows->image.id)
10834          break;
10835        /*
10836          Respond to a user key press.
10837        */
10838        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10839          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10840        *(command+length)='\0';
10841        if( IfMagickTrue(image->debug) )
10842          (void) LogMagickEvent(X11Event,GetMagickModule(),
10843            "Key press: 0x%lx (%s)",(long) key_symbol,command);
10844        switch ((int) key_symbol)
10845        {
10846          case XK_Escape:
10847          case XK_F20:
10848          {
10849            /*
10850              Prematurely exit.
10851            */
10852            paste_image=DestroyImage(paste_image);
10853            state|=EscapeState;
10854            state|=ExitState;
10855            break;
10856          }
10857          case XK_F1:
10858          case XK_Help:
10859          {
10860            (void) XSetFunction(display,windows->image.highlight_context,
10861              GXcopy);
10862            XTextViewWidget(display,resource_info,windows,MagickFalse,
10863              "Help Viewer - Image Composite",ImagePasteHelp);
10864            (void) XSetFunction(display,windows->image.highlight_context,
10865              GXinvert);
10866            break;
10867          }
10868          default:
10869          {
10870            (void) XBell(display,0);
10871            break;
10872          }
10873        }
10874        break;
10875      }
10876      case MotionNotify:
10877      {
10878        /*
10879          Map and unmap Info widget as text cursor crosses its boundaries.
10880        */
10881        x=event.xmotion.x;
10882        y=event.xmotion.y;
10883        if( IfMagickTrue(windows->info.mapped) )
10884          {
10885            if ((x < (int) (windows->info.x+windows->info.width)) &&
10886                (y < (int) (windows->info.y+windows->info.height)))
10887              (void) XWithdrawWindow(display,windows->info.id,
10888                windows->info.screen);
10889          }
10890        else
10891          if ((x > (int) (windows->info.x+windows->info.width)) ||
10892              (y > (int) (windows->info.y+windows->info.height)))
10893            (void) XMapWindow(display,windows->info.id);
10894        paste_info.x=(ssize_t) windows->image.x+x;
10895        paste_info.y=(ssize_t) windows->image.y+y;
10896        break;
10897      }
10898      default:
10899      {
10900        if( IfMagickTrue(image->debug) )
10901          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10902            event.type);
10903        break;
10904      }
10905    }
10906  } while ((state & ExitState) == 0);
10907  (void) XSelectInput(display,windows->image.id,
10908    windows->image.attributes.event_mask);
10909  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10910  XSetCursorState(display,windows,MagickFalse);
10911  (void) XFreeCursor(display,cursor);
10912  if ((state & EscapeState) != 0)
10913    return(MagickTrue);
10914  /*
10915    Image pasting is relative to image configuration.
10916  */
10917  XSetCursorState(display,windows,MagickTrue);
10918  XCheckRefreshWindows(display,windows);
10919  width=(unsigned int) image->columns;
10920  height=(unsigned int) image->rows;
10921  x=0;
10922  y=0;
10923  if (windows->image.crop_geometry != (char *) NULL)
10924    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10925  scale_factor=(double) width/windows->image.ximage->width;
10926  paste_info.x+=x;
10927  paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10928  paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10929  scale_factor=(double) height/windows->image.ximage->height;
10930  paste_info.y+=y;
10931  paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10932  paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10933  /*
10934    Paste image with X Image window.
10935  */
10936  (void) CompositeImage(image,paste_image,compose,MagickTrue,paste_info.x,
10937    paste_info.y,exception);
10938  paste_image=DestroyImage(paste_image);
10939  XSetCursorState(display,windows,MagickFalse);
10940  /*
10941    Update image colormap.
10942  */
10943  XConfigureImageColormap(display,resource_info,windows,image,exception);
10944  (void) XConfigureImage(display,resource_info,windows,image,exception);
10945  return(MagickTrue);
10946}
10947
10948/*
10949%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10950%                                                                             %
10951%                                                                             %
10952%                                                                             %
10953+   X P r i n t I m a g e                                                     %
10954%                                                                             %
10955%                                                                             %
10956%                                                                             %
10957%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10958%
10959%  XPrintImage() prints an image to a Postscript printer.
10960%
10961%  The format of the XPrintImage method is:
10962%
10963%      MagickBooleanType XPrintImage(Display *display,
10964%        XResourceInfo *resource_info,XWindows *windows,Image *image,
10965%        ExceptionInfo *exception)
10966%
10967%  A description of each parameter follows:
10968%
10969%    o display: Specifies a connection to an X server; returned from
10970%      XOpenDisplay.
10971%
10972%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10973%
10974%    o windows: Specifies a pointer to a XWindows structure.
10975%
10976%    o image: the image.
10977%
10978%    o exception: return any errors or warnings in this structure.
10979%
10980*/
10981static MagickBooleanType XPrintImage(Display *display,
10982  XResourceInfo *resource_info,XWindows *windows,Image *image,
10983  ExceptionInfo *exception)
10984{
10985  char
10986    filename[MaxTextExtent],
10987    geometry[MaxTextExtent];
10988
10989  Image
10990    *print_image;
10991
10992  ImageInfo
10993    *image_info;
10994
10995  MagickStatusType
10996    status;
10997
10998  /*
10999    Request Postscript page geometry from user.
11000  */
11001  image_info=CloneImageInfo(resource_info->image_info);
11002  (void) FormatLocaleString(geometry,MaxTextExtent,"Letter");
11003  if (image_info->page != (char *) NULL)
11004    (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
11005  XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
11006    "Select Postscript Page Geometry:",geometry);
11007  if (*geometry == '\0')
11008    return(MagickTrue);
11009  image_info->page=GetPageGeometry(geometry);
11010  /*
11011    Apply image transforms.
11012  */
11013  XSetCursorState(display,windows,MagickTrue);
11014  XCheckRefreshWindows(display,windows);
11015  print_image=CloneImage(image,0,0,MagickTrue,exception);
11016  if (print_image == (Image *) NULL)
11017    return(MagickFalse);
11018  (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
11019    windows->image.ximage->width,windows->image.ximage->height);
11020  (void) TransformImage(&print_image,windows->image.crop_geometry,geometry,
11021    exception);
11022  /*
11023    Print image.
11024  */
11025  (void) AcquireUniqueFilename(filename);
11026  (void) FormatLocaleString(print_image->filename,MaxTextExtent,"print:%s",
11027    filename);
11028  status=WriteImage(image_info,print_image,exception);
11029  (void) RelinquishUniqueFileResource(filename);
11030  print_image=DestroyImage(print_image);
11031  image_info=DestroyImageInfo(image_info);
11032  XSetCursorState(display,windows,MagickFalse);
11033  return(IsMagickTrue(status));
11034}
11035
11036/*
11037%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11038%                                                                             %
11039%                                                                             %
11040%                                                                             %
11041+   X R O I I m a g e                                                         %
11042%                                                                             %
11043%                                                                             %
11044%                                                                             %
11045%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11046%
11047%  XROIImage() applies an image processing technique to a region of interest.
11048%
11049%  The format of the XROIImage method is:
11050%
11051%      MagickBooleanType XROIImage(Display *display,
11052%        XResourceInfo *resource_info,XWindows *windows,Image **image,
11053%        ExceptionInfo *exception)
11054%
11055%  A description of each parameter follows:
11056%
11057%    o display: Specifies a connection to an X server; returned from
11058%      XOpenDisplay.
11059%
11060%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11061%
11062%    o windows: Specifies a pointer to a XWindows structure.
11063%
11064%    o image: the image; returned from ReadImage.
11065%
11066%    o exception: return any errors or warnings in this structure.
11067%
11068*/
11069static MagickBooleanType XROIImage(Display *display,
11070  XResourceInfo *resource_info,XWindows *windows,Image **image,
11071  ExceptionInfo *exception)
11072{
11073#define ApplyMenus  7
11074
11075  static const char
11076    *ROIMenu[] =
11077    {
11078      "Help",
11079      "Dismiss",
11080      (char *) NULL
11081    },
11082    *ApplyMenu[] =
11083    {
11084      "File",
11085      "Edit",
11086      "Transform",
11087      "Enhance",
11088      "Effects",
11089      "F/X",
11090      "Miscellany",
11091      "Help",
11092      "Dismiss",
11093      (char *) NULL
11094    },
11095    *FileMenu[] =
11096    {
11097      "Save...",
11098      "Print...",
11099      (char *) NULL
11100    },
11101    *EditMenu[] =
11102    {
11103      "Undo",
11104      "Redo",
11105      (char *) NULL
11106    },
11107    *TransformMenu[] =
11108    {
11109      "Flop",
11110      "Flip",
11111      "Rotate Right",
11112      "Rotate Left",
11113      (char *) NULL
11114    },
11115    *EnhanceMenu[] =
11116    {
11117      "Hue...",
11118      "Saturation...",
11119      "Brightness...",
11120      "Gamma...",
11121      "Spiff",
11122      "Dull",
11123      "Contrast Stretch...",
11124      "Sigmoidal Contrast...",
11125      "Normalize",
11126      "Equalize",
11127      "Negate",
11128      "Grayscale",
11129      "Map...",
11130      "Quantize...",
11131      (char *) NULL
11132    },
11133    *EffectsMenu[] =
11134    {
11135      "Despeckle",
11136      "Emboss",
11137      "Reduce Noise",
11138      "Add Noise",
11139      "Sharpen...",
11140      "Blur...",
11141      "Threshold...",
11142      "Edge Detect...",
11143      "Spread...",
11144      "Shade...",
11145      "Raise...",
11146      "Segment...",
11147      (char *) NULL
11148    },
11149    *FXMenu[] =
11150    {
11151      "Solarize...",
11152      "Sepia Tone...",
11153      "Swirl...",
11154      "Implode...",
11155      "Vignette...",
11156      "Wave...",
11157      "Oil Paint...",
11158      "Charcoal Draw...",
11159      (char *) NULL
11160    },
11161    *MiscellanyMenu[] =
11162    {
11163      "Image Info",
11164      "Zoom Image",
11165      "Show Preview...",
11166      "Show Histogram",
11167      "Show Matte",
11168      (char *) NULL
11169    };
11170
11171  static const char
11172    **Menus[ApplyMenus] =
11173    {
11174      FileMenu,
11175      EditMenu,
11176      TransformMenu,
11177      EnhanceMenu,
11178      EffectsMenu,
11179      FXMenu,
11180      MiscellanyMenu
11181    };
11182
11183  static const CommandType
11184    ApplyCommands[] =
11185    {
11186      NullCommand,
11187      NullCommand,
11188      NullCommand,
11189      NullCommand,
11190      NullCommand,
11191      NullCommand,
11192      NullCommand,
11193      HelpCommand,
11194      QuitCommand
11195    },
11196    FileCommands[] =
11197    {
11198      SaveCommand,
11199      PrintCommand
11200    },
11201    EditCommands[] =
11202    {
11203      UndoCommand,
11204      RedoCommand
11205    },
11206    TransformCommands[] =
11207    {
11208      FlopCommand,
11209      FlipCommand,
11210      RotateRightCommand,
11211      RotateLeftCommand
11212    },
11213    EnhanceCommands[] =
11214    {
11215      HueCommand,
11216      SaturationCommand,
11217      BrightnessCommand,
11218      GammaCommand,
11219      SpiffCommand,
11220      DullCommand,
11221      ContrastStretchCommand,
11222      SigmoidalContrastCommand,
11223      NormalizeCommand,
11224      EqualizeCommand,
11225      NegateCommand,
11226      GrayscaleCommand,
11227      MapCommand,
11228      QuantizeCommand
11229    },
11230    EffectsCommands[] =
11231    {
11232      DespeckleCommand,
11233      EmbossCommand,
11234      ReduceNoiseCommand,
11235      AddNoiseCommand,
11236      SharpenCommand,
11237      BlurCommand,
11238      EdgeDetectCommand,
11239      SpreadCommand,
11240      ShadeCommand,
11241      RaiseCommand,
11242      SegmentCommand
11243    },
11244    FXCommands[] =
11245    {
11246      SolarizeCommand,
11247      SepiaToneCommand,
11248      SwirlCommand,
11249      ImplodeCommand,
11250      VignetteCommand,
11251      WaveCommand,
11252      OilPaintCommand,
11253      CharcoalDrawCommand
11254    },
11255    MiscellanyCommands[] =
11256    {
11257      InfoCommand,
11258      ZoomCommand,
11259      ShowPreviewCommand,
11260      ShowHistogramCommand,
11261      ShowMatteCommand
11262    },
11263    ROICommands[] =
11264    {
11265      ROIHelpCommand,
11266      ROIDismissCommand
11267    };
11268
11269  static const CommandType
11270    *Commands[ApplyMenus] =
11271    {
11272      FileCommands,
11273      EditCommands,
11274      TransformCommands,
11275      EnhanceCommands,
11276      EffectsCommands,
11277      FXCommands,
11278      MiscellanyCommands
11279    };
11280
11281  char
11282    command[MaxTextExtent],
11283    text[MaxTextExtent];
11284
11285  CommandType
11286    command_type;
11287
11288  Cursor
11289    cursor;
11290
11291  Image
11292    *roi_image;
11293
11294  int
11295    entry,
11296    id,
11297    x,
11298    y;
11299
11300  double
11301    scale_factor;
11302
11303  MagickProgressMonitor
11304    progress_monitor;
11305
11306  RectangleInfo
11307    crop_info,
11308    highlight_info,
11309    roi_info;
11310
11311  unsigned int
11312    height,
11313    width;
11314
11315  size_t
11316    state;
11317
11318  XEvent
11319    event;
11320
11321  /*
11322    Map Command widget.
11323  */
11324  (void) CloneString(&windows->command.name,"ROI");
11325  windows->command.data=0;
11326  (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11327  (void) XMapRaised(display,windows->command.id);
11328  XClientMessage(display,windows->image.id,windows->im_protocols,
11329    windows->im_update_widget,CurrentTime);
11330  /*
11331    Track pointer until button 1 is pressed.
11332  */
11333  XQueryPosition(display,windows->image.id,&x,&y);
11334  (void) XSelectInput(display,windows->image.id,
11335    windows->image.attributes.event_mask | PointerMotionMask);
11336  roi_info.x=(ssize_t) windows->image.x+x;
11337  roi_info.y=(ssize_t) windows->image.y+y;
11338  roi_info.width=0;
11339  roi_info.height=0;
11340  cursor=XCreateFontCursor(display,XC_fleur);
11341  state=DefaultState;
11342  do
11343  {
11344    if( IfMagickTrue(windows->info.mapped) )
11345      {
11346        /*
11347          Display pointer position.
11348        */
11349        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
11350          (long) roi_info.x,(long) roi_info.y);
11351        XInfoWidget(display,windows,text);
11352      }
11353    /*
11354      Wait for next event.
11355    */
11356    XScreenEvent(display,windows,&event,exception);
11357    if (event.xany.window == windows->command.id)
11358      {
11359        /*
11360          Select a command from the Command widget.
11361        */
11362        id=XCommandWidget(display,windows,ROIMenu,&event);
11363        if (id < 0)
11364          continue;
11365        switch (ROICommands[id])
11366        {
11367          case ROIHelpCommand:
11368          {
11369            XTextViewWidget(display,resource_info,windows,MagickFalse,
11370              "Help Viewer - Region of Interest",ImageROIHelp);
11371            break;
11372          }
11373          case ROIDismissCommand:
11374          {
11375            /*
11376              Prematurely exit.
11377            */
11378            state|=EscapeState;
11379            state|=ExitState;
11380            break;
11381          }
11382          default:
11383            break;
11384        }
11385        continue;
11386      }
11387    switch (event.type)
11388    {
11389      case ButtonPress:
11390      {
11391        if (event.xbutton.button != Button1)
11392          break;
11393        if (event.xbutton.window != windows->image.id)
11394          break;
11395        /*
11396          Note first corner of region of interest rectangle-- exit loop.
11397        */
11398        (void) XCheckDefineCursor(display,windows->image.id,cursor);
11399        roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11400        roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11401        state|=ExitState;
11402        break;
11403      }
11404      case ButtonRelease:
11405        break;
11406      case Expose:
11407        break;
11408      case KeyPress:
11409      {
11410        KeySym
11411          key_symbol;
11412
11413        if (event.xkey.window != windows->image.id)
11414          break;
11415        /*
11416          Respond to a user key press.
11417        */
11418        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11419          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11420        switch ((int) key_symbol)
11421        {
11422          case XK_Escape:
11423          case XK_F20:
11424          {
11425            /*
11426              Prematurely exit.
11427            */
11428            state|=EscapeState;
11429            state|=ExitState;
11430            break;
11431          }
11432          case XK_F1:
11433          case XK_Help:
11434          {
11435            XTextViewWidget(display,resource_info,windows,MagickFalse,
11436              "Help Viewer - Region of Interest",ImageROIHelp);
11437            break;
11438          }
11439          default:
11440          {
11441            (void) XBell(display,0);
11442            break;
11443          }
11444        }
11445        break;
11446      }
11447      case MotionNotify:
11448      {
11449        /*
11450          Map and unmap Info widget as text cursor crosses its boundaries.
11451        */
11452        x=event.xmotion.x;
11453        y=event.xmotion.y;
11454        if( IfMagickTrue(windows->info.mapped) )
11455          {
11456            if ((x < (int) (windows->info.x+windows->info.width)) &&
11457                (y < (int) (windows->info.y+windows->info.height)))
11458              (void) XWithdrawWindow(display,windows->info.id,
11459                windows->info.screen);
11460          }
11461        else
11462          if ((x > (int) (windows->info.x+windows->info.width)) ||
11463              (y > (int) (windows->info.y+windows->info.height)))
11464            (void) XMapWindow(display,windows->info.id);
11465        roi_info.x=(ssize_t) windows->image.x+x;
11466        roi_info.y=(ssize_t) windows->image.y+y;
11467        break;
11468      }
11469      default:
11470        break;
11471    }
11472  } while ((state & ExitState) == 0);
11473  (void) XSelectInput(display,windows->image.id,
11474    windows->image.attributes.event_mask);
11475  if ((state & EscapeState) != 0)
11476    {
11477      /*
11478        User want to exit without region of interest.
11479      */
11480      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11481      (void) XFreeCursor(display,cursor);
11482      return(MagickTrue);
11483    }
11484  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11485  do
11486  {
11487    /*
11488      Size rectangle as pointer moves until the mouse button is released.
11489    */
11490    x=(int) roi_info.x;
11491    y=(int) roi_info.y;
11492    roi_info.width=0;
11493    roi_info.height=0;
11494    state=DefaultState;
11495    do
11496    {
11497      highlight_info=roi_info;
11498      highlight_info.x=roi_info.x-windows->image.x;
11499      highlight_info.y=roi_info.y-windows->image.y;
11500      if ((highlight_info.width > 3) && (highlight_info.height > 3))
11501        {
11502          /*
11503            Display info and draw region of interest rectangle.
11504          */
11505          if( IfMagickFalse(windows->info.mapped) )
11506            (void) XMapWindow(display,windows->info.id);
11507          (void) FormatLocaleString(text,MaxTextExtent,
11508            " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11509            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11510          XInfoWidget(display,windows,text);
11511          XHighlightRectangle(display,windows->image.id,
11512            windows->image.highlight_context,&highlight_info);
11513        }
11514      else
11515        if( IfMagickTrue(windows->info.mapped) )
11516          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11517      /*
11518        Wait for next event.
11519      */
11520      XScreenEvent(display,windows,&event,exception);
11521      if ((highlight_info.width > 3) && (highlight_info.height > 3))
11522        XHighlightRectangle(display,windows->image.id,
11523          windows->image.highlight_context,&highlight_info);
11524      switch (event.type)
11525      {
11526        case ButtonPress:
11527        {
11528          roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11529          roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11530          break;
11531        }
11532        case ButtonRelease:
11533        {
11534          /*
11535            User has committed to region of interest rectangle.
11536          */
11537          roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11538          roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11539          XSetCursorState(display,windows,MagickFalse);
11540          state|=ExitState;
11541          if (LocaleCompare(windows->command.name,"Apply") == 0)
11542            break;
11543          (void) CloneString(&windows->command.name,"Apply");
11544          windows->command.data=ApplyMenus;
11545          (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11546          break;
11547        }
11548        case Expose:
11549          break;
11550        case MotionNotify:
11551        {
11552          roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11553          roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11554        }
11555        default:
11556          break;
11557      }
11558      if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11559          ((state & ExitState) != 0))
11560        {
11561          /*
11562            Check boundary conditions.
11563          */
11564          if (roi_info.x < 0)
11565            roi_info.x=0;
11566          else
11567            if (roi_info.x > (ssize_t) windows->image.ximage->width)
11568              roi_info.x=(ssize_t) windows->image.ximage->width;
11569          if ((int) roi_info.x < x)
11570            roi_info.width=(unsigned int) (x-roi_info.x);
11571          else
11572            {
11573              roi_info.width=(unsigned int) (roi_info.x-x);
11574              roi_info.x=(ssize_t) x;
11575            }
11576          if (roi_info.y < 0)
11577            roi_info.y=0;
11578          else
11579            if (roi_info.y > (ssize_t) windows->image.ximage->height)
11580              roi_info.y=(ssize_t) windows->image.ximage->height;
11581          if ((int) roi_info.y < y)
11582            roi_info.height=(unsigned int) (y-roi_info.y);
11583          else
11584            {
11585              roi_info.height=(unsigned int) (roi_info.y-y);
11586              roi_info.y=(ssize_t) y;
11587            }
11588        }
11589    } while ((state & ExitState) == 0);
11590    /*
11591      Wait for user to grab a corner of the rectangle or press return.
11592    */
11593    state=DefaultState;
11594    command_type=NullCommand;
11595    (void) XMapWindow(display,windows->info.id);
11596    do
11597    {
11598      if( IfMagickTrue(windows->info.mapped) )
11599        {
11600          /*
11601            Display pointer position.
11602          */
11603          (void) FormatLocaleString(text,MaxTextExtent,
11604            " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11605            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11606          XInfoWidget(display,windows,text);
11607        }
11608      highlight_info=roi_info;
11609      highlight_info.x=roi_info.x-windows->image.x;
11610      highlight_info.y=roi_info.y-windows->image.y;
11611      if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11612        {
11613          state|=EscapeState;
11614          state|=ExitState;
11615          break;
11616        }
11617      if ((state & UpdateRegionState) != 0)
11618        {
11619          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11620          switch (command_type)
11621          {
11622            case UndoCommand:
11623            case RedoCommand:
11624            {
11625              (void) XMagickCommand(display,resource_info,windows,command_type,
11626                image,exception);
11627              break;
11628            }
11629            default:
11630            {
11631              /*
11632                Region of interest is relative to image configuration.
11633              */
11634              progress_monitor=SetImageProgressMonitor(*image,
11635                (MagickProgressMonitor) NULL,(*image)->client_data);
11636              crop_info=roi_info;
11637              width=(unsigned int) (*image)->columns;
11638              height=(unsigned int) (*image)->rows;
11639              x=0;
11640              y=0;
11641              if (windows->image.crop_geometry != (char *) NULL)
11642                (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11643                  &width,&height);
11644              scale_factor=(double) width/windows->image.ximage->width;
11645              crop_info.x+=x;
11646              crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11647              crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11648              scale_factor=(double)
11649                height/windows->image.ximage->height;
11650              crop_info.y+=y;
11651              crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11652              crop_info.height=(unsigned int)
11653                (scale_factor*crop_info.height+0.5);
11654              roi_image=CropImage(*image,&crop_info,exception);
11655              (void) SetImageProgressMonitor(*image,progress_monitor,
11656                (*image)->client_data);
11657              if (roi_image == (Image *) NULL)
11658                continue;
11659              /*
11660                Apply image processing technique to the region of interest.
11661              */
11662              windows->image.orphan=MagickTrue;
11663              (void) XMagickCommand(display,resource_info,windows,command_type,
11664                &roi_image,exception);
11665              progress_monitor=SetImageProgressMonitor(*image,
11666                (MagickProgressMonitor) NULL,(*image)->client_data);
11667              (void) XMagickCommand(display,resource_info,windows,
11668                SaveToUndoBufferCommand,image,exception);
11669              windows->image.orphan=MagickFalse;
11670              (void) CompositeImage(*image,roi_image,CopyCompositeOp,
11671                MagickTrue,crop_info.x,crop_info.y,exception);
11672              roi_image=DestroyImage(roi_image);
11673              (void) SetImageProgressMonitor(*image,progress_monitor,
11674                (*image)->client_data);
11675              break;
11676            }
11677          }
11678          if (command_type != InfoCommand)
11679            {
11680              XConfigureImageColormap(display,resource_info,windows,*image,
11681                exception);
11682              (void) XConfigureImage(display,resource_info,windows,*image,
11683                exception);
11684            }
11685          XCheckRefreshWindows(display,windows);
11686          XInfoWidget(display,windows,text);
11687          (void) XSetFunction(display,windows->image.highlight_context,
11688            GXinvert);
11689          state&=(~UpdateRegionState);
11690        }
11691      XHighlightRectangle(display,windows->image.id,
11692        windows->image.highlight_context,&highlight_info);
11693      XScreenEvent(display,windows,&event,exception);
11694      if (event.xany.window == windows->command.id)
11695        {
11696          /*
11697            Select a command from the Command widget.
11698          */
11699          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11700          command_type=NullCommand;
11701          id=XCommandWidget(display,windows,ApplyMenu,&event);
11702          if (id >= 0)
11703            {
11704              (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11705              command_type=ApplyCommands[id];
11706              if (id < ApplyMenus)
11707                {
11708                  /*
11709                    Select a command from a pop-up menu.
11710                  */
11711                  entry=XMenuWidget(display,windows,ApplyMenu[id],
11712                    (const char **) Menus[id],command);
11713                  if (entry >= 0)
11714                    {
11715                      (void) CopyMagickString(command,Menus[id][entry],
11716                        MaxTextExtent);
11717                      command_type=Commands[id][entry];
11718                    }
11719                }
11720            }
11721          (void) XSetFunction(display,windows->image.highlight_context,
11722            GXinvert);
11723          XHighlightRectangle(display,windows->image.id,
11724            windows->image.highlight_context,&highlight_info);
11725          if (command_type == HelpCommand)
11726            {
11727              (void) XSetFunction(display,windows->image.highlight_context,
11728                GXcopy);
11729              XTextViewWidget(display,resource_info,windows,MagickFalse,
11730                "Help Viewer - Region of Interest",ImageROIHelp);
11731              (void) XSetFunction(display,windows->image.highlight_context,
11732                GXinvert);
11733              continue;
11734            }
11735          if (command_type == QuitCommand)
11736            {
11737              /*
11738                exit.
11739              */
11740              state|=EscapeState;
11741              state|=ExitState;
11742              continue;
11743            }
11744          if (command_type != NullCommand)
11745            state|=UpdateRegionState;
11746          continue;
11747        }
11748      XHighlightRectangle(display,windows->image.id,
11749        windows->image.highlight_context,&highlight_info);
11750      switch (event.type)
11751      {
11752        case ButtonPress:
11753        {
11754          x=windows->image.x;
11755          y=windows->image.y;
11756          if (event.xbutton.button != Button1)
11757            break;
11758          if (event.xbutton.window != windows->image.id)
11759            break;
11760          x=windows->image.x+event.xbutton.x;
11761          y=windows->image.y+event.xbutton.y;
11762          if ((x < (int) (roi_info.x+RoiDelta)) &&
11763              (x > (int) (roi_info.x-RoiDelta)) &&
11764              (y < (int) (roi_info.y+RoiDelta)) &&
11765              (y > (int) (roi_info.y-RoiDelta)))
11766            {
11767              roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11768              roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11769              state|=UpdateConfigurationState;
11770              break;
11771            }
11772          if ((x < (int) (roi_info.x+RoiDelta)) &&
11773              (x > (int) (roi_info.x-RoiDelta)) &&
11774              (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11775              (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11776            {
11777              roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11778              state|=UpdateConfigurationState;
11779              break;
11780            }
11781          if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11782              (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11783              (y < (int) (roi_info.y+RoiDelta)) &&
11784              (y > (int) (roi_info.y-RoiDelta)))
11785            {
11786              roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11787              state|=UpdateConfigurationState;
11788              break;
11789            }
11790          if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11791              (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11792              (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11793              (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11794            {
11795              state|=UpdateConfigurationState;
11796              break;
11797            }
11798        }
11799        case ButtonRelease:
11800        {
11801          if (event.xbutton.window == windows->pan.id)
11802            if ((highlight_info.x != crop_info.x-windows->image.x) ||
11803                (highlight_info.y != crop_info.y-windows->image.y))
11804              XHighlightRectangle(display,windows->image.id,
11805                windows->image.highlight_context,&highlight_info);
11806          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11807            event.xbutton.time);
11808          break;
11809        }
11810        case Expose:
11811        {
11812          if (event.xexpose.window == windows->image.id)
11813            if (event.xexpose.count == 0)
11814              {
11815                event.xexpose.x=(int) highlight_info.x;
11816                event.xexpose.y=(int) highlight_info.y;
11817                event.xexpose.width=(int) highlight_info.width;
11818                event.xexpose.height=(int) highlight_info.height;
11819                XRefreshWindow(display,&windows->image,&event);
11820              }
11821          if (event.xexpose.window == windows->info.id)
11822            if (event.xexpose.count == 0)
11823              XInfoWidget(display,windows,text);
11824          break;
11825        }
11826        case KeyPress:
11827        {
11828          KeySym
11829            key_symbol;
11830
11831          if (event.xkey.window != windows->image.id)
11832            break;
11833          /*
11834            Respond to a user key press.
11835          */
11836          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11837            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11838          switch ((int) key_symbol)
11839          {
11840            case XK_Shift_L:
11841            case XK_Shift_R:
11842              break;
11843            case XK_Escape:
11844            case XK_F20:
11845              state|=EscapeState;
11846            case XK_Return:
11847            {
11848              state|=ExitState;
11849              break;
11850            }
11851            case XK_Home:
11852            case XK_KP_Home:
11853            {
11854              roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11855              roi_info.y=(ssize_t) (windows->image.height/2L-
11856                roi_info.height/2L);
11857              break;
11858            }
11859            case XK_Left:
11860            case XK_KP_Left:
11861            {
11862              roi_info.x--;
11863              break;
11864            }
11865            case XK_Up:
11866            case XK_KP_Up:
11867            case XK_Next:
11868            {
11869              roi_info.y--;
11870              break;
11871            }
11872            case XK_Right:
11873            case XK_KP_Right:
11874            {
11875              roi_info.x++;
11876              break;
11877            }
11878            case XK_Prior:
11879            case XK_Down:
11880            case XK_KP_Down:
11881            {
11882              roi_info.y++;
11883              break;
11884            }
11885            case XK_F1:
11886            case XK_Help:
11887            {
11888              (void) XSetFunction(display,windows->image.highlight_context,
11889                GXcopy);
11890              XTextViewWidget(display,resource_info,windows,MagickFalse,
11891                "Help Viewer - Region of Interest",ImageROIHelp);
11892              (void) XSetFunction(display,windows->image.highlight_context,
11893                GXinvert);
11894              break;
11895            }
11896            default:
11897            {
11898              command_type=XImageWindowCommand(display,resource_info,windows,
11899                event.xkey.state,key_symbol,image,exception);
11900              if (command_type != NullCommand)
11901                state|=UpdateRegionState;
11902              break;
11903            }
11904          }
11905          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11906            event.xkey.time);
11907          break;
11908        }
11909        case KeyRelease:
11910          break;
11911        case MotionNotify:
11912        {
11913          if (event.xbutton.window != windows->image.id)
11914            break;
11915          /*
11916            Map and unmap Info widget as text cursor crosses its boundaries.
11917          */
11918          x=event.xmotion.x;
11919          y=event.xmotion.y;
11920          if( IfMagickTrue(windows->info.mapped) )
11921            {
11922              if ((x < (int) (windows->info.x+windows->info.width)) &&
11923                  (y < (int) (windows->info.y+windows->info.height)))
11924                (void) XWithdrawWindow(display,windows->info.id,
11925                  windows->info.screen);
11926            }
11927          else
11928            if ((x > (int) (windows->info.x+windows->info.width)) ||
11929                (y > (int) (windows->info.y+windows->info.height)))
11930              (void) XMapWindow(display,windows->info.id);
11931          roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11932          roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11933          break;
11934        }
11935        case SelectionRequest:
11936        {
11937          XSelectionEvent
11938            notify;
11939
11940          XSelectionRequestEvent
11941            *request;
11942
11943          /*
11944            Set primary selection.
11945          */
11946          (void) FormatLocaleString(text,MaxTextExtent,
11947            "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11948            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11949          request=(&(event.xselectionrequest));
11950          (void) XChangeProperty(request->display,request->requestor,
11951            request->property,request->target,8,PropModeReplace,
11952            (unsigned char *) text,(int) strlen(text));
11953          notify.type=SelectionNotify;
11954          notify.display=request->display;
11955          notify.requestor=request->requestor;
11956          notify.selection=request->selection;
11957          notify.target=request->target;
11958          notify.time=request->time;
11959          if (request->property == None)
11960            notify.property=request->target;
11961          else
11962            notify.property=request->property;
11963          (void) XSendEvent(request->display,request->requestor,False,0,
11964            (XEvent *) &notify);
11965        }
11966        default:
11967          break;
11968      }
11969      if ((state & UpdateConfigurationState) != 0)
11970        {
11971          (void) XPutBackEvent(display,&event);
11972          (void) XCheckDefineCursor(display,windows->image.id,cursor);
11973          break;
11974        }
11975    } while ((state & ExitState) == 0);
11976  } while ((state & ExitState) == 0);
11977  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11978  XSetCursorState(display,windows,MagickFalse);
11979  if ((state & EscapeState) != 0)
11980    return(MagickTrue);
11981  return(MagickTrue);
11982}
11983
11984/*
11985%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11986%                                                                             %
11987%                                                                             %
11988%                                                                             %
11989+   X R o t a t e I m a g e                                                   %
11990%                                                                             %
11991%                                                                             %
11992%                                                                             %
11993%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11994%
11995%  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
11996%  rotation angle is computed from the slope of a line drawn by the user.
11997%
11998%  The format of the XRotateImage method is:
11999%
12000%      MagickBooleanType XRotateImage(Display *display,
12001%        XResourceInfo *resource_info,XWindows *windows,double degrees,
12002%        Image **image,ExceptionInfo *exception)
12003%
12004%  A description of each parameter follows:
12005%
12006%    o display: Specifies a connection to an X server; returned from
12007%      XOpenDisplay.
12008%
12009%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12010%
12011%    o windows: Specifies a pointer to a XWindows structure.
12012%
12013%    o degrees: Specifies the number of degrees to rotate the image.
12014%
12015%    o image: the image.
12016%
12017%    o exception: return any errors or warnings in this structure.
12018%
12019*/
12020static MagickBooleanType XRotateImage(Display *display,
12021  XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
12022  ExceptionInfo *exception)
12023{
12024  static const char
12025    *RotateMenu[] =
12026    {
12027      "Pixel Color",
12028      "Direction",
12029      "Help",
12030      "Dismiss",
12031      (char *) NULL
12032    };
12033
12034  static ModeType
12035    direction = HorizontalRotateCommand;
12036
12037  static const ModeType
12038    DirectionCommands[] =
12039    {
12040      HorizontalRotateCommand,
12041      VerticalRotateCommand
12042    },
12043    RotateCommands[] =
12044    {
12045      RotateColorCommand,
12046      RotateDirectionCommand,
12047      RotateHelpCommand,
12048      RotateDismissCommand
12049    };
12050
12051  static unsigned int
12052    pen_id = 0;
12053
12054  char
12055    command[MaxTextExtent],
12056    text[MaxTextExtent];
12057
12058  Image
12059    *rotate_image;
12060
12061  int
12062    id,
12063    x,
12064    y;
12065
12066  double
12067    normalized_degrees;
12068
12069  register int
12070    i;
12071
12072  unsigned int
12073    height,
12074    rotations,
12075    width;
12076
12077  if (degrees == 0.0)
12078    {
12079      unsigned int
12080        distance;
12081
12082      size_t
12083        state;
12084
12085      XEvent
12086        event;
12087
12088      XSegment
12089        rotate_info;
12090
12091      /*
12092        Map Command widget.
12093      */
12094      (void) CloneString(&windows->command.name,"Rotate");
12095      windows->command.data=2;
12096      (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12097      (void) XMapRaised(display,windows->command.id);
12098      XClientMessage(display,windows->image.id,windows->im_protocols,
12099        windows->im_update_widget,CurrentTime);
12100      /*
12101        Wait for first button press.
12102      */
12103      (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12104      XQueryPosition(display,windows->image.id,&x,&y);
12105      rotate_info.x1=x;
12106      rotate_info.y1=y;
12107      rotate_info.x2=x;
12108      rotate_info.y2=y;
12109      state=DefaultState;
12110      do
12111      {
12112        XHighlightLine(display,windows->image.id,
12113          windows->image.highlight_context,&rotate_info);
12114        /*
12115          Wait for next event.
12116        */
12117        XScreenEvent(display,windows,&event,exception);
12118        XHighlightLine(display,windows->image.id,
12119          windows->image.highlight_context,&rotate_info);
12120        if (event.xany.window == windows->command.id)
12121          {
12122            /*
12123              Select a command from the Command widget.
12124            */
12125            id=XCommandWidget(display,windows,RotateMenu,&event);
12126            if (id < 0)
12127              continue;
12128            (void) XSetFunction(display,windows->image.highlight_context,
12129              GXcopy);
12130            switch (RotateCommands[id])
12131            {
12132              case RotateColorCommand:
12133              {
12134                const char
12135                  *ColorMenu[MaxNumberPens];
12136
12137                int
12138                  pen_number;
12139
12140                XColor
12141                  color;
12142
12143                /*
12144                  Initialize menu selections.
12145                */
12146                for (i=0; i < (int) (MaxNumberPens-2); i++)
12147                  ColorMenu[i]=resource_info->pen_colors[i];
12148                ColorMenu[MaxNumberPens-2]="Browser...";
12149                ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12150                /*
12151                  Select a pen color from the pop-up menu.
12152                */
12153                pen_number=XMenuWidget(display,windows,RotateMenu[id],
12154                  (const char **) ColorMenu,command);
12155                if (pen_number < 0)
12156                  break;
12157                if (pen_number == (MaxNumberPens-2))
12158                  {
12159                    static char
12160                      color_name[MaxTextExtent] = "gray";
12161
12162                    /*
12163                      Select a pen color from a dialog.
12164                    */
12165                    resource_info->pen_colors[pen_number]=color_name;
12166                    XColorBrowserWidget(display,windows,"Select",color_name);
12167                    if (*color_name == '\0')
12168                      break;
12169                  }
12170                /*
12171                  Set pen color.
12172                */
12173                (void) XParseColor(display,windows->map_info->colormap,
12174                  resource_info->pen_colors[pen_number],&color);
12175                XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12176                  (unsigned int) MaxColors,&color);
12177                windows->pixel_info->pen_colors[pen_number]=color;
12178                pen_id=(unsigned int) pen_number;
12179                break;
12180              }
12181              case RotateDirectionCommand:
12182              {
12183                static const char
12184                  *Directions[] =
12185                  {
12186                    "horizontal",
12187                    "vertical",
12188                    (char *) NULL,
12189                  };
12190
12191                /*
12192                  Select a command from the pop-up menu.
12193                */
12194                id=XMenuWidget(display,windows,RotateMenu[id],
12195                  Directions,command);
12196                if (id >= 0)
12197                  direction=DirectionCommands[id];
12198                break;
12199              }
12200              case RotateHelpCommand:
12201              {
12202                XTextViewWidget(display,resource_info,windows,MagickFalse,
12203                  "Help Viewer - Image Rotation",ImageRotateHelp);
12204                break;
12205              }
12206              case RotateDismissCommand:
12207              {
12208                /*
12209                  Prematurely exit.
12210                */
12211                state|=EscapeState;
12212                state|=ExitState;
12213                break;
12214              }
12215              default:
12216                break;
12217            }
12218            (void) XSetFunction(display,windows->image.highlight_context,
12219              GXinvert);
12220            continue;
12221          }
12222        switch (event.type)
12223        {
12224          case ButtonPress:
12225          {
12226            if (event.xbutton.button != Button1)
12227              break;
12228            if (event.xbutton.window != windows->image.id)
12229              break;
12230            /*
12231              exit loop.
12232            */
12233            (void) XSetFunction(display,windows->image.highlight_context,
12234              GXcopy);
12235            rotate_info.x1=event.xbutton.x;
12236            rotate_info.y1=event.xbutton.y;
12237            state|=ExitState;
12238            break;
12239          }
12240          case ButtonRelease:
12241            break;
12242          case Expose:
12243            break;
12244          case KeyPress:
12245          {
12246            char
12247              command[MaxTextExtent];
12248
12249            KeySym
12250              key_symbol;
12251
12252            if (event.xkey.window != windows->image.id)
12253              break;
12254            /*
12255              Respond to a user key press.
12256            */
12257            (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12258              sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12259            switch ((int) key_symbol)
12260            {
12261              case XK_Escape:
12262              case XK_F20:
12263              {
12264                /*
12265                  Prematurely exit.
12266                */
12267                state|=EscapeState;
12268                state|=ExitState;
12269                break;
12270              }
12271              case XK_F1:
12272              case XK_Help:
12273              {
12274                (void) XSetFunction(display,windows->image.highlight_context,
12275                  GXcopy);
12276                XTextViewWidget(display,resource_info,windows,MagickFalse,
12277                  "Help Viewer - Image Rotation",ImageRotateHelp);
12278                (void) XSetFunction(display,windows->image.highlight_context,
12279                  GXinvert);
12280                break;
12281              }
12282              default:
12283              {
12284                (void) XBell(display,0);
12285                break;
12286              }
12287            }
12288            break;
12289          }
12290          case MotionNotify:
12291          {
12292            rotate_info.x1=event.xmotion.x;
12293            rotate_info.y1=event.xmotion.y;
12294          }
12295        }
12296        rotate_info.x2=rotate_info.x1;
12297        rotate_info.y2=rotate_info.y1;
12298        if (direction == HorizontalRotateCommand)
12299          rotate_info.x2+=32;
12300        else
12301          rotate_info.y2-=32;
12302      } while ((state & ExitState) == 0);
12303      (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12304      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12305      if ((state & EscapeState) != 0)
12306        return(MagickTrue);
12307      /*
12308        Draw line as pointer moves until the mouse button is released.
12309      */
12310      distance=0;
12311      (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12312      state=DefaultState;
12313      do
12314      {
12315        if (distance > 9)
12316          {
12317            /*
12318              Display info and draw rotation line.
12319            */
12320            if( IfMagickFalse(windows->info.mapped) )
12321              (void) XMapWindow(display,windows->info.id);
12322            (void) FormatLocaleString(text,MaxTextExtent," %g",
12323              direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12324            XInfoWidget(display,windows,text);
12325            XHighlightLine(display,windows->image.id,
12326              windows->image.highlight_context,&rotate_info);
12327          }
12328        else
12329          if( IfMagickTrue(windows->info.mapped) )
12330            (void) XWithdrawWindow(display,windows->info.id,
12331              windows->info.screen);
12332        /*
12333          Wait for next event.
12334        */
12335        XScreenEvent(display,windows,&event,exception);
12336        if (distance > 9)
12337          XHighlightLine(display,windows->image.id,
12338            windows->image.highlight_context,&rotate_info);
12339        switch (event.type)
12340        {
12341          case ButtonPress:
12342            break;
12343          case ButtonRelease:
12344          {
12345            /*
12346              User has committed to rotation line.
12347            */
12348            rotate_info.x2=event.xbutton.x;
12349            rotate_info.y2=event.xbutton.y;
12350            state|=ExitState;
12351            break;
12352          }
12353          case Expose:
12354            break;
12355          case MotionNotify:
12356          {
12357            rotate_info.x2=event.xmotion.x;
12358            rotate_info.y2=event.xmotion.y;
12359          }
12360          default:
12361            break;
12362        }
12363        /*
12364          Check boundary conditions.
12365        */
12366        if (rotate_info.x2 < 0)
12367          rotate_info.x2=0;
12368        else
12369          if (rotate_info.x2 > (int) windows->image.width)
12370            rotate_info.x2=(short) windows->image.width;
12371        if (rotate_info.y2 < 0)
12372          rotate_info.y2=0;
12373        else
12374          if (rotate_info.y2 > (int) windows->image.height)
12375            rotate_info.y2=(short) windows->image.height;
12376        /*
12377          Compute rotation angle from the slope of the line.
12378        */
12379        degrees=0.0;
12380        distance=(unsigned int)
12381          ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12382          ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12383        if (distance > 9)
12384          degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12385            rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12386      } while ((state & ExitState) == 0);
12387      (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12388      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12389      if (distance <= 9)
12390        return(MagickTrue);
12391    }
12392  if (direction == VerticalRotateCommand)
12393    degrees-=90.0;
12394  if (degrees == 0.0)
12395    return(MagickTrue);
12396  /*
12397    Rotate image.
12398  */
12399  normalized_degrees=degrees;
12400  while (normalized_degrees < -45.0)
12401    normalized_degrees+=360.0;
12402  for (rotations=0; normalized_degrees > 45.0; rotations++)
12403    normalized_degrees-=90.0;
12404  if (normalized_degrees != 0.0)
12405    (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12406      exception);
12407  XSetCursorState(display,windows,MagickTrue);
12408  XCheckRefreshWindows(display,windows);
12409  (*image)->background_color.red=(double) ScaleShortToQuantum(
12410    windows->pixel_info->pen_colors[pen_id].red);
12411  (*image)->background_color.green=(double) ScaleShortToQuantum(
12412    windows->pixel_info->pen_colors[pen_id].green);
12413  (*image)->background_color.blue=(double) ScaleShortToQuantum(
12414    windows->pixel_info->pen_colors[pen_id].blue);
12415  rotate_image=RotateImage(*image,degrees,exception);
12416  XSetCursorState(display,windows,MagickFalse);
12417  if (rotate_image == (Image *) NULL)
12418    return(MagickFalse);
12419  *image=DestroyImage(*image);
12420  *image=rotate_image;
12421  if (windows->image.crop_geometry != (char *) NULL)
12422    {
12423      /*
12424        Rotate crop geometry.
12425      */
12426      width=(unsigned int) (*image)->columns;
12427      height=(unsigned int) (*image)->rows;
12428      (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12429      switch (rotations % 4)
12430      {
12431        default:
12432        case 0:
12433          break;
12434        case 1:
12435        {
12436          /*
12437            Rotate 90 degrees.
12438          */
12439          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12440            "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12441            (int) height-y,x);
12442          break;
12443        }
12444        case 2:
12445        {
12446          /*
12447            Rotate 180 degrees.
12448          */
12449          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12450            "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12451          break;
12452        }
12453        case 3:
12454        {
12455          /*
12456            Rotate 270 degrees.
12457          */
12458          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12459            "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12460          break;
12461        }
12462      }
12463    }
12464  if( IfMagickTrue(windows->image.orphan) )
12465    return(MagickTrue);
12466  if (normalized_degrees != 0.0)
12467    {
12468      /*
12469        Update image colormap.
12470      */
12471      windows->image.window_changes.width=(int) (*image)->columns;
12472      windows->image.window_changes.height=(int) (*image)->rows;
12473      if (windows->image.crop_geometry != (char *) NULL)
12474        {
12475          /*
12476            Obtain dimensions of image from crop geometry.
12477          */
12478          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12479            &width,&height);
12480          windows->image.window_changes.width=(int) width;
12481          windows->image.window_changes.height=(int) height;
12482        }
12483      XConfigureImageColormap(display,resource_info,windows,*image,exception);
12484    }
12485  else
12486    if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12487      {
12488        windows->image.window_changes.width=windows->image.ximage->height;
12489        windows->image.window_changes.height=windows->image.ximage->width;
12490      }
12491  /*
12492    Update image configuration.
12493  */
12494  (void) XConfigureImage(display,resource_info,windows,*image,exception);
12495  return(MagickTrue);
12496}
12497
12498/*
12499%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12500%                                                                             %
12501%                                                                             %
12502%                                                                             %
12503+   X S a v e I m a g e                                                       %
12504%                                                                             %
12505%                                                                             %
12506%                                                                             %
12507%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12508%
12509%  XSaveImage() saves an image to a file.
12510%
12511%  The format of the XSaveImage method is:
12512%
12513%      MagickBooleanType XSaveImage(Display *display,
12514%        XResourceInfo *resource_info,XWindows *windows,Image *image,
12515%        ExceptionInfo *exception)
12516%
12517%  A description of each parameter follows:
12518%
12519%    o display: Specifies a connection to an X server; returned from
12520%      XOpenDisplay.
12521%
12522%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12523%
12524%    o windows: Specifies a pointer to a XWindows structure.
12525%
12526%    o image: the image.
12527%
12528%    o exception: return any errors or warnings in this structure.
12529%
12530*/
12531static MagickBooleanType XSaveImage(Display *display,
12532  XResourceInfo *resource_info,XWindows *windows,Image *image,
12533  ExceptionInfo *exception)
12534{
12535  char
12536    filename[MaxTextExtent],
12537    geometry[MaxTextExtent];
12538
12539  Image
12540    *save_image;
12541
12542  ImageInfo
12543    *image_info;
12544
12545  MagickStatusType
12546    status;
12547
12548  /*
12549    Request file name from user.
12550  */
12551  if (resource_info->write_filename != (char *) NULL)
12552    (void) CopyMagickString(filename,resource_info->write_filename,
12553      MaxTextExtent);
12554  else
12555    {
12556      char
12557        path[MaxTextExtent];
12558
12559      int
12560        status;
12561
12562      GetPathComponent(image->filename,HeadPath,path);
12563      GetPathComponent(image->filename,TailPath,filename);
12564      if (*path != '\0')
12565        {
12566          if (*path == '\0')
12567            (void) CopyMagickString(path,".",MaxTextExtent);
12568          status=chdir(path);
12569          if (status == -1)
12570            (void) ThrowMagickException(exception,GetMagickModule(),
12571              FileOpenError,"UnableToOpenFile","%s",path);
12572        }
12573    }
12574  XFileBrowserWidget(display,windows,"Save",filename);
12575  if (*filename == '\0')
12576    return(MagickTrue);
12577  if( IfMagickTrue(IsPathAccessible(filename)) )
12578    {
12579      int
12580        status;
12581
12582      /*
12583        File exists-- seek user's permission before overwriting.
12584      */
12585      status=XConfirmWidget(display,windows,"Overwrite",filename);
12586      if (status <= 0)
12587        return(MagickTrue);
12588    }
12589  image_info=CloneImageInfo(resource_info->image_info);
12590  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12591  (void) SetImageInfo(image_info,1,exception);
12592  if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12593      (LocaleCompare(image_info->magick,"JPG") == 0))
12594    {
12595      char
12596        quality[MaxTextExtent];
12597
12598      int
12599        status;
12600
12601      /*
12602        Request JPEG quality from user.
12603      */
12604      (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
12605        image->quality);
12606      status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12607        quality);
12608      if (*quality == '\0')
12609        return(MagickTrue);
12610      image->quality=StringToUnsignedLong(quality);
12611      image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12612    }
12613  if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12614      (LocaleCompare(image_info->magick,"PDF") == 0) ||
12615      (LocaleCompare(image_info->magick,"PS") == 0) ||
12616      (LocaleCompare(image_info->magick,"PS2") == 0))
12617    {
12618      char
12619        geometry[MaxTextExtent];
12620
12621      /*
12622        Request page geometry from user.
12623      */
12624      (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12625      if (LocaleCompare(image_info->magick,"PDF") == 0)
12626        (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12627      if (image_info->page != (char *) NULL)
12628        (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12629      XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12630        "Select page geometry:",geometry);
12631      if (*geometry != '\0')
12632        image_info->page=GetPageGeometry(geometry);
12633    }
12634  /*
12635    Apply image transforms.
12636  */
12637  XSetCursorState(display,windows,MagickTrue);
12638  XCheckRefreshWindows(display,windows);
12639  save_image=CloneImage(image,0,0,MagickTrue,exception);
12640  if (save_image == (Image *) NULL)
12641    return(MagickFalse);
12642  (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
12643    windows->image.ximage->width,windows->image.ximage->height);
12644  (void) TransformImage(&save_image,windows->image.crop_geometry,geometry,
12645    exception);
12646  /*
12647    Write image.
12648  */
12649  (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12650  status=WriteImage(image_info,save_image,exception);
12651  if( IfMagickTrue(status) )
12652    image->taint=MagickFalse;
12653  save_image=DestroyImage(save_image);
12654  image_info=DestroyImageInfo(image_info);
12655  XSetCursorState(display,windows,MagickFalse);
12656  return(IsMagickTrue(status));
12657}
12658
12659/*
12660%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12661%                                                                             %
12662%                                                                             %
12663%                                                                             %
12664+   X S c r e e n E v e n t                                                   %
12665%                                                                             %
12666%                                                                             %
12667%                                                                             %
12668%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12669%
12670%  XScreenEvent() handles global events associated with the Pan and Magnify
12671%  windows.
12672%
12673%  The format of the XScreenEvent function is:
12674%
12675%      void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12676%        ExceptionInfo *exception)
12677%
12678%  A description of each parameter follows:
12679%
12680%    o display: Specifies a pointer to the Display structure;  returned from
12681%      XOpenDisplay.
12682%
12683%    o windows: Specifies a pointer to a XWindows structure.
12684%
12685%    o event: Specifies a pointer to a X11 XEvent structure.
12686%
12687%    o exception: return any errors or warnings in this structure.
12688%
12689*/
12690
12691#if defined(__cplusplus) || defined(c_plusplus)
12692extern "C" {
12693#endif
12694
12695static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12696{
12697  register XWindows
12698    *windows;
12699
12700  windows=(XWindows *) data;
12701  if ((event->type == ClientMessage) &&
12702      (event->xclient.window == windows->image.id))
12703    return(MagickFalse);
12704  return(MagickTrue);
12705}
12706
12707#if defined(__cplusplus) || defined(c_plusplus)
12708}
12709#endif
12710
12711static void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12712  ExceptionInfo *exception)
12713{
12714  register int
12715    x,
12716    y;
12717
12718  (void) XIfEvent(display,event,XPredicate,(char *) windows);
12719  if (event->xany.window == windows->command.id)
12720    return;
12721  switch (event->type)
12722  {
12723    case ButtonPress:
12724    case ButtonRelease:
12725    {
12726      if ((event->xbutton.button == Button3) &&
12727          (event->xbutton.state & Mod1Mask))
12728        {
12729          /*
12730            Convert Alt-Button3 to Button2.
12731          */
12732          event->xbutton.button=Button2;
12733          event->xbutton.state&=(~Mod1Mask);
12734        }
12735      if (event->xbutton.window == windows->backdrop.id)
12736        {
12737          (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12738            event->xbutton.time);
12739          break;
12740        }
12741      if (event->xbutton.window == windows->pan.id)
12742        {
12743          XPanImage(display,windows,event,exception);
12744          break;
12745        }
12746      if (event->xbutton.window == windows->image.id)
12747        if (event->xbutton.button == Button2)
12748          {
12749            /*
12750              Update magnified image.
12751            */
12752            x=event->xbutton.x;
12753            y=event->xbutton.y;
12754            if (x < 0)
12755              x=0;
12756            else
12757              if (x >= (int) windows->image.width)
12758                x=(int) (windows->image.width-1);
12759            windows->magnify.x=(int) windows->image.x+x;
12760            if (y < 0)
12761              y=0;
12762            else
12763             if (y >= (int) windows->image.height)
12764               y=(int) (windows->image.height-1);
12765            windows->magnify.y=windows->image.y+y;
12766            if( IfMagickFalse(windows->magnify.mapped) )
12767              (void) XMapRaised(display,windows->magnify.id);
12768            XMakeMagnifyImage(display,windows,exception);
12769            if (event->type == ButtonRelease)
12770              (void) XWithdrawWindow(display,windows->info.id,
12771                windows->info.screen);
12772            break;
12773          }
12774      break;
12775    }
12776    case ClientMessage:
12777    {
12778      /*
12779        If client window delete message, exit.
12780      */
12781      if (event->xclient.message_type != windows->wm_protocols)
12782        break;
12783      if (*event->xclient.data.l != (long) windows->wm_delete_window)
12784        break;
12785      if (event->xclient.window == windows->magnify.id)
12786        {
12787          (void) XWithdrawWindow(display,windows->magnify.id,
12788            windows->magnify.screen);
12789          break;
12790        }
12791      break;
12792    }
12793    case ConfigureNotify:
12794    {
12795      if (event->xconfigure.window == windows->magnify.id)
12796        {
12797          unsigned int
12798            magnify;
12799
12800          /*
12801            Magnify window has a new configuration.
12802          */
12803          windows->magnify.width=(unsigned int) event->xconfigure.width;
12804          windows->magnify.height=(unsigned int) event->xconfigure.height;
12805          if( IfMagickFalse(windows->magnify.mapped) )
12806            break;
12807          magnify=1;
12808          while ((int) magnify <= event->xconfigure.width)
12809            magnify<<=1;
12810          while ((int) magnify <= event->xconfigure.height)
12811            magnify<<=1;
12812          magnify>>=1;
12813          if (((int) magnify != event->xconfigure.width) ||
12814              ((int) magnify != event->xconfigure.height))
12815            {
12816              XWindowChanges
12817                window_changes;
12818
12819              window_changes.width=(int) magnify;
12820              window_changes.height=(int) magnify;
12821              (void) XReconfigureWMWindow(display,windows->magnify.id,
12822                windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12823                &window_changes);
12824              break;
12825            }
12826          XMakeMagnifyImage(display,windows,exception);
12827          break;
12828        }
12829      break;
12830    }
12831    case Expose:
12832    {
12833      if (event->xexpose.window == windows->image.id)
12834        {
12835          XRefreshWindow(display,&windows->image,event);
12836          break;
12837        }
12838      if (event->xexpose.window == windows->pan.id)
12839        if (event->xexpose.count == 0)
12840          {
12841            XDrawPanRectangle(display,windows);
12842            break;
12843          }
12844      if (event->xexpose.window == windows->magnify.id)
12845        if (event->xexpose.count == 0)
12846          {
12847            XMakeMagnifyImage(display,windows,exception);
12848            break;
12849          }
12850      break;
12851    }
12852    case KeyPress:
12853    {
12854      char
12855        command[MaxTextExtent];
12856
12857      KeySym
12858        key_symbol;
12859
12860      if (event->xkey.window != windows->magnify.id)
12861        break;
12862      /*
12863        Respond to a user key press.
12864      */
12865      (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12866        sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12867      XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol,
12868        exception);
12869      break;
12870    }
12871    case MapNotify:
12872    {
12873      if (event->xmap.window == windows->magnify.id)
12874        {
12875          windows->magnify.mapped=MagickTrue;
12876          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12877          break;
12878        }
12879      if (event->xmap.window == windows->info.id)
12880        {
12881          windows->info.mapped=MagickTrue;
12882          break;
12883        }
12884      break;
12885    }
12886    case MotionNotify:
12887    {
12888      while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12889      if (event->xmotion.window == windows->image.id)
12890        if( IfMagickTrue(windows->magnify.mapped) )
12891          {
12892            /*
12893              Update magnified image.
12894            */
12895            x=event->xmotion.x;
12896            y=event->xmotion.y;
12897            if (x < 0)
12898              x=0;
12899            else
12900              if (x >= (int) windows->image.width)
12901                x=(int) (windows->image.width-1);
12902            windows->magnify.x=(int) windows->image.x+x;
12903            if (y < 0)
12904              y=0;
12905            else
12906             if (y >= (int) windows->image.height)
12907               y=(int) (windows->image.height-1);
12908            windows->magnify.y=windows->image.y+y;
12909            XMakeMagnifyImage(display,windows,exception);
12910          }
12911      break;
12912    }
12913    case UnmapNotify:
12914    {
12915      if (event->xunmap.window == windows->magnify.id)
12916        {
12917          windows->magnify.mapped=MagickFalse;
12918          break;
12919        }
12920      if (event->xunmap.window == windows->info.id)
12921        {
12922          windows->info.mapped=MagickFalse;
12923          break;
12924        }
12925      break;
12926    }
12927    default:
12928      break;
12929  }
12930}
12931
12932/*
12933%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12934%                                                                             %
12935%                                                                             %
12936%                                                                             %
12937+   X S e t C r o p G e o m e t r y                                           %
12938%                                                                             %
12939%                                                                             %
12940%                                                                             %
12941%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12942%
12943%  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12944%  and translates it to a cropping geometry relative to the image.
12945%
12946%  The format of the XSetCropGeometry method is:
12947%
12948%      void XSetCropGeometry(Display *display,XWindows *windows,
12949%        RectangleInfo *crop_info,Image *image)
12950%
12951%  A description of each parameter follows:
12952%
12953%    o display: Specifies a connection to an X server; returned from
12954%      XOpenDisplay.
12955%
12956%    o windows: Specifies a pointer to a XWindows structure.
12957%
12958%    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12959%      Image window to crop.
12960%
12961%    o image: the image.
12962%
12963*/
12964static void XSetCropGeometry(Display *display,XWindows *windows,
12965  RectangleInfo *crop_info,Image *image)
12966{
12967  char
12968    text[MaxTextExtent];
12969
12970  int
12971    x,
12972    y;
12973
12974  double
12975    scale_factor;
12976
12977  unsigned int
12978    height,
12979    width;
12980
12981  if( IfMagickTrue(windows->info.mapped) )
12982    {
12983      /*
12984        Display info on cropping rectangle.
12985      */
12986      (void) FormatLocaleString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
12987        (double) crop_info->width,(double) crop_info->height,(double)
12988        crop_info->x,(double) crop_info->y);
12989      XInfoWidget(display,windows,text);
12990    }
12991  /*
12992    Cropping geometry is relative to any previous crop geometry.
12993  */
12994  x=0;
12995  y=0;
12996  width=(unsigned int) image->columns;
12997  height=(unsigned int) image->rows;
12998  if (windows->image.crop_geometry != (char *) NULL)
12999    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13000  else
13001    windows->image.crop_geometry=AcquireString((char *) NULL);
13002  /*
13003    Define the crop geometry string from the cropping rectangle.
13004  */
13005  scale_factor=(double) width/windows->image.ximage->width;
13006  if (crop_info->x > 0)
13007    x+=(int) (scale_factor*crop_info->x+0.5);
13008  width=(unsigned int) (scale_factor*crop_info->width+0.5);
13009  if (width == 0)
13010    width=1;
13011  scale_factor=(double) height/windows->image.ximage->height;
13012  if (crop_info->y > 0)
13013    y+=(int) (scale_factor*crop_info->y+0.5);
13014  height=(unsigned int) (scale_factor*crop_info->height+0.5);
13015  if (height == 0)
13016    height=1;
13017  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
13018    "%ux%u%+d%+d",width,height,x,y);
13019}
13020
13021/*
13022%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13023%                                                                             %
13024%                                                                             %
13025%                                                                             %
13026+   X T i l e I m a g e                                                       %
13027%                                                                             %
13028%                                                                             %
13029%                                                                             %
13030%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13031%
13032%  XTileImage() loads or deletes a selected tile from a visual image directory.
13033%  The load or delete command is chosen from a menu.
13034%
13035%  The format of the XTileImage method is:
13036%
13037%      Image *XTileImage(Display *display,XResourceInfo *resource_info,
13038%        XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13039%
13040%  A description of each parameter follows:
13041%
13042%    o tile_image:  XTileImage reads or deletes the tile image
13043%      and returns it.  A null image is returned if an error occurs.
13044%
13045%    o display: Specifies a connection to an X server;  returned from
13046%      XOpenDisplay.
13047%
13048%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13049%
13050%    o windows: Specifies a pointer to a XWindows structure.
13051%
13052%    o image: the image; returned from ReadImage.
13053%
13054%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
13055%      the entire image is refreshed.
13056%
13057%    o exception: return any errors or warnings in this structure.
13058%
13059*/
13060static Image *XTileImage(Display *display,XResourceInfo *resource_info,
13061  XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13062{
13063  static const char
13064    *VerbMenu[] =
13065    {
13066      "Load",
13067      "Next",
13068      "Former",
13069      "Delete",
13070      "Update",
13071      (char *) NULL,
13072    };
13073
13074  static const ModeType
13075    TileCommands[] =
13076    {
13077      TileLoadCommand,
13078      TileNextCommand,
13079      TileFormerCommand,
13080      TileDeleteCommand,
13081      TileUpdateCommand
13082    };
13083
13084  char
13085    command[MaxTextExtent],
13086    filename[MaxTextExtent];
13087
13088  Image
13089    *tile_image;
13090
13091  int
13092    id,
13093    status,
13094    tile,
13095    x,
13096    y;
13097
13098  double
13099    scale_factor;
13100
13101  register char
13102    *p,
13103    *q;
13104
13105  register int
13106    i;
13107
13108  unsigned int
13109    height,
13110    width;
13111
13112  /*
13113    Tile image is relative to montage image configuration.
13114  */
13115  x=0;
13116  y=0;
13117  width=(unsigned int) image->columns;
13118  height=(unsigned int) image->rows;
13119  if (windows->image.crop_geometry != (char *) NULL)
13120    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13121  scale_factor=(double) width/windows->image.ximage->width;
13122  event->xbutton.x+=windows->image.x;
13123  event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13124  scale_factor=(double) height/windows->image.ximage->height;
13125  event->xbutton.y+=windows->image.y;
13126  event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13127  /*
13128    Determine size and location of each tile in the visual image directory.
13129  */
13130  width=(unsigned int) image->columns;
13131  height=(unsigned int) image->rows;
13132  x=0;
13133  y=0;
13134  (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13135  tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13136    (event->xbutton.x-x)/width;
13137  if (tile < 0)
13138    {
13139      /*
13140        Button press is outside any tile.
13141      */
13142      (void) XBell(display,0);
13143      return((Image *) NULL);
13144    }
13145  /*
13146    Determine file name from the tile directory.
13147  */
13148  p=image->directory;
13149  for (i=tile; (i != 0) && (*p != '\0'); )
13150  {
13151    if (*p == '\n')
13152      i--;
13153    p++;
13154  }
13155  if (*p == '\0')
13156    {
13157      /*
13158        Button press is outside any tile.
13159      */
13160      (void) XBell(display,0);
13161      return((Image *) NULL);
13162    }
13163  /*
13164    Select a command from the pop-up menu.
13165  */
13166  id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13167  if (id < 0)
13168    return((Image *) NULL);
13169  q=p;
13170  while ((*q != '\n') && (*q != '\0'))
13171    q++;
13172  (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13173  /*
13174    Perform command for the selected tile.
13175  */
13176  XSetCursorState(display,windows,MagickTrue);
13177  XCheckRefreshWindows(display,windows);
13178  tile_image=NewImageList();
13179  switch (TileCommands[id])
13180  {
13181    case TileLoadCommand:
13182    {
13183      /*
13184        Load tile image.
13185      */
13186      XCheckRefreshWindows(display,windows);
13187      (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13188        MaxTextExtent);
13189      (void) CopyMagickString(resource_info->image_info->filename,filename,
13190        MaxTextExtent);
13191      tile_image=ReadImage(resource_info->image_info,exception);
13192      CatchException(exception);
13193      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13194      break;
13195    }
13196    case TileNextCommand:
13197    {
13198      /*
13199        Display next image.
13200      */
13201      XClientMessage(display,windows->image.id,windows->im_protocols,
13202        windows->im_next_image,CurrentTime);
13203      break;
13204    }
13205    case TileFormerCommand:
13206    {
13207      /*
13208        Display former image.
13209      */
13210      XClientMessage(display,windows->image.id,windows->im_protocols,
13211        windows->im_former_image,CurrentTime);
13212      break;
13213    }
13214    case TileDeleteCommand:
13215    {
13216      /*
13217        Delete tile image.
13218      */
13219      if( IfMagickFalse(IsPathAccessible(filename)) )
13220        {
13221          XNoticeWidget(display,windows,"Image file does not exist:",filename);
13222          break;
13223        }
13224      status=XConfirmWidget(display,windows,"Really delete tile",filename);
13225      if (status <= 0)
13226        break;
13227      status=IsMagickTrue(remove_utf8(filename));
13228      if( IfMagickTrue(status) )
13229        {
13230          XNoticeWidget(display,windows,"Unable to delete image file:",
13231            filename);
13232          break;
13233        }
13234    }
13235    case TileUpdateCommand:
13236    {
13237      int
13238        x_offset,
13239        y_offset;
13240
13241      PixelInfo
13242        pixel;
13243
13244      register int
13245        j;
13246
13247      register Quantum
13248        *s;
13249
13250      /*
13251        Ensure all the images exist.
13252      */
13253      tile=0;
13254      GetPixelInfo(image,&pixel);
13255      for (p=image->directory; *p != '\0'; p++)
13256      {
13257        CacheView
13258          *image_view;
13259
13260        q=p;
13261        while ((*q != '\n') && (*q != '\0'))
13262          q++;
13263        (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13264        p=q;
13265        if( IfMagickTrue(IsPathAccessible(filename)) )
13266          {
13267            tile++;
13268            continue;
13269          }
13270        /*
13271          Overwrite tile with background color.
13272        */
13273        x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13274        y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13275        image_view=AcquireAuthenticCacheView(image,exception);
13276        (void) GetOneCacheViewVirtualPixelInfo(image_view,0,0,&pixel,exception);
13277        for (i=0; i < (int) height; i++)
13278        {
13279          s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13280            y_offset+i,width,1,exception);
13281          if (s == (Quantum *) NULL)
13282            break;
13283          for (j=0; j < (int) width; j++)
13284          {
13285            SetPixelInfoPixel(image,&pixel,s);
13286            s+=GetPixelChannels(image);
13287          }
13288          if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
13289            break;
13290        }
13291        image_view=DestroyCacheView(image_view);
13292        tile++;
13293      }
13294      windows->image.window_changes.width=(int) image->columns;
13295      windows->image.window_changes.height=(int) image->rows;
13296      XConfigureImageColormap(display,resource_info,windows,image,exception);
13297      (void) XConfigureImage(display,resource_info,windows,image,exception);
13298      break;
13299    }
13300    default:
13301      break;
13302  }
13303  XSetCursorState(display,windows,MagickFalse);
13304  return(tile_image);
13305}
13306
13307/*
13308%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13309%                                                                             %
13310%                                                                             %
13311%                                                                             %
13312+   X T r a n s l a t e I m a g e                                             %
13313%                                                                             %
13314%                                                                             %
13315%                                                                             %
13316%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13317%
13318%  XTranslateImage() translates the image within an Image window by one pixel
13319%  as specified by the key symbol.  If the image has a montage string the
13320%  translation is respect to the width and height contained within the string.
13321%
13322%  The format of the XTranslateImage method is:
13323%
13324%      void XTranslateImage(Display *display,XWindows *windows,
13325%        Image *image,const KeySym key_symbol)
13326%
13327%  A description of each parameter follows:
13328%
13329%    o display: Specifies a connection to an X server; returned from
13330%      XOpenDisplay.
13331%
13332%    o windows: Specifies a pointer to a XWindows structure.
13333%
13334%    o image: the image.
13335%
13336%    o key_symbol: Specifies a KeySym which indicates which side of the image
13337%      to trim.
13338%
13339*/
13340static void XTranslateImage(Display *display,XWindows *windows,
13341  Image *image,const KeySym key_symbol)
13342{
13343  char
13344    text[MaxTextExtent];
13345
13346  int
13347    x,
13348    y;
13349
13350  unsigned int
13351    x_offset,
13352    y_offset;
13353
13354  /*
13355    User specified a pan position offset.
13356  */
13357  x_offset=windows->image.width;
13358  y_offset=windows->image.height;
13359  if (image->montage != (char *) NULL)
13360    (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13361  switch ((int) key_symbol)
13362  {
13363    case XK_Home:
13364    case XK_KP_Home:
13365    {
13366      windows->image.x=(int) windows->image.width/2;
13367      windows->image.y=(int) windows->image.height/2;
13368      break;
13369    }
13370    case XK_Left:
13371    case XK_KP_Left:
13372    {
13373      windows->image.x-=x_offset;
13374      break;
13375    }
13376    case XK_Next:
13377    case XK_Up:
13378    case XK_KP_Up:
13379    {
13380      windows->image.y-=y_offset;
13381      break;
13382    }
13383    case XK_Right:
13384    case XK_KP_Right:
13385    {
13386      windows->image.x+=x_offset;
13387      break;
13388    }
13389    case XK_Prior:
13390    case XK_Down:
13391    case XK_KP_Down:
13392    {
13393      windows->image.y+=y_offset;
13394      break;
13395    }
13396    default:
13397      return;
13398  }
13399  /*
13400    Check boundary conditions.
13401  */
13402  if (windows->image.x < 0)
13403    windows->image.x=0;
13404  else
13405    if ((int) (windows->image.x+windows->image.width) >
13406        windows->image.ximage->width)
13407      windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13408  if (windows->image.y < 0)
13409    windows->image.y=0;
13410  else
13411    if ((int) (windows->image.y+windows->image.height) >
13412        windows->image.ximage->height)
13413      windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13414  /*
13415    Refresh Image window.
13416  */
13417  (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
13418    windows->image.width,windows->image.height,windows->image.x,
13419    windows->image.y);
13420  XInfoWidget(display,windows,text);
13421  XCheckRefreshWindows(display,windows);
13422  XDrawPanRectangle(display,windows);
13423  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13424  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13425}
13426
13427/*
13428%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13429%                                                                             %
13430%                                                                             %
13431%                                                                             %
13432+   X T r i m I m a g e                                                       %
13433%                                                                             %
13434%                                                                             %
13435%                                                                             %
13436%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13437%
13438%  XTrimImage() trims the edges from the Image window.
13439%
13440%  The format of the XTrimImage method is:
13441%
13442%      MagickBooleanType XTrimImage(Display *display,
13443%        XResourceInfo *resource_info,XWindows *windows,Image *image,
13444%        ExceptionInfo *exception)
13445%
13446%  A description of each parameter follows:
13447%
13448%    o display: Specifies a connection to an X server; returned from
13449%      XOpenDisplay.
13450%
13451%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13452%
13453%    o windows: Specifies a pointer to a XWindows structure.
13454%
13455%    o image: the image.
13456%
13457%    o exception: return any errors or warnings in this structure.
13458%
13459*/
13460static MagickBooleanType XTrimImage(Display *display,
13461  XResourceInfo *resource_info,XWindows *windows,Image *image,
13462  ExceptionInfo *exception)
13463{
13464  RectangleInfo
13465    trim_info;
13466
13467  register int
13468    x,
13469    y;
13470
13471  size_t
13472    background,
13473    pixel;
13474
13475  /*
13476    Trim edges from image.
13477  */
13478  XSetCursorState(display,windows,MagickTrue);
13479  XCheckRefreshWindows(display,windows);
13480  /*
13481    Crop the left edge.
13482  */
13483  background=XGetPixel(windows->image.ximage,0,0);
13484  trim_info.width=(size_t) windows->image.ximage->width;
13485  for (x=0; x < windows->image.ximage->width; x++)
13486  {
13487    for (y=0; y < windows->image.ximage->height; y++)
13488    {
13489      pixel=XGetPixel(windows->image.ximage,x,y);
13490      if (pixel != background)
13491        break;
13492    }
13493    if (y < windows->image.ximage->height)
13494      break;
13495  }
13496  trim_info.x=(ssize_t) x;
13497  if (trim_info.x == (ssize_t) windows->image.ximage->width)
13498    {
13499      XSetCursorState(display,windows,MagickFalse);
13500      return(MagickFalse);
13501    }
13502  /*
13503    Crop the right edge.
13504  */
13505  background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13506  for (x=windows->image.ximage->width-1; x != 0; x--)
13507  {
13508    for (y=0; y < windows->image.ximage->height; y++)
13509    {
13510      pixel=XGetPixel(windows->image.ximage,x,y);
13511      if (pixel != background)
13512        break;
13513    }
13514    if (y < windows->image.ximage->height)
13515      break;
13516  }
13517  trim_info.width=(size_t) (x-trim_info.x+1);
13518  /*
13519    Crop the top edge.
13520  */
13521  background=XGetPixel(windows->image.ximage,0,0);
13522  trim_info.height=(size_t) windows->image.ximage->height;
13523  for (y=0; y < windows->image.ximage->height; y++)
13524  {
13525    for (x=0; x < windows->image.ximage->width; x++)
13526    {
13527      pixel=XGetPixel(windows->image.ximage,x,y);
13528      if (pixel != background)
13529        break;
13530    }
13531    if (x < windows->image.ximage->width)
13532      break;
13533  }
13534  trim_info.y=(ssize_t) y;
13535  /*
13536    Crop the bottom edge.
13537  */
13538  background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13539  for (y=windows->image.ximage->height-1; y != 0; y--)
13540  {
13541    for (x=0; x < windows->image.ximage->width; x++)
13542    {
13543      pixel=XGetPixel(windows->image.ximage,x,y);
13544      if (pixel != background)
13545        break;
13546    }
13547    if (x < windows->image.ximage->width)
13548      break;
13549  }
13550  trim_info.height=(size_t) y-trim_info.y+1;
13551  if (((unsigned int) trim_info.width != windows->image.width) ||
13552      ((unsigned int) trim_info.height != windows->image.height))
13553    {
13554      /*
13555        Reconfigure Image window as defined by the trimming rectangle.
13556      */
13557      XSetCropGeometry(display,windows,&trim_info,image);
13558      windows->image.window_changes.width=(int) trim_info.width;
13559      windows->image.window_changes.height=(int) trim_info.height;
13560      (void) XConfigureImage(display,resource_info,windows,image,exception);
13561    }
13562  XSetCursorState(display,windows,MagickFalse);
13563  return(MagickTrue);
13564}
13565
13566/*
13567%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13568%                                                                             %
13569%                                                                             %
13570%                                                                             %
13571+   X V i s u a l D i r e c t o r y I m a g e                                 %
13572%                                                                             %
13573%                                                                             %
13574%                                                                             %
13575%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13576%
13577%  XVisualDirectoryImage() creates a Visual Image Directory.
13578%
13579%  The format of the XVisualDirectoryImage method is:
13580%
13581%      Image *XVisualDirectoryImage(Display *display,
13582%        XResourceInfo *resource_info,XWindows *windows,
13583%        ExceptionInfo *exception)
13584%
13585%  A description of each parameter follows:
13586%
13587%    o display: Specifies a connection to an X server; returned from
13588%      XOpenDisplay.
13589%
13590%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13591%
13592%    o windows: Specifies a pointer to a XWindows structure.
13593%
13594%    o exception: return any errors or warnings in this structure.
13595%
13596*/
13597static Image *XVisualDirectoryImage(Display *display,
13598  XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
13599{
13600#define TileImageTag  "Scale/Image"
13601#define XClientName  "montage"
13602
13603  char
13604    **filelist;
13605
13606  Image
13607    *images,
13608    *montage_image,
13609    *next_image,
13610    *thumbnail_image;
13611
13612  ImageInfo
13613    *read_info;
13614
13615  int
13616    number_files;
13617
13618  MagickBooleanType
13619    backdrop;
13620
13621  MagickStatusType
13622    status;
13623
13624  MontageInfo
13625    *montage_info;
13626
13627  RectangleInfo
13628    geometry;
13629
13630  register int
13631    i;
13632
13633  static char
13634    filename[MaxTextExtent] = "\0",
13635    filenames[MaxTextExtent] = "*";
13636
13637  XResourceInfo
13638    background_resources;
13639
13640  /*
13641    Request file name from user.
13642  */
13643  XFileBrowserWidget(display,windows,"Directory",filenames);
13644  if (*filenames == '\0')
13645    return((Image *) NULL);
13646  /*
13647    Expand the filenames.
13648  */
13649  filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13650  if (filelist == (char **) NULL)
13651    {
13652      ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13653        filenames);
13654      return((Image *) NULL);
13655    }
13656  number_files=1;
13657  filelist[0]=filenames;
13658  status=ExpandFilenames(&number_files,&filelist);
13659  if( IfMagickFalse(status) || (number_files == 0))
13660    {
13661      if (number_files == 0)
13662        ThrowXWindowFatalException(ImageError,"NoImagesWereFound",filenames)
13663      else
13664        ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13665          filenames);
13666      return((Image *) NULL);
13667    }
13668  /*
13669    Set image background resources.
13670  */
13671  background_resources=(*resource_info);
13672  background_resources.window_id=AcquireString("");
13673  (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
13674    "0x%lx",windows->image.id);
13675  background_resources.backdrop=MagickTrue;
13676  /*
13677    Read each image and convert them to a tile.
13678  */
13679  backdrop=IsMagickTrue( (windows->visual_info->klass == TrueColor) ||
13680    (windows->visual_info->klass == DirectColor) );
13681  read_info=CloneImageInfo(resource_info->image_info);
13682  (void) SetImageOption(read_info,"jpeg:size","120x120");
13683  (void) CloneString(&read_info->size,DefaultTileGeometry);
13684  (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13685    (void *) NULL);
13686  images=NewImageList();
13687  XSetCursorState(display,windows,MagickTrue);
13688  XCheckRefreshWindows(display,windows);
13689  for (i=0; i < (int) number_files; i++)
13690  {
13691    (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13692    filelist[i]=DestroyString(filelist[i]);
13693    *read_info->magick='\0';
13694    next_image=ReadImage(read_info,exception);
13695    CatchException(exception);
13696    if (next_image != (Image *) NULL)
13697      {
13698        (void) DeleteImageProperty(next_image,"label");
13699        (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13700          read_info,next_image,DefaultTileLabel,exception),exception);
13701        (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13702          exception);
13703        thumbnail_image=ThumbnailImage(next_image,geometry.width,
13704          geometry.height,exception);
13705        if (thumbnail_image != (Image *) NULL)
13706          {
13707            next_image=DestroyImage(next_image);
13708            next_image=thumbnail_image;
13709          }
13710        if (backdrop)
13711          {
13712            (void) XDisplayBackgroundImage(display,&background_resources,
13713              next_image,exception);
13714            XSetCursorState(display,windows,MagickTrue);
13715          }
13716        AppendImageToList(&images,next_image);
13717        if (images->progress_monitor != (MagickProgressMonitor) NULL)
13718          {
13719            MagickBooleanType
13720              proceed;
13721
13722            proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13723              (MagickSizeType) number_files);
13724            if( IfMagickFalse(proceed) )
13725              break;
13726          }
13727      }
13728  }
13729  filelist=(char **) RelinquishMagickMemory(filelist);
13730  if (images == (Image *) NULL)
13731    {
13732      read_info=DestroyImageInfo(read_info);
13733      XSetCursorState(display,windows,MagickFalse);
13734      ThrowXWindowFatalException(ImageError,"NoImagesWereLoaded",filenames);
13735      return((Image *) NULL);
13736    }
13737  /*
13738    Create the Visual Image Directory.
13739  */
13740  montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13741  montage_info->pointsize=10;
13742  if (resource_info->font != (char *) NULL)
13743    (void) CloneString(&montage_info->font,resource_info->font);
13744  (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13745  montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13746    images),exception);
13747  images=DestroyImageList(images);
13748  montage_info=DestroyMontageInfo(montage_info);
13749  read_info=DestroyImageInfo(read_info);
13750  XSetCursorState(display,windows,MagickFalse);
13751  if (montage_image == (Image *) NULL)
13752    return(montage_image);
13753  XClientMessage(display,windows->image.id,windows->im_protocols,
13754    windows->im_next_image,CurrentTime);
13755  return(montage_image);
13756}
13757
13758/*
13759%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13760%                                                                             %
13761%                                                                             %
13762%                                                                             %
13763%   X D i s p l a y B a c k g r o u n d I m a g e                             %
13764%                                                                             %
13765%                                                                             %
13766%                                                                             %
13767%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13768%
13769%  XDisplayBackgroundImage() displays an image in the background of a window.
13770%
13771%  The format of the XDisplayBackgroundImage method is:
13772%
13773%      MagickBooleanType XDisplayBackgroundImage(Display *display,
13774%        XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13775%
13776%  A description of each parameter follows:
13777%
13778%    o display: Specifies a connection to an X server;  returned from
13779%      XOpenDisplay.
13780%
13781%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13782%
13783%    o image: the image.
13784%
13785%    o exception: return any errors or warnings in this structure.
13786%
13787*/
13788MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13789  XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13790{
13791  char
13792    geometry[MaxTextExtent],
13793    visual_type[MaxTextExtent];
13794
13795  int
13796    height,
13797    status,
13798    width;
13799
13800  RectangleInfo
13801    geometry_info;
13802
13803  static XPixelInfo
13804    pixel;
13805
13806  static XStandardColormap
13807    *map_info;
13808
13809  static XVisualInfo
13810    *visual_info = (XVisualInfo *) NULL;
13811
13812  static XWindowInfo
13813    window_info;
13814
13815  size_t
13816    delay;
13817
13818  Window
13819    root_window;
13820
13821  XGCValues
13822    context_values;
13823
13824  XResourceInfo
13825    resources;
13826
13827  XWindowAttributes
13828    window_attributes;
13829
13830  /*
13831    Determine target window.
13832  */
13833  assert(image != (Image *) NULL);
13834  assert(image->signature == MagickSignature);
13835  if( IfMagickTrue(image->debug) )
13836    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13837  resources=(*resource_info);
13838  window_info.id=(Window) NULL;
13839  root_window=XRootWindow(display,XDefaultScreen(display));
13840  if (LocaleCompare(resources.window_id,"root") == 0)
13841    window_info.id=root_window;
13842  else
13843    {
13844      if (isdigit((unsigned char) *resources.window_id) != 0)
13845        window_info.id=XWindowByID(display,root_window,
13846          (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13847      if (window_info.id == (Window) NULL)
13848        window_info.id=XWindowByName(display,root_window,resources.window_id);
13849    }
13850  if (window_info.id == (Window) NULL)
13851    {
13852      ThrowXWindowFatalException(XServerError,"NoWindowWithSpecifiedIDExists",
13853        resources.window_id);
13854      return(MagickFalse);
13855    }
13856  /*
13857    Determine window visual id.
13858  */
13859  window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13860  window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13861  (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13862  status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13863  if (status != 0)
13864    (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
13865      XVisualIDFromVisual(window_attributes.visual));
13866  if (visual_info == (XVisualInfo *) NULL)
13867    {
13868      /*
13869        Allocate standard colormap.
13870      */
13871      map_info=XAllocStandardColormap();
13872      if (map_info == (XStandardColormap *) NULL)
13873        ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13874          image->filename);
13875      map_info->colormap=(Colormap) NULL;
13876      pixel.pixels=(unsigned long *) NULL;
13877      /*
13878        Initialize visual info.
13879      */
13880      resources.map_type=(char *) NULL;
13881      resources.visual_type=visual_type;
13882      visual_info=XBestVisualInfo(display,map_info,&resources);
13883      if (visual_info == (XVisualInfo *) NULL)
13884        ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13885          resources.visual_type);
13886      /*
13887        Initialize window info.
13888      */
13889      window_info.ximage=(XImage *) NULL;
13890      window_info.matte_image=(XImage *) NULL;
13891      window_info.pixmap=(Pixmap) NULL;
13892      window_info.matte_pixmap=(Pixmap) NULL;
13893    }
13894  /*
13895    Free previous root colors.
13896  */
13897  if (window_info.id == root_window)
13898    (void) XDestroyWindowColors(display,root_window);
13899  /*
13900    Initialize Standard Colormap.
13901  */
13902  resources.colormap=SharedColormap;
13903  XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel,
13904    exception);
13905  /*
13906    Graphic context superclass.
13907  */
13908  context_values.background=pixel.background_color.pixel;
13909  context_values.foreground=pixel.foreground_color.pixel;
13910  pixel.annotate_context=XCreateGC(display,window_info.id,
13911    (size_t) (GCBackground | GCForeground),&context_values);
13912  if (pixel.annotate_context == (GC) NULL)
13913    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13914      image->filename);
13915  /*
13916    Initialize Image window attributes.
13917  */
13918  window_info.name=AcquireString("\0");
13919  window_info.icon_name=AcquireString("\0");
13920  XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13921    &resources,&window_info);
13922  /*
13923    Create the X image.
13924  */
13925  window_info.width=(unsigned int) image->columns;
13926  window_info.height=(unsigned int) image->rows;
13927  if ((image->columns != window_info.width) ||
13928      (image->rows != window_info.height))
13929    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13930      image->filename);
13931  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
13932    window_attributes.width,window_attributes.height);
13933  geometry_info.width=window_info.width;
13934  geometry_info.height=window_info.height;
13935  geometry_info.x=(ssize_t) window_info.x;
13936  geometry_info.y=(ssize_t) window_info.y;
13937  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13938    &geometry_info.width,&geometry_info.height);
13939  window_info.width=(unsigned int) geometry_info.width;
13940  window_info.height=(unsigned int) geometry_info.height;
13941  window_info.x=(int) geometry_info.x;
13942  window_info.y=(int) geometry_info.y;
13943  status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13944    window_info.height,exception);
13945  if( IfMagickFalse(status) )
13946    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13947      image->filename);
13948  window_info.x=0;
13949  window_info.y=0;
13950  if( IfMagickTrue(image->debug) )
13951    {
13952      (void) LogMagickEvent(X11Event,GetMagickModule(),
13953        "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13954        (double) image->columns,(double) image->rows);
13955      if (image->colors != 0)
13956        (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13957          image->colors);
13958      (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13959    }
13960  /*
13961    Adjust image dimensions as specified by backdrop or geometry options.
13962  */
13963  width=(int) window_info.width;
13964  height=(int) window_info.height;
13965  if( IfMagickTrue(resources.backdrop) )
13966    {
13967      /*
13968        Center image on window.
13969      */
13970      window_info.x=(window_attributes.width/2)-
13971        (window_info.ximage->width/2);
13972      window_info.y=(window_attributes.height/2)-
13973        (window_info.ximage->height/2);
13974      width=window_attributes.width;
13975      height=window_attributes.height;
13976    }
13977  if ((resources.image_geometry != (char *) NULL) &&
13978      (*resources.image_geometry != '\0'))
13979    {
13980      char
13981        default_geometry[MaxTextExtent];
13982
13983      int
13984        flags,
13985        gravity;
13986
13987      XSizeHints
13988        *size_hints;
13989
13990      /*
13991        User specified geometry.
13992      */
13993      size_hints=XAllocSizeHints();
13994      if (size_hints == (XSizeHints *) NULL)
13995        ThrowXWindowFatalException(ResourceLimitFatalError,
13996          "MemoryAllocationFailed",image->filename);
13997      size_hints->flags=0L;
13998      (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
13999        width,height);
14000      flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
14001        default_geometry,window_info.border_width,size_hints,&window_info.x,
14002        &window_info.y,&width,&height,&gravity);
14003      if (flags & (XValue | YValue))
14004        {
14005          width=window_attributes.width;
14006          height=window_attributes.height;
14007        }
14008      (void) XFree((void *) size_hints);
14009    }
14010  /*
14011    Create the X pixmap.
14012  */
14013  window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
14014    (unsigned int) height,window_info.depth);
14015  if (window_info.pixmap == (Pixmap) NULL)
14016    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
14017      image->filename);
14018  /*
14019    Display pixmap on the window.
14020  */
14021  if (((unsigned int) width > window_info.width) ||
14022      ((unsigned int) height > window_info.height))
14023    (void) XFillRectangle(display,window_info.pixmap,
14024      window_info.annotate_context,0,0,(unsigned int) width,
14025      (unsigned int) height);
14026  (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
14027    window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
14028    window_info.width,(unsigned int) window_info.height);
14029  (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
14030  (void) XClearWindow(display,window_info.id);
14031  delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
14032  XDelay(display,delay == 0UL ? 10UL : delay);
14033  (void) XSync(display,MagickFalse);
14034  return(IsMagickTrue(window_info.id == root_window));
14035}
14036
14037/*
14038%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14039%                                                                             %
14040%                                                                             %
14041%                                                                             %
14042+   X D i s p l a y I m a g e                                                 %
14043%                                                                             %
14044%                                                                             %
14045%                                                                             %
14046%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14047%
14048%  XDisplayImage() displays an image via X11.  A new image is created and
14049%  returned if the user interactively transforms the displayed image.
14050%
14051%  The format of the XDisplayImage method is:
14052%
14053%      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14054%        char **argv,int argc,Image **image,size_t *state,
14055%        ExceptionInfo *exception)
14056%
14057%  A description of each parameter follows:
14058%
14059%    o nexus:  Method XDisplayImage returns an image when the
14060%      user chooses 'Open Image' from the command menu or picks a tile
14061%      from the image directory.  Otherwise a null image is returned.
14062%
14063%    o display: Specifies a connection to an X server;  returned from
14064%      XOpenDisplay.
14065%
14066%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14067%
14068%    o argv: Specifies the application's argument list.
14069%
14070%    o argc: Specifies the number of arguments.
14071%
14072%    o image: Specifies an address to an address of an Image structure;
14073%
14074%    o exception: return any errors or warnings in this structure.
14075%
14076*/
14077MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14078  char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
14079{
14080#define MagnifySize  256  /* must be a power of 2 */
14081#define MagickMenus  10
14082#define MagickTitle  "Commands"
14083
14084  static const char
14085    *CommandMenu[] =
14086    {
14087      "File",
14088      "Edit",
14089      "View",
14090      "Transform",
14091      "Enhance",
14092      "Effects",
14093      "F/X",
14094      "Image Edit",
14095      "Miscellany",
14096      "Help",
14097      (char *) NULL
14098    },
14099    *FileMenu[] =
14100    {
14101      "Open...",
14102      "Next",
14103      "Former",
14104      "Select...",
14105      "Save...",
14106      "Print...",
14107      "Delete...",
14108      "New...",
14109      "Visual Directory...",
14110      "Quit",
14111      (char *) NULL
14112    },
14113    *EditMenu[] =
14114    {
14115      "Undo",
14116      "Redo",
14117      "Cut",
14118      "Copy",
14119      "Paste",
14120      (char *) NULL
14121    },
14122    *ViewMenu[] =
14123    {
14124      "Half Size",
14125      "Original Size",
14126      "Double Size",
14127      "Resize...",
14128      "Apply",
14129      "Refresh",
14130      "Restore",
14131      (char *) NULL
14132    },
14133    *TransformMenu[] =
14134    {
14135      "Crop",
14136      "Chop",
14137      "Flop",
14138      "Flip",
14139      "Rotate Right",
14140      "Rotate Left",
14141      "Rotate...",
14142      "Shear...",
14143      "Roll...",
14144      "Trim Edges",
14145      (char *) NULL
14146    },
14147    *EnhanceMenu[] =
14148    {
14149      "Hue...",
14150      "Saturation...",
14151      "Brightness...",
14152      "Gamma...",
14153      "Spiff",
14154      "Dull",
14155      "Contrast Stretch...",
14156      "Sigmoidal Contrast...",
14157      "Normalize",
14158      "Equalize",
14159      "Negate",
14160      "Grayscale",
14161      "Map...",
14162      "Quantize...",
14163      (char *) NULL
14164    },
14165    *EffectsMenu[] =
14166    {
14167      "Despeckle",
14168      "Emboss",
14169      "Reduce Noise",
14170      "Add Noise...",
14171      "Sharpen...",
14172      "Blur...",
14173      "Threshold...",
14174      "Edge Detect...",
14175      "Spread...",
14176      "Shade...",
14177      "Raise...",
14178      "Segment...",
14179      (char *) NULL
14180    },
14181    *FXMenu[] =
14182    {
14183      "Solarize...",
14184      "Sepia Tone...",
14185      "Swirl...",
14186      "Implode...",
14187      "Vignette...",
14188      "Wave...",
14189      "Oil Paint...",
14190      "Charcoal Draw...",
14191      (char *) NULL
14192    },
14193    *ImageEditMenu[] =
14194    {
14195      "Annotate...",
14196      "Draw...",
14197      "Color...",
14198      "Matte...",
14199      "Composite...",
14200      "Add Border...",
14201      "Add Frame...",
14202      "Comment...",
14203      "Launch...",
14204      "Region of Interest...",
14205      (char *) NULL
14206    },
14207    *MiscellanyMenu[] =
14208    {
14209      "Image Info",
14210      "Zoom Image",
14211      "Show Preview...",
14212      "Show Histogram",
14213      "Show Matte",
14214      "Background...",
14215      "Slide Show...",
14216      "Preferences...",
14217      (char *) NULL
14218    },
14219    *HelpMenu[] =
14220    {
14221      "Overview",
14222      "Browse Documentation",
14223      "About Display",
14224      (char *) NULL
14225    },
14226    *ShortCutsMenu[] =
14227    {
14228      "Next",
14229      "Former",
14230      "Open...",
14231      "Save...",
14232      "Print...",
14233      "Undo",
14234      "Restore",
14235      "Image Info",
14236      "Quit",
14237      (char *) NULL
14238    },
14239    *VirtualMenu[] =
14240    {
14241      "Image Info",
14242      "Print",
14243      "Next",
14244      "Quit",
14245      (char *) NULL
14246    };
14247
14248  static const char
14249    **Menus[MagickMenus] =
14250    {
14251      FileMenu,
14252      EditMenu,
14253      ViewMenu,
14254      TransformMenu,
14255      EnhanceMenu,
14256      EffectsMenu,
14257      FXMenu,
14258      ImageEditMenu,
14259      MiscellanyMenu,
14260      HelpMenu
14261    };
14262
14263  static CommandType
14264    CommandMenus[] =
14265    {
14266      NullCommand,
14267      NullCommand,
14268      NullCommand,
14269      NullCommand,
14270      NullCommand,
14271      NullCommand,
14272      NullCommand,
14273      NullCommand,
14274      NullCommand,
14275      NullCommand,
14276    },
14277    FileCommands[] =
14278    {
14279      OpenCommand,
14280      NextCommand,
14281      FormerCommand,
14282      SelectCommand,
14283      SaveCommand,
14284      PrintCommand,
14285      DeleteCommand,
14286      NewCommand,
14287      VisualDirectoryCommand,
14288      QuitCommand
14289    },
14290    EditCommands[] =
14291    {
14292      UndoCommand,
14293      RedoCommand,
14294      CutCommand,
14295      CopyCommand,
14296      PasteCommand
14297    },
14298    ViewCommands[] =
14299    {
14300      HalfSizeCommand,
14301      OriginalSizeCommand,
14302      DoubleSizeCommand,
14303      ResizeCommand,
14304      ApplyCommand,
14305      RefreshCommand,
14306      RestoreCommand
14307    },
14308    TransformCommands[] =
14309    {
14310      CropCommand,
14311      ChopCommand,
14312      FlopCommand,
14313      FlipCommand,
14314      RotateRightCommand,
14315      RotateLeftCommand,
14316      RotateCommand,
14317      ShearCommand,
14318      RollCommand,
14319      TrimCommand
14320    },
14321    EnhanceCommands[] =
14322    {
14323      HueCommand,
14324      SaturationCommand,
14325      BrightnessCommand,
14326      GammaCommand,
14327      SpiffCommand,
14328      DullCommand,
14329      ContrastStretchCommand,
14330      SigmoidalContrastCommand,
14331      NormalizeCommand,
14332      EqualizeCommand,
14333      NegateCommand,
14334      GrayscaleCommand,
14335      MapCommand,
14336      QuantizeCommand
14337    },
14338    EffectsCommands[] =
14339    {
14340      DespeckleCommand,
14341      EmbossCommand,
14342      ReduceNoiseCommand,
14343      AddNoiseCommand,
14344      SharpenCommand,
14345      BlurCommand,
14346      ThresholdCommand,
14347      EdgeDetectCommand,
14348      SpreadCommand,
14349      ShadeCommand,
14350      RaiseCommand,
14351      SegmentCommand
14352    },
14353    FXCommands[] =
14354    {
14355      SolarizeCommand,
14356      SepiaToneCommand,
14357      SwirlCommand,
14358      ImplodeCommand,
14359      VignetteCommand,
14360      WaveCommand,
14361      OilPaintCommand,
14362      CharcoalDrawCommand
14363    },
14364    ImageEditCommands[] =
14365    {
14366      AnnotateCommand,
14367      DrawCommand,
14368      ColorCommand,
14369      MatteCommand,
14370      CompositeCommand,
14371      AddBorderCommand,
14372      AddFrameCommand,
14373      CommentCommand,
14374      LaunchCommand,
14375      RegionofInterestCommand
14376    },
14377    MiscellanyCommands[] =
14378    {
14379      InfoCommand,
14380      ZoomCommand,
14381      ShowPreviewCommand,
14382      ShowHistogramCommand,
14383      ShowMatteCommand,
14384      BackgroundCommand,
14385      SlideShowCommand,
14386      PreferencesCommand
14387    },
14388    HelpCommands[] =
14389    {
14390      HelpCommand,
14391      BrowseDocumentationCommand,
14392      VersionCommand
14393    },
14394    ShortCutsCommands[] =
14395    {
14396      NextCommand,
14397      FormerCommand,
14398      OpenCommand,
14399      SaveCommand,
14400      PrintCommand,
14401      UndoCommand,
14402      RestoreCommand,
14403      InfoCommand,
14404      QuitCommand
14405    },
14406    VirtualCommands[] =
14407    {
14408      InfoCommand,
14409      PrintCommand,
14410      NextCommand,
14411      QuitCommand
14412    };
14413
14414  static CommandType
14415    *Commands[MagickMenus] =
14416    {
14417      FileCommands,
14418      EditCommands,
14419      ViewCommands,
14420      TransformCommands,
14421      EnhanceCommands,
14422      EffectsCommands,
14423      FXCommands,
14424      ImageEditCommands,
14425      MiscellanyCommands,
14426      HelpCommands
14427    };
14428
14429  char
14430    command[MaxTextExtent],
14431    *directory,
14432    geometry[MaxTextExtent],
14433    resource_name[MaxTextExtent];
14434
14435  CommandType
14436    command_type;
14437
14438  Image
14439    *display_image,
14440    *nexus;
14441
14442  int
14443    entry,
14444    id;
14445
14446  KeySym
14447    key_symbol;
14448
14449  MagickStatusType
14450    context_mask,
14451    status;
14452
14453  RectangleInfo
14454    geometry_info;
14455
14456  register int
14457    i;
14458
14459  static char
14460    working_directory[MaxTextExtent];
14461
14462  static XPoint
14463    vid_info;
14464
14465  static XWindowInfo
14466    *magick_windows[MaxXWindows];
14467
14468  static unsigned int
14469    number_windows;
14470
14471  struct stat
14472    attributes;
14473
14474  time_t
14475    timer,
14476    timestamp,
14477    update_time;
14478
14479  unsigned int
14480    height,
14481    width;
14482
14483  size_t
14484    delay;
14485
14486  WarningHandler
14487    warning_handler;
14488
14489  Window
14490    root_window;
14491
14492  XClassHint
14493    *class_hints;
14494
14495  XEvent
14496    event;
14497
14498  XFontStruct
14499    *font_info;
14500
14501  XGCValues
14502    context_values;
14503
14504  XPixelInfo
14505    *icon_pixel,
14506    *pixel;
14507
14508  XResourceInfo
14509    *icon_resources;
14510
14511  XStandardColormap
14512    *icon_map,
14513    *map_info;
14514
14515  XVisualInfo
14516    *icon_visual,
14517    *visual_info;
14518
14519  XWindowChanges
14520    window_changes;
14521
14522  XWindows
14523    *windows;
14524
14525  XWMHints
14526    *manager_hints;
14527
14528  assert(image != (Image **) NULL);
14529  assert((*image)->signature == MagickSignature);
14530  if( IfMagickTrue((*image)->debug) )
14531    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14532  display_image=(*image);
14533  warning_handler=(WarningHandler) NULL;
14534  windows=XSetWindows((XWindows *) ~0);
14535  if (windows != (XWindows *) NULL)
14536    {
14537      int
14538        status;
14539
14540      if (*working_directory == '\0')
14541        (void) CopyMagickString(working_directory,".",MaxTextExtent);
14542      status=chdir(working_directory);
14543      if (status == -1)
14544        (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14545          "UnableToOpenFile","%s",working_directory);
14546      warning_handler=resource_info->display_warnings ?
14547        SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14548      warning_handler=resource_info->display_warnings ?
14549        SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14550    }
14551  else
14552    {
14553      /*
14554        Allocate windows structure.
14555      */
14556      resource_info->colors=display_image->colors;
14557      windows=XSetWindows(XInitializeWindows(display,resource_info));
14558      if (windows == (XWindows *) NULL)
14559        ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14560          (*image)->filename);
14561      /*
14562        Initialize window id's.
14563      */
14564      number_windows=0;
14565      magick_windows[number_windows++]=(&windows->icon);
14566      magick_windows[number_windows++]=(&windows->backdrop);
14567      magick_windows[number_windows++]=(&windows->image);
14568      magick_windows[number_windows++]=(&windows->info);
14569      magick_windows[number_windows++]=(&windows->command);
14570      magick_windows[number_windows++]=(&windows->widget);
14571      magick_windows[number_windows++]=(&windows->popup);
14572      magick_windows[number_windows++]=(&windows->magnify);
14573      magick_windows[number_windows++]=(&windows->pan);
14574      for (i=0; i < (int) number_windows; i++)
14575        magick_windows[i]->id=(Window) NULL;
14576      vid_info.x=0;
14577      vid_info.y=0;
14578    }
14579  /*
14580    Initialize font info.
14581  */
14582  if (windows->font_info != (XFontStruct *) NULL)
14583    (void) XFreeFont(display,windows->font_info);
14584  windows->font_info=XBestFont(display,resource_info,MagickFalse);
14585  if (windows->font_info == (XFontStruct *) NULL)
14586    ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14587      resource_info->font);
14588  /*
14589    Initialize Standard Colormap.
14590  */
14591  map_info=windows->map_info;
14592  icon_map=windows->icon_map;
14593  visual_info=windows->visual_info;
14594  icon_visual=windows->icon_visual;
14595  pixel=windows->pixel_info;
14596  icon_pixel=windows->icon_pixel;
14597  font_info=windows->font_info;
14598  icon_resources=windows->icon_resources;
14599  class_hints=windows->class_hints;
14600  manager_hints=windows->manager_hints;
14601  root_window=XRootWindow(display,visual_info->screen);
14602  nexus=NewImageList();
14603  if( IfMagickTrue(display_image->debug) )
14604    {
14605      (void) LogMagickEvent(X11Event,GetMagickModule(),
14606        "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14607        (double) display_image->scene,(double) display_image->columns,
14608        (double) display_image->rows);
14609      if (display_image->colors != 0)
14610        (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14611          display_image->colors);
14612      (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14613        display_image->magick);
14614    }
14615  XMakeStandardColormap(display,visual_info,resource_info,display_image,
14616    map_info,pixel,exception);
14617  display_image->taint=MagickFalse;
14618  /*
14619    Initialize graphic context.
14620  */
14621  windows->context.id=(Window) NULL;
14622  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14623    resource_info,&windows->context);
14624  (void) CloneString(&class_hints->res_name,resource_info->client_name);
14625  (void) CloneString(&class_hints->res_class,resource_info->client_name);
14626  class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14627  manager_hints->flags=InputHint | StateHint;
14628  manager_hints->input=MagickFalse;
14629  manager_hints->initial_state=WithdrawnState;
14630  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14631    &windows->context);
14632  if( IfMagickTrue(display_image->debug) )
14633    (void) LogMagickEvent(X11Event,GetMagickModule(),
14634      "Window id: 0x%lx (context)",windows->context.id);
14635  context_values.background=pixel->background_color.pixel;
14636  context_values.font=font_info->fid;
14637  context_values.foreground=pixel->foreground_color.pixel;
14638  context_values.graphics_exposures=MagickFalse;
14639  context_mask=(MagickStatusType)
14640    (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14641  if (pixel->annotate_context != (GC) NULL)
14642    (void) XFreeGC(display,pixel->annotate_context);
14643  pixel->annotate_context=XCreateGC(display,windows->context.id,
14644    context_mask,&context_values);
14645  if (pixel->annotate_context == (GC) NULL)
14646    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14647      display_image->filename);
14648  context_values.background=pixel->depth_color.pixel;
14649  if (pixel->widget_context != (GC) NULL)
14650    (void) XFreeGC(display,pixel->widget_context);
14651  pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14652    &context_values);
14653  if (pixel->widget_context == (GC) NULL)
14654    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14655      display_image->filename);
14656  context_values.background=pixel->foreground_color.pixel;
14657  context_values.foreground=pixel->background_color.pixel;
14658  context_values.plane_mask=context_values.background ^
14659    context_values.foreground;
14660  if (pixel->highlight_context != (GC) NULL)
14661    (void) XFreeGC(display,pixel->highlight_context);
14662  pixel->highlight_context=XCreateGC(display,windows->context.id,
14663    (size_t) (context_mask | GCPlaneMask),&context_values);
14664  if (pixel->highlight_context == (GC) NULL)
14665    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14666      display_image->filename);
14667  (void) XDestroyWindow(display,windows->context.id);
14668  /*
14669    Initialize icon window.
14670  */
14671  XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14672    icon_resources,&windows->icon);
14673  windows->icon.geometry=resource_info->icon_geometry;
14674  XBestIconSize(display,&windows->icon,display_image);
14675  windows->icon.attributes.colormap=XDefaultColormap(display,
14676    icon_visual->screen);
14677  windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14678  manager_hints->flags=InputHint | StateHint;
14679  manager_hints->input=MagickFalse;
14680  manager_hints->initial_state=IconicState;
14681  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14682    &windows->icon);
14683  if( IfMagickTrue(display_image->debug) )
14684    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14685      windows->icon.id);
14686  /*
14687    Initialize graphic context for icon window.
14688  */
14689  if (icon_pixel->annotate_context != (GC) NULL)
14690    (void) XFreeGC(display,icon_pixel->annotate_context);
14691  context_values.background=icon_pixel->background_color.pixel;
14692  context_values.foreground=icon_pixel->foreground_color.pixel;
14693  icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14694    (size_t) (GCBackground | GCForeground),&context_values);
14695  if (icon_pixel->annotate_context == (GC) NULL)
14696    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14697      display_image->filename);
14698  windows->icon.annotate_context=icon_pixel->annotate_context;
14699  /*
14700    Initialize Image window.
14701  */
14702  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14703    &windows->image);
14704  windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14705  if( IfMagickFalse(resource_info->use_shared_memory) )
14706    windows->image.shared_memory=MagickFalse;
14707  if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14708    {
14709      char
14710        *title;
14711
14712      title=InterpretImageProperties(resource_info->image_info,display_image,
14713        resource_info->title,exception);
14714      (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
14715      (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
14716      title=DestroyString(title);
14717    }
14718  else
14719    {
14720      char
14721        filename[MaxTextExtent];
14722
14723      /*
14724        Window name is the base of the filename.
14725      */
14726      GetPathComponent(display_image->magick_filename,TailPath,filename);
14727      if (display_image->scene == 0)
14728        (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14729          "%s: %s",MagickPackageName,filename);
14730      else
14731        (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14732          "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14733          (double) display_image->scene,(double) GetImageListLength(
14734          display_image));
14735      (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
14736    }
14737  if (resource_info->immutable)
14738    windows->image.immutable=MagickTrue;
14739  windows->image.use_pixmap=resource_info->use_pixmap;
14740  windows->image.geometry=resource_info->image_geometry;
14741  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14742    XDisplayWidth(display,visual_info->screen),
14743    XDisplayHeight(display,visual_info->screen));
14744  geometry_info.width=display_image->columns;
14745  geometry_info.height=display_image->rows;
14746  geometry_info.x=0;
14747  geometry_info.y=0;
14748  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14749    &geometry_info.width,&geometry_info.height);
14750  windows->image.width=(unsigned int) geometry_info.width;
14751  windows->image.height=(unsigned int) geometry_info.height;
14752  windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14753    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14754    KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14755    PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14756  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14757    resource_info,&windows->backdrop);
14758  if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14759    {
14760      /*
14761        Initialize backdrop window.
14762      */
14763      windows->backdrop.x=0;
14764      windows->backdrop.y=0;
14765      (void) CloneString(&windows->backdrop.name,"Backdrop");
14766      windows->backdrop.flags=(size_t) (USSize | USPosition);
14767      windows->backdrop.width=(unsigned int)
14768        XDisplayWidth(display,visual_info->screen);
14769      windows->backdrop.height=(unsigned int)
14770        XDisplayHeight(display,visual_info->screen);
14771      windows->backdrop.border_width=0;
14772      windows->backdrop.immutable=MagickTrue;
14773      windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14774        ButtonReleaseMask;
14775      windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14776        StructureNotifyMask;
14777      manager_hints->flags=IconWindowHint | InputHint | StateHint;
14778      manager_hints->icon_window=windows->icon.id;
14779      manager_hints->input=MagickTrue;
14780      manager_hints->initial_state=resource_info->iconic ? IconicState :
14781        NormalState;
14782      XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14783        &windows->backdrop);
14784      if( IfMagickTrue(display_image->debug) )
14785        (void) LogMagickEvent(X11Event,GetMagickModule(),
14786          "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14787      (void) XMapWindow(display,windows->backdrop.id);
14788      (void) XClearWindow(display,windows->backdrop.id);
14789      if (windows->image.id != (Window) NULL)
14790        {
14791          (void) XDestroyWindow(display,windows->image.id);
14792          windows->image.id=(Window) NULL;
14793        }
14794      /*
14795        Position image in the center the backdrop.
14796      */
14797      windows->image.flags|=USPosition;
14798      windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14799        (windows->image.width/2);
14800      windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14801        (windows->image.height/2);
14802    }
14803  manager_hints->flags=IconWindowHint | InputHint | StateHint;
14804  manager_hints->icon_window=windows->icon.id;
14805  manager_hints->input=MagickTrue;
14806  manager_hints->initial_state=resource_info->iconic ? IconicState :
14807    NormalState;
14808  if (windows->group_leader.id != (Window) NULL)
14809    {
14810      /*
14811        Follow the leader.
14812      */
14813      manager_hints->flags|=WindowGroupHint;
14814      manager_hints->window_group=windows->group_leader.id;
14815      (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14816      if( IfMagickTrue(display_image->debug) )
14817        (void) LogMagickEvent(X11Event,GetMagickModule(),
14818          "Window id: 0x%lx (group leader)",windows->group_leader.id);
14819    }
14820  XMakeWindow(display,
14821    (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14822    argv,argc,class_hints,manager_hints,&windows->image);
14823  (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14824    XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14825  if (windows->group_leader.id != (Window) NULL)
14826    (void) XSetTransientForHint(display,windows->image.id,
14827      windows->group_leader.id);
14828  if( IfMagickTrue(display_image->debug) )
14829    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14830      windows->image.id);
14831  /*
14832    Initialize Info widget.
14833  */
14834  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14835    &windows->info);
14836  (void) CloneString(&windows->info.name,"Info");
14837  (void) CloneString(&windows->info.icon_name,"Info");
14838  windows->info.border_width=1;
14839  windows->info.x=2;
14840  windows->info.y=2;
14841  windows->info.flags|=PPosition;
14842  windows->info.attributes.win_gravity=UnmapGravity;
14843  windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14844    StructureNotifyMask;
14845  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14846  manager_hints->input=MagickFalse;
14847  manager_hints->initial_state=NormalState;
14848  manager_hints->window_group=windows->image.id;
14849  XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14850    &windows->info);
14851  windows->info.highlight_stipple=XCreateBitmapFromData(display,
14852    windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14853  windows->info.shadow_stipple=XCreateBitmapFromData(display,
14854    windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14855  (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14856  if( IfMagickTrue(windows->image.mapped) )
14857    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14858  if( IfMagickTrue(display_image->debug) )
14859    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14860      windows->info.id);
14861  /*
14862    Initialize Command widget.
14863  */
14864  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14865    resource_info,&windows->command);
14866  windows->command.data=MagickMenus;
14867  (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14868  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
14869    resource_info->client_name);
14870  windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14871    resource_name,"geometry",(char *) NULL);
14872  (void) CloneString(&windows->command.name,MagickTitle);
14873  windows->command.border_width=0;
14874  windows->command.flags|=PPosition;
14875  windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14876    ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14877    OwnerGrabButtonMask | StructureNotifyMask;
14878  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14879  manager_hints->input=MagickTrue;
14880  manager_hints->initial_state=NormalState;
14881  manager_hints->window_group=windows->image.id;
14882  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14883    &windows->command);
14884  windows->command.highlight_stipple=XCreateBitmapFromData(display,
14885    windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14886    HighlightHeight);
14887  windows->command.shadow_stipple=XCreateBitmapFromData(display,
14888    windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14889  (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14890  if( IfMagickTrue(windows->command.mapped) )
14891    (void) XMapRaised(display,windows->command.id);
14892  if( IfMagickTrue(display_image->debug) )
14893    (void) LogMagickEvent(X11Event,GetMagickModule(),
14894      "Window id: 0x%lx (command)",windows->command.id);
14895  /*
14896    Initialize Widget window.
14897  */
14898  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14899    resource_info,&windows->widget);
14900  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
14901    resource_info->client_name);
14902  windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14903    resource_name,"geometry",(char *) NULL);
14904  windows->widget.border_width=0;
14905  windows->widget.flags|=PPosition;
14906  windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14907    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14908    KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14909    StructureNotifyMask;
14910  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14911  manager_hints->input=MagickTrue;
14912  manager_hints->initial_state=NormalState;
14913  manager_hints->window_group=windows->image.id;
14914  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14915    &windows->widget);
14916  windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14917    windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14918  windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14919    windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14920  (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14921  if( IfMagickTrue(display_image->debug) )
14922    (void) LogMagickEvent(X11Event,GetMagickModule(),
14923      "Window id: 0x%lx (widget)",windows->widget.id);
14924  /*
14925    Initialize popup window.
14926  */
14927  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14928    resource_info,&windows->popup);
14929  windows->popup.border_width=0;
14930  windows->popup.flags|=PPosition;
14931  windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14932    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14933    KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14934  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14935  manager_hints->input=MagickTrue;
14936  manager_hints->initial_state=NormalState;
14937  manager_hints->window_group=windows->image.id;
14938  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14939    &windows->popup);
14940  windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14941    windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14942  windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14943    windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14944  (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14945  if( IfMagickTrue(display_image->debug) )
14946    (void) LogMagickEvent(X11Event,GetMagickModule(),
14947      "Window id: 0x%lx (pop up)",windows->popup.id);
14948  /*
14949    Initialize Magnify window and cursor.
14950  */
14951  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14952    resource_info,&windows->magnify);
14953  if( IfMagickFalse(resource_info->use_shared_memory) )
14954    windows->magnify.shared_memory=MagickFalse;
14955  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
14956    resource_info->client_name);
14957  windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14958    resource_name,"geometry",(char *) NULL);
14959  (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14960    resource_info->magnify);
14961  if (windows->magnify.cursor != (Cursor) NULL)
14962    (void) XFreeCursor(display,windows->magnify.cursor);
14963  windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14964    map_info->colormap,resource_info->background_color,
14965    resource_info->foreground_color);
14966  if (windows->magnify.cursor == (Cursor) NULL)
14967    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14968      display_image->filename);
14969  windows->magnify.width=MagnifySize;
14970  windows->magnify.height=MagnifySize;
14971  windows->magnify.flags|=PPosition;
14972  windows->magnify.min_width=MagnifySize;
14973  windows->magnify.min_height=MagnifySize;
14974  windows->magnify.width_inc=MagnifySize;
14975  windows->magnify.height_inc=MagnifySize;
14976  windows->magnify.data=resource_info->magnify;
14977  windows->magnify.attributes.cursor=windows->magnify.cursor;
14978  windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14979    ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14980    StructureNotifyMask;
14981  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14982  manager_hints->input=MagickTrue;
14983  manager_hints->initial_state=NormalState;
14984  manager_hints->window_group=windows->image.id;
14985  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14986    &windows->magnify);
14987  if( IfMagickTrue(display_image->debug) )
14988    (void) LogMagickEvent(X11Event,GetMagickModule(),
14989      "Window id: 0x%lx (magnify)",windows->magnify.id);
14990  (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14991  /*
14992    Initialize panning window.
14993  */
14994  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14995    resource_info,&windows->pan);
14996  (void) CloneString(&windows->pan.name,"Pan Icon");
14997  windows->pan.width=windows->icon.width;
14998  windows->pan.height=windows->icon.height;
14999  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
15000    resource_info->client_name);
15001  windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
15002    resource_name,"geometry",(char *) NULL);
15003  (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
15004    &windows->pan.width,&windows->pan.height);
15005  windows->pan.flags|=PPosition;
15006  windows->pan.immutable=MagickTrue;
15007  windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
15008    ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
15009    StructureNotifyMask;
15010  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
15011  manager_hints->input=MagickFalse;
15012  manager_hints->initial_state=NormalState;
15013  manager_hints->window_group=windows->image.id;
15014  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
15015    &windows->pan);
15016  if( IfMagickTrue(display_image->debug) )
15017    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
15018      windows->pan.id);
15019  (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
15020  if( IfMagickTrue(windows->info.mapped) )
15021    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15022  if( IfMagickFalse(windows->image.mapped) ||
15023      (windows->backdrop.id != (Window) NULL))
15024    (void) XMapWindow(display,windows->image.id);
15025  /*
15026    Set our progress monitor and warning handlers.
15027  */
15028  if (warning_handler == (WarningHandler) NULL)
15029    {
15030      warning_handler=resource_info->display_warnings ?
15031        SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
15032      warning_handler=resource_info->display_warnings ?
15033        SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
15034    }
15035  /*
15036    Initialize Image and Magnify X images.
15037  */
15038  windows->image.x=0;
15039  windows->image.y=0;
15040  windows->magnify.shape=MagickFalse;
15041  width=(unsigned int) display_image->columns;
15042  height=(unsigned int) display_image->rows;
15043  if ((display_image->columns != width) || (display_image->rows != height))
15044    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15045      display_image->filename);
15046  status=XMakeImage(display,resource_info,&windows->image,display_image,
15047    width,height,exception);
15048  if( IfMagickFalse(status) )
15049    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15050      display_image->filename);
15051  status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
15052    windows->magnify.width,windows->magnify.height,exception);
15053  if( IfMagickFalse(status) )
15054    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15055      display_image->filename);
15056  if( IfMagickTrue(windows->magnify.mapped) )
15057    (void) XMapRaised(display,windows->magnify.id);
15058  if( IfMagickTrue(windows->pan.mapped) )
15059    (void) XMapRaised(display,windows->pan.id);
15060  windows->image.window_changes.width=(int) display_image->columns;
15061  windows->image.window_changes.height=(int) display_image->rows;
15062  (void) XConfigureImage(display,resource_info,windows,display_image,exception);
15063  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15064  (void) XSync(display,MagickFalse);
15065  /*
15066    Respond to events.
15067  */
15068  delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
15069  timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15070  update_time=0;
15071  if( IfMagickTrue(resource_info->update) )
15072    {
15073      MagickBooleanType
15074        status;
15075
15076      /*
15077        Determine when file data was last modified.
15078      */
15079      status=GetPathAttributes(display_image->filename,&attributes);
15080      if( IfMagickTrue(status) )
15081        update_time=attributes.st_mtime;
15082    }
15083  *state&=(~FormerImageState);
15084  *state&=(~MontageImageState);
15085  *state&=(~NextImageState);
15086  do
15087  {
15088    /*
15089      Handle a window event.
15090    */
15091    if( IfMagickTrue(windows->image.mapped) )
15092      if ((display_image->delay != 0) || (resource_info->update != 0))
15093        {
15094          if (timer < time((time_t *) NULL))
15095            {
15096              if( IfMagickFalse(resource_info->update) )
15097                *state|=NextImageState | ExitState;
15098              else
15099                {
15100                  MagickBooleanType
15101                    status;
15102
15103                  /*
15104                    Determine if image file was modified.
15105                  */
15106                  status=GetPathAttributes(display_image->filename,&attributes);
15107                  if( IfMagickTrue(status) )
15108                    if (update_time != attributes.st_mtime)
15109                      {
15110                        /*
15111                          Redisplay image.
15112                        */
15113                        (void) FormatLocaleString(
15114                          resource_info->image_info->filename,MaxTextExtent,
15115                          "%s:%s",display_image->magick,
15116                          display_image->filename);
15117                        nexus=ReadImage(resource_info->image_info,exception);
15118                        if (nexus != (Image *) NULL)
15119                          {
15120                            nexus=DestroyImage(nexus);
15121                            *state|=NextImageState | ExitState;
15122                          }
15123                      }
15124                  delay=display_image->delay/MagickMax(
15125                    display_image->ticks_per_second,1L);
15126                  timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15127                }
15128            }
15129          if (XEventsQueued(display,QueuedAfterFlush) == 0)
15130            {
15131              /*
15132                Do not block if delay > 0.
15133              */
15134              XDelay(display,SuspendTime << 2);
15135              continue;
15136            }
15137        }
15138    timestamp=time((time_t *) NULL);
15139    (void) XNextEvent(display,&event);
15140    if( IfMagickFalse(windows->image.stasis) )
15141      windows->image.stasis=IsMagickTrue((time((time_t *) NULL)-timestamp) > 0);
15142    if( IfMagickFalse(windows->magnify.stasis) )
15143      windows->magnify.stasis=IsMagickTrue((time((time_t *) NULL)-timestamp) > 0);
15144    if (event.xany.window == windows->command.id)
15145      {
15146        /*
15147          Select a command from the Command widget.
15148        */
15149        id=XCommandWidget(display,windows,CommandMenu,&event);
15150        if (id < 0)
15151          continue;
15152        (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15153        command_type=CommandMenus[id];
15154        if (id < MagickMenus)
15155          {
15156            /*
15157              Select a command from a pop-up menu.
15158            */
15159            entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15160              command);
15161            if (entry < 0)
15162              continue;
15163            (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15164            command_type=Commands[id][entry];
15165          }
15166        if (command_type != NullCommand)
15167          nexus=XMagickCommand(display,resource_info,windows,command_type,
15168            &display_image,exception);
15169        continue;
15170      }
15171    switch (event.type)
15172    {
15173      case ButtonPress:
15174      {
15175        if( IfMagickTrue(display_image->debug) )
15176          (void) LogMagickEvent(X11Event,GetMagickModule(),
15177            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15178            event.xbutton.button,event.xbutton.x,event.xbutton.y);
15179        if ((event.xbutton.button == Button3) &&
15180            (event.xbutton.state & Mod1Mask))
15181          {
15182            /*
15183              Convert Alt-Button3 to Button2.
15184            */
15185            event.xbutton.button=Button2;
15186            event.xbutton.state&=(~Mod1Mask);
15187          }
15188        if (event.xbutton.window == windows->backdrop.id)
15189          {
15190            (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15191              event.xbutton.time);
15192            break;
15193          }
15194        if (event.xbutton.window == windows->image.id)
15195          {
15196            switch (event.xbutton.button)
15197            {
15198              case Button1:
15199              {
15200                if (resource_info->immutable)
15201                  {
15202                    /*
15203                      Select a command from the Virtual menu.
15204                    */
15205                    entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15206                      command);
15207                    if (entry >= 0)
15208                      nexus=XMagickCommand(display,resource_info,windows,
15209                        VirtualCommands[entry],&display_image,exception);
15210                    break;
15211                  }
15212                /*
15213                  Map/unmap Command widget.
15214                */
15215                if( IfMagickTrue(windows->command.mapped) )
15216                  (void) XWithdrawWindow(display,windows->command.id,
15217                    windows->command.screen);
15218                else
15219                  {
15220                    (void) XCommandWidget(display,windows,CommandMenu,
15221                      (XEvent *) NULL);
15222                    (void) XMapRaised(display,windows->command.id);
15223                  }
15224                break;
15225              }
15226              case Button2:
15227              {
15228                /*
15229                  User pressed the image magnify button.
15230                */
15231                (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15232                  &display_image,exception);
15233                XMagnifyImage(display,windows,&event,exception);
15234                break;
15235              }
15236              case Button3:
15237              {
15238                if (resource_info->immutable)
15239                  {
15240                    /*
15241                      Select a command from the Virtual menu.
15242                    */
15243                    entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15244                      command);
15245                    if (entry >= 0)
15246                      nexus=XMagickCommand(display,resource_info,windows,
15247                        VirtualCommands[entry],&display_image,exception);
15248                    break;
15249                  }
15250                if (display_image->montage != (char *) NULL)
15251                  {
15252                    /*
15253                      Open or delete a tile from a visual image directory.
15254                    */
15255                    nexus=XTileImage(display,resource_info,windows,
15256                      display_image,&event,exception);
15257                    if (nexus != (Image *) NULL)
15258                      *state|=MontageImageState | NextImageState | ExitState;
15259                    vid_info.x=(short int) windows->image.x;
15260                    vid_info.y=(short int) windows->image.y;
15261                    break;
15262                  }
15263                /*
15264                  Select a command from the Short Cuts menu.
15265                */
15266                entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15267                  command);
15268                if (entry >= 0)
15269                  nexus=XMagickCommand(display,resource_info,windows,
15270                    ShortCutsCommands[entry],&display_image,exception);
15271                break;
15272              }
15273              case Button4:
15274              {
15275                /*
15276                  Wheel up.
15277                */
15278                XTranslateImage(display,windows,*image,XK_Up);
15279                break;
15280              }
15281              case Button5:
15282              {
15283                /*
15284                  Wheel down.
15285                */
15286                XTranslateImage(display,windows,*image,XK_Down);
15287                break;
15288              }
15289              default:
15290                break;
15291            }
15292            break;
15293          }
15294        if (event.xbutton.window == windows->magnify.id)
15295          {
15296            int
15297              factor;
15298
15299            static const char
15300              *MagnifyMenu[] =
15301              {
15302                "2",
15303                "4",
15304                "5",
15305                "6",
15306                "7",
15307                "8",
15308                "9",
15309                "3",
15310                (char *) NULL,
15311              };
15312
15313            static KeySym
15314              MagnifyCommands[] =
15315              {
15316                XK_2,
15317                XK_4,
15318                XK_5,
15319                XK_6,
15320                XK_7,
15321                XK_8,
15322                XK_9,
15323                XK_3
15324              };
15325
15326            /*
15327              Select a magnify factor from the pop-up menu.
15328            */
15329            factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15330            if (factor >= 0)
15331              XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor],
15332                exception);
15333            break;
15334          }
15335        if (event.xbutton.window == windows->pan.id)
15336          {
15337            switch (event.xbutton.button)
15338            {
15339              case Button4:
15340              {
15341                /*
15342                  Wheel up.
15343                */
15344                XTranslateImage(display,windows,*image,XK_Up);
15345                break;
15346              }
15347              case Button5:
15348              {
15349                /*
15350                  Wheel down.
15351                */
15352                XTranslateImage(display,windows,*image,XK_Down);
15353                break;
15354              }
15355              default:
15356              {
15357                XPanImage(display,windows,&event,exception);
15358                break;
15359              }
15360            }
15361            break;
15362          }
15363        delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15364          1L);
15365        timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15366        break;
15367      }
15368      case ButtonRelease:
15369      {
15370        if( IfMagickTrue(display_image->debug) )
15371          (void) LogMagickEvent(X11Event,GetMagickModule(),
15372            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15373            event.xbutton.button,event.xbutton.x,event.xbutton.y);
15374        break;
15375      }
15376      case ClientMessage:
15377      {
15378        if( IfMagickTrue(display_image->debug) )
15379          (void) LogMagickEvent(X11Event,GetMagickModule(),
15380            "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15381            event.xclient.message_type,event.xclient.format,(unsigned long)
15382            event.xclient.data.l[0]);
15383        if (event.xclient.message_type == windows->im_protocols)
15384          {
15385            if (*event.xclient.data.l == (long) windows->im_update_widget)
15386              {
15387                (void) CloneString(&windows->command.name,MagickTitle);
15388                windows->command.data=MagickMenus;
15389                (void) XCommandWidget(display,windows,CommandMenu,
15390                  (XEvent *) NULL);
15391                break;
15392              }
15393            if (*event.xclient.data.l == (long) windows->im_update_colormap)
15394              {
15395                /*
15396                  Update graphic context and window colormap.
15397                */
15398                for (i=0; i < (int) number_windows; i++)
15399                {
15400                  if (magick_windows[i]->id == windows->icon.id)
15401                    continue;
15402                  context_values.background=pixel->background_color.pixel;
15403                  context_values.foreground=pixel->foreground_color.pixel;
15404                  (void) XChangeGC(display,magick_windows[i]->annotate_context,
15405                    context_mask,&context_values);
15406                  (void) XChangeGC(display,magick_windows[i]->widget_context,
15407                    context_mask,&context_values);
15408                  context_values.background=pixel->foreground_color.pixel;
15409                  context_values.foreground=pixel->background_color.pixel;
15410                  context_values.plane_mask=context_values.background ^
15411                    context_values.foreground;
15412                  (void) XChangeGC(display,magick_windows[i]->highlight_context,
15413                    (size_t) (context_mask | GCPlaneMask),
15414                    &context_values);
15415                  magick_windows[i]->attributes.background_pixel=
15416                    pixel->background_color.pixel;
15417                  magick_windows[i]->attributes.border_pixel=
15418                    pixel->border_color.pixel;
15419                  magick_windows[i]->attributes.colormap=map_info->colormap;
15420                  (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15421                    (unsigned long) magick_windows[i]->mask,
15422                    &magick_windows[i]->attributes);
15423                }
15424                if( IfMagickTrue(windows->pan.mapped) )
15425                  {
15426                    (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15427                      windows->pan.pixmap);
15428                    (void) XClearWindow(display,windows->pan.id);
15429                    XDrawPanRectangle(display,windows);
15430                  }
15431                if (windows->backdrop.id != (Window) NULL)
15432                  (void) XInstallColormap(display,map_info->colormap);
15433                break;
15434              }
15435            if (*event.xclient.data.l == (long) windows->im_former_image)
15436              {
15437                *state|=FormerImageState | ExitState;
15438                break;
15439              }
15440            if (*event.xclient.data.l == (long) windows->im_next_image)
15441              {
15442                *state|=NextImageState | ExitState;
15443                break;
15444              }
15445            if (*event.xclient.data.l == (long) windows->im_retain_colors)
15446              {
15447                *state|=RetainColorsState;
15448                break;
15449              }
15450            if (*event.xclient.data.l == (long) windows->im_exit)
15451              {
15452                *state|=ExitState;
15453                break;
15454              }
15455            break;
15456          }
15457        if (event.xclient.message_type == windows->dnd_protocols)
15458          {
15459            Atom
15460              selection,
15461              type;
15462
15463            int
15464              format,
15465              status;
15466
15467            unsigned char
15468              *data;
15469
15470            unsigned long
15471              after,
15472              length;
15473
15474            /*
15475              Display image named by the Drag-and-Drop selection.
15476            */
15477            if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15478              break;
15479            selection=XInternAtom(display,"DndSelection",MagickFalse);
15480            status=XGetWindowProperty(display,root_window,selection,0L,(long)
15481              MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15482              &length,&after,&data);
15483            if ((status != Success) || (length == 0))
15484              break;
15485            if (*event.xclient.data.l == 2)
15486              {
15487                /*
15488                  Offix DND.
15489                */
15490                (void) CopyMagickString(resource_info->image_info->filename,
15491                  (char *) data,MaxTextExtent);
15492              }
15493            else
15494              {
15495                /*
15496                  XDND.
15497                */
15498                if (strncmp((char *) data, "file:", 5) != 0)
15499                  {
15500                    (void) XFree((void *) data);
15501                    break;
15502                  }
15503                (void) CopyMagickString(resource_info->image_info->filename,
15504                  ((char *) data)+5,MaxTextExtent);
15505              }
15506            nexus=ReadImage(resource_info->image_info,exception);
15507            CatchException(exception);
15508            if (nexus != (Image *) NULL)
15509              *state|=NextImageState | ExitState;
15510            (void) XFree((void *) data);
15511            break;
15512          }
15513        /*
15514          If client window delete message, exit.
15515        */
15516        if (event.xclient.message_type != windows->wm_protocols)
15517          break;
15518        if (*event.xclient.data.l != (long) windows->wm_delete_window)
15519          break;
15520        (void) XWithdrawWindow(display,event.xclient.window,
15521          visual_info->screen);
15522        if (event.xclient.window == windows->image.id)
15523          {
15524            *state|=ExitState;
15525            break;
15526          }
15527        if (event.xclient.window == windows->pan.id)
15528          {
15529            /*
15530              Restore original image size when pan window is deleted.
15531            */
15532            windows->image.window_changes.width=windows->image.ximage->width;
15533            windows->image.window_changes.height=windows->image.ximage->height;
15534            (void) XConfigureImage(display,resource_info,windows,
15535              display_image,exception);
15536          }
15537        break;
15538      }
15539      case ConfigureNotify:
15540      {
15541        if( IfMagickTrue(display_image->debug) )
15542          (void) LogMagickEvent(X11Event,GetMagickModule(),
15543            "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15544            event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15545            event.xconfigure.y,event.xconfigure.send_event);
15546        if (event.xconfigure.window == windows->image.id)
15547          {
15548            /*
15549              Image window has a new configuration.
15550            */
15551            if (event.xconfigure.send_event != 0)
15552              {
15553                XWindowChanges
15554                  window_changes;
15555
15556                /*
15557                  Position the transient windows relative of the Image window.
15558                */
15559                if (windows->command.geometry == (char *) NULL)
15560                  if( IfMagickFalse(windows->command.mapped) )
15561                    {
15562                      windows->command.x=event.xconfigure.x-
15563                        windows->command.width-25;
15564                      windows->command.y=event.xconfigure.y;
15565                      XConstrainWindowPosition(display,&windows->command);
15566                      window_changes.x=windows->command.x;
15567                      window_changes.y=windows->command.y;
15568                      (void) XReconfigureWMWindow(display,windows->command.id,
15569                        windows->command.screen,(unsigned int) (CWX | CWY),
15570                        &window_changes);
15571                    }
15572                if (windows->widget.geometry == (char *) NULL)
15573                  if( IfMagickFalse(windows->widget.mapped) )
15574                    {
15575                      windows->widget.x=event.xconfigure.x+
15576                        event.xconfigure.width/10;
15577                      windows->widget.y=event.xconfigure.y+
15578                        event.xconfigure.height/10;
15579                      XConstrainWindowPosition(display,&windows->widget);
15580                      window_changes.x=windows->widget.x;
15581                      window_changes.y=windows->widget.y;
15582                      (void) XReconfigureWMWindow(display,windows->widget.id,
15583                        windows->widget.screen,(unsigned int) (CWX | CWY),
15584                        &window_changes);
15585                    }
15586                if (windows->magnify.geometry == (char *) NULL)
15587                  if( IfMagickFalse(windows->magnify.mapped) )
15588                    {
15589                      windows->magnify.x=event.xconfigure.x+
15590                        event.xconfigure.width+25;
15591                      windows->magnify.y=event.xconfigure.y;
15592                      XConstrainWindowPosition(display,&windows->magnify);
15593                      window_changes.x=windows->magnify.x;
15594                      window_changes.y=windows->magnify.y;
15595                      (void) XReconfigureWMWindow(display,windows->magnify.id,
15596                        windows->magnify.screen,(unsigned int) (CWX | CWY),
15597                        &window_changes);
15598                    }
15599                if (windows->pan.geometry == (char *) NULL)
15600                  if( IfMagickFalse(windows->pan.mapped) )
15601                    {
15602                      windows->pan.x=event.xconfigure.x+
15603                        event.xconfigure.width+25;
15604                      windows->pan.y=event.xconfigure.y+
15605                        windows->magnify.height+50;
15606                      XConstrainWindowPosition(display,&windows->pan);
15607                      window_changes.x=windows->pan.x;
15608                      window_changes.y=windows->pan.y;
15609                      (void) XReconfigureWMWindow(display,windows->pan.id,
15610                        windows->pan.screen,(unsigned int) (CWX | CWY),
15611                        &window_changes);
15612                    }
15613              }
15614            if ((event.xconfigure.width == (int) windows->image.width) &&
15615                (event.xconfigure.height == (int) windows->image.height))
15616              break;
15617            windows->image.width=(unsigned int) event.xconfigure.width;
15618            windows->image.height=(unsigned int) event.xconfigure.height;
15619            windows->image.x=0;
15620            windows->image.y=0;
15621            if (display_image->montage != (char *) NULL)
15622              {
15623                windows->image.x=vid_info.x;
15624                windows->image.y=vid_info.y;
15625              }
15626            if( IfMagickTrue(windows->image.mapped) &&
15627                IfMagickTrue(windows->image.stasis) )
15628              {
15629                /*
15630                  Update image window configuration.
15631                */
15632                windows->image.window_changes.width=event.xconfigure.width;
15633                windows->image.window_changes.height=event.xconfigure.height;
15634                (void) XConfigureImage(display,resource_info,windows,
15635                  display_image,exception);
15636              }
15637            /*
15638              Update pan window configuration.
15639            */
15640            if ((event.xconfigure.width < windows->image.ximage->width) ||
15641                (event.xconfigure.height < windows->image.ximage->height))
15642              {
15643                (void) XMapRaised(display,windows->pan.id);
15644                XDrawPanRectangle(display,windows);
15645              }
15646            else
15647              if( IfMagickTrue(windows->pan.mapped) )
15648                (void) XWithdrawWindow(display,windows->pan.id,
15649                  windows->pan.screen);
15650            break;
15651          }
15652        if (event.xconfigure.window == windows->magnify.id)
15653          {
15654            unsigned int
15655              magnify;
15656
15657            /*
15658              Magnify window has a new configuration.
15659            */
15660            windows->magnify.width=(unsigned int) event.xconfigure.width;
15661            windows->magnify.height=(unsigned int) event.xconfigure.height;
15662            if( IfMagickFalse(windows->magnify.mapped) )
15663              break;
15664            magnify=1;
15665            while ((int) magnify <= event.xconfigure.width)
15666              magnify<<=1;
15667            while ((int) magnify <= event.xconfigure.height)
15668              magnify<<=1;
15669            magnify>>=1;
15670            if (((int) magnify != event.xconfigure.width) ||
15671                ((int) magnify != event.xconfigure.height))
15672              {
15673                window_changes.width=(int) magnify;
15674                window_changes.height=(int) magnify;
15675                (void) XReconfigureWMWindow(display,windows->magnify.id,
15676                  windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15677                  &window_changes);
15678                break;
15679              }
15680            if( IfMagickTrue(windows->magnify.mapped) &&
15681                IfMagickTrue(windows->magnify.stasis) )
15682              {
15683                status=XMakeImage(display,resource_info,&windows->magnify,
15684                  display_image,windows->magnify.width,windows->magnify.height,
15685                  exception);
15686                XMakeMagnifyImage(display,windows,exception);
15687              }
15688            break;
15689          }
15690        if( IfMagickTrue(windows->magnify.mapped) &&
15691            (event.xconfigure.window == windows->pan.id))
15692          {
15693            /*
15694              Pan icon window has a new configuration.
15695            */
15696            if (event.xconfigure.send_event != 0)
15697              {
15698                windows->pan.x=event.xconfigure.x;
15699                windows->pan.y=event.xconfigure.y;
15700              }
15701            windows->pan.width=(unsigned int) event.xconfigure.width;
15702            windows->pan.height=(unsigned int) event.xconfigure.height;
15703            break;
15704          }
15705        if (event.xconfigure.window == windows->icon.id)
15706          {
15707            /*
15708              Icon window has a new configuration.
15709            */
15710            windows->icon.width=(unsigned int) event.xconfigure.width;
15711            windows->icon.height=(unsigned int) event.xconfigure.height;
15712            break;
15713          }
15714        break;
15715      }
15716      case DestroyNotify:
15717      {
15718        /*
15719          Group leader has exited.
15720        */
15721        if( IfMagickTrue(display_image->debug) )
15722          (void) LogMagickEvent(X11Event,GetMagickModule(),
15723            "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15724        if (event.xdestroywindow.window == windows->group_leader.id)
15725          {
15726            *state|=ExitState;
15727            break;
15728          }
15729        break;
15730      }
15731      case EnterNotify:
15732      {
15733        /*
15734          Selectively install colormap.
15735        */
15736        if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15737          if (event.xcrossing.mode != NotifyUngrab)
15738            XInstallColormap(display,map_info->colormap);
15739        break;
15740      }
15741      case Expose:
15742      {
15743        if( IfMagickTrue(display_image->debug) )
15744          (void) LogMagickEvent(X11Event,GetMagickModule(),
15745            "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15746            event.xexpose.width,event.xexpose.height,event.xexpose.x,
15747            event.xexpose.y);
15748        /*
15749          Refresh windows that are now exposed.
15750        */
15751        if ((event.xexpose.window == windows->image.id) &&
15752            IfMagickTrue(windows->image.mapped) )
15753          {
15754            XRefreshWindow(display,&windows->image,&event);
15755            delay=display_image->delay/MagickMax(
15756              display_image->ticks_per_second,1L);
15757            timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15758            break;
15759          }
15760        if ((event.xexpose.window == windows->magnify.id) &&
15761            IfMagickTrue(windows->magnify.mapped))
15762          {
15763            XMakeMagnifyImage(display,windows,exception);
15764            break;
15765          }
15766        if (event.xexpose.window == windows->pan.id)
15767          {
15768            XDrawPanRectangle(display,windows);
15769            break;
15770          }
15771        if (event.xexpose.window == windows->icon.id)
15772          {
15773            XRefreshWindow(display,&windows->icon,&event);
15774            break;
15775          }
15776        break;
15777      }
15778      case KeyPress:
15779      {
15780        int
15781          length;
15782
15783        /*
15784          Respond to a user key press.
15785        */
15786        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15787          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15788        *(command+length)='\0';
15789        if( IfMagickTrue(display_image->debug) )
15790          (void) LogMagickEvent(X11Event,GetMagickModule(),
15791            "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15792            key_symbol,command);
15793        if (event.xkey.window == windows->image.id)
15794          {
15795            command_type=XImageWindowCommand(display,resource_info,windows,
15796              event.xkey.state,key_symbol,&display_image,exception);
15797            if (command_type != NullCommand)
15798              nexus=XMagickCommand(display,resource_info,windows,command_type,
15799                &display_image,exception);
15800          }
15801        if (event.xkey.window == windows->magnify.id)
15802          XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol,
15803            exception);
15804        if (event.xkey.window == windows->pan.id)
15805          {
15806            if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15807              (void) XWithdrawWindow(display,windows->pan.id,
15808                windows->pan.screen);
15809            else
15810              if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15811                XTextViewWidget(display,resource_info,windows,MagickFalse,
15812                  "Help Viewer - Image Pan",ImagePanHelp);
15813              else
15814                XTranslateImage(display,windows,*image,key_symbol);
15815          }
15816        delay=display_image->delay/MagickMax(
15817          display_image->ticks_per_second,1L);
15818        timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15819        break;
15820      }
15821      case KeyRelease:
15822      {
15823        /*
15824          Respond to a user key release.
15825        */
15826        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15827          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15828        if( IfMagickTrue(display_image->debug) )
15829          (void) LogMagickEvent(X11Event,GetMagickModule(),
15830            "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15831        break;
15832      }
15833      case LeaveNotify:
15834      {
15835        /*
15836          Selectively uninstall colormap.
15837        */
15838        if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15839          if (event.xcrossing.mode != NotifyUngrab)
15840            XUninstallColormap(display,map_info->colormap);
15841        break;
15842      }
15843      case MapNotify:
15844      {
15845        if( IfMagickTrue(display_image->debug) )
15846          (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15847            event.xmap.window);
15848        if (event.xmap.window == windows->backdrop.id)
15849          {
15850            (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15851              CurrentTime);
15852            windows->backdrop.mapped=MagickTrue;
15853            break;
15854          }
15855        if (event.xmap.window == windows->image.id)
15856          {
15857            if (windows->backdrop.id != (Window) NULL)
15858              (void) XInstallColormap(display,map_info->colormap);
15859            if (LocaleCompare(display_image->magick,"LOGO") == 0)
15860              {
15861                if (LocaleCompare(display_image->filename,"LOGO") == 0)
15862                  nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15863              }
15864            if (((int) windows->image.width < windows->image.ximage->width) ||
15865                ((int) windows->image.height < windows->image.ximage->height))
15866              (void) XMapRaised(display,windows->pan.id);
15867            windows->image.mapped=MagickTrue;
15868            break;
15869          }
15870        if (event.xmap.window == windows->magnify.id)
15871          {
15872            XMakeMagnifyImage(display,windows,exception);
15873            windows->magnify.mapped=MagickTrue;
15874            (void) XWithdrawWindow(display,windows->info.id,
15875              windows->info.screen);
15876            break;
15877          }
15878        if (event.xmap.window == windows->pan.id)
15879          {
15880            XMakePanImage(display,resource_info,windows,display_image,
15881              exception);
15882            windows->pan.mapped=MagickTrue;
15883            break;
15884          }
15885        if (event.xmap.window == windows->info.id)
15886          {
15887            windows->info.mapped=MagickTrue;
15888            break;
15889          }
15890        if (event.xmap.window == windows->icon.id)
15891          {
15892            MagickBooleanType
15893              taint;
15894
15895            /*
15896              Create an icon image.
15897            */
15898            taint=display_image->taint;
15899            XMakeStandardColormap(display,icon_visual,icon_resources,
15900              display_image,icon_map,icon_pixel,exception);
15901            (void) XMakeImage(display,icon_resources,&windows->icon,
15902              display_image,windows->icon.width,windows->icon.height,
15903              exception);
15904            display_image->taint=taint;
15905            (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15906              windows->icon.pixmap);
15907            (void) XClearWindow(display,windows->icon.id);
15908            (void) XWithdrawWindow(display,windows->info.id,
15909              windows->info.screen);
15910            windows->icon.mapped=MagickTrue;
15911            break;
15912          }
15913        if (event.xmap.window == windows->command.id)
15914          {
15915            windows->command.mapped=MagickTrue;
15916            break;
15917          }
15918        if (event.xmap.window == windows->popup.id)
15919          {
15920            windows->popup.mapped=MagickTrue;
15921            break;
15922          }
15923        if (event.xmap.window == windows->widget.id)
15924          {
15925            windows->widget.mapped=MagickTrue;
15926            break;
15927          }
15928        break;
15929      }
15930      case MappingNotify:
15931      {
15932        (void) XRefreshKeyboardMapping(&event.xmapping);
15933        break;
15934      }
15935      case NoExpose:
15936        break;
15937      case PropertyNotify:
15938      {
15939        Atom
15940          type;
15941
15942        int
15943          format,
15944          status;
15945
15946        unsigned char
15947          *data;
15948
15949        unsigned long
15950          after,
15951          length;
15952
15953        if( IfMagickTrue(display_image->debug) )
15954          (void) LogMagickEvent(X11Event,GetMagickModule(),
15955            "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15956            event.xproperty.atom,event.xproperty.state);
15957        if (event.xproperty.atom != windows->im_remote_command)
15958          break;
15959        /*
15960          Display image named by the remote command protocol.
15961        */
15962        status=XGetWindowProperty(display,event.xproperty.window,
15963          event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15964          AnyPropertyType,&type,&format,&length,&after,&data);
15965        if ((status != Success) || (length == 0))
15966          break;
15967        if (LocaleCompare((char *) data,"-quit") == 0)
15968          {
15969            XClientMessage(display,windows->image.id,windows->im_protocols,
15970              windows->im_exit,CurrentTime);
15971            (void) XFree((void *) data);
15972            break;
15973          }
15974        (void) CopyMagickString(resource_info->image_info->filename,
15975          (char *) data,MaxTextExtent);
15976        (void) XFree((void *) data);
15977        nexus=ReadImage(resource_info->image_info,exception);
15978        CatchException(exception);
15979        if (nexus != (Image *) NULL)
15980          *state|=NextImageState | ExitState;
15981        break;
15982      }
15983      case ReparentNotify:
15984      {
15985        if( IfMagickTrue(display_image->debug) )
15986          (void) LogMagickEvent(X11Event,GetMagickModule(),
15987            "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15988            event.xreparent.window);
15989        break;
15990      }
15991      case UnmapNotify:
15992      {
15993        if( IfMagickTrue(display_image->debug) )
15994          (void) LogMagickEvent(X11Event,GetMagickModule(),
15995            "Unmap Notify: 0x%lx",event.xunmap.window);
15996        if (event.xunmap.window == windows->backdrop.id)
15997          {
15998            windows->backdrop.mapped=MagickFalse;
15999            break;
16000          }
16001        if (event.xunmap.window == windows->image.id)
16002          {
16003            windows->image.mapped=MagickFalse;
16004            break;
16005          }
16006        if (event.xunmap.window == windows->magnify.id)
16007          {
16008            windows->magnify.mapped=MagickFalse;
16009            break;
16010          }
16011        if (event.xunmap.window == windows->pan.id)
16012          {
16013            windows->pan.mapped=MagickFalse;
16014            break;
16015          }
16016        if (event.xunmap.window == windows->info.id)
16017          {
16018            windows->info.mapped=MagickFalse;
16019            break;
16020          }
16021        if (event.xunmap.window == windows->icon.id)
16022          {
16023            if (map_info->colormap == icon_map->colormap)
16024              XConfigureImageColormap(display,resource_info,windows,
16025                display_image,exception);
16026            (void) XFreeStandardColormap(display,icon_visual,icon_map,
16027              icon_pixel);
16028            windows->icon.mapped=MagickFalse;
16029            break;
16030          }
16031        if (event.xunmap.window == windows->command.id)
16032          {
16033            windows->command.mapped=MagickFalse;
16034            break;
16035          }
16036        if (event.xunmap.window == windows->popup.id)
16037          {
16038            if (windows->backdrop.id != (Window) NULL)
16039              (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16040                CurrentTime);
16041            windows->popup.mapped=MagickFalse;
16042            break;
16043          }
16044        if (event.xunmap.window == windows->widget.id)
16045          {
16046            if (windows->backdrop.id != (Window) NULL)
16047              (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16048                CurrentTime);
16049            windows->widget.mapped=MagickFalse;
16050            break;
16051          }
16052        break;
16053      }
16054      default:
16055      {
16056        if( IfMagickTrue(display_image->debug) )
16057          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
16058            event.type);
16059        break;
16060      }
16061    }
16062  } while (!(*state & ExitState));
16063  if ((*state & ExitState) == 0)
16064    (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
16065      &display_image,exception);
16066  else
16067    if( IfMagickTrue(resource_info->confirm_edit) )
16068      {
16069        /*
16070          Query user if image has changed.
16071        */
16072        if( IfMagickFalse(resource_info->immutable) &&
16073            IfMagickTrue(display_image->taint))
16074          {
16075            int
16076              status;
16077
16078            status=XConfirmWidget(display,windows,"Your image changed.",
16079              "Do you want to save it");
16080            if (status == 0)
16081              *state&=(~ExitState);
16082            else
16083              if (status > 0)
16084                (void) XMagickCommand(display,resource_info,windows,SaveCommand,
16085                  &display_image,exception);
16086          }
16087      }
16088  if ((windows->visual_info->klass == GrayScale) ||
16089      (windows->visual_info->klass == PseudoColor) ||
16090      (windows->visual_info->klass == DirectColor))
16091    {
16092      /*
16093        Withdraw pan and Magnify window.
16094      */
16095      if( IfMagickTrue(windows->info.mapped) )
16096        (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16097      if( IfMagickTrue(windows->magnify.mapped) )
16098        (void) XWithdrawWindow(display,windows->magnify.id,
16099          windows->magnify.screen);
16100      if( IfMagickTrue(windows->command.mapped) )
16101        (void) XWithdrawWindow(display,windows->command.id,
16102          windows->command.screen);
16103    }
16104  if( IfMagickTrue(windows->pan.mapped) )
16105    (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16106  if( IfMagickFalse(resource_info->backdrop) )
16107    if (windows->backdrop.mapped)
16108      {
16109        (void) XWithdrawWindow(display,windows->backdrop.id,
16110          windows->backdrop.screen);
16111        (void) XDestroyWindow(display,windows->backdrop.id);
16112        windows->backdrop.id=(Window) NULL;
16113        (void) XWithdrawWindow(display,windows->image.id,
16114          windows->image.screen);
16115        (void) XDestroyWindow(display,windows->image.id);
16116        windows->image.id=(Window) NULL;
16117      }
16118  XSetCursorState(display,windows,MagickTrue);
16119  XCheckRefreshWindows(display,windows);
16120  if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16121    *state&=(~ExitState);
16122  if (*state & ExitState)
16123    {
16124      /*
16125        Free Standard Colormap.
16126      */
16127      (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16128      if (resource_info->map_type == (char *) NULL)
16129        (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16130      /*
16131        Free X resources.
16132      */
16133      if (resource_info->copy_image != (Image *) NULL)
16134        {
16135          resource_info->copy_image=DestroyImage(resource_info->copy_image);
16136          resource_info->copy_image=NewImageList();
16137        }
16138      DestroyXResources();
16139    }
16140  (void) XSync(display,MagickFalse);
16141  /*
16142    Restore our progress monitor and warning handlers.
16143  */
16144  (void) SetErrorHandler(warning_handler);
16145  (void) SetWarningHandler(warning_handler);
16146  /*
16147    Change to home directory.
16148  */
16149  directory=getcwd(working_directory,MaxTextExtent);
16150  (void) directory;
16151  {
16152    int
16153      status;
16154
16155    if (*resource_info->home_directory == '\0')
16156      (void) CopyMagickString(resource_info->home_directory,".",MaxTextExtent);
16157    status=chdir(resource_info->home_directory);
16158    if (status == -1)
16159      (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
16160        "UnableToOpenFile","%s",resource_info->home_directory);
16161  }
16162  *image=display_image;
16163  return(nexus);
16164}
16165#else
16166
16167/*
16168%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16169%                                                                             %
16170%                                                                             %
16171%                                                                             %
16172+   D i s p l a y I m a g e s                                                 %
16173%                                                                             %
16174%                                                                             %
16175%                                                                             %
16176%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16177%
16178%  DisplayImages() displays an image sequence to any X window screen.  It
16179%  returns a value other than 0 if successful.  Check the exception member
16180%  of image to determine the reason for any failure.
16181%
16182%  The format of the DisplayImages method is:
16183%
16184%      MagickBooleanType DisplayImages(const ImageInfo *image_info,
16185%        Image *images,ExceptionInfo *exception)
16186%
16187%  A description of each parameter follows:
16188%
16189%    o image_info: the image info.
16190%
16191%    o image: the image.
16192%
16193%    o exception: return any errors or warnings in this structure.
16194%
16195*/
16196MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16197  Image *image,ExceptionInfo *exception)
16198{
16199  assert(image_info != (const ImageInfo *) NULL);
16200  assert(image_info->signature == MagickSignature);
16201  assert(image != (Image *) NULL);
16202  assert(image->signature == MagickSignature);
16203  if( IfMagickTrue(image->debug) )
16204    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16205  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16206    "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image->filename);
16207  return(MagickFalse);
16208}
16209
16210/*
16211%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16212%                                                                             %
16213%                                                                             %
16214%                                                                             %
16215+   R e m o t e D i s p l a y C o m m a n d                                   %
16216%                                                                             %
16217%                                                                             %
16218%                                                                             %
16219%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16220%
16221%  RemoteDisplayCommand() encourages a remote display program to display the
16222%  specified image filename.
16223%
16224%  The format of the RemoteDisplayCommand method is:
16225%
16226%      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16227%        const char *window,const char *filename,ExceptionInfo *exception)
16228%
16229%  A description of each parameter follows:
16230%
16231%    o image_info: the image info.
16232%
16233%    o window: Specifies the name or id of an X window.
16234%
16235%    o filename: the name of the image filename to display.
16236%
16237%    o exception: return any errors or warnings in this structure.
16238%
16239*/
16240MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16241  const char *window,const char *filename,ExceptionInfo *exception)
16242{
16243  assert(image_info != (const ImageInfo *) NULL);
16244  assert(image_info->signature == MagickSignature);
16245  assert(filename != (char *) NULL);
16246  (void) window;
16247  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16248  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16249    "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image_info->filename);
16250  return(MagickFalse);
16251}
16252#endif
16253