display.c revision e2a912b6c9086c98ec838baa0824cd8deca55538
1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3%                                                                             %
4%                                                                             %
5%                                                                             %
6%               DDDD   IIIII  SSSSS  PPPP   L       AAA   Y   Y               %
7%               D   D    I    SS     P   P  L      A   A   Y Y                %
8%               D   D    I     SSS   PPPP   L      AAAAA    Y                 %
9%               D   D    I       SS  P      L      A   A    Y                 %
10%               DDDD   IIIII  SSSSS  P      LLLLL  A   A    Y                 %
11%                                                                             %
12%                                                                             %
13%        MagickCore Methods to Interactively Display and Edit an Image        %
14%                                                                             %
15%                             Software Design                                 %
16%                               John Cristy                                   %
17%                                July 1992                                    %
18%                                                                             %
19%                                                                             %
20%  Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization      %
21%  dedicated to making software imaging solutions freely available.           %
22%                                                                             %
23%  You may not use this file except in compliance with the License.  You may  %
24%  obtain a copy of the License at                                            %
25%                                                                             %
26%    http://www.imagemagick.org/script/license.php                            %
27%                                                                             %
28%  Unless required by applicable law or agreed to in writing, software        %
29%  distributed under the License is distributed on an "AS IS" BASIS,          %
30%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31%  See the License for the specific language governing permissions and        %
32%  limitations under the License.                                             %
33%                                                                             %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37*/
38
39/*
40  Include declarations.
41*/
42#include "MagickCore/studio.h"
43#include "MagickCore/artifact.h"
44#include "MagickCore/blob.h"
45#include "MagickCore/cache.h"
46#include "MagickCore/cache-private.h"
47#include "MagickCore/client.h"
48#include "MagickCore/color.h"
49#include "MagickCore/colorspace.h"
50#include "MagickCore/composite.h"
51#include "MagickCore/constitute.h"
52#include "MagickCore/decorate.h"
53#include "MagickCore/delegate.h"
54#include "MagickCore/display.h"
55#include "MagickCore/display-private.h"
56#include "MagickCore/distort.h"
57#include "MagickCore/draw.h"
58#include "MagickCore/effect.h"
59#include "MagickCore/enhance.h"
60#include "MagickCore/exception.h"
61#include "MagickCore/exception-private.h"
62#include "MagickCore/fx.h"
63#include "MagickCore/geometry.h"
64#include "MagickCore/image.h"
65#include "MagickCore/image-private.h"
66#include "MagickCore/list.h"
67#include "MagickCore/log.h"
68#include "MagickCore/magick.h"
69#include "MagickCore/memory_.h"
70#include "MagickCore/monitor.h"
71#include "MagickCore/monitor-private.h"
72#include "MagickCore/montage.h"
73#include "MagickCore/option.h"
74#include "MagickCore/paint.h"
75#include "MagickCore/pixel.h"
76#include "MagickCore/pixel-accessor.h"
77#include "MagickCore/PreRvIcccm.h"
78#include "MagickCore/property.h"
79#include "MagickCore/quantum.h"
80#include "MagickCore/quantum-private.h"
81#include "MagickCore/resize.h"
82#include "MagickCore/resource_.h"
83#include "MagickCore/shear.h"
84#include "MagickCore/segment.h"
85#include "MagickCore/string_.h"
86#include "MagickCore/string-private.h"
87#include "MagickCore/transform.h"
88#include "MagickCore/threshold.h"
89#include "MagickCore/utility.h"
90#include "MagickCore/utility-private.h"
91#include "MagickCore/version.h"
92#include "MagickCore/widget.h"
93#include "MagickCore/widget-private.h"
94#include "MagickCore/xwindow.h"
95#include "MagickCore/xwindow-private.h"
96
97#if defined(MAGICKCORE_X11_DELEGATE)
98/*
99  Define declarations.
100*/
101#define MaxColors  MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
102
103/*
104  Constant declarations.
105*/
106static const unsigned char
107  HighlightBitmap[8] =
108  {
109    0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
110  },
111  OpaqueBitmap[8] =
112  {
113    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
114  },
115  ShadowBitmap[8] =
116  {
117    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
118  };
119
120static const char
121  *PageSizes[] =
122  {
123    "Letter",
124    "Tabloid",
125    "Ledger",
126    "Legal",
127    "Statement",
128    "Executive",
129    "A3",
130    "A4",
131    "A5",
132    "B4",
133    "B5",
134    "Folio",
135    "Quarto",
136    "10x14",
137    (char *) NULL
138  };
139
140/*
141  Help widget declarations.
142*/
143static const char
144  *ImageAnnotateHelp[] =
145  {
146    "In annotate mode, the Command widget has these options:",
147    "",
148    "    Font Name",
149    "      fixed",
150    "      variable",
151    "      5x8",
152    "      6x10",
153    "      7x13bold",
154    "      8x13bold",
155    "      9x15bold",
156    "      10x20",
157    "      12x24",
158    "      Browser...",
159    "    Font Color",
160    "      black",
161    "      blue",
162    "      cyan",
163    "      green",
164    "      gray",
165    "      red",
166    "      magenta",
167    "      yellow",
168    "      white",
169    "      transparent",
170    "      Browser...",
171    "    Font Color",
172    "      black",
173    "      blue",
174    "      cyan",
175    "      green",
176    "      gray",
177    "      red",
178    "      magenta",
179    "      yellow",
180    "      white",
181    "      transparent",
182    "      Browser...",
183    "    Rotate Text",
184    "      -90",
185    "      -45",
186    "      -30",
187    "      0",
188    "      30",
189    "      45",
190    "      90",
191    "      180",
192    "      Dialog...",
193    "    Help",
194    "    Dismiss",
195    "",
196    "Choose a font name from the Font Name sub-menu.  Additional",
197    "font names can be specified with the font browser.  You can",
198    "change the menu names by setting the X resources font1",
199    "through font9.",
200    "",
201    "Choose a font color from the Font Color sub-menu.",
202    "Additional font colors can be specified with the color",
203    "browser.  You can change the menu colors by setting the X",
204    "resources pen1 through pen9.",
205    "",
206    "If you select the color browser and press Grab, you can",
207    "choose the font color by moving the pointer to the desired",
208    "color on the screen and press any button.",
209    "",
210    "If you choose to rotate the text, choose Rotate Text from the",
211    "menu and select an angle.  Typically you will only want to",
212    "rotate one line of text at a time.  Depending on the angle you",
213    "choose, subsequent lines may end up overwriting each other.",
214    "",
215    "Choosing a font and its color is optional.  The default font",
216    "is fixed and the default color is black.  However, you must",
217    "choose a location to begin entering text and press button 1.",
218    "An underscore character will appear at the location of the",
219    "pointer.  The cursor changes to a pencil to indicate you are",
220    "in text mode.  To exit immediately, press Dismiss.",
221    "",
222    "In text mode, any key presses will display the character at",
223    "the location of the underscore and advance the underscore",
224    "cursor.  Enter your text and once completed press Apply to",
225    "finish your image annotation.  To correct errors press BACK",
226    "SPACE.  To delete an entire line of text, press DELETE.  Any",
227    "text that exceeds the boundaries of the image window is",
228    "automagically continued onto the next line.",
229    "",
230    "The actual color you request for the font is saved in the",
231    "image.  However, the color that appears in your image window",
232    "may be different.  For example, on a monochrome screen the",
233    "text will appear black or white even if you choose the color",
234    "red as the font color.  However, the image saved to a file",
235    "with -write is written with red lettering.  To assure the",
236    "correct color text in the final image, any PseudoClass image",
237    "is promoted to DirectClass (see miff(5)).  To force a",
238    "PseudoClass image to remain PseudoClass, use -colors.",
239    (char *) NULL,
240  },
241  *ImageChopHelp[] =
242  {
243    "In chop mode, the Command widget has these options:",
244    "",
245    "    Direction",
246    "      horizontal",
247    "      vertical",
248    "    Help",
249    "    Dismiss",
250    "",
251    "If the you choose the horizontal direction (this the",
252    "default), the area of the image between the two horizontal",
253    "endpoints of the chop line is removed.  Otherwise, the area",
254    "of the image between the two vertical endpoints of the chop",
255    "line is removed.",
256    "",
257    "Select a location within the image window to begin your chop,",
258    "press and hold any button.  Next, move the pointer to",
259    "another location in the image.  As you move a line will",
260    "connect the initial location and the pointer.  When you",
261    "release the button, the area within the image to chop is",
262    "determined by which direction you choose from the Command",
263    "widget.",
264    "",
265    "To cancel the image chopping, move the pointer back to the",
266    "starting point of the line and release the button.",
267    (char *) NULL,
268  },
269  *ImageColorEditHelp[] =
270  {
271    "In color edit mode, the Command widget has these options:",
272    "",
273    "    Method",
274    "      point",
275    "      replace",
276    "      floodfill",
277    "      filltoborder",
278    "      reset",
279    "    Pixel Color",
280    "      black",
281    "      blue",
282    "      cyan",
283    "      green",
284    "      gray",
285    "      red",
286    "      magenta",
287    "      yellow",
288    "      white",
289    "      Browser...",
290    "    Border Color",
291    "      black",
292    "      blue",
293    "      cyan",
294    "      green",
295    "      gray",
296    "      red",
297    "      magenta",
298    "      yellow",
299    "      white",
300    "      Browser...",
301    "    Fuzz",
302    "      0%",
303    "      2%",
304    "      5%",
305    "      10%",
306    "      15%",
307    "      Dialog...",
308    "    Undo",
309    "    Help",
310    "    Dismiss",
311    "",
312    "Choose a color editing method from the Method sub-menu",
313    "of the Command widget.  The point method recolors any pixel",
314    "selected with the pointer until the button is released.  The",
315    "replace method recolors any pixel that matches the color of",
316    "the pixel you select with a button press.  Floodfill recolors",
317    "any pixel that matches the color of the pixel you select with",
318    "a button press and is a neighbor.  Whereas filltoborder recolors",
319    "any neighbor pixel that is not the border color.  Finally reset",
320    "changes the entire image to the designated color.",
321    "",
322    "Next, choose a pixel color from the Pixel Color sub-menu.",
323    "Additional pixel colors can be specified with the color",
324    "browser.  You can change the menu colors by setting the X",
325    "resources pen1 through pen9.",
326    "",
327    "Now press button 1 to select a pixel within the image window",
328    "to change its color.  Additional pixels may be recolored as",
329    "prescribed by the method you choose.",
330    "",
331    "If the Magnify widget is mapped, it can be helpful in positioning",
332    "your pointer within the image (refer to button 2).",
333    "",
334    "The actual color you request for the pixels is saved in the",
335    "image.  However, the color that appears in your image window",
336    "may be different.  For example, on a monochrome screen the",
337    "pixel will appear black or white even if you choose the",
338    "color red as the pixel color.  However, the image saved to a",
339    "file with -write is written with red pixels.  To assure the",
340    "correct color text in the final image, any PseudoClass image",
341    "is promoted to DirectClass (see miff(5)).  To force a",
342    "PseudoClass image to remain PseudoClass, use -colors.",
343    (char *) NULL,
344  },
345  *ImageCompositeHelp[] =
346  {
347    "First a widget window is displayed requesting you to enter an",
348    "image name. Press Composite, Grab or type a file name.",
349    "Press Cancel if you choose not to create a composite image.",
350    "When you choose Grab, move the pointer to the desired window",
351    "and press any button.",
352    "",
353    "If the Composite image does not have any matte information,",
354    "you are informed and the file browser is displayed again.",
355    "Enter the name of a mask image.  The image is typically",
356    "grayscale and the same size as the composite image.  If the",
357    "image is not grayscale, it is converted to grayscale and the",
358    "resulting intensities are used as matte information.",
359    "",
360    "A small window appears showing the location of the cursor in",
361    "the image window. You are now in composite mode.  To exit",
362    "immediately, press Dismiss.  In composite mode, the Command",
363    "widget has these options:",
364    "",
365    "    Operators",
366    "      Over",
367    "      In",
368    "      Out",
369    "      Atop",
370    "      Xor",
371    "      Plus",
372    "      Minus",
373    "      Add",
374    "      Subtract",
375    "      Difference",
376    "      Multiply",
377    "      Bumpmap",
378    "      Copy",
379    "      CopyRed",
380    "      CopyGreen",
381    "      CopyBlue",
382    "      CopyOpacity",
383    "      Clear",
384    "    Dissolve",
385    "    Displace",
386    "    Help",
387    "    Dismiss",
388    "",
389    "Choose a composite operation from the Operators sub-menu of",
390    "the Command widget.  How each operator behaves is described",
391    "below.  Image window is the image currently displayed on",
392    "your X server and image is the image obtained with the File",
393    "Browser widget.",
394    "",
395    "Over     The result is the union of the two image shapes,",
396    "         with image obscuring image window in the region of",
397    "         overlap.",
398    "",
399    "In       The result is simply image cut by the shape of",
400    "         image window.  None of the image data of image",
401    "         window is in the result.",
402    "",
403    "Out      The resulting image is image with the shape of",
404    "         image window cut out.",
405    "",
406    "Atop     The result is the same shape as image image window,",
407    "         with image obscuring image window where the image",
408    "         shapes overlap.  Note this differs from over",
409    "         because the portion of image outside image window's",
410    "         shape does not appear in the result.",
411    "",
412    "Xor      The result is the image data from both image and",
413    "         image window that is outside the overlap region.",
414    "         The overlap region is blank.",
415    "",
416    "Plus     The result is just the sum of the image data.",
417    "         Output values are cropped to QuantumRange (no overflow).",
418    "",
419    "Minus    The result of image - image window, with underflow",
420    "         cropped to zero.",
421    "",
422    "Add      The result of image + image window, with overflow",
423    "         wrapping around (mod 256).",
424    "",
425    "Subtract The result of image - image window, with underflow",
426    "         wrapping around (mod 256).  The add and subtract",
427    "         operators can be used to perform reversible",
428    "         transformations.",
429    "",
430    "Difference",
431    "         The result of abs(image - image window).  This",
432    "         useful for comparing two very similar images.",
433    "",
434    "Multiply",
435    "         The result of image * image window.  This",
436    "         useful for the creation of drop-shadows.",
437    "",
438    "Bumpmap  The result of surface normals from image * image",
439    "         window.",
440    "",
441    "Copy     The resulting image is image window replaced with",
442    "         image.  Here the matte information is ignored.",
443    "",
444    "CopyRed  The red layer of the image window is replace with",
445    "         the red layer of the image.  The other layers are",
446    "         untouched.",
447    "",
448    "CopyGreen",
449    "         The green layer of the image window is replace with",
450    "         the green layer of the image.  The other layers are",
451    "         untouched.",
452    "",
453    "CopyBlue The blue layer of the image window is replace with",
454    "         the blue layer of the image.  The other layers are",
455    "         untouched.",
456    "",
457    "CopyOpacity",
458    "         The matte layer of the image window is replace with",
459    "         the matte layer of the image.  The other layers are",
460    "         untouched.",
461    "",
462    "The image compositor requires a matte, or alpha channel in",
463    "the image for some operations.  This extra channel usually",
464    "defines a mask which represents a sort of a cookie-cutter",
465    "for the image.  This the case when matte is opaque (full",
466    "coverage) for pixels inside the shape, zero outside, and",
467    "between 0 and QuantumRange on the boundary.  If image does not",
468    "have a matte channel, it is initialized with 0 for any pixel",
469    "matching in color to pixel location (0,0), otherwise QuantumRange.",
470    "",
471    "If you choose Dissolve, the composite operator becomes Over.  The",
472    "image matte channel percent transparency is initialized to factor.",
473    "The image window is initialized to (100-factor). Where factor is the",
474    "value you specify in the Dialog widget.",
475    "",
476    "Displace shifts the image pixels as defined by a displacement",
477    "map.  With this option, image is used as a displacement map.",
478    "Black, within the displacement map, is a maximum positive",
479    "displacement.  White is a maximum negative displacement and",
480    "middle gray is neutral.  The displacement is scaled to determine",
481    "the pixel shift.  By default, the displacement applies in both the",
482    "horizontal and vertical directions.  However, if you specify a mask,",
483    "image is the horizontal X displacement and mask the vertical Y",
484    "displacement.",
485    "",
486    "Note that matte information for image window is not retained",
487    "for colormapped X server visuals (e.g. StaticColor,",
488    "StaticColor, GrayScale, PseudoColor).  Correct compositing",
489    "behavior may require a TrueColor or DirectColor visual or a",
490    "Standard Colormap.",
491    "",
492    "Choosing a composite operator is optional.  The default",
493    "operator is replace.  However, you must choose a location to",
494    "composite your image and press button 1.  Press and hold the",
495    "button before releasing and an outline of the image will",
496    "appear to help you identify your location.",
497    "",
498    "The actual colors of the composite image is saved.  However,",
499    "the color that appears in image window may be different.",
500    "For example, on a monochrome screen image window will appear",
501    "black or white even though your composited image may have",
502    "many colors.  If the image is saved to a file it is written",
503    "with the correct colors.  To assure the correct colors are",
504    "saved in the final image, any PseudoClass image is promoted",
505    "to DirectClass (see miff(5)).  To force a PseudoClass image",
506    "to remain PseudoClass, use -colors.",
507    (char *) NULL,
508  },
509  *ImageCutHelp[] =
510  {
511    "In cut mode, the Command widget has these options:",
512    "",
513    "    Help",
514    "    Dismiss",
515    "",
516    "To define a cut region, press button 1 and drag.  The",
517    "cut region is defined by a highlighted rectangle that",
518    "expands or contracts as it follows the pointer.  Once you",
519    "are satisfied with the cut region, release the button.",
520    "You are now in rectify mode.  In rectify mode, the Command",
521    "widget has these options:",
522    "",
523    "    Cut",
524    "    Help",
525    "    Dismiss",
526    "",
527    "You can make adjustments by moving the pointer to one of the",
528    "cut rectangle corners, pressing a button, and dragging.",
529    "Finally, press Cut to commit your copy region.  To",
530    "exit without cutting the image, press Dismiss.",
531    (char *) NULL,
532  },
533  *ImageCopyHelp[] =
534  {
535    "In copy mode, the Command widget has these options:",
536    "",
537    "    Help",
538    "    Dismiss",
539    "",
540    "To define a copy region, press button 1 and drag.  The",
541    "copy region is defined by a highlighted rectangle that",
542    "expands or contracts as it follows the pointer.  Once you",
543    "are satisfied with the copy region, release the button.",
544    "You are now in rectify mode.  In rectify mode, the Command",
545    "widget has these options:",
546    "",
547    "    Copy",
548    "    Help",
549    "    Dismiss",
550    "",
551    "You can make adjustments by moving the pointer to one of the",
552    "copy rectangle corners, pressing a button, and dragging.",
553    "Finally, press Copy to commit your copy region.  To",
554    "exit without copying the image, press Dismiss.",
555    (char *) NULL,
556  },
557  *ImageCropHelp[] =
558  {
559    "In crop mode, the Command widget has these options:",
560    "",
561    "    Help",
562    "    Dismiss",
563    "",
564    "To define a cropping region, press button 1 and drag.  The",
565    "cropping region is defined by a highlighted rectangle that",
566    "expands or contracts as it follows the pointer.  Once you",
567    "are satisfied with the cropping region, release the button.",
568    "You are now in rectify mode.  In rectify mode, the Command",
569    "widget has these options:",
570    "",
571    "    Crop",
572    "    Help",
573    "    Dismiss",
574    "",
575    "You can make adjustments by moving the pointer to one of the",
576    "cropping rectangle corners, pressing a button, and dragging.",
577    "Finally, press Crop to commit your cropping region.  To",
578    "exit without cropping the image, press Dismiss.",
579    (char *) NULL,
580  },
581  *ImageDrawHelp[] =
582  {
583    "The cursor changes to a crosshair to indicate you are in",
584    "draw mode.  To exit immediately, press Dismiss.  In draw mode,",
585    "the Command widget has these options:",
586    "",
587    "    Element",
588    "      point",
589    "      line",
590    "      rectangle",
591    "      fill rectangle",
592    "      circle",
593    "      fill circle",
594    "      ellipse",
595    "      fill ellipse",
596    "      polygon",
597    "      fill polygon",
598    "    Color",
599    "      black",
600    "      blue",
601    "      cyan",
602    "      green",
603    "      gray",
604    "      red",
605    "      magenta",
606    "      yellow",
607    "      white",
608    "      transparent",
609    "      Browser...",
610    "    Stipple",
611    "      Brick",
612    "      Diagonal",
613    "      Scales",
614    "      Vertical",
615    "      Wavy",
616    "      Translucent",
617    "      Opaque",
618    "      Open...",
619    "    Width",
620    "      1",
621    "      2",
622    "      4",
623    "      8",
624    "      16",
625    "      Dialog...",
626    "    Undo",
627    "    Help",
628    "    Dismiss",
629    "",
630    "Choose a drawing primitive from the Element sub-menu.",
631    "",
632    "Choose a color from the Color sub-menu.  Additional",
633    "colors can be specified with the color browser.",
634    "",
635    "If you choose the color browser and press Grab, you can",
636    "select the color by moving the pointer to the desired",
637    "color on the screen and press any button.  The transparent",
638    "color updates the image matte channel and is useful for",
639    "image compositing.",
640    "",
641    "Choose a stipple, if appropriate, from the Stipple sub-menu.",
642    "Additional stipples can be specified with the file browser.",
643    "Stipples obtained from the file browser must be on disk in the",
644    "X11 bitmap format.",
645    "",
646    "Choose a width, if appropriate, from the Width sub-menu.  To",
647    "choose a specific width select the Dialog widget.",
648    "",
649    "Choose a point in the Image window and press button 1 and",
650    "hold.  Next, move the pointer to another location in the",
651    "image.  As you move, a line connects the initial location and",
652    "the pointer.  When you release the button, the image is",
653    "updated with the primitive you just drew.  For polygons, the",
654    "image is updated when you press and release the button without",
655    "moving the pointer.",
656    "",
657    "To cancel image drawing, move the pointer back to the",
658    "starting point of the line and release the button.",
659    (char *) NULL,
660  },
661  *DisplayHelp[] =
662  {
663    "BUTTONS",
664    "  The effects of each button press is described below.  Three",
665    "  buttons are required.  If you have a two button mouse,",
666    "  button 1 and 3 are returned.  Press ALT and button 3 to",
667    "  simulate button 2.",
668    "",
669    "  1    Press this button to map or unmap the Command widget.",
670    "",
671    "  2    Press and drag to define a region of the image to",
672    "       magnify.",
673    "",
674    "  3    Press and drag to choose from a select set of commands.",
675    "       This button behaves differently if the image being",
676    "       displayed is a visual image directory.  Here, choose a",
677    "       particular tile of the directory and press this button and",
678    "       drag to select a command from a pop-up menu.  Choose from",
679    "       these menu items:",
680    "",
681    "           Open",
682    "           Next",
683    "           Former",
684    "           Delete",
685    "           Update",
686    "",
687    "       If you choose Open, the image represented by the tile is",
688    "       displayed.  To return to the visual image directory, choose",
689    "       Next from the Command widget.  Next and Former moves to the",
690    "       next or former image respectively.  Choose Delete to delete",
691    "       a particular image tile.  Finally, choose Update to",
692    "       synchronize all the image tiles with their respective",
693    "       images.",
694    "",
695    "COMMAND WIDGET",
696    "  The Command widget lists a number of sub-menus and commands.",
697    "  They are",
698    "",
699    "      File",
700    "        Open...",
701    "        Next",
702    "        Former",
703    "        Select...",
704    "        Save...",
705    "        Print...",
706    "        Delete...",
707    "        New...",
708    "        Visual Directory...",
709    "        Quit",
710    "      Edit",
711    "        Undo",
712    "        Redo",
713    "        Cut",
714    "        Copy",
715    "        Paste",
716    "      View",
717    "        Half Size",
718    "        Original Size",
719    "        Double Size",
720    "        Resize...",
721    "        Apply",
722    "        Refresh",
723    "        Restore",
724    "      Transform",
725    "        Crop",
726    "        Chop",
727    "        Flop",
728    "        Flip",
729    "        Rotate Right",
730    "        Rotate Left",
731    "        Rotate...",
732    "        Shear...",
733    "        Roll...",
734    "        Trim Edges",
735    "      Enhance",
736    "        Brightness...",
737    "        Saturation...",
738    "        Hue...",
739    "        Gamma...",
740    "        Sharpen...",
741    "        Dull",
742    "        Contrast Stretch...",
743    "        Sigmoidal Contrast...",
744    "        Normalize",
745    "        Equalize",
746    "        Negate",
747    "        Grayscale",
748    "        Map...",
749    "        Quantize...",
750    "      Effects",
751    "        Despeckle",
752    "        Emboss",
753    "        Reduce Noise",
754    "        Add Noise",
755    "        Sharpen...",
756    "        Blur...",
757    "        Threshold...",
758    "        Edge Detect...",
759    "        Spread...",
760    "        Shade...",
761    "        Painting...",
762    "        Segment...",
763    "      F/X",
764    "        Solarize...",
765    "        Sepia Tone...",
766    "        Swirl...",
767    "        Implode...",
768    "        Vignette...",
769    "        Wave...",
770    "        Oil Painting...",
771    "        Charcoal Drawing...",
772    "      Image Edit",
773    "        Annotate...",
774    "        Draw...",
775    "        Color...",
776    "        Matte...",
777    "        Composite...",
778    "        Add Border...",
779    "        Add Frame...",
780    "        Comment...",
781    "        Launch...",
782    "        Region of Interest...",
783    "      Miscellany",
784    "        Image Info",
785    "        Zoom Image",
786    "        Show Preview...",
787    "        Show Histogram",
788    "        Show Matte",
789    "        Background...",
790    "        Slide Show",
791    "        Preferences...",
792    "      Help",
793    "        Overview",
794    "        Browse Documentation",
795    "        About Display",
796    "",
797    "  Menu items with a indented triangle have a sub-menu.  They",
798    "  are represented above as the indented items.  To access a",
799    "  sub-menu item, move the pointer to the appropriate menu and",
800    "  press a button and drag.  When you find the desired sub-menu",
801    "  item, release the button and the command is executed.  Move",
802    "  the pointer away from the sub-menu if you decide not to",
803    "  execute a particular command.",
804    "",
805    "KEYBOARD ACCELERATORS",
806    "  Accelerators are one or two key presses that effect a",
807    "  particular command.  The keyboard accelerators that",
808    "  display(1) understands is:",
809    "",
810    "  Ctl+O     Press to open an image from a file.",
811    "",
812    "  space     Press to display the next image.",
813    "",
814    "            If the image is a multi-paged document such as a Postscript",
815    "            document, you can skip ahead several pages by preceding",
816    "            this command with a number.  For example to display the",
817    "            third page beyond the current page, press 3<space>.",
818    "",
819    "  backspace Press to display the former image.",
820    "",
821    "            If the image is a multi-paged document such as a Postscript",
822    "            document, you can skip behind several pages by preceding",
823    "            this command with a number.  For example to display the",
824    "            third page preceding the current page, press 3<backspace>.",
825    "",
826    "  Ctl+S     Press to write the image to a file.",
827    "",
828    "  Ctl+P     Press to print the image to a Postscript printer.",
829    "",
830    "  Ctl+D     Press to delete an image file.",
831    "",
832    "  Ctl+N     Press to create a blank canvas.",
833    "",
834    "  Ctl+Q     Press to discard all images and exit program.",
835    "",
836    "  Ctl+Z     Press to undo last image transformation.",
837    "",
838    "  Ctl+R     Press to redo last image transformation.",
839    "",
840    "  Ctl+X     Press to cut a region of the image.",
841    "",
842    "  Ctl+C     Press to copy a region of the image.",
843    "",
844    "  Ctl+V     Press to paste a region to the image.",
845    "",
846    "  <         Press to half the image size.",
847    "",
848    "  -         Press to return to the original image size.",
849    "",
850    "  >         Press to double the image size.",
851    "",
852    "  %         Press to resize the image to a width and height you",
853    "            specify.",
854    "",
855    "Cmd-A       Press to make any image transformations permanent."
856    "",
857    "            By default, any image size transformations are applied",
858    "            to the original image to create the image displayed on",
859    "            the X server.  However, the transformations are not",
860    "            permanent (i.e. the original image does not change",
861    "            size only the X image does).  For example, if you",
862    "            press > the X image will appear to double in size,",
863    "            but the original image will in fact remain the same size.",
864    "            To force the original image to double in size, press >",
865    "            followed by Cmd-A.",
866    "",
867    "  @         Press to refresh the image window.",
868    "",
869    "  C         Press to cut out a rectangular region of the image.",
870    "",
871    "  [         Press to chop the image.",
872    "",
873    "  H         Press to flop image in the horizontal direction.",
874    "",
875    "  V         Press to flip image in the vertical direction.",
876    "",
877    "  /         Press to rotate the image 90 degrees clockwise.",
878    "",
879    " \\         Press to rotate the image 90 degrees counter-clockwise.",
880    "",
881    "  *         Press to rotate the image the number of degrees you",
882    "            specify.",
883    "",
884    "  S         Press to shear the image the number of degrees you",
885    "            specify.",
886    "",
887    "  R         Press to roll the image.",
888    "",
889    "  T         Press to trim the image edges.",
890    "",
891    "  Shft-H    Press to vary the image hue.",
892    "",
893    "  Shft-S    Press to vary the color saturation.",
894    "",
895    "  Shft-L    Press to vary the color brightness.",
896    "",
897    "  Shft-G    Press to gamma correct the image.",
898    "",
899    "  Shft-C    Press to sharpen the image contrast.",
900    "",
901    "  Shft-Z    Press to dull the image contrast.",
902    "",
903    "  =         Press to perform histogram equalization on the image.",
904    "",
905    "  Shft-N    Press to perform histogram normalization on the image.",
906    "",
907    "  Shft-~    Press to negate the colors of the image.",
908    "",
909    "  .         Press to convert the image colors to gray.",
910    "",
911    "  Shft-#    Press to set the maximum number of unique colors in the",
912    "            image.",
913    "",
914    "  F2        Press to reduce the speckles in an image.",
915    "",
916    "  F3        Press to eliminate peak noise from an image.",
917    "",
918    "  F4        Press to add noise to an image.",
919    "",
920    "  F5        Press to sharpen an image.",
921    "",
922    "  F6        Press to delete an image file.",
923    "",
924    "  F7        Press to threshold the image.",
925    "",
926    "  F8        Press to detect edges within an image.",
927    "",
928    "  F9        Press to emboss an image.",
929    "",
930    "  F10       Press to displace pixels by a random amount.",
931    "",
932    "  F11       Press to negate all pixels above the threshold level.",
933    "",
934    "  F12       Press to shade the image using a distant light source.",
935    "",
936    "  F13       Press to lighten or darken image edges to create a 3-D effect.",
937    "",
938    "  F14       Press to segment the image by color.",
939    "",
940    "  Meta-S    Press to swirl image pixels about the center.",
941    "",
942    "  Meta-I    Press to implode image pixels about the center.",
943    "",
944    "  Meta-W    Press to alter an image along a sine wave.",
945    "",
946    "  Meta-P    Press to simulate an oil painting.",
947    "",
948    "  Meta-C    Press to simulate a charcoal drawing.",
949    "",
950    "  Alt-A     Press to annotate the image with text.",
951    "",
952    "  Alt-D     Press to draw on an image.",
953    "",
954    "  Alt-P     Press to edit an image pixel color.",
955    "",
956    "  Alt-M     Press to edit the image matte information.",
957    "",
958    "  Alt-V     Press to composite the image with another.",
959    "",
960    "  Alt-B     Press to add a border to the image.",
961    "",
962    "  Alt-F     Press to add an ornamental border to the image.",
963    "",
964    "  Alt-Shft-!",
965    "            Press to add an image comment.",
966    "",
967    "  Ctl-A     Press to apply image processing techniques to a region",
968    "            of interest.",
969    "",
970    "  Shft-?    Press to display information about the image.",
971    "",
972    "  Shft-+    Press to map the zoom image window.",
973    "",
974    "  Shft-P    Press to preview an image enhancement, effect, or f/x.",
975    "",
976    "  F1        Press to display helpful information about display(1).",
977    "",
978    "  Find      Press to browse documentation about ImageMagick.",
979    "",
980    "  1-9       Press to change the level of magnification.",
981    "",
982    "  Use the arrow keys to move the image one pixel up, down,",
983    "  left, or right within the magnify window.  Be sure to first",
984    "  map the magnify window by pressing button 2.",
985    "",
986    "  Press ALT and one of the arrow keys to trim off one pixel",
987    "  from any side of the image.",
988    (char *) NULL,
989  },
990  *ImageMatteEditHelp[] =
991  {
992    "Matte information within an image is useful for some",
993    "operations such as image compositing (See IMAGE",
994    "COMPOSITING).  This extra channel usually defines a mask",
995    "which represents a sort of a cookie-cutter for the image.",
996    "This the case when matte is opaque (full coverage) for",
997    "pixels inside the shape, zero outside, and between 0 and",
998    "QuantumRange on the boundary.",
999    "",
1000    "A small window appears showing the location of the cursor in",
1001    "the image window. You are now in matte edit mode.  To exit",
1002    "immediately, press Dismiss.  In matte edit mode, the Command",
1003    "widget has these options:",
1004    "",
1005    "    Method",
1006    "      point",
1007    "      replace",
1008    "      floodfill",
1009    "      filltoborder",
1010    "      reset",
1011    "    Border Color",
1012    "      black",
1013    "      blue",
1014    "      cyan",
1015    "      green",
1016    "      gray",
1017    "      red",
1018    "      magenta",
1019    "      yellow",
1020    "      white",
1021    "      Browser...",
1022    "    Fuzz",
1023    "      0%",
1024    "      2%",
1025    "      5%",
1026    "      10%",
1027    "      15%",
1028    "      Dialog...",
1029    "    Matte",
1030    "      Opaque",
1031    "      Transparent",
1032    "      Dialog...",
1033    "    Undo",
1034    "    Help",
1035    "    Dismiss",
1036    "",
1037    "Choose a matte editing method from the Method sub-menu of",
1038    "the Command widget.  The point method changes the matte value",
1039    "of any pixel selected with the pointer until the button is",
1040    "is released.  The replace method changes the matte value of",
1041    "any pixel that matches the color of the pixel you select with",
1042    "a button press.  Floodfill changes the matte value of any pixel",
1043    "that matches the color of the pixel you select with a button",
1044    "press and is a neighbor.  Whereas filltoborder changes the matte",
1045    "value any neighbor pixel that is not the border color.  Finally",
1046    "reset changes the entire image to the designated matte value.",
1047    "",
1048    "Choose Matte Value and pick Opaque or Transarent.  For other values",
1049    "select the Dialog entry.  Here a dialog appears requesting a matte",
1050    "value.  The value you select is assigned as the opacity value of the",
1051    "selected pixel or pixels.",
1052    "",
1053    "Now, press any button to select a pixel within the image",
1054    "window to change its matte value.",
1055    "",
1056    "If the Magnify widget is mapped, it can be helpful in positioning",
1057    "your pointer within the image (refer to button 2).",
1058    "",
1059    "Matte information is only valid in a DirectClass image.",
1060    "Therefore, any PseudoClass image is promoted to DirectClass",
1061    "(see miff(5)).  Note that matte information for PseudoClass",
1062    "is not retained for colormapped X server visuals (e.g.",
1063    "StaticColor, StaticColor, GrayScale, PseudoColor) unless you",
1064    "immediately save your image to a file (refer to Write).",
1065    "Correct matte editing behavior may require a TrueColor or",
1066    "DirectColor visual or a Standard Colormap.",
1067    (char *) NULL,
1068  },
1069  *ImagePanHelp[] =
1070  {
1071    "When an image exceeds the width or height of the X server",
1072    "screen, display maps a small panning icon.  The rectangle",
1073    "within the panning icon shows the area that is currently",
1074    "displayed in the image window.  To pan about the image,",
1075    "press any button and drag the pointer within the panning",
1076    "icon.  The pan rectangle moves with the pointer and the",
1077    "image window is updated to reflect the location of the",
1078    "rectangle within the panning icon.  When you have selected",
1079    "the area of the image you wish to view, release the button.",
1080    "",
1081    "Use the arrow keys to pan the image one pixel up, down,",
1082    "left, or right within the image window.",
1083    "",
1084    "The panning icon is withdrawn if the image becomes smaller",
1085    "than the dimensions of the X server screen.",
1086    (char *) NULL,
1087  },
1088  *ImagePasteHelp[] =
1089  {
1090    "A small window appears showing the location of the cursor in",
1091    "the image window. You are now in paste mode.  To exit",
1092    "immediately, press Dismiss.  In paste mode, the Command",
1093    "widget has these options:",
1094    "",
1095    "    Operators",
1096    "      over",
1097    "      in",
1098    "      out",
1099    "      atop",
1100    "      xor",
1101    "      plus",
1102    "      minus",
1103    "      add",
1104    "      subtract",
1105    "      difference",
1106    "      replace",
1107    "    Help",
1108    "    Dismiss",
1109    "",
1110    "Choose a composite operation from the Operators sub-menu of",
1111    "the Command widget.  How each operator behaves is described",
1112    "below.  Image window is the image currently displayed on",
1113    "your X server and image is the image obtained with the File",
1114    "Browser widget.",
1115    "",
1116    "Over     The result is the union of the two image shapes,",
1117    "         with image obscuring image window in the region of",
1118    "         overlap.",
1119    "",
1120    "In       The result is simply image cut by the shape of",
1121    "         image window.  None of the image data of image",
1122    "         window is in the result.",
1123    "",
1124    "Out      The resulting image is image with the shape of",
1125    "         image window cut out.",
1126    "",
1127    "Atop     The result is the same shape as image image window,",
1128    "         with image obscuring image window where the image",
1129    "         shapes overlap.  Note this differs from over",
1130    "         because the portion of image outside image window's",
1131    "         shape does not appear in the result.",
1132    "",
1133    "Xor      The result is the image data from both image and",
1134    "         image window that is outside the overlap region.",
1135    "         The overlap region is blank.",
1136    "",
1137    "Plus     The result is just the sum of the image data.",
1138    "         Output values are cropped to QuantumRange (no overflow).",
1139    "         This operation is independent of the matte",
1140    "         channels.",
1141    "",
1142    "Minus    The result of image - image window, with underflow",
1143    "         cropped to zero.",
1144    "",
1145    "Add      The result of image + image window, with overflow",
1146    "         wrapping around (mod 256).",
1147    "",
1148    "Subtract The result of image - image window, with underflow",
1149    "         wrapping around (mod 256).  The add and subtract",
1150    "         operators can be used to perform reversible",
1151    "         transformations.",
1152    "",
1153    "Difference",
1154    "         The result of abs(image - image window).  This",
1155    "         useful for comparing two very similar images.",
1156    "",
1157    "Copy     The resulting image is image window replaced with",
1158    "         image.  Here the matte information is ignored.",
1159    "",
1160    "CopyRed  The red layer of the image window is replace with",
1161    "         the red layer of the image.  The other layers are",
1162    "         untouched.",
1163    "",
1164    "CopyGreen",
1165    "         The green layer of the image window is replace with",
1166    "         the green layer of the image.  The other layers are",
1167    "         untouched.",
1168    "",
1169    "CopyBlue The blue layer of the image window is replace with",
1170    "         the blue layer of the image.  The other layers are",
1171    "         untouched.",
1172    "",
1173    "CopyOpacity",
1174    "         The matte layer of the image window is replace with",
1175    "         the matte layer of the image.  The other layers are",
1176    "         untouched.",
1177    "",
1178    "The image compositor requires a matte, or alpha channel in",
1179    "the image for some operations.  This extra channel usually",
1180    "defines a mask which represents a sort of a cookie-cutter",
1181    "for the image.  This the case when matte is opaque (full",
1182    "coverage) for pixels inside the shape, zero outside, and",
1183    "between 0 and QuantumRange on the boundary.  If image does not",
1184    "have a matte channel, it is initialized with 0 for any pixel",
1185    "matching in color to pixel location (0,0), otherwise QuantumRange.",
1186    "",
1187    "Note that matte information for image window is not retained",
1188    "for colormapped X server visuals (e.g. StaticColor,",
1189    "StaticColor, GrayScale, PseudoColor).  Correct compositing",
1190    "behavior may require a TrueColor or DirectColor visual or a",
1191    "Standard Colormap.",
1192    "",
1193    "Choosing a composite operator is optional.  The default",
1194    "operator is replace.  However, you must choose a location to",
1195    "paste your image and press button 1.  Press and hold the",
1196    "button before releasing and an outline of the image will",
1197    "appear to help you identify your location.",
1198    "",
1199    "The actual colors of the pasted image is saved.  However,",
1200    "the color that appears in image window may be different.",
1201    "For example, on a monochrome screen image window will appear",
1202    "black or white even though your pasted image may have",
1203    "many colors.  If the image is saved to a file it is written",
1204    "with the correct colors.  To assure the correct colors are",
1205    "saved in the final image, any PseudoClass image is promoted",
1206    "to DirectClass (see miff(5)).  To force a PseudoClass image",
1207    "to remain PseudoClass, use -colors.",
1208    (char *) NULL,
1209  },
1210  *ImageROIHelp[] =
1211  {
1212    "In region of interest mode, the Command widget has these",
1213    "options:",
1214    "",
1215    "    Help",
1216    "    Dismiss",
1217    "",
1218    "To define a region of interest, press button 1 and drag.",
1219    "The region of interest is defined by a highlighted rectangle",
1220    "that expands or contracts as it follows the pointer.  Once",
1221    "you are satisfied with the region of interest, release the",
1222    "button.  You are now in apply mode.  In apply mode the",
1223    "Command widget has these options:",
1224    "",
1225    "      File",
1226    "        Save...",
1227    "        Print...",
1228    "      Edit",
1229    "        Undo",
1230    "        Redo",
1231    "      Transform",
1232    "        Flop",
1233    "        Flip",
1234    "        Rotate Right",
1235    "        Rotate Left",
1236    "      Enhance",
1237    "        Hue...",
1238    "        Saturation...",
1239    "        Brightness...",
1240    "        Gamma...",
1241    "        Spiff",
1242    "        Dull",
1243    "        Contrast Stretch",
1244    "        Sigmoidal Contrast...",
1245    "        Normalize",
1246    "        Equalize",
1247    "        Negate",
1248    "        Grayscale",
1249    "        Map...",
1250    "        Quantize...",
1251    "      Effects",
1252    "        Despeckle",
1253    "        Emboss",
1254    "        Reduce Noise",
1255    "        Sharpen...",
1256    "        Blur...",
1257    "        Threshold...",
1258    "        Edge Detect...",
1259    "        Spread...",
1260    "        Shade...",
1261    "        Raise...",
1262    "        Segment...",
1263    "      F/X",
1264    "        Solarize...",
1265    "        Sepia Tone...",
1266    "        Swirl...",
1267    "        Implode...",
1268    "        Vignette...",
1269    "        Wave...",
1270    "        Oil Painting...",
1271    "        Charcoal Drawing...",
1272    "      Miscellany",
1273    "        Image Info",
1274    "        Zoom Image",
1275    "        Show Preview...",
1276    "        Show Histogram",
1277    "        Show Matte",
1278    "      Help",
1279    "      Dismiss",
1280    "",
1281    "You can make adjustments to the region of interest by moving",
1282    "the pointer to one of the rectangle corners, pressing a",
1283    "button, and dragging.  Finally, choose an image processing",
1284    "technique from the Command widget.  You can choose more than",
1285    "one image processing technique to apply to an area.",
1286    "Alternatively, you can move the region of interest before",
1287    "applying another image processing technique.  To exit, press",
1288    "Dismiss.",
1289    (char *) NULL,
1290  },
1291  *ImageRotateHelp[] =
1292  {
1293    "In rotate mode, the Command widget has these options:",
1294    "",
1295    "    Pixel Color",
1296    "      black",
1297    "      blue",
1298    "      cyan",
1299    "      green",
1300    "      gray",
1301    "      red",
1302    "      magenta",
1303    "      yellow",
1304    "      white",
1305    "      Browser...",
1306    "    Direction",
1307    "      horizontal",
1308    "      vertical",
1309    "    Help",
1310    "    Dismiss",
1311    "",
1312    "Choose a background color from the Pixel Color sub-menu.",
1313    "Additional background colors can be specified with the color",
1314    "browser.  You can change the menu colors by setting the X",
1315    "resources pen1 through pen9.",
1316    "",
1317    "If you choose the color browser and press Grab, you can",
1318    "select the background color by moving the pointer to the",
1319    "desired color on the screen and press any button.",
1320    "",
1321    "Choose a point in the image window and press this button and",
1322    "hold.  Next, move the pointer to another location in the",
1323    "image.  As you move a line connects the initial location and",
1324    "the pointer.  When you release the button, the degree of",
1325    "image rotation is determined by the slope of the line you",
1326    "just drew.  The slope is relative to the direction you",
1327    "choose from the Direction sub-menu of the Command widget.",
1328    "",
1329    "To cancel the image rotation, move the pointer back to the",
1330    "starting point of the line and release the button.",
1331    (char *) NULL,
1332  };
1333
1334/*
1335  Enumeration declarations.
1336*/
1337typedef enum
1338{
1339  CopyMode,
1340  CropMode,
1341  CutMode
1342} ClipboardMode;
1343
1344typedef enum
1345{
1346  OpenCommand,
1347  NextCommand,
1348  FormerCommand,
1349  SelectCommand,
1350  SaveCommand,
1351  PrintCommand,
1352  DeleteCommand,
1353  NewCommand,
1354  VisualDirectoryCommand,
1355  QuitCommand,
1356  UndoCommand,
1357  RedoCommand,
1358  CutCommand,
1359  CopyCommand,
1360  PasteCommand,
1361  HalfSizeCommand,
1362  OriginalSizeCommand,
1363  DoubleSizeCommand,
1364  ResizeCommand,
1365  ApplyCommand,
1366  RefreshCommand,
1367  RestoreCommand,
1368  CropCommand,
1369  ChopCommand,
1370  FlopCommand,
1371  FlipCommand,
1372  RotateRightCommand,
1373  RotateLeftCommand,
1374  RotateCommand,
1375  ShearCommand,
1376  RollCommand,
1377  TrimCommand,
1378  HueCommand,
1379  SaturationCommand,
1380  BrightnessCommand,
1381  GammaCommand,
1382  SpiffCommand,
1383  DullCommand,
1384  ContrastStretchCommand,
1385  SigmoidalContrastCommand,
1386  NormalizeCommand,
1387  EqualizeCommand,
1388  NegateCommand,
1389  GrayscaleCommand,
1390  MapCommand,
1391  QuantizeCommand,
1392  DespeckleCommand,
1393  EmbossCommand,
1394  ReduceNoiseCommand,
1395  AddNoiseCommand,
1396  SharpenCommand,
1397  BlurCommand,
1398  ThresholdCommand,
1399  EdgeDetectCommand,
1400  SpreadCommand,
1401  ShadeCommand,
1402  RaiseCommand,
1403  SegmentCommand,
1404  SolarizeCommand,
1405  SepiaToneCommand,
1406  SwirlCommand,
1407  ImplodeCommand,
1408  VignetteCommand,
1409  WaveCommand,
1410  OilPaintCommand,
1411  CharcoalDrawCommand,
1412  AnnotateCommand,
1413  DrawCommand,
1414  ColorCommand,
1415  MatteCommand,
1416  CompositeCommand,
1417  AddBorderCommand,
1418  AddFrameCommand,
1419  CommentCommand,
1420  LaunchCommand,
1421  RegionofInterestCommand,
1422  ROIHelpCommand,
1423  ROIDismissCommand,
1424  InfoCommand,
1425  ZoomCommand,
1426  ShowPreviewCommand,
1427  ShowHistogramCommand,
1428  ShowMatteCommand,
1429  BackgroundCommand,
1430  SlideShowCommand,
1431  PreferencesCommand,
1432  HelpCommand,
1433  BrowseDocumentationCommand,
1434  VersionCommand,
1435  SaveToUndoBufferCommand,
1436  FreeBuffersCommand,
1437  NullCommand
1438} CommandType;
1439
1440typedef enum
1441{
1442  AnnotateNameCommand,
1443  AnnotateFontColorCommand,
1444  AnnotateBackgroundColorCommand,
1445  AnnotateRotateCommand,
1446  AnnotateHelpCommand,
1447  AnnotateDismissCommand,
1448  TextHelpCommand,
1449  TextApplyCommand,
1450  ChopDirectionCommand,
1451  ChopHelpCommand,
1452  ChopDismissCommand,
1453  HorizontalChopCommand,
1454  VerticalChopCommand,
1455  ColorEditMethodCommand,
1456  ColorEditColorCommand,
1457  ColorEditBorderCommand,
1458  ColorEditFuzzCommand,
1459  ColorEditUndoCommand,
1460  ColorEditHelpCommand,
1461  ColorEditDismissCommand,
1462  CompositeOperatorsCommand,
1463  CompositeDissolveCommand,
1464  CompositeDisplaceCommand,
1465  CompositeHelpCommand,
1466  CompositeDismissCommand,
1467  CropHelpCommand,
1468  CropDismissCommand,
1469  RectifyCopyCommand,
1470  RectifyHelpCommand,
1471  RectifyDismissCommand,
1472  DrawElementCommand,
1473  DrawColorCommand,
1474  DrawStippleCommand,
1475  DrawWidthCommand,
1476  DrawUndoCommand,
1477  DrawHelpCommand,
1478  DrawDismissCommand,
1479  MatteEditMethod,
1480  MatteEditBorderCommand,
1481  MatteEditFuzzCommand,
1482  MatteEditValueCommand,
1483  MatteEditUndoCommand,
1484  MatteEditHelpCommand,
1485  MatteEditDismissCommand,
1486  PasteOperatorsCommand,
1487  PasteHelpCommand,
1488  PasteDismissCommand,
1489  RotateColorCommand,
1490  RotateDirectionCommand,
1491  RotateCropCommand,
1492  RotateSharpenCommand,
1493  RotateHelpCommand,
1494  RotateDismissCommand,
1495  HorizontalRotateCommand,
1496  VerticalRotateCommand,
1497  TileLoadCommand,
1498  TileNextCommand,
1499  TileFormerCommand,
1500  TileDeleteCommand,
1501  TileUpdateCommand
1502} ModeType;
1503
1504/*
1505  Stipples.
1506*/
1507#define BricksWidth  20
1508#define BricksHeight  20
1509#define DiagonalWidth  16
1510#define DiagonalHeight  16
1511#define HighlightWidth  8
1512#define HighlightHeight  8
1513#define OpaqueWidth  8
1514#define OpaqueHeight  8
1515#define ScalesWidth  16
1516#define ScalesHeight  16
1517#define ShadowWidth  8
1518#define ShadowHeight  8
1519#define VerticalWidth  16
1520#define VerticalHeight  16
1521#define WavyWidth  16
1522#define WavyHeight  16
1523
1524/*
1525  Constant declaration.
1526*/
1527static const int
1528  RoiDelta = 8;
1529
1530static const unsigned char
1531  BricksBitmap[] =
1532  {
1533    0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1534    0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1535    0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1536    0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1537    0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1538  },
1539  DiagonalBitmap[] =
1540  {
1541    0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1542    0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1543    0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1544  },
1545  ScalesBitmap[] =
1546  {
1547    0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1548    0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1549    0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1550  },
1551  VerticalBitmap[] =
1552  {
1553    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1554    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1555    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1556  },
1557  WavyBitmap[] =
1558  {
1559    0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1560    0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1561    0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1562  };
1563
1564/*
1565  Function prototypes.
1566*/
1567static CommandType
1568  XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1569    const MagickStatusType,KeySym,Image **,ExceptionInfo *);
1570
1571static Image
1572  *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
1573    Image **,ExceptionInfo *),
1574  *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1575  *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *,
1576    ExceptionInfo *),
1577  *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *,
1578    ExceptionInfo *);
1579
1580static MagickBooleanType
1581  XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *,
1582    ExceptionInfo *),
1583  XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **,
1584    ExceptionInfo *),
1585  XChopImage(Display *,XResourceInfo *,XWindows *,Image **,
1586    ExceptionInfo *),
1587  XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode,
1588    ExceptionInfo *),
1589  XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1590    ExceptionInfo *),
1591  XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *,
1592    ExceptionInfo *),
1593  XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1594  XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1595    ExceptionInfo *),
1596  XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1597    ExceptionInfo *),
1598  XPasteImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1599  XPrintImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1600  XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **,
1601    ExceptionInfo *),
1602  XROIImage(Display *,XResourceInfo *,XWindows *,Image **,ExceptionInfo *),
1603  XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1604  XTrimImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
1605
1606static void
1607  XDrawPanRectangle(Display *,XWindows *),
1608  XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **,
1609    ExceptionInfo *),
1610  XMagnifyImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1611  XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1612  XPanImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1613  XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1614    const KeySym,ExceptionInfo *),
1615  XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1616  XScreenEvent(Display *,XWindows *,XEvent *,ExceptionInfo *),
1617  XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1618
1619/*
1620%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1621%                                                                             %
1622%                                                                             %
1623%                                                                             %
1624%   D i s p l a y I m a g e s                                                 %
1625%                                                                             %
1626%                                                                             %
1627%                                                                             %
1628%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1629%
1630%  DisplayImages() displays an image sequence to any X window screen.  It
1631%  returns a value other than 0 if successful.  Check the exception member
1632%  of image to determine the reason for any failure.
1633%
1634%  The format of the DisplayImages method is:
1635%
1636%      MagickBooleanType DisplayImages(const ImageInfo *image_info,
1637%        Image *images,ExceptionInfo *exception)
1638%
1639%  A description of each parameter follows:
1640%
1641%    o image_info: the image info.
1642%
1643%    o image: the image.
1644%
1645%    o exception: return any errors or warnings in this structure.
1646%
1647*/
1648MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1649  Image *images,ExceptionInfo *exception)
1650{
1651  char
1652    *argv[1];
1653
1654  Display
1655    *display;
1656
1657  Image
1658    *image;
1659
1660  register ssize_t
1661    i;
1662
1663  size_t
1664    state;
1665
1666  XrmDatabase
1667    resource_database;
1668
1669  XResourceInfo
1670    resource_info;
1671
1672  assert(image_info != (const ImageInfo *) NULL);
1673  assert(image_info->signature == MagickSignature);
1674  assert(images != (Image *) NULL);
1675  assert(images->signature == MagickSignature);
1676  if (images->debug != MagickFalse)
1677    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1678  display=XOpenDisplay(image_info->server_name);
1679  if (display == (Display *) NULL)
1680    {
1681      (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1682        "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1683      return(MagickFalse);
1684    }
1685  if (exception->severity != UndefinedException)
1686    CatchException(exception);
1687  (void) XSetErrorHandler(XError);
1688  resource_database=XGetResourceDatabase(display,GetClientName());
1689  (void) ResetMagickMemory(&resource_info,0,sizeof(resource_info));
1690  XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1691  if (image_info->page != (char *) NULL)
1692    resource_info.image_geometry=AcquireString(image_info->page);
1693  resource_info.immutable=MagickTrue;
1694  argv[0]=AcquireString(GetClientName());
1695  state=DefaultState;
1696  for (i=0; (state & ExitState) == 0; i++)
1697  {
1698    if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1699      break;
1700    image=GetImageFromList(images,i % GetImageListLength(images));
1701    (void) XDisplayImage(display,&resource_info,argv,1,&image,&state,exception);
1702  }
1703  SetErrorHandler((ErrorHandler) NULL);
1704  SetWarningHandler((WarningHandler) NULL);
1705  argv[0]=DestroyString(argv[0]);
1706  (void) XCloseDisplay(display);
1707  XDestroyResourceInfo(&resource_info);
1708  if (exception->severity != UndefinedException)
1709    return(MagickFalse);
1710  return(MagickTrue);
1711}
1712
1713/*
1714%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1715%                                                                             %
1716%                                                                             %
1717%                                                                             %
1718%   R e m o t e D i s p l a y C o m m a n d                                   %
1719%                                                                             %
1720%                                                                             %
1721%                                                                             %
1722%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1723%
1724%  RemoteDisplayCommand() encourages a remote display program to display the
1725%  specified image filename.
1726%
1727%  The format of the RemoteDisplayCommand method is:
1728%
1729%      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1730%        const char *window,const char *filename,ExceptionInfo *exception)
1731%
1732%  A description of each parameter follows:
1733%
1734%    o image_info: the image info.
1735%
1736%    o window: Specifies the name or id of an X window.
1737%
1738%    o filename: the name of the image filename to display.
1739%
1740%    o exception: return any errors or warnings in this structure.
1741%
1742*/
1743MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1744  const char *window,const char *filename,ExceptionInfo *exception)
1745{
1746  Display
1747    *display;
1748
1749  MagickStatusType
1750    status;
1751
1752  assert(image_info != (const ImageInfo *) NULL);
1753  assert(image_info->signature == MagickSignature);
1754  assert(filename != (char *) NULL);
1755  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1756  display=XOpenDisplay(image_info->server_name);
1757  if (display == (Display *) NULL)
1758    {
1759      (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1760        "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1761      return(MagickFalse);
1762    }
1763  (void) XSetErrorHandler(XError);
1764  status=XRemoteCommand(display,window,filename);
1765  (void) XCloseDisplay(display);
1766  return(status != 0 ? MagickTrue : MagickFalse);
1767}
1768
1769/*
1770%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1771%                                                                             %
1772%                                                                             %
1773%                                                                             %
1774+   X A n n o t a t e E d i t I m a g e                                       %
1775%                                                                             %
1776%                                                                             %
1777%                                                                             %
1778%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1779%
1780%  XAnnotateEditImage() annotates the image with text.
1781%
1782%  The format of the XAnnotateEditImage method is:
1783%
1784%      MagickBooleanType XAnnotateEditImage(Display *display,
1785%        XResourceInfo *resource_info,XWindows *windows,Image *image,
1786%        ExceptionInfo *exception)
1787%
1788%  A description of each parameter follows:
1789%
1790%    o display: Specifies a connection to an X server;  returned from
1791%      XOpenDisplay.
1792%
1793%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1794%
1795%    o windows: Specifies a pointer to a XWindows structure.
1796%
1797%    o image: the image; returned from ReadImage.
1798%
1799*/
1800
1801static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
1802{
1803  if (x > y)
1804    return(x);
1805  return(y);
1806}
1807
1808static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
1809{
1810  if (x < y)
1811    return(x);
1812  return(y);
1813}
1814
1815static MagickBooleanType XAnnotateEditImage(Display *display,
1816  XResourceInfo *resource_info,XWindows *windows,Image *image,
1817  ExceptionInfo *exception)
1818{
1819  static const char
1820    *AnnotateMenu[] =
1821    {
1822      "Font Name",
1823      "Font Color",
1824      "Box Color",
1825      "Rotate Text",
1826      "Help",
1827      "Dismiss",
1828      (char *) NULL
1829    },
1830    *TextMenu[] =
1831    {
1832      "Help",
1833      "Apply",
1834      (char *) NULL
1835    };
1836
1837  static const ModeType
1838    AnnotateCommands[] =
1839    {
1840      AnnotateNameCommand,
1841      AnnotateFontColorCommand,
1842      AnnotateBackgroundColorCommand,
1843      AnnotateRotateCommand,
1844      AnnotateHelpCommand,
1845      AnnotateDismissCommand
1846    },
1847    TextCommands[] =
1848    {
1849      TextHelpCommand,
1850      TextApplyCommand
1851    };
1852
1853  static MagickBooleanType
1854    transparent_box = MagickTrue,
1855    transparent_pen = MagickFalse;
1856
1857  static MagickRealType
1858    degrees = 0.0;
1859
1860  static unsigned int
1861    box_id = MaxNumberPens-2,
1862    font_id = 0,
1863    pen_id = 0;
1864
1865  char
1866    command[MaxTextExtent],
1867    text[MaxTextExtent];
1868
1869  const char
1870    *ColorMenu[MaxNumberPens+1];
1871
1872  Cursor
1873    cursor;
1874
1875  GC
1876    annotate_context;
1877
1878  int
1879    id,
1880    pen_number,
1881    status,
1882    x,
1883    y;
1884
1885  KeySym
1886    key_symbol;
1887
1888  register char
1889    *p;
1890
1891  register ssize_t
1892    i;
1893
1894  unsigned int
1895    height,
1896    width;
1897
1898  size_t
1899    state;
1900
1901  XAnnotateInfo
1902    *annotate_info,
1903    *previous_info;
1904
1905  XColor
1906    color;
1907
1908  XFontStruct
1909    *font_info;
1910
1911  XEvent
1912    event,
1913    text_event;
1914
1915  /*
1916    Map Command widget.
1917  */
1918  (void) CloneString(&windows->command.name,"Annotate");
1919  windows->command.data=4;
1920  (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1921  (void) XMapRaised(display,windows->command.id);
1922  XClientMessage(display,windows->image.id,windows->im_protocols,
1923    windows->im_update_widget,CurrentTime);
1924  /*
1925    Track pointer until button 1 is pressed.
1926  */
1927  XQueryPosition(display,windows->image.id,&x,&y);
1928  (void) XSelectInput(display,windows->image.id,
1929    windows->image.attributes.event_mask | PointerMotionMask);
1930  cursor=XCreateFontCursor(display,XC_left_side);
1931  (void) XCheckDefineCursor(display,windows->image.id,cursor);
1932  state=DefaultState;
1933  do
1934  {
1935    if (windows->info.mapped != MagickFalse)
1936      {
1937        /*
1938          Display pointer position.
1939        */
1940        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
1941          x+windows->image.x,y+windows->image.y);
1942        XInfoWidget(display,windows,text);
1943      }
1944    /*
1945      Wait for next event.
1946    */
1947    XScreenEvent(display,windows,&event,exception);
1948    if (event.xany.window == windows->command.id)
1949      {
1950        /*
1951          Select a command from the Command widget.
1952        */
1953        id=XCommandWidget(display,windows,AnnotateMenu,&event);
1954        (void) XCheckDefineCursor(display,windows->image.id,cursor);
1955        if (id < 0)
1956          continue;
1957        switch (AnnotateCommands[id])
1958        {
1959          case AnnotateNameCommand:
1960          {
1961            const char
1962              *FontMenu[MaxNumberFonts];
1963
1964            int
1965              font_number;
1966
1967            /*
1968              Initialize menu selections.
1969            */
1970            for (i=0; i < MaxNumberFonts; i++)
1971              FontMenu[i]=resource_info->font_name[i];
1972            FontMenu[MaxNumberFonts-2]="Browser...";
1973            FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1974            /*
1975              Select a font name from the pop-up menu.
1976            */
1977            font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1978              (const char **) FontMenu,command);
1979            if (font_number < 0)
1980              break;
1981            if (font_number == (MaxNumberFonts-2))
1982              {
1983                static char
1984                  font_name[MaxTextExtent] = "fixed";
1985
1986                /*
1987                  Select a font name from a browser.
1988                */
1989                resource_info->font_name[font_number]=font_name;
1990                XFontBrowserWidget(display,windows,"Select",font_name);
1991                if (*font_name == '\0')
1992                  break;
1993              }
1994            /*
1995              Initialize font info.
1996            */
1997            font_info=XLoadQueryFont(display,resource_info->font_name[
1998              font_number]);
1999            if (font_info == (XFontStruct *) NULL)
2000              {
2001                XNoticeWidget(display,windows,"Unable to load font:",
2002                  resource_info->font_name[font_number]);
2003                break;
2004              }
2005            font_id=(unsigned int) font_number;
2006            (void) XFreeFont(display,font_info);
2007            break;
2008          }
2009          case AnnotateFontColorCommand:
2010          {
2011            /*
2012              Initialize menu selections.
2013            */
2014            for (i=0; i < (int) (MaxNumberPens-2); i++)
2015              ColorMenu[i]=resource_info->pen_colors[i];
2016            ColorMenu[MaxNumberPens-2]="transparent";
2017            ColorMenu[MaxNumberPens-1]="Browser...";
2018            ColorMenu[MaxNumberPens]=(const char *) NULL;
2019            /*
2020              Select a pen color from the pop-up menu.
2021            */
2022            pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2023              (const char **) ColorMenu,command);
2024            if (pen_number < 0)
2025              break;
2026            transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
2027              MagickFalse;
2028            if (transparent_pen != MagickFalse)
2029              break;
2030            if (pen_number == (MaxNumberPens-1))
2031              {
2032                static char
2033                  color_name[MaxTextExtent] = "gray";
2034
2035                /*
2036                  Select a pen color from a dialog.
2037                */
2038                resource_info->pen_colors[pen_number]=color_name;
2039                XColorBrowserWidget(display,windows,"Select",color_name);
2040                if (*color_name == '\0')
2041                  break;
2042              }
2043            /*
2044              Set pen color.
2045            */
2046            (void) XParseColor(display,windows->map_info->colormap,
2047              resource_info->pen_colors[pen_number],&color);
2048            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2049              (unsigned int) MaxColors,&color);
2050            windows->pixel_info->pen_colors[pen_number]=color;
2051            pen_id=(unsigned int) pen_number;
2052            break;
2053          }
2054          case AnnotateBackgroundColorCommand:
2055          {
2056            /*
2057              Initialize menu selections.
2058            */
2059            for (i=0; i < (int) (MaxNumberPens-2); i++)
2060              ColorMenu[i]=resource_info->pen_colors[i];
2061            ColorMenu[MaxNumberPens-2]="transparent";
2062            ColorMenu[MaxNumberPens-1]="Browser...";
2063            ColorMenu[MaxNumberPens]=(const char *) NULL;
2064            /*
2065              Select a pen color from the pop-up menu.
2066            */
2067            pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2068              (const char **) ColorMenu,command);
2069            if (pen_number < 0)
2070              break;
2071            transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2072              MagickFalse;
2073            if (transparent_box != MagickFalse)
2074              break;
2075            if (pen_number == (MaxNumberPens-1))
2076              {
2077                static char
2078                  color_name[MaxTextExtent] = "gray";
2079
2080                /*
2081                  Select a pen color from a dialog.
2082                */
2083                resource_info->pen_colors[pen_number]=color_name;
2084                XColorBrowserWidget(display,windows,"Select",color_name);
2085                if (*color_name == '\0')
2086                  break;
2087              }
2088            /*
2089              Set pen color.
2090            */
2091            (void) XParseColor(display,windows->map_info->colormap,
2092              resource_info->pen_colors[pen_number],&color);
2093            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2094              (unsigned int) MaxColors,&color);
2095            windows->pixel_info->pen_colors[pen_number]=color;
2096            box_id=(unsigned int) pen_number;
2097            break;
2098          }
2099          case AnnotateRotateCommand:
2100          {
2101            int
2102              entry;
2103
2104            static char
2105              angle[MaxTextExtent] = "30.0";
2106
2107            static const char
2108              *RotateMenu[] =
2109              {
2110                "-90",
2111                "-45",
2112                "-30",
2113                "0",
2114                "30",
2115                "45",
2116                "90",
2117                "180",
2118                "Dialog...",
2119                (char *) NULL,
2120              };
2121
2122            /*
2123              Select a command from the pop-up menu.
2124            */
2125            entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2126              command);
2127            if (entry < 0)
2128              break;
2129            if (entry != 8)
2130              {
2131                degrees=StringToDouble(RotateMenu[entry],(char **) NULL);
2132                break;
2133              }
2134            (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2135              angle);
2136            if (*angle == '\0')
2137              break;
2138            degrees=StringToDouble(angle,(char **) NULL);
2139            break;
2140          }
2141          case AnnotateHelpCommand:
2142          {
2143            XTextViewWidget(display,resource_info,windows,MagickFalse,
2144              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2145            break;
2146          }
2147          case AnnotateDismissCommand:
2148          {
2149            /*
2150              Prematurely exit.
2151            */
2152            state|=EscapeState;
2153            state|=ExitState;
2154            break;
2155          }
2156          default:
2157            break;
2158        }
2159        continue;
2160      }
2161    switch (event.type)
2162    {
2163      case ButtonPress:
2164      {
2165        if (event.xbutton.button != Button1)
2166          break;
2167        if (event.xbutton.window != windows->image.id)
2168          break;
2169        /*
2170          Change to text entering mode.
2171        */
2172        x=event.xbutton.x;
2173        y=event.xbutton.y;
2174        state|=ExitState;
2175        break;
2176      }
2177      case ButtonRelease:
2178        break;
2179      case Expose:
2180        break;
2181      case KeyPress:
2182      {
2183        if (event.xkey.window != windows->image.id)
2184          break;
2185        /*
2186          Respond to a user key press.
2187        */
2188        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2189          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2190        switch ((int) key_symbol)
2191        {
2192          case XK_Escape:
2193          case XK_F20:
2194          {
2195            /*
2196              Prematurely exit.
2197            */
2198            state|=EscapeState;
2199            state|=ExitState;
2200            break;
2201          }
2202          case XK_F1:
2203          case XK_Help:
2204          {
2205            XTextViewWidget(display,resource_info,windows,MagickFalse,
2206              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2207            break;
2208          }
2209          default:
2210          {
2211            (void) XBell(display,0);
2212            break;
2213          }
2214        }
2215        break;
2216      }
2217      case MotionNotify:
2218      {
2219        /*
2220          Map and unmap Info widget as cursor crosses its boundaries.
2221        */
2222        x=event.xmotion.x;
2223        y=event.xmotion.y;
2224        if (windows->info.mapped != MagickFalse)
2225          {
2226            if ((x < (int) (windows->info.x+windows->info.width)) &&
2227                (y < (int) (windows->info.y+windows->info.height)))
2228              (void) XWithdrawWindow(display,windows->info.id,
2229                windows->info.screen);
2230          }
2231        else
2232          if ((x > (int) (windows->info.x+windows->info.width)) ||
2233              (y > (int) (windows->info.y+windows->info.height)))
2234            (void) XMapWindow(display,windows->info.id);
2235        break;
2236      }
2237      default:
2238        break;
2239    }
2240  } while ((state & ExitState) == 0);
2241  (void) XSelectInput(display,windows->image.id,
2242    windows->image.attributes.event_mask);
2243  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2244  if ((state & EscapeState) != 0)
2245    return(MagickTrue);
2246  /*
2247    Set font info and check boundary conditions.
2248  */
2249  font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2250  if (font_info == (XFontStruct *) NULL)
2251    {
2252      XNoticeWidget(display,windows,"Unable to load font:",
2253        resource_info->font_name[font_id]);
2254      font_info=windows->font_info;
2255    }
2256  if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2257    x=(int) windows->image.width-font_info->max_bounds.width;
2258  if (y < (int) (font_info->ascent+font_info->descent))
2259    y=(int) font_info->ascent+font_info->descent;
2260  if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2261      ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2262    return(MagickFalse);
2263  /*
2264    Initialize annotate structure.
2265  */
2266  annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2267  if (annotate_info == (XAnnotateInfo *) NULL)
2268    return(MagickFalse);
2269  XGetAnnotateInfo(annotate_info);
2270  annotate_info->x=x;
2271  annotate_info->y=y;
2272  if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
2273    annotate_info->stencil=OpaqueStencil;
2274  else
2275    if (transparent_box == MagickFalse)
2276      annotate_info->stencil=BackgroundStencil;
2277    else
2278      annotate_info->stencil=ForegroundStencil;
2279  annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2280  annotate_info->degrees=degrees;
2281  annotate_info->font_info=font_info;
2282  annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2283    windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
2284    sizeof(*annotate_info->text));
2285  if (annotate_info->text == (char *) NULL)
2286    return(MagickFalse);
2287  /*
2288    Create cursor and set graphic context.
2289  */
2290  cursor=XCreateFontCursor(display,XC_pencil);
2291  (void) XCheckDefineCursor(display,windows->image.id,cursor);
2292  annotate_context=windows->image.annotate_context;
2293  (void) XSetFont(display,annotate_context,font_info->fid);
2294  (void) XSetBackground(display,annotate_context,
2295    windows->pixel_info->pen_colors[box_id].pixel);
2296  (void) XSetForeground(display,annotate_context,
2297    windows->pixel_info->pen_colors[pen_id].pixel);
2298  /*
2299    Begin annotating the image with text.
2300  */
2301  (void) CloneString(&windows->command.name,"Text");
2302  windows->command.data=0;
2303  (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2304  state=DefaultState;
2305  (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2306  text_event.xexpose.width=(int) font_info->max_bounds.width;
2307  text_event.xexpose.height=font_info->max_bounds.ascent+
2308    font_info->max_bounds.descent;
2309  p=annotate_info->text;
2310  do
2311  {
2312    /*
2313      Display text cursor.
2314    */
2315    *p='\0';
2316    (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2317    /*
2318      Wait for next event.
2319    */
2320    XScreenEvent(display,windows,&event,exception);
2321    if (event.xany.window == windows->command.id)
2322      {
2323        /*
2324          Select a command from the Command widget.
2325        */
2326        (void) XSetBackground(display,annotate_context,
2327          windows->pixel_info->background_color.pixel);
2328        (void) XSetForeground(display,annotate_context,
2329          windows->pixel_info->foreground_color.pixel);
2330        id=XCommandWidget(display,windows,AnnotateMenu,&event);
2331        (void) XSetBackground(display,annotate_context,
2332          windows->pixel_info->pen_colors[box_id].pixel);
2333        (void) XSetForeground(display,annotate_context,
2334          windows->pixel_info->pen_colors[pen_id].pixel);
2335        if (id < 0)
2336          continue;
2337        switch (TextCommands[id])
2338        {
2339          case TextHelpCommand:
2340          {
2341            XTextViewWidget(display,resource_info,windows,MagickFalse,
2342              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2343            (void) XCheckDefineCursor(display,windows->image.id,cursor);
2344            break;
2345          }
2346          case TextApplyCommand:
2347          {
2348            /*
2349              Finished annotating.
2350            */
2351            annotate_info->width=(unsigned int) XTextWidth(font_info,
2352              annotate_info->text,(int) strlen(annotate_info->text));
2353            XRefreshWindow(display,&windows->image,&text_event);
2354            state|=ExitState;
2355            break;
2356          }
2357          default:
2358            break;
2359        }
2360        continue;
2361      }
2362    /*
2363      Erase text cursor.
2364    */
2365    text_event.xexpose.x=x;
2366    text_event.xexpose.y=y-font_info->max_bounds.ascent;
2367    (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2368      (unsigned int) text_event.xexpose.width,(unsigned int)
2369      text_event.xexpose.height,MagickFalse);
2370    XRefreshWindow(display,&windows->image,&text_event);
2371    switch (event.type)
2372    {
2373      case ButtonPress:
2374      {
2375        if (event.xbutton.window != windows->image.id)
2376          break;
2377        if (event.xbutton.button == Button2)
2378          {
2379            /*
2380              Request primary selection.
2381            */
2382            (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2383              windows->image.id,CurrentTime);
2384            break;
2385          }
2386        break;
2387      }
2388      case Expose:
2389      {
2390        if (event.xexpose.count == 0)
2391          {
2392            XAnnotateInfo
2393              *text_info;
2394
2395            /*
2396              Refresh Image window.
2397            */
2398            XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2399            text_info=annotate_info;
2400            while (text_info != (XAnnotateInfo *) NULL)
2401            {
2402              if (annotate_info->stencil == ForegroundStencil)
2403                (void) XDrawString(display,windows->image.id,annotate_context,
2404                  text_info->x,text_info->y,text_info->text,
2405                  (int) strlen(text_info->text));
2406              else
2407                (void) XDrawImageString(display,windows->image.id,
2408                  annotate_context,text_info->x,text_info->y,text_info->text,
2409                  (int) strlen(text_info->text));
2410              text_info=text_info->previous;
2411            }
2412            (void) XDrawString(display,windows->image.id,annotate_context,
2413              x,y,"_",1);
2414          }
2415        break;
2416      }
2417      case KeyPress:
2418      {
2419        int
2420          length;
2421
2422        if (event.xkey.window != windows->image.id)
2423          break;
2424        /*
2425          Respond to a user key press.
2426        */
2427        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2428          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2429        *(command+length)='\0';
2430        if (((event.xkey.state & ControlMask) != 0) ||
2431            ((event.xkey.state & Mod1Mask) != 0))
2432          state|=ModifierState;
2433        if ((state & ModifierState) != 0)
2434          switch ((int) key_symbol)
2435          {
2436            case XK_u:
2437            case XK_U:
2438            {
2439              key_symbol=DeleteCommand;
2440              break;
2441            }
2442            default:
2443              break;
2444          }
2445        switch ((int) key_symbol)
2446        {
2447          case XK_BackSpace:
2448          {
2449            /*
2450              Erase one character.
2451            */
2452            if (p == annotate_info->text)
2453              {
2454                if (annotate_info->previous == (XAnnotateInfo *) NULL)
2455                  break;
2456                else
2457                  {
2458                    /*
2459                      Go to end of the previous line of text.
2460                    */
2461                    annotate_info=annotate_info->previous;
2462                    p=annotate_info->text;
2463                    x=annotate_info->x+annotate_info->width;
2464                    y=annotate_info->y;
2465                    if (annotate_info->width != 0)
2466                      p+=strlen(annotate_info->text);
2467                    break;
2468                  }
2469              }
2470            p--;
2471            x-=XTextWidth(font_info,p,1);
2472            text_event.xexpose.x=x;
2473            text_event.xexpose.y=y-font_info->max_bounds.ascent;
2474            XRefreshWindow(display,&windows->image,&text_event);
2475            break;
2476          }
2477          case XK_bracketleft:
2478          {
2479            key_symbol=XK_Escape;
2480            break;
2481          }
2482          case DeleteCommand:
2483          {
2484            /*
2485              Erase the entire line of text.
2486            */
2487            while (p != annotate_info->text)
2488            {
2489              p--;
2490              x-=XTextWidth(font_info,p,1);
2491              text_event.xexpose.x=x;
2492              XRefreshWindow(display,&windows->image,&text_event);
2493            }
2494            break;
2495          }
2496          case XK_Escape:
2497          case XK_F20:
2498          {
2499            /*
2500              Finished annotating.
2501            */
2502            annotate_info->width=(unsigned int) XTextWidth(font_info,
2503              annotate_info->text,(int) strlen(annotate_info->text));
2504            XRefreshWindow(display,&windows->image,&text_event);
2505            state|=ExitState;
2506            break;
2507          }
2508          default:
2509          {
2510            /*
2511              Draw a single character on the Image window.
2512            */
2513            if ((state & ModifierState) != 0)
2514              break;
2515            if (*command == '\0')
2516              break;
2517            *p=(*command);
2518            if (annotate_info->stencil == ForegroundStencil)
2519              (void) XDrawString(display,windows->image.id,annotate_context,
2520                x,y,p,1);
2521            else
2522              (void) XDrawImageString(display,windows->image.id,
2523                annotate_context,x,y,p,1);
2524            x+=XTextWidth(font_info,p,1);
2525            p++;
2526            if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2527              break;
2528          }
2529          case XK_Return:
2530          case XK_KP_Enter:
2531          {
2532            /*
2533              Advance to the next line of text.
2534            */
2535            *p='\0';
2536            annotate_info->width=(unsigned int) XTextWidth(font_info,
2537              annotate_info->text,(int) strlen(annotate_info->text));
2538            if (annotate_info->next != (XAnnotateInfo *) NULL)
2539              {
2540                /*
2541                  Line of text already exists.
2542                */
2543                annotate_info=annotate_info->next;
2544                x=annotate_info->x;
2545                y=annotate_info->y;
2546                p=annotate_info->text;
2547                break;
2548              }
2549            annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2550              sizeof(*annotate_info->next));
2551            if (annotate_info->next == (XAnnotateInfo *) NULL)
2552              return(MagickFalse);
2553            *annotate_info->next=(*annotate_info);
2554            annotate_info->next->previous=annotate_info;
2555            annotate_info=annotate_info->next;
2556            annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2557              windows->image.width/MagickMax((ssize_t)
2558              font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2559            if (annotate_info->text == (char *) NULL)
2560              return(MagickFalse);
2561            annotate_info->y+=annotate_info->height;
2562            if (annotate_info->y > (int) windows->image.height)
2563              annotate_info->y=(int) annotate_info->height;
2564            annotate_info->next=(XAnnotateInfo *) NULL;
2565            x=annotate_info->x;
2566            y=annotate_info->y;
2567            p=annotate_info->text;
2568            break;
2569          }
2570        }
2571        break;
2572      }
2573      case KeyRelease:
2574      {
2575        /*
2576          Respond to a user key release.
2577        */
2578        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2579          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2580        state&=(~ModifierState);
2581        break;
2582      }
2583      case SelectionNotify:
2584      {
2585        Atom
2586          type;
2587
2588        int
2589          format;
2590
2591        unsigned char
2592          *data;
2593
2594        unsigned long
2595          after,
2596          length;
2597
2598        /*
2599          Obtain response from primary selection.
2600        */
2601        if (event.xselection.property == (Atom) None)
2602          break;
2603        status=XGetWindowProperty(display,event.xselection.requestor,
2604          event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
2605          &type,&format,&length,&after,&data);
2606        if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2607            (length == 0))
2608          break;
2609        /*
2610          Annotate Image window with primary selection.
2611        */
2612        for (i=0; i < (ssize_t) length; i++)
2613        {
2614          if ((char) data[i] != '\n')
2615            {
2616              /*
2617                Draw a single character on the Image window.
2618              */
2619              *p=(char) data[i];
2620              (void) XDrawString(display,windows->image.id,annotate_context,
2621                x,y,p,1);
2622              x+=XTextWidth(font_info,p,1);
2623              p++;
2624              if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2625                continue;
2626            }
2627          /*
2628            Advance to the next line of text.
2629          */
2630          *p='\0';
2631          annotate_info->width=(unsigned int) XTextWidth(font_info,
2632            annotate_info->text,(int) strlen(annotate_info->text));
2633          if (annotate_info->next != (XAnnotateInfo *) NULL)
2634            {
2635              /*
2636                Line of text already exists.
2637              */
2638              annotate_info=annotate_info->next;
2639              x=annotate_info->x;
2640              y=annotate_info->y;
2641              p=annotate_info->text;
2642              continue;
2643            }
2644          annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2645            sizeof(*annotate_info->next));
2646          if (annotate_info->next == (XAnnotateInfo *) NULL)
2647            return(MagickFalse);
2648          *annotate_info->next=(*annotate_info);
2649          annotate_info->next->previous=annotate_info;
2650          annotate_info=annotate_info->next;
2651          annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2652            windows->image.width/MagickMax((ssize_t)
2653            font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2654          if (annotate_info->text == (char *) NULL)
2655            return(MagickFalse);
2656          annotate_info->y+=annotate_info->height;
2657          if (annotate_info->y > (int) windows->image.height)
2658            annotate_info->y=(int) annotate_info->height;
2659          annotate_info->next=(XAnnotateInfo *) NULL;
2660          x=annotate_info->x;
2661          y=annotate_info->y;
2662          p=annotate_info->text;
2663        }
2664        (void) XFree((void *) data);
2665        break;
2666      }
2667      default:
2668        break;
2669    }
2670  } while ((state & ExitState) == 0);
2671  (void) XFreeCursor(display,cursor);
2672  /*
2673    Annotation is relative to image configuration.
2674  */
2675  width=(unsigned int) image->columns;
2676  height=(unsigned int) image->rows;
2677  x=0;
2678  y=0;
2679  if (windows->image.crop_geometry != (char *) NULL)
2680    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2681  /*
2682    Initialize annotated image.
2683  */
2684  XSetCursorState(display,windows,MagickTrue);
2685  XCheckRefreshWindows(display,windows);
2686  while (annotate_info != (XAnnotateInfo *) NULL)
2687  {
2688    if (annotate_info->width == 0)
2689      {
2690        /*
2691          No text on this line--  go to the next line of text.
2692        */
2693        previous_info=annotate_info->previous;
2694        annotate_info->text=(char *)
2695          RelinquishMagickMemory(annotate_info->text);
2696        annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2697        annotate_info=previous_info;
2698        continue;
2699      }
2700    /*
2701      Determine pixel index for box and pen color.
2702    */
2703    windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2704    if (windows->pixel_info->colors != 0)
2705      for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2706        if (windows->pixel_info->pixels[i] ==
2707            windows->pixel_info->pen_colors[box_id].pixel)
2708          {
2709            windows->pixel_info->box_index=(unsigned short) i;
2710            break;
2711          }
2712    windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2713    if (windows->pixel_info->colors != 0)
2714      for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2715        if (windows->pixel_info->pixels[i] ==
2716            windows->pixel_info->pen_colors[pen_id].pixel)
2717          {
2718            windows->pixel_info->pen_index=(unsigned short) i;
2719            break;
2720          }
2721    /*
2722      Define the annotate geometry string.
2723    */
2724    annotate_info->x=(int)
2725      width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2726    annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2727      windows->image.y)/windows->image.ximage->height;
2728    (void) FormatLocaleString(annotate_info->geometry,MaxTextExtent,
2729      "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2730      height*annotate_info->height/windows->image.ximage->height,
2731      annotate_info->x+x,annotate_info->y+y);
2732    /*
2733      Annotate image with text.
2734    */
2735    status=XAnnotateImage(display,windows->pixel_info,annotate_info,image,
2736      exception);
2737    if (status == 0)
2738      return(MagickFalse);
2739    /*
2740      Free up memory.
2741    */
2742    previous_info=annotate_info->previous;
2743    annotate_info->text=DestroyString(annotate_info->text);
2744    annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2745    annotate_info=previous_info;
2746  }
2747  (void) XSetForeground(display,annotate_context,
2748    windows->pixel_info->foreground_color.pixel);
2749  (void) XSetBackground(display,annotate_context,
2750    windows->pixel_info->background_color.pixel);
2751  (void) XSetFont(display,annotate_context,windows->font_info->fid);
2752  XSetCursorState(display,windows,MagickFalse);
2753  (void) XFreeFont(display,font_info);
2754  /*
2755    Update image configuration.
2756  */
2757  XConfigureImageColormap(display,resource_info,windows,image,exception);
2758  (void) XConfigureImage(display,resource_info,windows,image,exception);
2759  return(MagickTrue);
2760}
2761
2762/*
2763%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2764%                                                                             %
2765%                                                                             %
2766%                                                                             %
2767+   X B a c k g r o u n d I m a g e                                           %
2768%                                                                             %
2769%                                                                             %
2770%                                                                             %
2771%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2772%
2773%  XBackgroundImage() displays the image in the background of a window.
2774%
2775%  The format of the XBackgroundImage method is:
2776%
2777%      MagickBooleanType XBackgroundImage(Display *display,
2778%        XResourceInfo *resource_info,XWindows *windows,Image **image,
2779%        ExceptionInfo *exception)
2780%
2781%  A description of each parameter follows:
2782%
2783%    o display: Specifies a connection to an X server; returned from
2784%      XOpenDisplay.
2785%
2786%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2787%
2788%    o windows: Specifies a pointer to a XWindows structure.
2789%
2790%    o image: the image.
2791%
2792%    o exception: return any errors or warnings in this structure.
2793%
2794*/
2795static MagickBooleanType XBackgroundImage(Display *display,
2796  XResourceInfo *resource_info,XWindows *windows,Image **image,
2797  ExceptionInfo *exception)
2798{
2799#define BackgroundImageTag  "Background/Image"
2800
2801  int
2802    status;
2803
2804  static char
2805    window_id[MaxTextExtent] = "root";
2806
2807  XResourceInfo
2808    background_resources;
2809
2810  /*
2811    Put image in background.
2812  */
2813  status=XDialogWidget(display,windows,"Background",
2814    "Enter window id (id 0x00 selects window with pointer):",window_id);
2815  if (*window_id == '\0')
2816    return(MagickFalse);
2817  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
2818    exception);
2819  XInfoWidget(display,windows,BackgroundImageTag);
2820  XSetCursorState(display,windows,MagickTrue);
2821  XCheckRefreshWindows(display,windows);
2822  background_resources=(*resource_info);
2823  background_resources.window_id=window_id;
2824  background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2825  status=XDisplayBackgroundImage(display,&background_resources,*image,
2826    exception);
2827  if (status != MagickFalse)
2828    XClientMessage(display,windows->image.id,windows->im_protocols,
2829      windows->im_retain_colors,CurrentTime);
2830  XSetCursorState(display,windows,MagickFalse);
2831  (void) XMagickCommand(display,resource_info,windows,UndoCommand,image,
2832    exception);
2833  return(MagickTrue);
2834}
2835
2836/*
2837%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2838%                                                                             %
2839%                                                                             %
2840%                                                                             %
2841+   X C h o p I m a g e                                                       %
2842%                                                                             %
2843%                                                                             %
2844%                                                                             %
2845%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2846%
2847%  XChopImage() chops the X image.
2848%
2849%  The format of the XChopImage method is:
2850%
2851%    MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2852%      XWindows *windows,Image **image,ExceptionInfo *exception)
2853%
2854%  A description of each parameter follows:
2855%
2856%    o display: Specifies a connection to an X server; returned from
2857%      XOpenDisplay.
2858%
2859%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2860%
2861%    o windows: Specifies a pointer to a XWindows structure.
2862%
2863%    o image: the image.
2864%
2865%    o exception: return any errors or warnings in this structure.
2866%
2867*/
2868static MagickBooleanType XChopImage(Display *display,
2869  XResourceInfo *resource_info,XWindows *windows,Image **image,
2870  ExceptionInfo *exception)
2871{
2872  static const char
2873    *ChopMenu[] =
2874    {
2875      "Direction",
2876      "Help",
2877      "Dismiss",
2878      (char *) NULL
2879    };
2880
2881  static ModeType
2882    direction = HorizontalChopCommand;
2883
2884  static const ModeType
2885    ChopCommands[] =
2886    {
2887      ChopDirectionCommand,
2888      ChopHelpCommand,
2889      ChopDismissCommand
2890    },
2891    DirectionCommands[] =
2892    {
2893      HorizontalChopCommand,
2894      VerticalChopCommand
2895    };
2896
2897  char
2898    text[MaxTextExtent];
2899
2900  Image
2901    *chop_image;
2902
2903  int
2904    id,
2905    x,
2906    y;
2907
2908  MagickRealType
2909    scale_factor;
2910
2911  RectangleInfo
2912    chop_info;
2913
2914  unsigned int
2915    distance,
2916    height,
2917    width;
2918
2919  size_t
2920    state;
2921
2922  XEvent
2923    event;
2924
2925  XSegment
2926    segment_info;
2927
2928  /*
2929    Map Command widget.
2930  */
2931  (void) CloneString(&windows->command.name,"Chop");
2932  windows->command.data=1;
2933  (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2934  (void) XMapRaised(display,windows->command.id);
2935  XClientMessage(display,windows->image.id,windows->im_protocols,
2936    windows->im_update_widget,CurrentTime);
2937  /*
2938    Track pointer until button 1 is pressed.
2939  */
2940  XQueryPosition(display,windows->image.id,&x,&y);
2941  (void) XSelectInput(display,windows->image.id,
2942    windows->image.attributes.event_mask | PointerMotionMask);
2943  state=DefaultState;
2944  do
2945  {
2946    if (windows->info.mapped != MagickFalse)
2947      {
2948        /*
2949          Display pointer position.
2950        */
2951        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
2952          x+windows->image.x,y+windows->image.y);
2953        XInfoWidget(display,windows,text);
2954      }
2955    /*
2956      Wait for next event.
2957    */
2958    XScreenEvent(display,windows,&event,exception);
2959    if (event.xany.window == windows->command.id)
2960      {
2961        /*
2962          Select a command from the Command widget.
2963        */
2964        id=XCommandWidget(display,windows,ChopMenu,&event);
2965        if (id < 0)
2966          continue;
2967        switch (ChopCommands[id])
2968        {
2969          case ChopDirectionCommand:
2970          {
2971            char
2972              command[MaxTextExtent];
2973
2974            static const char
2975              *Directions[] =
2976              {
2977                "horizontal",
2978                "vertical",
2979                (char *) NULL,
2980              };
2981
2982            /*
2983              Select a command from the pop-up menu.
2984            */
2985            id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2986            if (id >= 0)
2987              direction=DirectionCommands[id];
2988            break;
2989          }
2990          case ChopHelpCommand:
2991          {
2992            XTextViewWidget(display,resource_info,windows,MagickFalse,
2993              "Help Viewer - Image Chop",ImageChopHelp);
2994            break;
2995          }
2996          case ChopDismissCommand:
2997          {
2998            /*
2999              Prematurely exit.
3000            */
3001            state|=EscapeState;
3002            state|=ExitState;
3003            break;
3004          }
3005          default:
3006            break;
3007        }
3008        continue;
3009      }
3010    switch (event.type)
3011    {
3012      case ButtonPress:
3013      {
3014        if (event.xbutton.button != Button1)
3015          break;
3016        if (event.xbutton.window != windows->image.id)
3017          break;
3018        /*
3019          User has committed to start point of chopping line.
3020        */
3021        segment_info.x1=(short int) event.xbutton.x;
3022        segment_info.x2=(short int) event.xbutton.x;
3023        segment_info.y1=(short int) event.xbutton.y;
3024        segment_info.y2=(short int) event.xbutton.y;
3025        state|=ExitState;
3026        break;
3027      }
3028      case ButtonRelease:
3029        break;
3030      case Expose:
3031        break;
3032      case KeyPress:
3033      {
3034        char
3035          command[MaxTextExtent];
3036
3037        KeySym
3038          key_symbol;
3039
3040        if (event.xkey.window != windows->image.id)
3041          break;
3042        /*
3043          Respond to a user key press.
3044        */
3045        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3046          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3047        switch ((int) key_symbol)
3048        {
3049          case XK_Escape:
3050          case XK_F20:
3051          {
3052            /*
3053              Prematurely exit.
3054            */
3055            state|=EscapeState;
3056            state|=ExitState;
3057            break;
3058          }
3059          case XK_F1:
3060          case XK_Help:
3061          {
3062            (void) XSetFunction(display,windows->image.highlight_context,
3063              GXcopy);
3064            XTextViewWidget(display,resource_info,windows,MagickFalse,
3065              "Help Viewer - Image Chop",ImageChopHelp);
3066            (void) XSetFunction(display,windows->image.highlight_context,
3067              GXinvert);
3068            break;
3069          }
3070          default:
3071          {
3072            (void) XBell(display,0);
3073            break;
3074          }
3075        }
3076        break;
3077      }
3078      case MotionNotify:
3079      {
3080        /*
3081          Map and unmap Info widget as text cursor crosses its boundaries.
3082        */
3083        x=event.xmotion.x;
3084        y=event.xmotion.y;
3085        if (windows->info.mapped != MagickFalse)
3086          {
3087            if ((x < (int) (windows->info.x+windows->info.width)) &&
3088                (y < (int) (windows->info.y+windows->info.height)))
3089              (void) XWithdrawWindow(display,windows->info.id,
3090                windows->info.screen);
3091          }
3092        else
3093          if ((x > (int) (windows->info.x+windows->info.width)) ||
3094              (y > (int) (windows->info.y+windows->info.height)))
3095            (void) XMapWindow(display,windows->info.id);
3096      }
3097    }
3098  } while ((state & ExitState) == 0);
3099  (void) XSelectInput(display,windows->image.id,
3100    windows->image.attributes.event_mask);
3101  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3102  if ((state & EscapeState) != 0)
3103    return(MagickTrue);
3104  /*
3105    Draw line as pointer moves until the mouse button is released.
3106  */
3107  chop_info.width=0;
3108  chop_info.height=0;
3109  chop_info.x=0;
3110  chop_info.y=0;
3111  distance=0;
3112  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3113  state=DefaultState;
3114  do
3115  {
3116    if (distance > 9)
3117      {
3118        /*
3119          Display info and draw chopping line.
3120        */
3121        if (windows->info.mapped == MagickFalse)
3122          (void) XMapWindow(display,windows->info.id);
3123        (void) FormatLocaleString(text,MaxTextExtent,
3124          " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3125          chop_info.height,(double) chop_info.x,(double) chop_info.y);
3126        XInfoWidget(display,windows,text);
3127        XHighlightLine(display,windows->image.id,
3128          windows->image.highlight_context,&segment_info);
3129      }
3130    else
3131      if (windows->info.mapped != MagickFalse)
3132        (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3133    /*
3134      Wait for next event.
3135    */
3136    XScreenEvent(display,windows,&event,exception);
3137    if (distance > 9)
3138      XHighlightLine(display,windows->image.id,
3139        windows->image.highlight_context,&segment_info);
3140    switch (event.type)
3141    {
3142      case ButtonPress:
3143      {
3144        segment_info.x2=(short int) event.xmotion.x;
3145        segment_info.y2=(short int) event.xmotion.y;
3146        break;
3147      }
3148      case ButtonRelease:
3149      {
3150        /*
3151          User has committed to chopping line.
3152        */
3153        segment_info.x2=(short int) event.xbutton.x;
3154        segment_info.y2=(short int) event.xbutton.y;
3155        state|=ExitState;
3156        break;
3157      }
3158      case Expose:
3159        break;
3160      case MotionNotify:
3161      {
3162        segment_info.x2=(short int) event.xmotion.x;
3163        segment_info.y2=(short int) event.xmotion.y;
3164      }
3165      default:
3166        break;
3167    }
3168    /*
3169      Check boundary conditions.
3170    */
3171    if (segment_info.x2 < 0)
3172      segment_info.x2=0;
3173    else
3174      if (segment_info.x2 > windows->image.ximage->width)
3175        segment_info.x2=windows->image.ximage->width;
3176    if (segment_info.y2 < 0)
3177      segment_info.y2=0;
3178    else
3179      if (segment_info.y2 > windows->image.ximage->height)
3180        segment_info.y2=windows->image.ximage->height;
3181    distance=(unsigned int)
3182      (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3183       ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3184    /*
3185      Compute chopping geometry.
3186    */
3187    if (direction == HorizontalChopCommand)
3188      {
3189        chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3190        chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3191        chop_info.height=0;
3192        chop_info.y=0;
3193        if (segment_info.x1 > (int) segment_info.x2)
3194          {
3195            chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3196            chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3197          }
3198      }
3199    else
3200      {
3201        chop_info.width=0;
3202        chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3203        chop_info.x=0;
3204        chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3205        if (segment_info.y1 > segment_info.y2)
3206          {
3207            chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3208            chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3209          }
3210      }
3211  } while ((state & ExitState) == 0);
3212  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3213  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3214  if (distance <= 9)
3215    return(MagickTrue);
3216  /*
3217    Image chopping is relative to image configuration.
3218  */
3219  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
3220    exception);
3221  XSetCursorState(display,windows,MagickTrue);
3222  XCheckRefreshWindows(display,windows);
3223  windows->image.window_changes.width=windows->image.ximage->width-
3224    (unsigned int) chop_info.width;
3225  windows->image.window_changes.height=windows->image.ximage->height-
3226    (unsigned int) chop_info.height;
3227  width=(unsigned int) (*image)->columns;
3228  height=(unsigned int) (*image)->rows;
3229  x=0;
3230  y=0;
3231  if (windows->image.crop_geometry != (char *) NULL)
3232    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3233  scale_factor=(MagickRealType) width/windows->image.ximage->width;
3234  chop_info.x+=x;
3235  chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3236  chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3237  scale_factor=(MagickRealType) height/windows->image.ximage->height;
3238  chop_info.y+=y;
3239  chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3240  chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3241  /*
3242    Chop image.
3243  */
3244  chop_image=ChopImage(*image,&chop_info,exception);
3245  XSetCursorState(display,windows,MagickFalse);
3246  if (chop_image == (Image *) NULL)
3247    return(MagickFalse);
3248  *image=DestroyImage(*image);
3249  *image=chop_image;
3250  /*
3251    Update image configuration.
3252  */
3253  XConfigureImageColormap(display,resource_info,windows,*image,exception);
3254  (void) XConfigureImage(display,resource_info,windows,*image,exception);
3255  return(MagickTrue);
3256}
3257
3258/*
3259%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3260%                                                                             %
3261%                                                                             %
3262%                                                                             %
3263+   X C o l o r E d i t I m a g e                                             %
3264%                                                                             %
3265%                                                                             %
3266%                                                                             %
3267%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3268%
3269%  XColorEditImage() allows the user to interactively change the color of one
3270%  pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3271%
3272%  The format of the XColorEditImage method is:
3273%
3274%      MagickBooleanType XColorEditImage(Display *display,
3275%        XResourceInfo *resource_info,XWindows *windows,Image **image,
3276%          ExceptionInfo *exception)
3277%
3278%  A description of each parameter follows:
3279%
3280%    o display: Specifies a connection to an X server;  returned from
3281%      XOpenDisplay.
3282%
3283%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3284%
3285%    o windows: Specifies a pointer to a XWindows structure.
3286%
3287%    o image: the image; returned from ReadImage.
3288%
3289%    o exception: return any errors or warnings in this structure.
3290%
3291*/
3292static MagickBooleanType XColorEditImage(Display *display,
3293  XResourceInfo *resource_info,XWindows *windows,Image **image,
3294  ExceptionInfo *exception)
3295{
3296  static const char
3297    *ColorEditMenu[] =
3298    {
3299      "Method",
3300      "Pixel Color",
3301      "Border Color",
3302      "Fuzz",
3303      "Undo",
3304      "Help",
3305      "Dismiss",
3306      (char *) NULL
3307    };
3308
3309  static const ModeType
3310    ColorEditCommands[] =
3311    {
3312      ColorEditMethodCommand,
3313      ColorEditColorCommand,
3314      ColorEditBorderCommand,
3315      ColorEditFuzzCommand,
3316      ColorEditUndoCommand,
3317      ColorEditHelpCommand,
3318      ColorEditDismissCommand
3319    };
3320
3321  static PaintMethod
3322    method = PointMethod;
3323
3324  static unsigned int
3325    pen_id = 0;
3326
3327  static XColor
3328    border_color = { 0, 0, 0, 0, 0, 0 };
3329
3330  char
3331    command[MaxTextExtent],
3332    text[MaxTextExtent];
3333
3334  Cursor
3335    cursor;
3336
3337  int
3338    entry,
3339    id,
3340    x,
3341    x_offset,
3342    y,
3343    y_offset;
3344
3345  register Quantum
3346    *q;
3347
3348  register ssize_t
3349    i;
3350
3351  unsigned int
3352    height,
3353    width;
3354
3355  size_t
3356    state;
3357
3358  XColor
3359    color;
3360
3361  XEvent
3362    event;
3363
3364  /*
3365    Map Command widget.
3366  */
3367  (void) CloneString(&windows->command.name,"Color Edit");
3368  windows->command.data=4;
3369  (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3370  (void) XMapRaised(display,windows->command.id);
3371  XClientMessage(display,windows->image.id,windows->im_protocols,
3372    windows->im_update_widget,CurrentTime);
3373  /*
3374    Make cursor.
3375  */
3376  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3377    resource_info->background_color,resource_info->foreground_color);
3378  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3379  /*
3380    Track pointer until button 1 is pressed.
3381  */
3382  XQueryPosition(display,windows->image.id,&x,&y);
3383  (void) XSelectInput(display,windows->image.id,
3384    windows->image.attributes.event_mask | PointerMotionMask);
3385  state=DefaultState;
3386  do
3387  {
3388    if (windows->info.mapped != MagickFalse)
3389      {
3390        /*
3391          Display pointer position.
3392        */
3393        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
3394          x+windows->image.x,y+windows->image.y);
3395        XInfoWidget(display,windows,text);
3396      }
3397    /*
3398      Wait for next event.
3399    */
3400    XScreenEvent(display,windows,&event,exception);
3401    if (event.xany.window == windows->command.id)
3402      {
3403        /*
3404          Select a command from the Command widget.
3405        */
3406        id=XCommandWidget(display,windows,ColorEditMenu,&event);
3407        if (id < 0)
3408          {
3409            (void) XCheckDefineCursor(display,windows->image.id,cursor);
3410            continue;
3411          }
3412        switch (ColorEditCommands[id])
3413        {
3414          case ColorEditMethodCommand:
3415          {
3416            char
3417              **methods;
3418
3419            /*
3420              Select a method from the pop-up menu.
3421            */
3422            methods=(char **) GetCommandOptions(MagickMethodOptions);
3423            if (methods == (char **) NULL)
3424              break;
3425            entry=XMenuWidget(display,windows,ColorEditMenu[id],
3426              (const char **) methods,command);
3427            if (entry >= 0)
3428              method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3429                MagickFalse,methods[entry]);
3430            methods=DestroyStringList(methods);
3431            break;
3432          }
3433          case ColorEditColorCommand:
3434          {
3435            const char
3436              *ColorMenu[MaxNumberPens];
3437
3438            int
3439              pen_number;
3440
3441            /*
3442              Initialize menu selections.
3443            */
3444            for (i=0; i < (int) (MaxNumberPens-2); i++)
3445              ColorMenu[i]=resource_info->pen_colors[i];
3446            ColorMenu[MaxNumberPens-2]="Browser...";
3447            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3448            /*
3449              Select a pen color from the pop-up menu.
3450            */
3451            pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3452              (const char **) ColorMenu,command);
3453            if (pen_number < 0)
3454              break;
3455            if (pen_number == (MaxNumberPens-2))
3456              {
3457                static char
3458                  color_name[MaxTextExtent] = "gray";
3459
3460                /*
3461                  Select a pen color from a dialog.
3462                */
3463                resource_info->pen_colors[pen_number]=color_name;
3464                XColorBrowserWidget(display,windows,"Select",color_name);
3465                if (*color_name == '\0')
3466                  break;
3467              }
3468            /*
3469              Set pen color.
3470            */
3471            (void) XParseColor(display,windows->map_info->colormap,
3472              resource_info->pen_colors[pen_number],&color);
3473            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3474              (unsigned int) MaxColors,&color);
3475            windows->pixel_info->pen_colors[pen_number]=color;
3476            pen_id=(unsigned int) pen_number;
3477            break;
3478          }
3479          case ColorEditBorderCommand:
3480          {
3481            const char
3482              *ColorMenu[MaxNumberPens];
3483
3484            int
3485              pen_number;
3486
3487            /*
3488              Initialize menu selections.
3489            */
3490            for (i=0; i < (int) (MaxNumberPens-2); i++)
3491              ColorMenu[i]=resource_info->pen_colors[i];
3492            ColorMenu[MaxNumberPens-2]="Browser...";
3493            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3494            /*
3495              Select a pen color from the pop-up menu.
3496            */
3497            pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3498              (const char **) ColorMenu,command);
3499            if (pen_number < 0)
3500              break;
3501            if (pen_number == (MaxNumberPens-2))
3502              {
3503                static char
3504                  color_name[MaxTextExtent] = "gray";
3505
3506                /*
3507                  Select a pen color from a dialog.
3508                */
3509                resource_info->pen_colors[pen_number]=color_name;
3510                XColorBrowserWidget(display,windows,"Select",color_name);
3511                if (*color_name == '\0')
3512                  break;
3513              }
3514            /*
3515              Set border color.
3516            */
3517            (void) XParseColor(display,windows->map_info->colormap,
3518              resource_info->pen_colors[pen_number],&border_color);
3519            break;
3520          }
3521          case ColorEditFuzzCommand:
3522          {
3523            static char
3524              fuzz[MaxTextExtent];
3525
3526            static const char
3527              *FuzzMenu[] =
3528              {
3529                "0%",
3530                "2%",
3531                "5%",
3532                "10%",
3533                "15%",
3534                "Dialog...",
3535                (char *) NULL,
3536              };
3537
3538            /*
3539              Select a command from the pop-up menu.
3540            */
3541            entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3542              command);
3543            if (entry < 0)
3544              break;
3545            if (entry != 5)
3546              {
3547                (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
3548                  QuantumRange+1.0);
3549                break;
3550              }
3551            (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
3552            (void) XDialogWidget(display,windows,"Ok",
3553              "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3554            if (*fuzz == '\0')
3555              break;
3556            (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
3557            (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
3558              1.0);
3559            break;
3560          }
3561          case ColorEditUndoCommand:
3562          {
3563            (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3564              image,exception);
3565            break;
3566          }
3567          case ColorEditHelpCommand:
3568          default:
3569          {
3570            XTextViewWidget(display,resource_info,windows,MagickFalse,
3571              "Help Viewer - Image Annotation",ImageColorEditHelp);
3572            break;
3573          }
3574          case ColorEditDismissCommand:
3575          {
3576            /*
3577              Prematurely exit.
3578            */
3579            state|=EscapeState;
3580            state|=ExitState;
3581            break;
3582          }
3583        }
3584        (void) XCheckDefineCursor(display,windows->image.id,cursor);
3585        continue;
3586      }
3587    switch (event.type)
3588    {
3589      case ButtonPress:
3590      {
3591        if (event.xbutton.button != Button1)
3592          break;
3593        if ((event.xbutton.window != windows->image.id) &&
3594            (event.xbutton.window != windows->magnify.id))
3595          break;
3596        /*
3597          exit loop.
3598        */
3599        x=event.xbutton.x;
3600        y=event.xbutton.y;
3601        (void) XMagickCommand(display,resource_info,windows,
3602          SaveToUndoBufferCommand,image,exception);
3603        state|=UpdateConfigurationState;
3604        break;
3605      }
3606      case ButtonRelease:
3607      {
3608        if (event.xbutton.button != Button1)
3609          break;
3610        if ((event.xbutton.window != windows->image.id) &&
3611            (event.xbutton.window != windows->magnify.id))
3612          break;
3613        /*
3614          Update colormap information.
3615        */
3616        x=event.xbutton.x;
3617        y=event.xbutton.y;
3618        XConfigureImageColormap(display,resource_info,windows,*image,exception);
3619        (void) XConfigureImage(display,resource_info,windows,*image,exception);
3620        XInfoWidget(display,windows,text);
3621        (void) XCheckDefineCursor(display,windows->image.id,cursor);
3622        state&=(~UpdateConfigurationState);
3623        break;
3624      }
3625      case Expose:
3626        break;
3627      case KeyPress:
3628      {
3629        KeySym
3630          key_symbol;
3631
3632        if (event.xkey.window == windows->magnify.id)
3633          {
3634            Window
3635              window;
3636
3637            window=windows->magnify.id;
3638            while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3639          }
3640        if (event.xkey.window != windows->image.id)
3641          break;
3642        /*
3643          Respond to a user key press.
3644        */
3645        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3646          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3647        switch ((int) key_symbol)
3648        {
3649          case XK_Escape:
3650          case XK_F20:
3651          {
3652            /*
3653              Prematurely exit.
3654            */
3655            state|=ExitState;
3656            break;
3657          }
3658          case XK_F1:
3659          case XK_Help:
3660          {
3661            XTextViewWidget(display,resource_info,windows,MagickFalse,
3662              "Help Viewer - Image Annotation",ImageColorEditHelp);
3663            break;
3664          }
3665          default:
3666          {
3667            (void) XBell(display,0);
3668            break;
3669          }
3670        }
3671        break;
3672      }
3673      case MotionNotify:
3674      {
3675        /*
3676          Map and unmap Info widget as cursor crosses its boundaries.
3677        */
3678        x=event.xmotion.x;
3679        y=event.xmotion.y;
3680        if (windows->info.mapped != MagickFalse)
3681          {
3682            if ((x < (int) (windows->info.x+windows->info.width)) &&
3683                (y < (int) (windows->info.y+windows->info.height)))
3684              (void) XWithdrawWindow(display,windows->info.id,
3685                windows->info.screen);
3686          }
3687        else
3688          if ((x > (int) (windows->info.x+windows->info.width)) ||
3689              (y > (int) (windows->info.y+windows->info.height)))
3690            (void) XMapWindow(display,windows->info.id);
3691        break;
3692      }
3693      default:
3694        break;
3695    }
3696    if (event.xany.window == windows->magnify.id)
3697      {
3698        x=windows->magnify.x-windows->image.x;
3699        y=windows->magnify.y-windows->image.y;
3700      }
3701    x_offset=x;
3702    y_offset=y;
3703    if ((state & UpdateConfigurationState) != 0)
3704      {
3705        CacheView
3706          *image_view;
3707
3708        int
3709          x,
3710          y;
3711
3712        /*
3713          Pixel edit is relative to image configuration.
3714        */
3715        (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3716          MagickTrue);
3717        color=windows->pixel_info->pen_colors[pen_id];
3718        XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3719        width=(unsigned int) (*image)->columns;
3720        height=(unsigned int) (*image)->rows;
3721        x=0;
3722        y=0;
3723        if (windows->image.crop_geometry != (char *) NULL)
3724          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3725            &width,&height);
3726        x_offset=(int)
3727          (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3728        y_offset=(int)
3729          (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3730        if ((x_offset < 0) || (y_offset < 0))
3731          continue;
3732        if ((x_offset >= (int) (*image)->columns) ||
3733            (y_offset >= (int) (*image)->rows))
3734          continue;
3735        image_view=AcquireCacheView(*image);
3736        switch (method)
3737        {
3738          case PointMethod:
3739          default:
3740          {
3741            /*
3742              Update color information using point algorithm.
3743            */
3744            if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3745              return(MagickFalse);
3746            q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3747              (ssize_t) y_offset,1,1,exception);
3748            if (q == (Quantum *) NULL)
3749              break;
3750            SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3751            SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3752            SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3753            (void) SyncCacheViewAuthenticPixels(image_view,exception);
3754            break;
3755          }
3756          case ReplaceMethod:
3757          {
3758            PixelInfo
3759              pixel,
3760              target;
3761
3762            Quantum
3763              virtual_pixel[CompositePixelChannel];
3764
3765            /*
3766              Update color information using replace algorithm.
3767            */
3768            (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
3769              (ssize_t) y_offset,virtual_pixel,exception);
3770            target.red=virtual_pixel[RedPixelChannel];
3771            target.green=virtual_pixel[GreenPixelChannel];
3772            target.blue=virtual_pixel[BluePixelChannel];
3773            target.alpha=virtual_pixel[AlphaPixelChannel];
3774            if ((*image)->storage_class == DirectClass)
3775              {
3776                for (y=0; y < (int) (*image)->rows; y++)
3777                {
3778                  q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3779                    (*image)->columns,1,exception);
3780                  if (q == (Quantum *) NULL)
3781                    break;
3782                  for (x=0; x < (int) (*image)->columns; x++)
3783                  {
3784                    GetPixelInfoPixel(*image,q,&pixel);
3785                    if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
3786                      {
3787                        SetPixelRed(*image,ScaleShortToQuantum(
3788                          color.red),q);
3789                        SetPixelGreen(*image,ScaleShortToQuantum(
3790                          color.green),q);
3791                        SetPixelBlue(*image,ScaleShortToQuantum(
3792                          color.blue),q);
3793                      }
3794                    q+=GetPixelChannels(*image);
3795                  }
3796                  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3797                    break;
3798                }
3799              }
3800            else
3801              {
3802                for (i=0; i < (ssize_t) (*image)->colors; i++)
3803                  if (IsFuzzyEquivalencePixelInfo((*image)->colormap+i,&target))
3804                    {
3805                      (*image)->colormap[i].red=ScaleShortToQuantum(
3806                        color.red);
3807                      (*image)->colormap[i].green=ScaleShortToQuantum(
3808                        color.green);
3809                      (*image)->colormap[i].blue=ScaleShortToQuantum(
3810                        color.blue);
3811                    }
3812                (void) SyncImage(*image,exception);
3813              }
3814            break;
3815          }
3816          case FloodfillMethod:
3817          case FillToBorderMethod:
3818          {
3819            DrawInfo
3820              *draw_info;
3821
3822            PixelInfo
3823              target;
3824
3825            /*
3826              Update color information using floodfill algorithm.
3827            */
3828            (void) GetOneVirtualMagickPixel(*image,
3829              GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
3830              y_offset,&target,exception);
3831            if (method == FillToBorderMethod)
3832              {
3833                target.red=(MagickRealType)
3834                  ScaleShortToQuantum(border_color.red);
3835                target.green=(MagickRealType)
3836                  ScaleShortToQuantum(border_color.green);
3837                target.blue=(MagickRealType)
3838                  ScaleShortToQuantum(border_color.blue);
3839              }
3840            draw_info=CloneDrawInfo(resource_info->image_info,
3841              (DrawInfo *) NULL);
3842            (void) QueryColorCompliance(resource_info->pen_colors[pen_id],
3843              AllCompliance,&draw_info->fill,exception);
3844            (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
3845              x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
3846              MagickFalse : MagickTrue,exception);
3847            draw_info=DestroyDrawInfo(draw_info);
3848            break;
3849          }
3850          case ResetMethod:
3851          {
3852            /*
3853              Update color information using reset algorithm.
3854            */
3855            if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3856              return(MagickFalse);
3857            for (y=0; y < (int) (*image)->rows; y++)
3858            {
3859              q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3860                (*image)->columns,1,exception);
3861              if (q == (Quantum *) NULL)
3862                break;
3863              for (x=0; x < (int) (*image)->columns; x++)
3864              {
3865                SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3866                SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3867                SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3868                q+=GetPixelChannels(*image);
3869              }
3870              if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3871                break;
3872            }
3873            break;
3874          }
3875        }
3876        image_view=DestroyCacheView(image_view);
3877        state&=(~UpdateConfigurationState);
3878      }
3879  } while ((state & ExitState) == 0);
3880  (void) XSelectInput(display,windows->image.id,
3881    windows->image.attributes.event_mask);
3882  XSetCursorState(display,windows,MagickFalse);
3883  (void) XFreeCursor(display,cursor);
3884  return(MagickTrue);
3885}
3886
3887/*
3888%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3889%                                                                             %
3890%                                                                             %
3891%                                                                             %
3892+   X C o m p o s i t e I m a g e                                             %
3893%                                                                             %
3894%                                                                             %
3895%                                                                             %
3896%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3897%
3898%  XCompositeImage() requests an image name from the user, reads the image and
3899%  composites it with the X window image at a location the user chooses with
3900%  the pointer.
3901%
3902%  The format of the XCompositeImage method is:
3903%
3904%      MagickBooleanType XCompositeImage(Display *display,
3905%        XResourceInfo *resource_info,XWindows *windows,Image *image,
3906%        ExceptionInfo *exception)
3907%
3908%  A description of each parameter follows:
3909%
3910%    o display: Specifies a connection to an X server;  returned from
3911%      XOpenDisplay.
3912%
3913%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3914%
3915%    o windows: Specifies a pointer to a XWindows structure.
3916%
3917%    o image: the image; returned from ReadImage.
3918%
3919%    o exception: return any errors or warnings in this structure.
3920%
3921*/
3922static MagickBooleanType XCompositeImage(Display *display,
3923  XResourceInfo *resource_info,XWindows *windows,Image *image,
3924  ExceptionInfo *exception)
3925{
3926  static char
3927    displacement_geometry[MaxTextExtent] = "30x30",
3928    filename[MaxTextExtent] = "\0";
3929
3930  static const char
3931    *CompositeMenu[] =
3932    {
3933      "Operators",
3934      "Dissolve",
3935      "Displace",
3936      "Help",
3937      "Dismiss",
3938      (char *) NULL
3939    };
3940
3941  static CompositeOperator
3942    compose = CopyCompositeOp;
3943
3944  static const ModeType
3945    CompositeCommands[] =
3946    {
3947      CompositeOperatorsCommand,
3948      CompositeDissolveCommand,
3949      CompositeDisplaceCommand,
3950      CompositeHelpCommand,
3951      CompositeDismissCommand
3952    };
3953
3954  char
3955    text[MaxTextExtent];
3956
3957  Cursor
3958    cursor;
3959
3960  Image
3961    *composite_image;
3962
3963  int
3964    entry,
3965    id,
3966    x,
3967    y;
3968
3969  MagickRealType
3970    blend,
3971    scale_factor;
3972
3973  RectangleInfo
3974    highlight_info,
3975    composite_info;
3976
3977  unsigned int
3978    height,
3979    width;
3980
3981  size_t
3982    state;
3983
3984  XEvent
3985    event;
3986
3987  /*
3988    Request image file name from user.
3989  */
3990  XFileBrowserWidget(display,windows,"Composite",filename);
3991  if (*filename == '\0')
3992    return(MagickTrue);
3993  /*
3994    Read image.
3995  */
3996  XSetCursorState(display,windows,MagickTrue);
3997  XCheckRefreshWindows(display,windows);
3998  (void) CopyMagickString(resource_info->image_info->filename,filename,
3999    MaxTextExtent);
4000  composite_image=ReadImage(resource_info->image_info,exception);
4001  CatchException(exception);
4002  XSetCursorState(display,windows,MagickFalse);
4003  if (composite_image == (Image *) NULL)
4004    return(MagickFalse);
4005  /*
4006    Map Command widget.
4007  */
4008  (void) CloneString(&windows->command.name,"Composite");
4009  windows->command.data=1;
4010  (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
4011  (void) XMapRaised(display,windows->command.id);
4012  XClientMessage(display,windows->image.id,windows->im_protocols,
4013    windows->im_update_widget,CurrentTime);
4014  /*
4015    Track pointer until button 1 is pressed.
4016  */
4017  XQueryPosition(display,windows->image.id,&x,&y);
4018  (void) XSelectInput(display,windows->image.id,
4019    windows->image.attributes.event_mask | PointerMotionMask);
4020  composite_info.x=(ssize_t) windows->image.x+x;
4021  composite_info.y=(ssize_t) windows->image.y+y;
4022  composite_info.width=0;
4023  composite_info.height=0;
4024  cursor=XCreateFontCursor(display,XC_ul_angle);
4025  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4026  blend=0.0;
4027  state=DefaultState;
4028  do
4029  {
4030    if (windows->info.mapped != MagickFalse)
4031      {
4032        /*
4033          Display pointer position.
4034        */
4035        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4036          (long) composite_info.x,(long) composite_info.y);
4037        XInfoWidget(display,windows,text);
4038      }
4039    highlight_info=composite_info;
4040    highlight_info.x=composite_info.x-windows->image.x;
4041    highlight_info.y=composite_info.y-windows->image.y;
4042    XHighlightRectangle(display,windows->image.id,
4043      windows->image.highlight_context,&highlight_info);
4044    /*
4045      Wait for next event.
4046    */
4047    XScreenEvent(display,windows,&event,exception);
4048    XHighlightRectangle(display,windows->image.id,
4049      windows->image.highlight_context,&highlight_info);
4050    if (event.xany.window == windows->command.id)
4051      {
4052        /*
4053          Select a command from the Command widget.
4054        */
4055        id=XCommandWidget(display,windows,CompositeMenu,&event);
4056        if (id < 0)
4057          continue;
4058        switch (CompositeCommands[id])
4059        {
4060          case CompositeOperatorsCommand:
4061          {
4062            char
4063              command[MaxTextExtent],
4064              **operators;
4065
4066            /*
4067              Select a command from the pop-up menu.
4068            */
4069            operators=GetCommandOptions(MagickComposeOptions);
4070            if (operators == (char **) NULL)
4071              break;
4072            entry=XMenuWidget(display,windows,CompositeMenu[id],
4073              (const char **) operators,command);
4074            if (entry >= 0)
4075              compose=(CompositeOperator) ParseCommandOption(
4076                MagickComposeOptions,MagickFalse,operators[entry]);
4077            operators=DestroyStringList(operators);
4078            break;
4079          }
4080          case CompositeDissolveCommand:
4081          {
4082            static char
4083              factor[MaxTextExtent] = "20.0";
4084
4085            /*
4086              Dissolve the two images a given percent.
4087            */
4088            (void) XSetFunction(display,windows->image.highlight_context,
4089              GXcopy);
4090            (void) XDialogWidget(display,windows,"Dissolve",
4091              "Enter the blend factor (0.0 - 99.9%):",factor);
4092            (void) XSetFunction(display,windows->image.highlight_context,
4093              GXinvert);
4094            if (*factor == '\0')
4095              break;
4096            blend=StringToDouble(factor,(char **) NULL);
4097            compose=DissolveCompositeOp;
4098            break;
4099          }
4100          case CompositeDisplaceCommand:
4101          {
4102            /*
4103              Get horizontal and vertical scale displacement geometry.
4104            */
4105            (void) XSetFunction(display,windows->image.highlight_context,
4106              GXcopy);
4107            (void) XDialogWidget(display,windows,"Displace",
4108              "Enter the horizontal and vertical scale:",displacement_geometry);
4109            (void) XSetFunction(display,windows->image.highlight_context,
4110              GXinvert);
4111            if (*displacement_geometry == '\0')
4112              break;
4113            compose=DisplaceCompositeOp;
4114            break;
4115          }
4116          case CompositeHelpCommand:
4117          {
4118            (void) XSetFunction(display,windows->image.highlight_context,
4119              GXcopy);
4120            XTextViewWidget(display,resource_info,windows,MagickFalse,
4121              "Help Viewer - Image Composite",ImageCompositeHelp);
4122            (void) XSetFunction(display,windows->image.highlight_context,
4123              GXinvert);
4124            break;
4125          }
4126          case CompositeDismissCommand:
4127          {
4128            /*
4129              Prematurely exit.
4130            */
4131            state|=EscapeState;
4132            state|=ExitState;
4133            break;
4134          }
4135          default:
4136            break;
4137        }
4138        continue;
4139      }
4140    switch (event.type)
4141    {
4142      case ButtonPress:
4143      {
4144        if (image->debug != MagickFalse)
4145          (void) LogMagickEvent(X11Event,GetMagickModule(),
4146            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4147            event.xbutton.button,event.xbutton.x,event.xbutton.y);
4148        if (event.xbutton.button != Button1)
4149          break;
4150        if (event.xbutton.window != windows->image.id)
4151          break;
4152        /*
4153          Change cursor.
4154        */
4155        composite_info.width=composite_image->columns;
4156        composite_info.height=composite_image->rows;
4157        (void) XCheckDefineCursor(display,windows->image.id,cursor);
4158        composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4159        composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4160        break;
4161      }
4162      case ButtonRelease:
4163      {
4164        if (image->debug != MagickFalse)
4165          (void) LogMagickEvent(X11Event,GetMagickModule(),
4166            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4167            event.xbutton.button,event.xbutton.x,event.xbutton.y);
4168        if (event.xbutton.button != Button1)
4169          break;
4170        if (event.xbutton.window != windows->image.id)
4171          break;
4172        if ((composite_info.width != 0) && (composite_info.height != 0))
4173          {
4174            /*
4175              User has selected the location of the composite image.
4176            */
4177            composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4178            composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4179            state|=ExitState;
4180          }
4181        break;
4182      }
4183      case Expose:
4184        break;
4185      case KeyPress:
4186      {
4187        char
4188          command[MaxTextExtent];
4189
4190        KeySym
4191          key_symbol;
4192
4193        int
4194          length;
4195
4196        if (event.xkey.window != windows->image.id)
4197          break;
4198        /*
4199          Respond to a user key press.
4200        */
4201        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4202          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4203        *(command+length)='\0';
4204        if (image->debug != MagickFalse)
4205          (void) LogMagickEvent(X11Event,GetMagickModule(),
4206            "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4207        switch ((int) key_symbol)
4208        {
4209          case XK_Escape:
4210          case XK_F20:
4211          {
4212            /*
4213              Prematurely exit.
4214            */
4215            composite_image=DestroyImage(composite_image);
4216            state|=EscapeState;
4217            state|=ExitState;
4218            break;
4219          }
4220          case XK_F1:
4221          case XK_Help:
4222          {
4223            (void) XSetFunction(display,windows->image.highlight_context,
4224              GXcopy);
4225            XTextViewWidget(display,resource_info,windows,MagickFalse,
4226              "Help Viewer - Image Composite",ImageCompositeHelp);
4227            (void) XSetFunction(display,windows->image.highlight_context,
4228              GXinvert);
4229            break;
4230          }
4231          default:
4232          {
4233            (void) XBell(display,0);
4234            break;
4235          }
4236        }
4237        break;
4238      }
4239      case MotionNotify:
4240      {
4241        /*
4242          Map and unmap Info widget as text cursor crosses its boundaries.
4243        */
4244        x=event.xmotion.x;
4245        y=event.xmotion.y;
4246        if (windows->info.mapped != MagickFalse)
4247          {
4248            if ((x < (int) (windows->info.x+windows->info.width)) &&
4249                (y < (int) (windows->info.y+windows->info.height)))
4250              (void) XWithdrawWindow(display,windows->info.id,
4251                windows->info.screen);
4252          }
4253        else
4254          if ((x > (int) (windows->info.x+windows->info.width)) ||
4255              (y > (int) (windows->info.y+windows->info.height)))
4256            (void) XMapWindow(display,windows->info.id);
4257        composite_info.x=(ssize_t) windows->image.x+x;
4258        composite_info.y=(ssize_t) windows->image.y+y;
4259        break;
4260      }
4261      default:
4262      {
4263        if (image->debug != MagickFalse)
4264          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4265            event.type);
4266        break;
4267      }
4268    }
4269  } while ((state & ExitState) == 0);
4270  (void) XSelectInput(display,windows->image.id,
4271    windows->image.attributes.event_mask);
4272  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4273  XSetCursorState(display,windows,MagickFalse);
4274  (void) XFreeCursor(display,cursor);
4275  if ((state & EscapeState) != 0)
4276    return(MagickTrue);
4277  /*
4278    Image compositing is relative to image configuration.
4279  */
4280  XSetCursorState(display,windows,MagickTrue);
4281  XCheckRefreshWindows(display,windows);
4282  width=(unsigned int) image->columns;
4283  height=(unsigned int) image->rows;
4284  x=0;
4285  y=0;
4286  if (windows->image.crop_geometry != (char *) NULL)
4287    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4288  scale_factor=(MagickRealType) width/windows->image.ximage->width;
4289  composite_info.x+=x;
4290  composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4291  composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4292  scale_factor=(MagickRealType) height/windows->image.ximage->height;
4293  composite_info.y+=y;
4294  composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4295  composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4296  if ((composite_info.width != composite_image->columns) ||
4297      (composite_info.height != composite_image->rows))
4298    {
4299      Image
4300        *resize_image;
4301
4302      /*
4303        Scale composite image.
4304      */
4305      resize_image=ResizeImage(composite_image,composite_info.width,
4306        composite_info.height,composite_image->filter,composite_image->blur,
4307        exception);
4308      composite_image=DestroyImage(composite_image);
4309      if (resize_image == (Image *) NULL)
4310        {
4311          XSetCursorState(display,windows,MagickFalse);
4312          return(MagickFalse);
4313        }
4314      composite_image=resize_image;
4315    }
4316  if (compose == DisplaceCompositeOp)
4317    (void) SetImageArtifact(composite_image,"compose:args",
4318      displacement_geometry);
4319  if (blend != 0.0)
4320    {
4321      CacheView
4322        *image_view;
4323
4324      int
4325        y;
4326
4327      Quantum
4328        opacity;
4329
4330      register int
4331        x;
4332
4333      register Quantum
4334        *q;
4335
4336      /*
4337        Create mattes for blending.
4338      */
4339      (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,exception);
4340      opacity=(Quantum) (ScaleQuantumToChar((Quantum) QuantumRange)-
4341        ((ssize_t) ScaleQuantumToChar((Quantum) QuantumRange)*blend)/100);
4342      if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
4343        return(MagickFalse);
4344      image->matte=MagickTrue;
4345      image_view=AcquireCacheView(image);
4346      for (y=0; y < (int) image->rows; y++)
4347      {
4348        q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4349          exception);
4350        if (q == (Quantum *) NULL)
4351          break;
4352        for (x=0; x < (int) image->columns; x++)
4353        {
4354          SetPixelAlpha(image,opacity,q);
4355          q+=GetPixelChannels(image);
4356        }
4357        if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4358          break;
4359      }
4360      image_view=DestroyCacheView(image_view);
4361    }
4362  /*
4363    Composite image with X Image window.
4364  */
4365  (void) CompositeImage(image,compose,composite_image,composite_info.x,
4366    composite_info.y,exception);
4367  composite_image=DestroyImage(composite_image);
4368  XSetCursorState(display,windows,MagickFalse);
4369  /*
4370    Update image configuration.
4371  */
4372  XConfigureImageColormap(display,resource_info,windows,image,exception);
4373  (void) XConfigureImage(display,resource_info,windows,image,exception);
4374  return(MagickTrue);
4375}
4376
4377/*
4378%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4379%                                                                             %
4380%                                                                             %
4381%                                                                             %
4382+   X C o n f i g u r e I m a g e                                             %
4383%                                                                             %
4384%                                                                             %
4385%                                                                             %
4386%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4387%
4388%  XConfigureImage() creates a new X image.  It also notifies the window
4389%  manager of the new image size and configures the transient widows.
4390%
4391%  The format of the XConfigureImage method is:
4392%
4393%      MagickBooleanType XConfigureImage(Display *display,
4394%        XResourceInfo *resource_info,XWindows *windows,Image *image,
4395%        ExceptionInfo *exception)
4396%
4397%  A description of each parameter follows:
4398%
4399%    o display: Specifies a connection to an X server; returned from
4400%      XOpenDisplay.
4401%
4402%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4403%
4404%    o windows: Specifies a pointer to a XWindows structure.
4405%
4406%    o image: the image.
4407%
4408%    o exception: return any errors or warnings in this structure.
4409%
4410%    o exception: return any errors or warnings in this structure.
4411%
4412*/
4413static MagickBooleanType XConfigureImage(Display *display,
4414  XResourceInfo *resource_info,XWindows *windows,Image *image,
4415  ExceptionInfo *exception)
4416{
4417  char
4418    geometry[MaxTextExtent];
4419
4420  MagickStatusType
4421    status;
4422
4423  size_t
4424    mask,
4425    height,
4426    width;
4427
4428  ssize_t
4429    x,
4430    y;
4431
4432  XSizeHints
4433    *size_hints;
4434
4435  XWindowChanges
4436    window_changes;
4437
4438  /*
4439    Dismiss if window dimensions are zero.
4440  */
4441  width=(unsigned int) windows->image.window_changes.width;
4442  height=(unsigned int) windows->image.window_changes.height;
4443  if (image->debug != MagickFalse)
4444    (void) LogMagickEvent(X11Event,GetMagickModule(),
4445      "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4446      windows->image.ximage->height,(double) width,(double) height);
4447  if ((width*height) == 0)
4448    return(MagickTrue);
4449  x=0;
4450  y=0;
4451  /*
4452    Resize image to fit Image window dimensions.
4453  */
4454  XSetCursorState(display,windows,MagickTrue);
4455  (void) XFlush(display);
4456  if (((int) width != windows->image.ximage->width) ||
4457      ((int) height != windows->image.ximage->height))
4458    image->taint=MagickTrue;
4459  windows->magnify.x=(int)
4460    width*windows->magnify.x/windows->image.ximage->width;
4461  windows->magnify.y=(int)
4462    height*windows->magnify.y/windows->image.ximage->height;
4463  windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4464  windows->image.y=(int)
4465    (height*windows->image.y/windows->image.ximage->height);
4466  status=XMakeImage(display,resource_info,&windows->image,image,
4467    (unsigned int) width,(unsigned int) height,exception);
4468  if (status == MagickFalse)
4469    XNoticeWidget(display,windows,"Unable to configure X image:",
4470      windows->image.name);
4471  /*
4472    Notify window manager of the new configuration.
4473  */
4474  if (resource_info->image_geometry != (char *) NULL)
4475    (void) FormatLocaleString(geometry,MaxTextExtent,"%s>!",
4476      resource_info->image_geometry);
4477  else
4478    (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
4479      XDisplayWidth(display,windows->image.screen),
4480      XDisplayHeight(display,windows->image.screen));
4481  (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4482  window_changes.width=(int) width;
4483  if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4484    window_changes.width=XDisplayWidth(display,windows->image.screen);
4485  window_changes.height=(int) height;
4486  if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4487    window_changes.height=XDisplayHeight(display,windows->image.screen);
4488  mask=(size_t) (CWWidth | CWHeight);
4489  if (resource_info->backdrop)
4490    {
4491      mask|=CWX | CWY;
4492      window_changes.x=(int)
4493        ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4494      window_changes.y=(int)
4495        ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4496    }
4497  (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4498    (unsigned int) mask,&window_changes);
4499  (void) XClearWindow(display,windows->image.id);
4500  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4501  /*
4502    Update Magnify window configuration.
4503  */
4504  if (windows->magnify.mapped != MagickFalse)
4505    XMakeMagnifyImage(display,windows,exception);
4506  windows->pan.crop_geometry=windows->image.crop_geometry;
4507  XBestIconSize(display,&windows->pan,image);
4508  while (((windows->pan.width << 1) < MaxIconSize) &&
4509         ((windows->pan.height << 1) < MaxIconSize))
4510  {
4511    windows->pan.width<<=1;
4512    windows->pan.height<<=1;
4513  }
4514  if (windows->pan.geometry != (char *) NULL)
4515    (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4516      &windows->pan.width,&windows->pan.height);
4517  window_changes.width=(int) windows->pan.width;
4518  window_changes.height=(int) windows->pan.height;
4519  size_hints=XAllocSizeHints();
4520  if (size_hints != (XSizeHints *) NULL)
4521    {
4522      /*
4523        Set new size hints.
4524      */
4525      size_hints->flags=PSize | PMinSize | PMaxSize;
4526      size_hints->width=window_changes.width;
4527      size_hints->height=window_changes.height;
4528      size_hints->min_width=size_hints->width;
4529      size_hints->min_height=size_hints->height;
4530      size_hints->max_width=size_hints->width;
4531      size_hints->max_height=size_hints->height;
4532      (void) XSetNormalHints(display,windows->pan.id,size_hints);
4533      (void) XFree((void *) size_hints);
4534    }
4535  (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4536    (unsigned int) (CWWidth | CWHeight),&window_changes);
4537  /*
4538    Update icon window configuration.
4539  */
4540  windows->icon.crop_geometry=windows->image.crop_geometry;
4541  XBestIconSize(display,&windows->icon,image);
4542  window_changes.width=(int) windows->icon.width;
4543  window_changes.height=(int) windows->icon.height;
4544  (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4545    (unsigned int) (CWWidth | CWHeight),&window_changes);
4546  XSetCursorState(display,windows,MagickFalse);
4547  return(status != 0 ? MagickTrue : MagickFalse);
4548}
4549
4550/*
4551%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4552%                                                                             %
4553%                                                                             %
4554%                                                                             %
4555+   X C r o p I m a g e                                                       %
4556%                                                                             %
4557%                                                                             %
4558%                                                                             %
4559%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4560%
4561%  XCropImage() allows the user to select a region of the image and crop, copy,
4562%  or cut it.  For copy or cut, the image can subsequently be composited onto
4563%  the image with XPasteImage.
4564%
4565%  The format of the XCropImage method is:
4566%
4567%      MagickBooleanType XCropImage(Display *display,
4568%        XResourceInfo *resource_info,XWindows *windows,Image *image,
4569%        const ClipboardMode mode,ExceptionInfo *exception)
4570%
4571%  A description of each parameter follows:
4572%
4573%    o display: Specifies a connection to an X server; returned from
4574%      XOpenDisplay.
4575%
4576%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4577%
4578%    o windows: Specifies a pointer to a XWindows structure.
4579%
4580%    o image: the image; returned from ReadImage.
4581%
4582%    o mode: This unsigned value specified whether the image should be
4583%      cropped, copied, or cut.
4584%
4585%    o exception: return any errors or warnings in this structure.
4586%
4587*/
4588static MagickBooleanType XCropImage(Display *display,
4589  XResourceInfo *resource_info,XWindows *windows,Image *image,
4590  const ClipboardMode mode,ExceptionInfo *exception)
4591{
4592  static const char
4593    *CropModeMenu[] =
4594    {
4595      "Help",
4596      "Dismiss",
4597      (char *) NULL
4598    },
4599    *RectifyModeMenu[] =
4600    {
4601      "Crop",
4602      "Help",
4603      "Dismiss",
4604      (char *) NULL
4605    };
4606
4607  static const ModeType
4608    CropCommands[] =
4609    {
4610      CropHelpCommand,
4611      CropDismissCommand
4612    },
4613    RectifyCommands[] =
4614    {
4615      RectifyCopyCommand,
4616      RectifyHelpCommand,
4617      RectifyDismissCommand
4618    };
4619
4620  CacheView
4621    *image_view;
4622
4623  char
4624    command[MaxTextExtent],
4625    text[MaxTextExtent];
4626
4627  Cursor
4628    cursor;
4629
4630  int
4631    id,
4632    x,
4633    y;
4634
4635  KeySym
4636    key_symbol;
4637
4638  Image
4639    *crop_image;
4640
4641  MagickRealType
4642    scale_factor;
4643
4644  RectangleInfo
4645    crop_info,
4646    highlight_info;
4647
4648  register Quantum
4649    *q;
4650
4651  unsigned int
4652    height,
4653    width;
4654
4655  size_t
4656    state;
4657
4658  XEvent
4659    event;
4660
4661  /*
4662    Map Command widget.
4663  */
4664  switch (mode)
4665  {
4666    case CopyMode:
4667    {
4668      (void) CloneString(&windows->command.name,"Copy");
4669      break;
4670    }
4671    case CropMode:
4672    {
4673      (void) CloneString(&windows->command.name,"Crop");
4674      break;
4675    }
4676    case CutMode:
4677    {
4678      (void) CloneString(&windows->command.name,"Cut");
4679      break;
4680    }
4681  }
4682  RectifyModeMenu[0]=windows->command.name;
4683  windows->command.data=0;
4684  (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4685  (void) XMapRaised(display,windows->command.id);
4686  XClientMessage(display,windows->image.id,windows->im_protocols,
4687    windows->im_update_widget,CurrentTime);
4688  /*
4689    Track pointer until button 1 is pressed.
4690  */
4691  XQueryPosition(display,windows->image.id,&x,&y);
4692  (void) XSelectInput(display,windows->image.id,
4693    windows->image.attributes.event_mask | PointerMotionMask);
4694  crop_info.x=(ssize_t) windows->image.x+x;
4695  crop_info.y=(ssize_t) windows->image.y+y;
4696  crop_info.width=0;
4697  crop_info.height=0;
4698  cursor=XCreateFontCursor(display,XC_fleur);
4699  state=DefaultState;
4700  do
4701  {
4702    if (windows->info.mapped != MagickFalse)
4703      {
4704        /*
4705          Display pointer position.
4706        */
4707        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4708          (long) crop_info.x,(long) crop_info.y);
4709        XInfoWidget(display,windows,text);
4710      }
4711    /*
4712      Wait for next event.
4713    */
4714    XScreenEvent(display,windows,&event,exception);
4715    if (event.xany.window == windows->command.id)
4716      {
4717        /*
4718          Select a command from the Command widget.
4719        */
4720        id=XCommandWidget(display,windows,CropModeMenu,&event);
4721        if (id < 0)
4722          continue;
4723        switch (CropCommands[id])
4724        {
4725          case CropHelpCommand:
4726          {
4727            switch (mode)
4728            {
4729              case CopyMode:
4730              {
4731                XTextViewWidget(display,resource_info,windows,MagickFalse,
4732                  "Help Viewer - Image Copy",ImageCopyHelp);
4733                break;
4734              }
4735              case CropMode:
4736              {
4737                XTextViewWidget(display,resource_info,windows,MagickFalse,
4738                  "Help Viewer - Image Crop",ImageCropHelp);
4739                break;
4740              }
4741              case CutMode:
4742              {
4743                XTextViewWidget(display,resource_info,windows,MagickFalse,
4744                  "Help Viewer - Image Cut",ImageCutHelp);
4745                break;
4746              }
4747            }
4748            break;
4749          }
4750          case CropDismissCommand:
4751          {
4752            /*
4753              Prematurely exit.
4754            */
4755            state|=EscapeState;
4756            state|=ExitState;
4757            break;
4758          }
4759          default:
4760            break;
4761        }
4762        continue;
4763      }
4764    switch (event.type)
4765    {
4766      case ButtonPress:
4767      {
4768        if (event.xbutton.button != Button1)
4769          break;
4770        if (event.xbutton.window != windows->image.id)
4771          break;
4772        /*
4773          Note first corner of cropping rectangle-- exit loop.
4774        */
4775        (void) XCheckDefineCursor(display,windows->image.id,cursor);
4776        crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4777        crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4778        state|=ExitState;
4779        break;
4780      }
4781      case ButtonRelease:
4782        break;
4783      case Expose:
4784        break;
4785      case KeyPress:
4786      {
4787        if (event.xkey.window != windows->image.id)
4788          break;
4789        /*
4790          Respond to a user key press.
4791        */
4792        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4793          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4794        switch ((int) key_symbol)
4795        {
4796          case XK_Escape:
4797          case XK_F20:
4798          {
4799            /*
4800              Prematurely exit.
4801            */
4802            state|=EscapeState;
4803            state|=ExitState;
4804            break;
4805          }
4806          case XK_F1:
4807          case XK_Help:
4808          {
4809            switch (mode)
4810            {
4811              case CopyMode:
4812              {
4813                XTextViewWidget(display,resource_info,windows,MagickFalse,
4814                  "Help Viewer - Image Copy",ImageCopyHelp);
4815                break;
4816              }
4817              case CropMode:
4818              {
4819                XTextViewWidget(display,resource_info,windows,MagickFalse,
4820                  "Help Viewer - Image Crop",ImageCropHelp);
4821                break;
4822              }
4823              case CutMode:
4824              {
4825                XTextViewWidget(display,resource_info,windows,MagickFalse,
4826                  "Help Viewer - Image Cut",ImageCutHelp);
4827                break;
4828              }
4829            }
4830            break;
4831          }
4832          default:
4833          {
4834            (void) XBell(display,0);
4835            break;
4836          }
4837        }
4838        break;
4839      }
4840      case MotionNotify:
4841      {
4842        if (event.xmotion.window != windows->image.id)
4843          break;
4844        /*
4845          Map and unmap Info widget as text cursor crosses its boundaries.
4846        */
4847        x=event.xmotion.x;
4848        y=event.xmotion.y;
4849        if (windows->info.mapped != MagickFalse)
4850          {
4851            if ((x < (int) (windows->info.x+windows->info.width)) &&
4852                (y < (int) (windows->info.y+windows->info.height)))
4853              (void) XWithdrawWindow(display,windows->info.id,
4854                windows->info.screen);
4855          }
4856        else
4857          if ((x > (int) (windows->info.x+windows->info.width)) ||
4858              (y > (int) (windows->info.y+windows->info.height)))
4859            (void) XMapWindow(display,windows->info.id);
4860        crop_info.x=(ssize_t) windows->image.x+x;
4861        crop_info.y=(ssize_t) windows->image.y+y;
4862        break;
4863      }
4864      default:
4865        break;
4866    }
4867  } while ((state & ExitState) == 0);
4868  (void) XSelectInput(display,windows->image.id,
4869    windows->image.attributes.event_mask);
4870  if ((state & EscapeState) != 0)
4871    {
4872      /*
4873        User want to exit without cropping.
4874      */
4875      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4876      (void) XFreeCursor(display,cursor);
4877      return(MagickTrue);
4878    }
4879  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4880  do
4881  {
4882    /*
4883      Size rectangle as pointer moves until the mouse button is released.
4884    */
4885    x=(int) crop_info.x;
4886    y=(int) crop_info.y;
4887    crop_info.width=0;
4888    crop_info.height=0;
4889    state=DefaultState;
4890    do
4891    {
4892      highlight_info=crop_info;
4893      highlight_info.x=crop_info.x-windows->image.x;
4894      highlight_info.y=crop_info.y-windows->image.y;
4895      if ((highlight_info.width > 3) && (highlight_info.height > 3))
4896        {
4897          /*
4898            Display info and draw cropping rectangle.
4899          */
4900          if (windows->info.mapped == MagickFalse)
4901            (void) XMapWindow(display,windows->info.id);
4902          (void) FormatLocaleString(text,MaxTextExtent,
4903            " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4904            crop_info.height,(double) crop_info.x,(double) crop_info.y);
4905          XInfoWidget(display,windows,text);
4906          XHighlightRectangle(display,windows->image.id,
4907            windows->image.highlight_context,&highlight_info);
4908        }
4909      else
4910        if (windows->info.mapped != MagickFalse)
4911          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4912      /*
4913        Wait for next event.
4914      */
4915      XScreenEvent(display,windows,&event,exception);
4916      if ((highlight_info.width > 3) && (highlight_info.height > 3))
4917        XHighlightRectangle(display,windows->image.id,
4918          windows->image.highlight_context,&highlight_info);
4919      switch (event.type)
4920      {
4921        case ButtonPress:
4922        {
4923          crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4924          crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4925          break;
4926        }
4927        case ButtonRelease:
4928        {
4929          /*
4930            User has committed to cropping rectangle.
4931          */
4932          crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4933          crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4934          XSetCursorState(display,windows,MagickFalse);
4935          state|=ExitState;
4936          windows->command.data=0;
4937          (void) XCommandWidget(display,windows,RectifyModeMenu,
4938            (XEvent *) NULL);
4939          break;
4940        }
4941        case Expose:
4942          break;
4943        case MotionNotify:
4944        {
4945          crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4946          crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4947        }
4948        default:
4949          break;
4950      }
4951      if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4952          ((state & ExitState) != 0))
4953        {
4954          /*
4955            Check boundary conditions.
4956          */
4957          if (crop_info.x < 0)
4958            crop_info.x=0;
4959          else
4960            if (crop_info.x > (ssize_t) windows->image.ximage->width)
4961              crop_info.x=(ssize_t) windows->image.ximage->width;
4962          if ((int) crop_info.x < x)
4963            crop_info.width=(unsigned int) (x-crop_info.x);
4964          else
4965            {
4966              crop_info.width=(unsigned int) (crop_info.x-x);
4967              crop_info.x=(ssize_t) x;
4968            }
4969          if (crop_info.y < 0)
4970            crop_info.y=0;
4971          else
4972            if (crop_info.y > (ssize_t) windows->image.ximage->height)
4973              crop_info.y=(ssize_t) windows->image.ximage->height;
4974          if ((int) crop_info.y < y)
4975            crop_info.height=(unsigned int) (y-crop_info.y);
4976          else
4977            {
4978              crop_info.height=(unsigned int) (crop_info.y-y);
4979              crop_info.y=(ssize_t) y;
4980            }
4981        }
4982    } while ((state & ExitState) == 0);
4983    /*
4984      Wait for user to grab a corner of the rectangle or press return.
4985    */
4986    state=DefaultState;
4987    (void) XMapWindow(display,windows->info.id);
4988    do
4989    {
4990      if (windows->info.mapped != MagickFalse)
4991        {
4992          /*
4993            Display pointer position.
4994          */
4995          (void) FormatLocaleString(text,MaxTextExtent,
4996            " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4997            crop_info.height,(double) crop_info.x,(double) crop_info.y);
4998          XInfoWidget(display,windows,text);
4999        }
5000      highlight_info=crop_info;
5001      highlight_info.x=crop_info.x-windows->image.x;
5002      highlight_info.y=crop_info.y-windows->image.y;
5003      if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
5004        {
5005          state|=EscapeState;
5006          state|=ExitState;
5007          break;
5008        }
5009      XHighlightRectangle(display,windows->image.id,
5010        windows->image.highlight_context,&highlight_info);
5011      XScreenEvent(display,windows,&event,exception);
5012      if (event.xany.window == windows->command.id)
5013        {
5014          /*
5015            Select a command from the Command widget.
5016          */
5017          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5018          id=XCommandWidget(display,windows,RectifyModeMenu,&event);
5019          (void) XSetFunction(display,windows->image.highlight_context,
5020            GXinvert);
5021          XHighlightRectangle(display,windows->image.id,
5022            windows->image.highlight_context,&highlight_info);
5023          if (id >= 0)
5024            switch (RectifyCommands[id])
5025            {
5026              case RectifyCopyCommand:
5027              {
5028                state|=ExitState;
5029                break;
5030              }
5031              case RectifyHelpCommand:
5032              {
5033                (void) XSetFunction(display,windows->image.highlight_context,
5034                  GXcopy);
5035                switch (mode)
5036                {
5037                  case CopyMode:
5038                  {
5039                    XTextViewWidget(display,resource_info,windows,MagickFalse,
5040                      "Help Viewer - Image Copy",ImageCopyHelp);
5041                    break;
5042                  }
5043                  case CropMode:
5044                  {
5045                    XTextViewWidget(display,resource_info,windows,MagickFalse,
5046                      "Help Viewer - Image Crop",ImageCropHelp);
5047                    break;
5048                  }
5049                  case CutMode:
5050                  {
5051                    XTextViewWidget(display,resource_info,windows,MagickFalse,
5052                      "Help Viewer - Image Cut",ImageCutHelp);
5053                    break;
5054                  }
5055                }
5056                (void) XSetFunction(display,windows->image.highlight_context,
5057                  GXinvert);
5058                break;
5059              }
5060              case RectifyDismissCommand:
5061              {
5062                /*
5063                  Prematurely exit.
5064                */
5065                state|=EscapeState;
5066                state|=ExitState;
5067                break;
5068              }
5069              default:
5070                break;
5071            }
5072          continue;
5073        }
5074      XHighlightRectangle(display,windows->image.id,
5075        windows->image.highlight_context,&highlight_info);
5076      switch (event.type)
5077      {
5078        case ButtonPress:
5079        {
5080          if (event.xbutton.button != Button1)
5081            break;
5082          if (event.xbutton.window != windows->image.id)
5083            break;
5084          x=windows->image.x+event.xbutton.x;
5085          y=windows->image.y+event.xbutton.y;
5086          if ((x < (int) (crop_info.x+RoiDelta)) &&
5087              (x > (int) (crop_info.x-RoiDelta)) &&
5088              (y < (int) (crop_info.y+RoiDelta)) &&
5089              (y > (int) (crop_info.y-RoiDelta)))
5090            {
5091              crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5092              crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5093              state|=UpdateConfigurationState;
5094              break;
5095            }
5096          if ((x < (int) (crop_info.x+RoiDelta)) &&
5097              (x > (int) (crop_info.x-RoiDelta)) &&
5098              (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5099              (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5100            {
5101              crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5102              state|=UpdateConfigurationState;
5103              break;
5104            }
5105          if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5106              (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5107              (y < (int) (crop_info.y+RoiDelta)) &&
5108              (y > (int) (crop_info.y-RoiDelta)))
5109            {
5110              crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5111              state|=UpdateConfigurationState;
5112              break;
5113            }
5114          if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5115              (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5116              (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5117              (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5118            {
5119              state|=UpdateConfigurationState;
5120              break;
5121            }
5122        }
5123        case ButtonRelease:
5124        {
5125          if (event.xbutton.window == windows->pan.id)
5126            if ((highlight_info.x != crop_info.x-windows->image.x) ||
5127                (highlight_info.y != crop_info.y-windows->image.y))
5128              XHighlightRectangle(display,windows->image.id,
5129                windows->image.highlight_context,&highlight_info);
5130          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5131            event.xbutton.time);
5132          break;
5133        }
5134        case Expose:
5135        {
5136          if (event.xexpose.window == windows->image.id)
5137            if (event.xexpose.count == 0)
5138              {
5139                event.xexpose.x=(int) highlight_info.x;
5140                event.xexpose.y=(int) highlight_info.y;
5141                event.xexpose.width=(int) highlight_info.width;
5142                event.xexpose.height=(int) highlight_info.height;
5143                XRefreshWindow(display,&windows->image,&event);
5144              }
5145          if (event.xexpose.window == windows->info.id)
5146            if (event.xexpose.count == 0)
5147              XInfoWidget(display,windows,text);
5148          break;
5149        }
5150        case KeyPress:
5151        {
5152          if (event.xkey.window != windows->image.id)
5153            break;
5154          /*
5155            Respond to a user key press.
5156          */
5157          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5158            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5159          switch ((int) key_symbol)
5160          {
5161            case XK_Escape:
5162            case XK_F20:
5163              state|=EscapeState;
5164            case XK_Return:
5165            {
5166              state|=ExitState;
5167              break;
5168            }
5169            case XK_Home:
5170            case XK_KP_Home:
5171            {
5172              crop_info.x=(ssize_t) (windows->image.width/2L-
5173                crop_info.width/2L);
5174              crop_info.y=(ssize_t) (windows->image.height/2L-
5175                crop_info.height/2L);
5176              break;
5177            }
5178            case XK_Left:
5179            case XK_KP_Left:
5180            {
5181              crop_info.x--;
5182              break;
5183            }
5184            case XK_Up:
5185            case XK_KP_Up:
5186            case XK_Next:
5187            {
5188              crop_info.y--;
5189              break;
5190            }
5191            case XK_Right:
5192            case XK_KP_Right:
5193            {
5194              crop_info.x++;
5195              break;
5196            }
5197            case XK_Prior:
5198            case XK_Down:
5199            case XK_KP_Down:
5200            {
5201              crop_info.y++;
5202              break;
5203            }
5204            case XK_F1:
5205            case XK_Help:
5206            {
5207              (void) XSetFunction(display,windows->image.highlight_context,
5208                GXcopy);
5209              switch (mode)
5210              {
5211                case CopyMode:
5212                {
5213                  XTextViewWidget(display,resource_info,windows,MagickFalse,
5214                    "Help Viewer - Image Copy",ImageCopyHelp);
5215                  break;
5216                }
5217                case CropMode:
5218                {
5219                  XTextViewWidget(display,resource_info,windows,MagickFalse,
5220                    "Help Viewer - Image Cropg",ImageCropHelp);
5221                  break;
5222                }
5223                case CutMode:
5224                {
5225                  XTextViewWidget(display,resource_info,windows,MagickFalse,
5226                    "Help Viewer - Image Cutg",ImageCutHelp);
5227                  break;
5228                }
5229              }
5230              (void) XSetFunction(display,windows->image.highlight_context,
5231                GXinvert);
5232              break;
5233            }
5234            default:
5235            {
5236              (void) XBell(display,0);
5237              break;
5238            }
5239          }
5240          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5241            event.xkey.time);
5242          break;
5243        }
5244        case KeyRelease:
5245          break;
5246        case MotionNotify:
5247        {
5248          if (event.xmotion.window != windows->image.id)
5249            break;
5250          /*
5251            Map and unmap Info widget as text cursor crosses its boundaries.
5252          */
5253          x=event.xmotion.x;
5254          y=event.xmotion.y;
5255          if (windows->info.mapped != MagickFalse)
5256            {
5257              if ((x < (int) (windows->info.x+windows->info.width)) &&
5258                  (y < (int) (windows->info.y+windows->info.height)))
5259                (void) XWithdrawWindow(display,windows->info.id,
5260                  windows->info.screen);
5261            }
5262          else
5263            if ((x > (int) (windows->info.x+windows->info.width)) ||
5264                (y > (int) (windows->info.y+windows->info.height)))
5265              (void) XMapWindow(display,windows->info.id);
5266          crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5267          crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5268          break;
5269        }
5270        case SelectionRequest:
5271        {
5272          XSelectionEvent
5273            notify;
5274
5275          XSelectionRequestEvent
5276            *request;
5277
5278          /*
5279            Set primary selection.
5280          */
5281          (void) FormatLocaleString(text,MaxTextExtent,
5282            "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5283            crop_info.height,(double) crop_info.x,(double) crop_info.y);
5284          request=(&(event.xselectionrequest));
5285          (void) XChangeProperty(request->display,request->requestor,
5286            request->property,request->target,8,PropModeReplace,
5287            (unsigned char *) text,(int) strlen(text));
5288          notify.type=SelectionNotify;
5289          notify.display=request->display;
5290          notify.requestor=request->requestor;
5291          notify.selection=request->selection;
5292          notify.target=request->target;
5293          notify.time=request->time;
5294          if (request->property == None)
5295            notify.property=request->target;
5296          else
5297            notify.property=request->property;
5298          (void) XSendEvent(request->display,request->requestor,False,0,
5299            (XEvent *) &notify);
5300        }
5301        default:
5302          break;
5303      }
5304      if ((state & UpdateConfigurationState) != 0)
5305        {
5306          (void) XPutBackEvent(display,&event);
5307          (void) XCheckDefineCursor(display,windows->image.id,cursor);
5308          break;
5309        }
5310    } while ((state & ExitState) == 0);
5311  } while ((state & ExitState) == 0);
5312  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5313  XSetCursorState(display,windows,MagickFalse);
5314  if ((state & EscapeState) != 0)
5315    return(MagickTrue);
5316  if (mode == CropMode)
5317    if (((int) crop_info.width != windows->image.ximage->width) ||
5318        ((int) crop_info.height != windows->image.ximage->height))
5319      {
5320        /*
5321          Reconfigure Image window as defined by cropping rectangle.
5322        */
5323        XSetCropGeometry(display,windows,&crop_info,image);
5324        windows->image.window_changes.width=(int) crop_info.width;
5325        windows->image.window_changes.height=(int) crop_info.height;
5326        (void) XConfigureImage(display,resource_info,windows,image,exception);
5327        return(MagickTrue);
5328      }
5329  /*
5330    Copy image before applying image transforms.
5331  */
5332  XSetCursorState(display,windows,MagickTrue);
5333  XCheckRefreshWindows(display,windows);
5334  width=(unsigned int) image->columns;
5335  height=(unsigned int) image->rows;
5336  x=0;
5337  y=0;
5338  if (windows->image.crop_geometry != (char *) NULL)
5339    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5340  scale_factor=(MagickRealType) width/windows->image.ximage->width;
5341  crop_info.x+=x;
5342  crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5343  crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5344  scale_factor=(MagickRealType) height/windows->image.ximage->height;
5345  crop_info.y+=y;
5346  crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5347  crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5348  crop_image=CropImage(image,&crop_info,exception);
5349  XSetCursorState(display,windows,MagickFalse);
5350  if (crop_image == (Image *) NULL)
5351    return(MagickFalse);
5352  if (resource_info->copy_image != (Image *) NULL)
5353    resource_info->copy_image=DestroyImage(resource_info->copy_image);
5354  resource_info->copy_image=crop_image;
5355  if (mode == CopyMode)
5356    {
5357      (void) XConfigureImage(display,resource_info,windows,image,exception);
5358      return(MagickTrue);
5359    }
5360  /*
5361    Cut image.
5362  */
5363  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
5364    return(MagickFalse);
5365  image->matte=MagickTrue;
5366  image_view=AcquireCacheView(image);
5367  for (y=0; y < (int) crop_info.height; y++)
5368  {
5369    q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5370      crop_info.width,1,exception);
5371    if (q == (Quantum *) NULL)
5372      break;
5373    for (x=0; x < (int) crop_info.width; x++)
5374    {
5375      SetPixelAlpha(image,TransparentAlpha,q);
5376      q+=GetPixelChannels(image);
5377    }
5378    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5379      break;
5380  }
5381  image_view=DestroyCacheView(image_view);
5382  /*
5383    Update image configuration.
5384  */
5385  XConfigureImageColormap(display,resource_info,windows,image,exception);
5386  (void) XConfigureImage(display,resource_info,windows,image,exception);
5387  return(MagickTrue);
5388}
5389
5390/*
5391%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5392%                                                                             %
5393%                                                                             %
5394%                                                                             %
5395+   X D r a w I m a g e                                                       %
5396%                                                                             %
5397%                                                                             %
5398%                                                                             %
5399%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5400%
5401%  XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5402%  the image.
5403%
5404%  The format of the XDrawEditImage method is:
5405%
5406%      MagickBooleanType XDrawEditImage(Display *display,
5407%        XResourceInfo *resource_info,XWindows *windows,Image **image,
5408%        ExceptionInfo *exception)
5409%
5410%  A description of each parameter follows:
5411%
5412%    o display: Specifies a connection to an X server; returned from
5413%      XOpenDisplay.
5414%
5415%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5416%
5417%    o windows: Specifies a pointer to a XWindows structure.
5418%
5419%    o image: the image.
5420%
5421%    o exception: return any errors or warnings in this structure.
5422%
5423*/
5424static MagickBooleanType XDrawEditImage(Display *display,
5425  XResourceInfo *resource_info,XWindows *windows,Image **image,
5426  ExceptionInfo *exception)
5427{
5428  static const char
5429    *DrawMenu[] =
5430    {
5431      "Element",
5432      "Color",
5433      "Stipple",
5434      "Width",
5435      "Undo",
5436      "Help",
5437      "Dismiss",
5438      (char *) NULL
5439    };
5440
5441  static ElementType
5442    element = PointElement;
5443
5444  static const ModeType
5445    DrawCommands[] =
5446    {
5447      DrawElementCommand,
5448      DrawColorCommand,
5449      DrawStippleCommand,
5450      DrawWidthCommand,
5451      DrawUndoCommand,
5452      DrawHelpCommand,
5453      DrawDismissCommand
5454    };
5455
5456  static Pixmap
5457    stipple = (Pixmap) NULL;
5458
5459  static unsigned int
5460    pen_id = 0,
5461    line_width = 1;
5462
5463  char
5464    command[MaxTextExtent],
5465    text[MaxTextExtent];
5466
5467  Cursor
5468    cursor;
5469
5470  int
5471    entry,
5472    id,
5473    number_coordinates,
5474    x,
5475    y;
5476
5477  MagickRealType
5478    degrees;
5479
5480  MagickStatusType
5481    status;
5482
5483  RectangleInfo
5484    rectangle_info;
5485
5486  register int
5487    i;
5488
5489  unsigned int
5490    distance,
5491    height,
5492    max_coordinates,
5493    width;
5494
5495  size_t
5496    state;
5497
5498  Window
5499    root_window;
5500
5501  XDrawInfo
5502    draw_info;
5503
5504  XEvent
5505    event;
5506
5507  XPoint
5508    *coordinate_info;
5509
5510  XSegment
5511    line_info;
5512
5513  /*
5514    Allocate polygon info.
5515  */
5516  max_coordinates=2048;
5517  coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5518    sizeof(*coordinate_info));
5519  if (coordinate_info == (XPoint *) NULL)
5520    {
5521      (void) ThrowMagickException(exception,GetMagickModule(),
5522        ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5523      return(MagickFalse);
5524    }
5525  /*
5526    Map Command widget.
5527  */
5528  (void) CloneString(&windows->command.name,"Draw");
5529  windows->command.data=4;
5530  (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5531  (void) XMapRaised(display,windows->command.id);
5532  XClientMessage(display,windows->image.id,windows->im_protocols,
5533    windows->im_update_widget,CurrentTime);
5534  /*
5535    Wait for first button press.
5536  */
5537  root_window=XRootWindow(display,XDefaultScreen(display));
5538  draw_info.stencil=OpaqueStencil;
5539  status=MagickTrue;
5540  cursor=XCreateFontCursor(display,XC_tcross);
5541  for ( ; ; )
5542  {
5543    XQueryPosition(display,windows->image.id,&x,&y);
5544    (void) XSelectInput(display,windows->image.id,
5545      windows->image.attributes.event_mask | PointerMotionMask);
5546    (void) XCheckDefineCursor(display,windows->image.id,cursor);
5547    state=DefaultState;
5548    do
5549    {
5550      if (windows->info.mapped != MagickFalse)
5551        {
5552          /*
5553            Display pointer position.
5554          */
5555          (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
5556            x+windows->image.x,y+windows->image.y);
5557          XInfoWidget(display,windows,text);
5558        }
5559      /*
5560        Wait for next event.
5561      */
5562      XScreenEvent(display,windows,&event,exception);
5563      if (event.xany.window == windows->command.id)
5564        {
5565          /*
5566            Select a command from the Command widget.
5567          */
5568          id=XCommandWidget(display,windows,DrawMenu,&event);
5569          if (id < 0)
5570            continue;
5571          switch (DrawCommands[id])
5572          {
5573            case DrawElementCommand:
5574            {
5575              static const char
5576                *Elements[] =
5577                {
5578                  "point",
5579                  "line",
5580                  "rectangle",
5581                  "fill rectangle",
5582                  "circle",
5583                  "fill circle",
5584                  "ellipse",
5585                  "fill ellipse",
5586                  "polygon",
5587                  "fill polygon",
5588                  (char *) NULL,
5589                };
5590
5591              /*
5592                Select a command from the pop-up menu.
5593              */
5594              element=(ElementType) (XMenuWidget(display,windows,
5595                DrawMenu[id],Elements,command)+1);
5596              break;
5597            }
5598            case DrawColorCommand:
5599            {
5600              const char
5601                *ColorMenu[MaxNumberPens+1];
5602
5603              int
5604                pen_number;
5605
5606              MagickBooleanType
5607                transparent;
5608
5609              XColor
5610                color;
5611
5612              /*
5613                Initialize menu selections.
5614              */
5615              for (i=0; i < (int) (MaxNumberPens-2); i++)
5616                ColorMenu[i]=resource_info->pen_colors[i];
5617              ColorMenu[MaxNumberPens-2]="transparent";
5618              ColorMenu[MaxNumberPens-1]="Browser...";
5619              ColorMenu[MaxNumberPens]=(char *) NULL;
5620              /*
5621                Select a pen color from the pop-up menu.
5622              */
5623              pen_number=XMenuWidget(display,windows,DrawMenu[id],
5624                (const char **) ColorMenu,command);
5625              if (pen_number < 0)
5626                break;
5627              transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5628                MagickFalse;
5629              if (transparent != MagickFalse)
5630                {
5631                  draw_info.stencil=TransparentStencil;
5632                  break;
5633                }
5634              if (pen_number == (MaxNumberPens-1))
5635                {
5636                  static char
5637                    color_name[MaxTextExtent] = "gray";
5638
5639                  /*
5640                    Select a pen color from a dialog.
5641                  */
5642                  resource_info->pen_colors[pen_number]=color_name;
5643                  XColorBrowserWidget(display,windows,"Select",color_name);
5644                  if (*color_name == '\0')
5645                    break;
5646                }
5647              /*
5648                Set pen color.
5649              */
5650              (void) XParseColor(display,windows->map_info->colormap,
5651                resource_info->pen_colors[pen_number],&color);
5652              XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5653                (unsigned int) MaxColors,&color);
5654              windows->pixel_info->pen_colors[pen_number]=color;
5655              pen_id=(unsigned int) pen_number;
5656              draw_info.stencil=OpaqueStencil;
5657              break;
5658            }
5659            case DrawStippleCommand:
5660            {
5661              Image
5662                *stipple_image;
5663
5664              ImageInfo
5665                *image_info;
5666
5667              int
5668                status;
5669
5670              static char
5671                filename[MaxTextExtent] = "\0";
5672
5673              static const char
5674                *StipplesMenu[] =
5675                {
5676                  "Brick",
5677                  "Diagonal",
5678                  "Scales",
5679                  "Vertical",
5680                  "Wavy",
5681                  "Translucent",
5682                  "Opaque",
5683                  (char *) NULL,
5684                  (char *) NULL,
5685                };
5686
5687              /*
5688                Select a command from the pop-up menu.
5689              */
5690              StipplesMenu[7]="Open...";
5691              entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5692                command);
5693              if (entry < 0)
5694                break;
5695              if (stipple != (Pixmap) NULL)
5696                (void) XFreePixmap(display,stipple);
5697              stipple=(Pixmap) NULL;
5698              if (entry != 7)
5699                {
5700                  switch (entry)
5701                  {
5702                    case 0:
5703                    {
5704                      stipple=XCreateBitmapFromData(display,root_window,
5705                        (char *) BricksBitmap,BricksWidth,BricksHeight);
5706                      break;
5707                    }
5708                    case 1:
5709                    {
5710                      stipple=XCreateBitmapFromData(display,root_window,
5711                        (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5712                      break;
5713                    }
5714                    case 2:
5715                    {
5716                      stipple=XCreateBitmapFromData(display,root_window,
5717                        (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5718                      break;
5719                    }
5720                    case 3:
5721                    {
5722                      stipple=XCreateBitmapFromData(display,root_window,
5723                        (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5724                      break;
5725                    }
5726                    case 4:
5727                    {
5728                      stipple=XCreateBitmapFromData(display,root_window,
5729                        (char *) WavyBitmap,WavyWidth,WavyHeight);
5730                      break;
5731                    }
5732                    case 5:
5733                    {
5734                      stipple=XCreateBitmapFromData(display,root_window,
5735                        (char *) HighlightBitmap,HighlightWidth,
5736                        HighlightHeight);
5737                      break;
5738                    }
5739                    case 6:
5740                    default:
5741                    {
5742                      stipple=XCreateBitmapFromData(display,root_window,
5743                        (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5744                      break;
5745                    }
5746                  }
5747                  break;
5748                }
5749              XFileBrowserWidget(display,windows,"Stipple",filename);
5750              if (*filename == '\0')
5751                break;
5752              /*
5753                Read image.
5754              */
5755              XSetCursorState(display,windows,MagickTrue);
5756              XCheckRefreshWindows(display,windows);
5757              image_info=AcquireImageInfo();
5758              (void) CopyMagickString(image_info->filename,filename,
5759                MaxTextExtent);
5760              stipple_image=ReadImage(image_info,exception);
5761              CatchException(exception);
5762              XSetCursorState(display,windows,MagickFalse);
5763              if (stipple_image == (Image *) NULL)
5764                break;
5765              (void) AcquireUniqueFileResource(filename);
5766              (void) FormatLocaleString(stipple_image->filename,MaxTextExtent,
5767                "xbm:%s",filename);
5768              (void) WriteImage(image_info,stipple_image,exception);
5769              stipple_image=DestroyImage(stipple_image);
5770              image_info=DestroyImageInfo(image_info);
5771              status=XReadBitmapFile(display,root_window,filename,&width,
5772                &height,&stipple,&x,&y);
5773              (void) RelinquishUniqueFileResource(filename);
5774              if ((status != BitmapSuccess) != 0)
5775                XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5776                  filename);
5777              break;
5778            }
5779            case DrawWidthCommand:
5780            {
5781              static char
5782                width[MaxTextExtent] = "0";
5783
5784              static const char
5785                *WidthsMenu[] =
5786                {
5787                  "1",
5788                  "2",
5789                  "4",
5790                  "8",
5791                  "16",
5792                  "Dialog...",
5793                  (char *) NULL,
5794                };
5795
5796              /*
5797                Select a command from the pop-up menu.
5798              */
5799              entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5800                command);
5801              if (entry < 0)
5802                break;
5803              if (entry != 5)
5804                {
5805                  line_width=(unsigned int) StringToUnsignedLong(
5806                    WidthsMenu[entry]);
5807                  break;
5808                }
5809              (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5810                width);
5811              if (*width == '\0')
5812                break;
5813              line_width=(unsigned int) StringToUnsignedLong(width);
5814              break;
5815            }
5816            case DrawUndoCommand:
5817            {
5818              (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5819                image,exception);
5820              break;
5821            }
5822            case DrawHelpCommand:
5823            {
5824              XTextViewWidget(display,resource_info,windows,MagickFalse,
5825                "Help Viewer - Image Rotation",ImageDrawHelp);
5826              (void) XCheckDefineCursor(display,windows->image.id,cursor);
5827              break;
5828            }
5829            case DrawDismissCommand:
5830            {
5831              /*
5832                Prematurely exit.
5833              */
5834              state|=EscapeState;
5835              state|=ExitState;
5836              break;
5837            }
5838            default:
5839              break;
5840          }
5841          (void) XCheckDefineCursor(display,windows->image.id,cursor);
5842          continue;
5843        }
5844      switch (event.type)
5845      {
5846        case ButtonPress:
5847        {
5848          if (event.xbutton.button != Button1)
5849            break;
5850          if (event.xbutton.window != windows->image.id)
5851            break;
5852          /*
5853            exit loop.
5854          */
5855          x=event.xbutton.x;
5856          y=event.xbutton.y;
5857          state|=ExitState;
5858          break;
5859        }
5860        case ButtonRelease:
5861          break;
5862        case Expose:
5863          break;
5864        case KeyPress:
5865        {
5866          KeySym
5867            key_symbol;
5868
5869          if (event.xkey.window != windows->image.id)
5870            break;
5871          /*
5872            Respond to a user key press.
5873          */
5874          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5875            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5876          switch ((int) key_symbol)
5877          {
5878            case XK_Escape:
5879            case XK_F20:
5880            {
5881              /*
5882                Prematurely exit.
5883              */
5884              state|=EscapeState;
5885              state|=ExitState;
5886              break;
5887            }
5888            case XK_F1:
5889            case XK_Help:
5890            {
5891              XTextViewWidget(display,resource_info,windows,MagickFalse,
5892                "Help Viewer - Image Rotation",ImageDrawHelp);
5893              break;
5894            }
5895            default:
5896            {
5897              (void) XBell(display,0);
5898              break;
5899            }
5900          }
5901          break;
5902        }
5903        case MotionNotify:
5904        {
5905          /*
5906            Map and unmap Info widget as text cursor crosses its boundaries.
5907          */
5908          x=event.xmotion.x;
5909          y=event.xmotion.y;
5910          if (windows->info.mapped != MagickFalse)
5911            {
5912              if ((x < (int) (windows->info.x+windows->info.width)) &&
5913                  (y < (int) (windows->info.y+windows->info.height)))
5914                (void) XWithdrawWindow(display,windows->info.id,
5915                  windows->info.screen);
5916            }
5917          else
5918            if ((x > (int) (windows->info.x+windows->info.width)) ||
5919                (y > (int) (windows->info.y+windows->info.height)))
5920              (void) XMapWindow(display,windows->info.id);
5921          break;
5922        }
5923      }
5924    } while ((state & ExitState) == 0);
5925    (void) XSelectInput(display,windows->image.id,
5926      windows->image.attributes.event_mask);
5927    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5928    if ((state & EscapeState) != 0)
5929      break;
5930    /*
5931      Draw element as pointer moves until the button is released.
5932    */
5933    distance=0;
5934    degrees=0.0;
5935    line_info.x1=x;
5936    line_info.y1=y;
5937    line_info.x2=x;
5938    line_info.y2=y;
5939    rectangle_info.x=(ssize_t) x;
5940    rectangle_info.y=(ssize_t) y;
5941    rectangle_info.width=0;
5942    rectangle_info.height=0;
5943    number_coordinates=1;
5944    coordinate_info->x=x;
5945    coordinate_info->y=y;
5946    (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5947    state=DefaultState;
5948    do
5949    {
5950      switch (element)
5951      {
5952        case PointElement:
5953        default:
5954        {
5955          if (number_coordinates > 1)
5956            {
5957              (void) XDrawLines(display,windows->image.id,
5958                windows->image.highlight_context,coordinate_info,
5959                number_coordinates,CoordModeOrigin);
5960              (void) FormatLocaleString(text,MaxTextExtent," %+d%+d",
5961                coordinate_info[number_coordinates-1].x,
5962                coordinate_info[number_coordinates-1].y);
5963              XInfoWidget(display,windows,text);
5964            }
5965          break;
5966        }
5967        case LineElement:
5968        {
5969          if (distance > 9)
5970            {
5971              /*
5972                Display angle of the line.
5973              */
5974              degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5975                line_info.y1),(double) (line_info.x2-line_info.x1)));
5976              (void) FormatLocaleString(text,MaxTextExtent," %g",
5977                (double) degrees);
5978              XInfoWidget(display,windows,text);
5979              XHighlightLine(display,windows->image.id,
5980                windows->image.highlight_context,&line_info);
5981            }
5982          else
5983            if (windows->info.mapped != MagickFalse)
5984              (void) XWithdrawWindow(display,windows->info.id,
5985                windows->info.screen);
5986          break;
5987        }
5988        case RectangleElement:
5989        case FillRectangleElement:
5990        {
5991          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5992            {
5993              /*
5994                Display info and draw drawing rectangle.
5995              */
5996              (void) FormatLocaleString(text,MaxTextExtent,
5997                " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5998                (double) rectangle_info.height,(double) rectangle_info.x,
5999                (double) rectangle_info.y);
6000              XInfoWidget(display,windows,text);
6001              XHighlightRectangle(display,windows->image.id,
6002                windows->image.highlight_context,&rectangle_info);
6003            }
6004          else
6005            if (windows->info.mapped != MagickFalse)
6006              (void) XWithdrawWindow(display,windows->info.id,
6007                windows->info.screen);
6008          break;
6009        }
6010        case CircleElement:
6011        case FillCircleElement:
6012        case EllipseElement:
6013        case FillEllipseElement:
6014        {
6015          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6016            {
6017              /*
6018                Display info and draw drawing rectangle.
6019              */
6020              (void) FormatLocaleString(text,MaxTextExtent,
6021                " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
6022                (double) rectangle_info.height,(double) rectangle_info.x,
6023                (double) rectangle_info.y);
6024              XInfoWidget(display,windows,text);
6025              XHighlightEllipse(display,windows->image.id,
6026                windows->image.highlight_context,&rectangle_info);
6027            }
6028          else
6029            if (windows->info.mapped != MagickFalse)
6030              (void) XWithdrawWindow(display,windows->info.id,
6031                windows->info.screen);
6032          break;
6033        }
6034        case PolygonElement:
6035        case FillPolygonElement:
6036        {
6037          if (number_coordinates > 1)
6038            (void) XDrawLines(display,windows->image.id,
6039              windows->image.highlight_context,coordinate_info,
6040              number_coordinates,CoordModeOrigin);
6041          if (distance > 9)
6042            {
6043              /*
6044                Display angle of the line.
6045              */
6046              degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
6047                line_info.y1),(double) (line_info.x2-line_info.x1)));
6048              (void) FormatLocaleString(text,MaxTextExtent," %g",
6049                (double) degrees);
6050              XInfoWidget(display,windows,text);
6051              XHighlightLine(display,windows->image.id,
6052                windows->image.highlight_context,&line_info);
6053            }
6054          else
6055            if (windows->info.mapped != MagickFalse)
6056              (void) XWithdrawWindow(display,windows->info.id,
6057                windows->info.screen);
6058          break;
6059        }
6060      }
6061      /*
6062        Wait for next event.
6063      */
6064      XScreenEvent(display,windows,&event,exception);
6065      switch (element)
6066      {
6067        case PointElement:
6068        default:
6069        {
6070          if (number_coordinates > 1)
6071            (void) XDrawLines(display,windows->image.id,
6072              windows->image.highlight_context,coordinate_info,
6073              number_coordinates,CoordModeOrigin);
6074          break;
6075        }
6076        case LineElement:
6077        {
6078          if (distance > 9)
6079            XHighlightLine(display,windows->image.id,
6080              windows->image.highlight_context,&line_info);
6081          break;
6082        }
6083        case RectangleElement:
6084        case FillRectangleElement:
6085        {
6086          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6087            XHighlightRectangle(display,windows->image.id,
6088              windows->image.highlight_context,&rectangle_info);
6089          break;
6090        }
6091        case CircleElement:
6092        case FillCircleElement:
6093        case EllipseElement:
6094        case FillEllipseElement:
6095        {
6096          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6097            XHighlightEllipse(display,windows->image.id,
6098              windows->image.highlight_context,&rectangle_info);
6099          break;
6100        }
6101        case PolygonElement:
6102        case FillPolygonElement:
6103        {
6104          if (number_coordinates > 1)
6105            (void) XDrawLines(display,windows->image.id,
6106              windows->image.highlight_context,coordinate_info,
6107              number_coordinates,CoordModeOrigin);
6108          if (distance > 9)
6109            XHighlightLine(display,windows->image.id,
6110              windows->image.highlight_context,&line_info);
6111          break;
6112        }
6113      }
6114      switch (event.type)
6115      {
6116        case ButtonPress:
6117          break;
6118        case ButtonRelease:
6119        {
6120          /*
6121            User has committed to element.
6122          */
6123          line_info.x2=event.xbutton.x;
6124          line_info.y2=event.xbutton.y;
6125          rectangle_info.x=(ssize_t) event.xbutton.x;
6126          rectangle_info.y=(ssize_t) event.xbutton.y;
6127          coordinate_info[number_coordinates].x=event.xbutton.x;
6128          coordinate_info[number_coordinates].y=event.xbutton.y;
6129          if (((element != PolygonElement) &&
6130               (element != FillPolygonElement)) || (distance <= 9))
6131            {
6132              state|=ExitState;
6133              break;
6134            }
6135          number_coordinates++;
6136          if (number_coordinates < (int) max_coordinates)
6137            {
6138              line_info.x1=event.xbutton.x;
6139              line_info.y1=event.xbutton.y;
6140              break;
6141            }
6142          max_coordinates<<=1;
6143          coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6144            max_coordinates,sizeof(*coordinate_info));
6145          if (coordinate_info == (XPoint *) NULL)
6146            (void) ThrowMagickException(exception,GetMagickModule(),
6147              ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6148          break;
6149        }
6150        case Expose:
6151          break;
6152        case MotionNotify:
6153        {
6154          if (event.xmotion.window != windows->image.id)
6155            break;
6156          if (element != PointElement)
6157            {
6158              line_info.x2=event.xmotion.x;
6159              line_info.y2=event.xmotion.y;
6160              rectangle_info.x=(ssize_t) event.xmotion.x;
6161              rectangle_info.y=(ssize_t) event.xmotion.y;
6162              break;
6163            }
6164          coordinate_info[number_coordinates].x=event.xbutton.x;
6165          coordinate_info[number_coordinates].y=event.xbutton.y;
6166          number_coordinates++;
6167          if (number_coordinates < (int) max_coordinates)
6168            break;
6169          max_coordinates<<=1;
6170          coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6171            max_coordinates,sizeof(*coordinate_info));
6172          if (coordinate_info == (XPoint *) NULL)
6173            (void) ThrowMagickException(exception,GetMagickModule(),
6174              ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6175          break;
6176        }
6177        default:
6178          break;
6179      }
6180      /*
6181        Check boundary conditions.
6182      */
6183      if (line_info.x2 < 0)
6184        line_info.x2=0;
6185      else
6186        if (line_info.x2 > (int) windows->image.width)
6187          line_info.x2=(short) windows->image.width;
6188      if (line_info.y2 < 0)
6189        line_info.y2=0;
6190      else
6191        if (line_info.y2 > (int) windows->image.height)
6192          line_info.y2=(short) windows->image.height;
6193      distance=(unsigned int)
6194        (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6195         ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6196      if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6197          ((state & ExitState) != 0))
6198        {
6199          if (rectangle_info.x < 0)
6200            rectangle_info.x=0;
6201          else
6202            if (rectangle_info.x > (ssize_t) windows->image.width)
6203              rectangle_info.x=(ssize_t) windows->image.width;
6204          if ((int) rectangle_info.x < x)
6205            rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6206          else
6207            {
6208              rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6209              rectangle_info.x=(ssize_t) x;
6210            }
6211          if (rectangle_info.y < 0)
6212            rectangle_info.y=0;
6213          else
6214            if (rectangle_info.y > (ssize_t) windows->image.height)
6215              rectangle_info.y=(ssize_t) windows->image.height;
6216          if ((int) rectangle_info.y < y)
6217            rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6218          else
6219            {
6220              rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6221              rectangle_info.y=(ssize_t) y;
6222            }
6223        }
6224    } while ((state & ExitState) == 0);
6225    (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6226    if ((element == PointElement) || (element == PolygonElement) ||
6227        (element == FillPolygonElement))
6228      {
6229        /*
6230          Determine polygon bounding box.
6231        */
6232        rectangle_info.x=(ssize_t) coordinate_info->x;
6233        rectangle_info.y=(ssize_t) coordinate_info->y;
6234        x=coordinate_info->x;
6235        y=coordinate_info->y;
6236        for (i=1; i < number_coordinates; i++)
6237        {
6238          if (coordinate_info[i].x > x)
6239            x=coordinate_info[i].x;
6240          if (coordinate_info[i].y > y)
6241            y=coordinate_info[i].y;
6242          if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6243            rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6244          if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6245            rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6246        }
6247        rectangle_info.width=(size_t) (x-rectangle_info.x);
6248        rectangle_info.height=(size_t) (y-rectangle_info.y);
6249        for (i=0; i < number_coordinates; i++)
6250        {
6251          coordinate_info[i].x-=rectangle_info.x;
6252          coordinate_info[i].y-=rectangle_info.y;
6253        }
6254      }
6255    else
6256      if (distance <= 9)
6257        continue;
6258      else
6259        if ((element == RectangleElement) ||
6260            (element == CircleElement) || (element == EllipseElement))
6261          {
6262            rectangle_info.width--;
6263            rectangle_info.height--;
6264          }
6265    /*
6266      Drawing is relative to image configuration.
6267    */
6268    draw_info.x=(int) rectangle_info.x;
6269    draw_info.y=(int) rectangle_info.y;
6270    (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6271      image,exception);
6272    width=(unsigned int) (*image)->columns;
6273    height=(unsigned int) (*image)->rows;
6274    x=0;
6275    y=0;
6276    if (windows->image.crop_geometry != (char *) NULL)
6277      (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6278    draw_info.x+=windows->image.x-(line_width/2);
6279    if (draw_info.x < 0)
6280      draw_info.x=0;
6281    draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6282    draw_info.y+=windows->image.y-(line_width/2);
6283    if (draw_info.y < 0)
6284      draw_info.y=0;
6285    draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6286    draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6287    if (draw_info.width > (unsigned int) (*image)->columns)
6288      draw_info.width=(unsigned int) (*image)->columns;
6289    draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6290    if (draw_info.height > (unsigned int) (*image)->rows)
6291      draw_info.height=(unsigned int) (*image)->rows;
6292    (void) FormatLocaleString(draw_info.geometry,MaxTextExtent,"%ux%u%+d%+d",
6293      width*draw_info.width/windows->image.ximage->width,
6294      height*draw_info.height/windows->image.ximage->height,
6295      draw_info.x+x,draw_info.y+y);
6296    /*
6297      Initialize drawing attributes.
6298    */
6299    draw_info.degrees=0.0;
6300    draw_info.element=element;
6301    draw_info.stipple=stipple;
6302    draw_info.line_width=line_width;
6303    draw_info.line_info=line_info;
6304    if (line_info.x1 > (int) (line_width/2))
6305      draw_info.line_info.x1=(short) line_width/2;
6306    if (line_info.y1 > (int) (line_width/2))
6307      draw_info.line_info.y1=(short) line_width/2;
6308    draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6309    draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6310    if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6311      {
6312        draw_info.line_info.x2=(-draw_info.line_info.x2);
6313        draw_info.line_info.y2=(-draw_info.line_info.y2);
6314      }
6315    if (draw_info.line_info.x2 < 0)
6316      {
6317        draw_info.line_info.x2=(-draw_info.line_info.x2);
6318        Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6319      }
6320    if (draw_info.line_info.y2 < 0)
6321      {
6322        draw_info.line_info.y2=(-draw_info.line_info.y2);
6323        Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6324      }
6325    draw_info.rectangle_info=rectangle_info;
6326    if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6327      draw_info.rectangle_info.x=(ssize_t) line_width/2;
6328    if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6329      draw_info.rectangle_info.y=(ssize_t) line_width/2;
6330    draw_info.number_coordinates=(unsigned int) number_coordinates;
6331    draw_info.coordinate_info=coordinate_info;
6332    windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6333    /*
6334      Draw element on image.
6335    */
6336    XSetCursorState(display,windows,MagickTrue);
6337    XCheckRefreshWindows(display,windows);
6338    status=XDrawImage(display,windows->pixel_info,&draw_info,*image,exception);
6339    XSetCursorState(display,windows,MagickFalse);
6340    /*
6341      Update image colormap and return to image drawing.
6342    */
6343    XConfigureImageColormap(display,resource_info,windows,*image,exception);
6344    (void) XConfigureImage(display,resource_info,windows,*image,exception);
6345  }
6346  XSetCursorState(display,windows,MagickFalse);
6347  coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6348  return(status != 0 ? MagickTrue : MagickFalse);
6349}
6350
6351/*
6352%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6353%                                                                             %
6354%                                                                             %
6355%                                                                             %
6356+   X D r a w P a n R e c t a n g l e                                         %
6357%                                                                             %
6358%                                                                             %
6359%                                                                             %
6360%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6361%
6362%  XDrawPanRectangle() draws a rectangle in the pan window.  The pan window
6363%  displays a zoom image and the rectangle shows which portion of the image is
6364%  displayed in the Image window.
6365%
6366%  The format of the XDrawPanRectangle method is:
6367%
6368%      XDrawPanRectangle(Display *display,XWindows *windows)
6369%
6370%  A description of each parameter follows:
6371%
6372%    o display: Specifies a connection to an X server;  returned from
6373%      XOpenDisplay.
6374%
6375%    o windows: Specifies a pointer to a XWindows structure.
6376%
6377*/
6378static void XDrawPanRectangle(Display *display,XWindows *windows)
6379{
6380  MagickRealType
6381    scale_factor;
6382
6383  RectangleInfo
6384    highlight_info;
6385
6386  /*
6387    Determine dimensions of the panning rectangle.
6388  */
6389  scale_factor=(MagickRealType) windows->pan.width/windows->image.ximage->width;
6390  highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6391  highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6392  scale_factor=(MagickRealType)
6393    windows->pan.height/windows->image.ximage->height;
6394  highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6395  highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6396  /*
6397    Display the panning rectangle.
6398  */
6399  (void) XClearWindow(display,windows->pan.id);
6400  XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6401    &highlight_info);
6402}
6403
6404/*
6405%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6406%                                                                             %
6407%                                                                             %
6408%                                                                             %
6409+   X I m a g e C a c h e                                                     %
6410%                                                                             %
6411%                                                                             %
6412%                                                                             %
6413%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6414%
6415%  XImageCache() handles the creation, manipulation, and destruction of the
6416%  image cache (undo and redo buffers).
6417%
6418%  The format of the XImageCache method is:
6419%
6420%      void XImageCache(Display *display,XResourceInfo *resource_info,
6421%        XWindows *windows,const CommandType command,Image **image,
6422%        ExceptionInfo *exception)
6423%
6424%  A description of each parameter follows:
6425%
6426%    o display: Specifies a connection to an X server; returned from
6427%      XOpenDisplay.
6428%
6429%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6430%
6431%    o windows: Specifies a pointer to a XWindows structure.
6432%
6433%    o command: Specifies a command to perform.
6434%
6435%    o image: the image;  XImageCache may transform the image and return a new
6436%      image pointer.
6437%
6438%    o exception: return any errors or warnings in this structure.
6439%
6440*/
6441static void XImageCache(Display *display,XResourceInfo *resource_info,
6442  XWindows *windows,const CommandType command,Image **image,
6443  ExceptionInfo *exception)
6444{
6445  Image
6446    *cache_image;
6447
6448  static Image
6449    *redo_image = (Image *) NULL,
6450    *undo_image = (Image *) NULL;
6451
6452  switch (command)
6453  {
6454    case FreeBuffersCommand:
6455    {
6456      /*
6457        Free memory from the undo and redo cache.
6458      */
6459      while (undo_image != (Image *) NULL)
6460      {
6461        cache_image=undo_image;
6462        undo_image=GetPreviousImageInList(undo_image);
6463        cache_image->list=DestroyImage(cache_image->list);
6464        cache_image=DestroyImage(cache_image);
6465      }
6466      undo_image=NewImageList();
6467      if (redo_image != (Image *) NULL)
6468        redo_image=DestroyImage(redo_image);
6469      redo_image=NewImageList();
6470      return;
6471    }
6472    case UndoCommand:
6473    {
6474      char
6475        image_geometry[MaxTextExtent];
6476
6477      /*
6478        Undo the last image transformation.
6479      */
6480      if (undo_image == (Image *) NULL)
6481        {
6482          (void) XBell(display,0);
6483          return;
6484        }
6485      cache_image=undo_image;
6486      undo_image=GetPreviousImageInList(undo_image);
6487      windows->image.window_changes.width=(int) cache_image->columns;
6488      windows->image.window_changes.height=(int) cache_image->rows;
6489      (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
6490        windows->image.ximage->width,windows->image.ximage->height);
6491      (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
6492        exception);
6493      if (windows->image.crop_geometry != (char *) NULL)
6494        windows->image.crop_geometry=(char *) RelinquishMagickMemory(
6495          windows->image.crop_geometry);
6496      windows->image.crop_geometry=cache_image->geometry;
6497      if (redo_image != (Image *) NULL)
6498        redo_image=DestroyImage(redo_image);
6499      redo_image=(*image);
6500      *image=cache_image->list;
6501      cache_image=DestroyImage(cache_image);
6502      if (windows->image.orphan != MagickFalse)
6503        return;
6504      XConfigureImageColormap(display,resource_info,windows,*image,exception);
6505      (void) XConfigureImage(display,resource_info,windows,*image,exception);
6506      return;
6507    }
6508    case CutCommand:
6509    case PasteCommand:
6510    case ApplyCommand:
6511    case HalfSizeCommand:
6512    case OriginalSizeCommand:
6513    case DoubleSizeCommand:
6514    case ResizeCommand:
6515    case TrimCommand:
6516    case CropCommand:
6517    case ChopCommand:
6518    case FlipCommand:
6519    case FlopCommand:
6520    case RotateRightCommand:
6521    case RotateLeftCommand:
6522    case RotateCommand:
6523    case ShearCommand:
6524    case RollCommand:
6525    case NegateCommand:
6526    case ContrastStretchCommand:
6527    case SigmoidalContrastCommand:
6528    case NormalizeCommand:
6529    case EqualizeCommand:
6530    case HueCommand:
6531    case SaturationCommand:
6532    case BrightnessCommand:
6533    case GammaCommand:
6534    case SpiffCommand:
6535    case DullCommand:
6536    case GrayscaleCommand:
6537    case MapCommand:
6538    case QuantizeCommand:
6539    case DespeckleCommand:
6540    case EmbossCommand:
6541    case ReduceNoiseCommand:
6542    case AddNoiseCommand:
6543    case SharpenCommand:
6544    case BlurCommand:
6545    case ThresholdCommand:
6546    case EdgeDetectCommand:
6547    case SpreadCommand:
6548    case ShadeCommand:
6549    case RaiseCommand:
6550    case SegmentCommand:
6551    case SolarizeCommand:
6552    case SepiaToneCommand:
6553    case SwirlCommand:
6554    case ImplodeCommand:
6555    case VignetteCommand:
6556    case WaveCommand:
6557    case OilPaintCommand:
6558    case CharcoalDrawCommand:
6559    case AnnotateCommand:
6560    case AddBorderCommand:
6561    case AddFrameCommand:
6562    case CompositeCommand:
6563    case CommentCommand:
6564    case LaunchCommand:
6565    case RegionofInterestCommand:
6566    case SaveToUndoBufferCommand:
6567    case RedoCommand:
6568    {
6569      Image
6570        *previous_image;
6571
6572      ssize_t
6573        bytes;
6574
6575      bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelInfo));
6576      if (undo_image != (Image *) NULL)
6577        {
6578          /*
6579            Ensure the undo cache has enough memory available.
6580          */
6581          previous_image=undo_image;
6582          while (previous_image != (Image *) NULL)
6583          {
6584            bytes+=previous_image->list->columns*previous_image->list->rows*
6585              sizeof(PixelInfo);
6586            if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6587              {
6588                previous_image=GetPreviousImageInList(previous_image);
6589                continue;
6590              }
6591            bytes-=previous_image->list->columns*previous_image->list->rows*
6592              sizeof(PixelInfo);
6593            if (previous_image == undo_image)
6594              undo_image=NewImageList();
6595            else
6596              previous_image->next->previous=NewImageList();
6597            break;
6598          }
6599          while (previous_image != (Image *) NULL)
6600          {
6601            /*
6602              Delete any excess memory from undo cache.
6603            */
6604            cache_image=previous_image;
6605            previous_image=GetPreviousImageInList(previous_image);
6606            cache_image->list=DestroyImage(cache_image->list);
6607            cache_image=DestroyImage(cache_image);
6608          }
6609        }
6610      if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6611        break;
6612      /*
6613        Save image before transformations are applied.
6614      */
6615      cache_image=AcquireImage((ImageInfo *) NULL,exception);
6616      if (cache_image == (Image *) NULL)
6617        break;
6618      XSetCursorState(display,windows,MagickTrue);
6619      XCheckRefreshWindows(display,windows);
6620      cache_image->list=CloneImage(*image,0,0,MagickTrue,exception);
6621      XSetCursorState(display,windows,MagickFalse);
6622      if (cache_image->list == (Image *) NULL)
6623        {
6624          cache_image=DestroyImage(cache_image);
6625          break;
6626        }
6627      cache_image->columns=(size_t) windows->image.ximage->width;
6628      cache_image->rows=(size_t) windows->image.ximage->height;
6629      cache_image->geometry=windows->image.crop_geometry;
6630      if (windows->image.crop_geometry != (char *) NULL)
6631        {
6632          cache_image->geometry=AcquireString((char *) NULL);
6633          (void) CopyMagickString(cache_image->geometry,
6634            windows->image.crop_geometry,MaxTextExtent);
6635        }
6636      if (undo_image == (Image *) NULL)
6637        {
6638          undo_image=cache_image;
6639          break;
6640        }
6641      undo_image->next=cache_image;
6642      undo_image->next->previous=undo_image;
6643      undo_image=undo_image->next;
6644      break;
6645    }
6646    default:
6647      break;
6648  }
6649  if (command == RedoCommand)
6650    {
6651      /*
6652        Redo the last image transformation.
6653      */
6654      if (redo_image == (Image *) NULL)
6655        {
6656          (void) XBell(display,0);
6657          return;
6658        }
6659      windows->image.window_changes.width=(int) redo_image->columns;
6660      windows->image.window_changes.height=(int) redo_image->rows;
6661      if (windows->image.crop_geometry != (char *) NULL)
6662        windows->image.crop_geometry=(char *)
6663          RelinquishMagickMemory(windows->image.crop_geometry);
6664      windows->image.crop_geometry=redo_image->geometry;
6665      *image=DestroyImage(*image);
6666      *image=redo_image;
6667      redo_image=NewImageList();
6668      if (windows->image.orphan != MagickFalse)
6669        return;
6670      XConfigureImageColormap(display,resource_info,windows,*image,exception);
6671      (void) XConfigureImage(display,resource_info,windows,*image,exception);
6672      return;
6673    }
6674  if (command != InfoCommand)
6675    return;
6676  /*
6677    Display image info.
6678  */
6679  XSetCursorState(display,windows,MagickTrue);
6680  XCheckRefreshWindows(display,windows);
6681  XDisplayImageInfo(display,resource_info,windows,undo_image,*image,exception);
6682  XSetCursorState(display,windows,MagickFalse);
6683}
6684
6685/*
6686%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6687%                                                                             %
6688%                                                                             %
6689%                                                                             %
6690+   X I m a g e W i n d o w C o m m a n d                                     %
6691%                                                                             %
6692%                                                                             %
6693%                                                                             %
6694%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6695%
6696%  XImageWindowCommand() makes a transform to the image or Image window as
6697%  specified by a user menu button or keyboard command.
6698%
6699%  The format of the XImageWindowCommand method is:
6700%
6701%      CommandType XImageWindowCommand(Display *display,
6702%        XResourceInfo *resource_info,XWindows *windows,
6703%        const MagickStatusType state,KeySym key_symbol,Image **image,
6704%        ExceptionInfo *exception)
6705%
6706%  A description of each parameter follows:
6707%
6708%    o nexus:  Method XImageWindowCommand returns an image when the
6709%      user chooses 'Open Image' from the command menu.  Otherwise a null
6710%      image is returned.
6711%
6712%    o display: Specifies a connection to an X server; returned from
6713%      XOpenDisplay.
6714%
6715%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6716%
6717%    o windows: Specifies a pointer to a XWindows structure.
6718%
6719%    o state: key mask.
6720%
6721%    o key_symbol: Specifies a command to perform.
6722%
6723%    o image: the image;  XImageWIndowCommand may transform the image and
6724%      return a new image pointer.
6725%
6726%    o exception: return any errors or warnings in this structure.
6727%
6728*/
6729static CommandType XImageWindowCommand(Display *display,
6730  XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6731  KeySym key_symbol,Image **image,ExceptionInfo *exception)
6732{
6733  static char
6734    delta[MaxTextExtent] = "";
6735
6736  static const char
6737    Digits[] = "01234567890";
6738
6739  static KeySym
6740    last_symbol = XK_0;
6741
6742  if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6743    {
6744      if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6745        {
6746          *delta='\0';
6747          resource_info->quantum=1;
6748        }
6749      last_symbol=key_symbol;
6750      delta[strlen(delta)+1]='\0';
6751      delta[strlen(delta)]=Digits[key_symbol-XK_0];
6752      resource_info->quantum=StringToLong(delta);
6753      return(NullCommand);
6754    }
6755  last_symbol=key_symbol;
6756  if (resource_info->immutable)
6757    {
6758      /*
6759        Virtual image window has a restricted command set.
6760      */
6761      switch (key_symbol)
6762      {
6763        case XK_question:
6764          return(InfoCommand);
6765        case XK_p:
6766        case XK_Print:
6767          return(PrintCommand);
6768        case XK_space:
6769          return(NextCommand);
6770        case XK_q:
6771        case XK_Escape:
6772          return(QuitCommand);
6773        default:
6774          break;
6775      }
6776      return(NullCommand);
6777    }
6778  switch ((int) key_symbol)
6779  {
6780    case XK_o:
6781    {
6782      if ((state & ControlMask) == 0)
6783        break;
6784      return(OpenCommand);
6785    }
6786    case XK_space:
6787      return(NextCommand);
6788    case XK_BackSpace:
6789      return(FormerCommand);
6790    case XK_s:
6791    {
6792      if ((state & Mod1Mask) != 0)
6793        return(SwirlCommand);
6794      if ((state & ControlMask) == 0)
6795        return(ShearCommand);
6796      return(SaveCommand);
6797    }
6798    case XK_p:
6799    case XK_Print:
6800    {
6801      if ((state & Mod1Mask) != 0)
6802        return(OilPaintCommand);
6803      if ((state & Mod4Mask) != 0)
6804        return(ColorCommand);
6805      if ((state & ControlMask) == 0)
6806        return(NullCommand);
6807      return(PrintCommand);
6808    }
6809    case XK_d:
6810    {
6811      if ((state & Mod4Mask) != 0)
6812        return(DrawCommand);
6813      if ((state & ControlMask) == 0)
6814        return(NullCommand);
6815      return(DeleteCommand);
6816    }
6817    case XK_Select:
6818    {
6819      if ((state & ControlMask) == 0)
6820        return(NullCommand);
6821      return(SelectCommand);
6822    }
6823    case XK_n:
6824    {
6825      if ((state & ControlMask) == 0)
6826        return(NullCommand);
6827      return(NewCommand);
6828    }
6829    case XK_q:
6830    case XK_Escape:
6831      return(QuitCommand);
6832    case XK_z:
6833    case XK_Undo:
6834    {
6835      if ((state & ControlMask) == 0)
6836        return(NullCommand);
6837      return(UndoCommand);
6838    }
6839    case XK_r:
6840    case XK_Redo:
6841    {
6842      if ((state & ControlMask) == 0)
6843        return(RollCommand);
6844      return(RedoCommand);
6845    }
6846    case XK_x:
6847    {
6848      if ((state & ControlMask) == 0)
6849        return(NullCommand);
6850      return(CutCommand);
6851    }
6852    case XK_c:
6853    {
6854      if ((state & Mod1Mask) != 0)
6855        return(CharcoalDrawCommand);
6856      if ((state & ControlMask) == 0)
6857        return(CropCommand);
6858      return(CopyCommand);
6859    }
6860    case XK_v:
6861    case XK_Insert:
6862    {
6863      if ((state & Mod4Mask) != 0)
6864        return(CompositeCommand);
6865      if ((state & ControlMask) == 0)
6866        return(FlipCommand);
6867      return(PasteCommand);
6868    }
6869    case XK_less:
6870      return(HalfSizeCommand);
6871    case XK_minus:
6872      return(OriginalSizeCommand);
6873    case XK_greater:
6874      return(DoubleSizeCommand);
6875    case XK_percent:
6876      return(ResizeCommand);
6877    case XK_at:
6878      return(RefreshCommand);
6879    case XK_bracketleft:
6880      return(ChopCommand);
6881    case XK_h:
6882      return(FlopCommand);
6883    case XK_slash:
6884      return(RotateRightCommand);
6885    case XK_backslash:
6886      return(RotateLeftCommand);
6887    case XK_asterisk:
6888      return(RotateCommand);
6889    case XK_t:
6890      return(TrimCommand);
6891    case XK_H:
6892      return(HueCommand);
6893    case XK_S:
6894      return(SaturationCommand);
6895    case XK_L:
6896      return(BrightnessCommand);
6897    case XK_G:
6898      return(GammaCommand);
6899    case XK_C:
6900      return(SpiffCommand);
6901    case XK_Z:
6902      return(DullCommand);
6903    case XK_N:
6904      return(NormalizeCommand);
6905    case XK_equal:
6906      return(EqualizeCommand);
6907    case XK_asciitilde:
6908      return(NegateCommand);
6909    case XK_period:
6910      return(GrayscaleCommand);
6911    case XK_numbersign:
6912      return(QuantizeCommand);
6913    case XK_F2:
6914      return(DespeckleCommand);
6915    case XK_F3:
6916      return(EmbossCommand);
6917    case XK_F4:
6918      return(ReduceNoiseCommand);
6919    case XK_F5:
6920      return(AddNoiseCommand);
6921    case XK_F6:
6922      return(SharpenCommand);
6923    case XK_F7:
6924      return(BlurCommand);
6925    case XK_F8:
6926      return(ThresholdCommand);
6927    case XK_F9:
6928      return(EdgeDetectCommand);
6929    case XK_F10:
6930      return(SpreadCommand);
6931    case XK_F11:
6932      return(ShadeCommand);
6933    case XK_F12:
6934      return(RaiseCommand);
6935    case XK_F13:
6936      return(SegmentCommand);
6937    case XK_i:
6938    {
6939      if ((state & Mod1Mask) == 0)
6940        return(NullCommand);
6941      return(ImplodeCommand);
6942    }
6943    case XK_w:
6944    {
6945      if ((state & Mod1Mask) == 0)
6946        return(NullCommand);
6947      return(WaveCommand);
6948    }
6949    case XK_m:
6950    {
6951      if ((state & Mod4Mask) == 0)
6952        return(NullCommand);
6953      return(MatteCommand);
6954    }
6955    case XK_b:
6956    {
6957      if ((state & Mod4Mask) == 0)
6958        return(NullCommand);
6959      return(AddBorderCommand);
6960    }
6961    case XK_f:
6962    {
6963      if ((state & Mod4Mask) == 0)
6964        return(NullCommand);
6965      return(AddFrameCommand);
6966    }
6967    case XK_exclam:
6968    {
6969      if ((state & Mod4Mask) == 0)
6970        return(NullCommand);
6971      return(CommentCommand);
6972    }
6973    case XK_a:
6974    {
6975      if ((state & Mod1Mask) != 0)
6976        return(ApplyCommand);
6977      if ((state & Mod4Mask) != 0)
6978        return(AnnotateCommand);
6979      if ((state & ControlMask) == 0)
6980        return(NullCommand);
6981      return(RegionofInterestCommand);
6982    }
6983    case XK_question:
6984      return(InfoCommand);
6985    case XK_plus:
6986      return(ZoomCommand);
6987    case XK_P:
6988    {
6989      if ((state & ShiftMask) == 0)
6990        return(NullCommand);
6991      return(ShowPreviewCommand);
6992    }
6993    case XK_Execute:
6994      return(LaunchCommand);
6995    case XK_F1:
6996      return(HelpCommand);
6997    case XK_Find:
6998      return(BrowseDocumentationCommand);
6999    case XK_Menu:
7000    {
7001      (void) XMapRaised(display,windows->command.id);
7002      return(NullCommand);
7003    }
7004    case XK_Next:
7005    case XK_Prior:
7006    case XK_Home:
7007    case XK_KP_Home:
7008    {
7009      XTranslateImage(display,windows,*image,key_symbol);
7010      return(NullCommand);
7011    }
7012    case XK_Up:
7013    case XK_KP_Up:
7014    case XK_Down:
7015    case XK_KP_Down:
7016    case XK_Left:
7017    case XK_KP_Left:
7018    case XK_Right:
7019    case XK_KP_Right:
7020    {
7021      if ((state & Mod1Mask) != 0)
7022        {
7023          RectangleInfo
7024            crop_info;
7025
7026          /*
7027            Trim one pixel from edge of image.
7028          */
7029          crop_info.x=0;
7030          crop_info.y=0;
7031          crop_info.width=(size_t) windows->image.ximage->width;
7032          crop_info.height=(size_t) windows->image.ximage->height;
7033          if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
7034            {
7035              if (resource_info->quantum >= (int) crop_info.height)
7036                resource_info->quantum=(int) crop_info.height-1;
7037              crop_info.height-=resource_info->quantum;
7038            }
7039          if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
7040            {
7041              if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
7042                resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
7043              crop_info.y+=resource_info->quantum;
7044              crop_info.height-=resource_info->quantum;
7045            }
7046          if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
7047            {
7048              if (resource_info->quantum >= (int) crop_info.width)
7049                resource_info->quantum=(int) crop_info.width-1;
7050              crop_info.width-=resource_info->quantum;
7051            }
7052          if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
7053            {
7054              if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
7055                resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
7056              crop_info.x+=resource_info->quantum;
7057              crop_info.width-=resource_info->quantum;
7058            }
7059          if ((int) (windows->image.x+windows->image.width) >
7060              (int) crop_info.width)
7061            windows->image.x=(int) (crop_info.width-windows->image.width);
7062          if ((int) (windows->image.y+windows->image.height) >
7063              (int) crop_info.height)
7064            windows->image.y=(int) (crop_info.height-windows->image.height);
7065          XSetCropGeometry(display,windows,&crop_info,*image);
7066          windows->image.window_changes.width=(int) crop_info.width;
7067          windows->image.window_changes.height=(int) crop_info.height;
7068          (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
7069          (void) XConfigureImage(display,resource_info,windows,*image,
7070            exception);
7071          return(NullCommand);
7072        }
7073      XTranslateImage(display,windows,*image,key_symbol);
7074      return(NullCommand);
7075    }
7076    default:
7077      return(NullCommand);
7078  }
7079  return(NullCommand);
7080}
7081
7082/*
7083%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7084%                                                                             %
7085%                                                                             %
7086%                                                                             %
7087+   X M a g i c k C o m m a n d                                               %
7088%                                                                             %
7089%                                                                             %
7090%                                                                             %
7091%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7092%
7093%  XMagickCommand() makes a transform to the image or Image window as
7094%  specified by a user menu button or keyboard command.
7095%
7096%  The format of the XMagickCommand method is:
7097%
7098%      Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7099%        XWindows *windows,const CommandType command,Image **image,
7100%        ExceptionInfo *exception)
7101%
7102%  A description of each parameter follows:
7103%
7104%    o display: Specifies a connection to an X server; returned from
7105%      XOpenDisplay.
7106%
7107%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7108%
7109%    o windows: Specifies a pointer to a XWindows structure.
7110%
7111%    o command: Specifies a command to perform.
7112%
7113%    o image: the image;  XMagickCommand may transform the image and return a
7114%      new image pointer.
7115%
7116%    o exception: return any errors or warnings in this structure.
7117%
7118*/
7119static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7120  XWindows *windows,const CommandType command,Image **image,
7121  ExceptionInfo *exception)
7122{
7123  char
7124    filename[MaxTextExtent],
7125    geometry[MaxTextExtent],
7126    modulate_factors[MaxTextExtent];
7127
7128  GeometryInfo
7129    geometry_info;
7130
7131  Image
7132    *nexus;
7133
7134  ImageInfo
7135    *image_info;
7136
7137  int
7138    x,
7139    y;
7140
7141  MagickStatusType
7142    flags,
7143    status;
7144
7145  QuantizeInfo
7146    quantize_info;
7147
7148  RectangleInfo
7149    page_geometry;
7150
7151  register int
7152    i;
7153
7154  static char
7155    color[MaxTextExtent] = "gray";
7156
7157  unsigned int
7158    height,
7159    width;
7160
7161  /*
7162    Process user command.
7163  */
7164  XCheckRefreshWindows(display,windows);
7165  XImageCache(display,resource_info,windows,command,image,exception);
7166  nexus=NewImageList();
7167  windows->image.window_changes.width=windows->image.ximage->width;
7168  windows->image.window_changes.height=windows->image.ximage->height;
7169  image_info=CloneImageInfo(resource_info->image_info);
7170  SetGeometryInfo(&geometry_info);
7171  GetQuantizeInfo(&quantize_info);
7172  switch (command)
7173  {
7174    case OpenCommand:
7175    {
7176      /*
7177        Load image.
7178      */
7179      nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7180      break;
7181    }
7182    case NextCommand:
7183    {
7184      /*
7185        Display next image.
7186      */
7187      for (i=0; i < resource_info->quantum; i++)
7188        XClientMessage(display,windows->image.id,windows->im_protocols,
7189          windows->im_next_image,CurrentTime);
7190      break;
7191    }
7192    case FormerCommand:
7193    {
7194      /*
7195        Display former image.
7196      */
7197      for (i=0; i < resource_info->quantum; i++)
7198        XClientMessage(display,windows->image.id,windows->im_protocols,
7199          windows->im_former_image,CurrentTime);
7200      break;
7201    }
7202    case SelectCommand:
7203    {
7204      int
7205        status;
7206
7207      /*
7208        Select image.
7209      */
7210      status=chdir(resource_info->home_directory);
7211      if (status == -1)
7212        (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
7213          "UnableToOpenFile","%s",resource_info->home_directory);
7214      nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7215      break;
7216    }
7217    case SaveCommand:
7218    {
7219      /*
7220        Save image.
7221      */
7222      status=XSaveImage(display,resource_info,windows,*image,exception);
7223      if (status == MagickFalse)
7224        {
7225          char
7226            message[MaxTextExtent];
7227
7228          (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7229            exception->reason != (char *) NULL ? exception->reason : "",
7230            exception->description != (char *) NULL ? exception->description :
7231            "");
7232          XNoticeWidget(display,windows,"Unable to save file:",message);
7233          break;
7234        }
7235      break;
7236    }
7237    case PrintCommand:
7238    {
7239      /*
7240        Print image.
7241      */
7242      status=XPrintImage(display,resource_info,windows,*image,exception);
7243      if (status == MagickFalse)
7244        {
7245          char
7246            message[MaxTextExtent];
7247
7248          (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7249            exception->reason != (char *) NULL ? exception->reason : "",
7250            exception->description != (char *) NULL ? exception->description :
7251            "");
7252          XNoticeWidget(display,windows,"Unable to print file:",message);
7253          break;
7254        }
7255      break;
7256    }
7257    case DeleteCommand:
7258    {
7259      static char
7260        filename[MaxTextExtent] = "\0";
7261
7262      /*
7263        Delete image file.
7264      */
7265      XFileBrowserWidget(display,windows,"Delete",filename);
7266      if (*filename == '\0')
7267        break;
7268      status=remove_utf8(filename) != 0 ? MagickTrue : MagickFalse;
7269      if (status != MagickFalse)
7270        XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7271      break;
7272    }
7273    case NewCommand:
7274    {
7275      int
7276        status;
7277
7278      static char
7279        color[MaxTextExtent] = "gray",
7280        geometry[MaxTextExtent] = "640x480";
7281
7282      static const char
7283        *format = "gradient";
7284
7285      /*
7286        Query user for canvas geometry.
7287      */
7288      status=XDialogWidget(display,windows,"New","Enter image geometry:",
7289        geometry);
7290      if (*geometry == '\0')
7291        break;
7292      if (status == 0)
7293        format="xc";
7294      XColorBrowserWidget(display,windows,"Select",color);
7295      if (*color == '\0')
7296        break;
7297      /*
7298        Create canvas.
7299      */
7300      (void) FormatLocaleString(image_info->filename,MaxTextExtent,
7301        "%s:%s",format,color);
7302      (void) CloneString(&image_info->size,geometry);
7303      nexus=ReadImage(image_info,exception);
7304      CatchException(exception);
7305      XClientMessage(display,windows->image.id,windows->im_protocols,
7306        windows->im_next_image,CurrentTime);
7307      break;
7308    }
7309    case VisualDirectoryCommand:
7310    {
7311      /*
7312        Visual Image directory.
7313      */
7314      nexus=XVisualDirectoryImage(display,resource_info,windows,exception);
7315      break;
7316    }
7317    case QuitCommand:
7318    {
7319      /*
7320        exit program.
7321      */
7322      if (resource_info->confirm_exit == MagickFalse)
7323        XClientMessage(display,windows->image.id,windows->im_protocols,
7324          windows->im_exit,CurrentTime);
7325      else
7326        {
7327          int
7328            status;
7329
7330          /*
7331            Confirm program exit.
7332          */
7333          status=XConfirmWidget(display,windows,"Do you really want to exit",
7334            resource_info->client_name);
7335          if (status > 0)
7336            XClientMessage(display,windows->image.id,windows->im_protocols,
7337              windows->im_exit,CurrentTime);
7338        }
7339      break;
7340    }
7341    case CutCommand:
7342    {
7343      /*
7344        Cut image.
7345      */
7346      (void) XCropImage(display,resource_info,windows,*image,CutMode,exception);
7347      break;
7348    }
7349    case CopyCommand:
7350    {
7351      /*
7352        Copy image.
7353      */
7354      (void) XCropImage(display,resource_info,windows,*image,CopyMode,
7355        exception);
7356      break;
7357    }
7358    case PasteCommand:
7359    {
7360      /*
7361        Paste image.
7362      */
7363      status=XPasteImage(display,resource_info,windows,*image,exception);
7364      if (status == MagickFalse)
7365        {
7366          XNoticeWidget(display,windows,"Unable to paste X image",
7367            (*image)->filename);
7368          break;
7369        }
7370      break;
7371    }
7372    case HalfSizeCommand:
7373    {
7374      /*
7375        Half image size.
7376      */
7377      windows->image.window_changes.width=windows->image.ximage->width/2;
7378      windows->image.window_changes.height=windows->image.ximage->height/2;
7379      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7380      break;
7381    }
7382    case OriginalSizeCommand:
7383    {
7384      /*
7385        Original image size.
7386      */
7387      windows->image.window_changes.width=(int) (*image)->columns;
7388      windows->image.window_changes.height=(int) (*image)->rows;
7389      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7390      break;
7391    }
7392    case DoubleSizeCommand:
7393    {
7394      /*
7395        Double the image size.
7396      */
7397      windows->image.window_changes.width=windows->image.ximage->width << 1;
7398      windows->image.window_changes.height=windows->image.ximage->height << 1;
7399      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7400      break;
7401    }
7402    case ResizeCommand:
7403    {
7404      int
7405        status;
7406
7407      size_t
7408        height,
7409        width;
7410
7411      ssize_t
7412        x,
7413        y;
7414
7415      /*
7416        Resize image.
7417      */
7418      width=(size_t) windows->image.ximage->width;
7419      height=(size_t) windows->image.ximage->height;
7420      x=0;
7421      y=0;
7422      (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g+0+0",
7423        (double) width,(double) height);
7424      status=XDialogWidget(display,windows,"Resize",
7425        "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7426      if (*geometry == '\0')
7427        break;
7428      if (status == 0)
7429        (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7430      (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7431      windows->image.window_changes.width=(int) width;
7432      windows->image.window_changes.height=(int) height;
7433      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7434      break;
7435    }
7436    case ApplyCommand:
7437    {
7438      char
7439        image_geometry[MaxTextExtent];
7440
7441      if ((windows->image.crop_geometry == (char *) NULL) &&
7442          ((int) (*image)->columns == windows->image.ximage->width) &&
7443          ((int) (*image)->rows == windows->image.ximage->height))
7444        break;
7445      /*
7446        Apply size transforms to image.
7447      */
7448      XSetCursorState(display,windows,MagickTrue);
7449      XCheckRefreshWindows(display,windows);
7450      /*
7451        Crop and/or scale displayed image.
7452      */
7453      (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
7454        windows->image.ximage->width,windows->image.ximage->height);
7455      (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
7456        exception);
7457      if (windows->image.crop_geometry != (char *) NULL)
7458        windows->image.crop_geometry=(char *) RelinquishMagickMemory(
7459          windows->image.crop_geometry);
7460      windows->image.x=0;
7461      windows->image.y=0;
7462      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7463      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7464      break;
7465    }
7466    case RefreshCommand:
7467    {
7468      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7469      break;
7470    }
7471    case RestoreCommand:
7472    {
7473      /*
7474        Restore Image window to its original size.
7475      */
7476      if ((windows->image.width == (unsigned int) (*image)->columns) &&
7477          (windows->image.height == (unsigned int) (*image)->rows) &&
7478          (windows->image.crop_geometry == (char *) NULL))
7479        {
7480          (void) XBell(display,0);
7481          break;
7482        }
7483      windows->image.window_changes.width=(int) (*image)->columns;
7484      windows->image.window_changes.height=(int) (*image)->rows;
7485      if (windows->image.crop_geometry != (char *) NULL)
7486        {
7487          windows->image.crop_geometry=(char *)
7488            RelinquishMagickMemory(windows->image.crop_geometry);
7489          windows->image.crop_geometry=(char *) NULL;
7490          windows->image.x=0;
7491          windows->image.y=0;
7492        }
7493      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7494      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7495      break;
7496    }
7497    case CropCommand:
7498    {
7499      /*
7500        Crop image.
7501      */
7502      (void) XCropImage(display,resource_info,windows,*image,CropMode,
7503        exception);
7504      break;
7505    }
7506    case ChopCommand:
7507    {
7508      /*
7509        Chop image.
7510      */
7511      status=XChopImage(display,resource_info,windows,image,exception);
7512      if (status == MagickFalse)
7513        {
7514          XNoticeWidget(display,windows,"Unable to cut X image",
7515            (*image)->filename);
7516          break;
7517        }
7518      break;
7519    }
7520    case FlopCommand:
7521    {
7522      Image
7523        *flop_image;
7524
7525      /*
7526        Flop image scanlines.
7527      */
7528      XSetCursorState(display,windows,MagickTrue);
7529      XCheckRefreshWindows(display,windows);
7530      flop_image=FlopImage(*image,exception);
7531      if (flop_image != (Image *) NULL)
7532        {
7533          *image=DestroyImage(*image);
7534          *image=flop_image;
7535        }
7536      CatchException(exception);
7537      XSetCursorState(display,windows,MagickFalse);
7538      if (windows->image.crop_geometry != (char *) NULL)
7539        {
7540          /*
7541            Flop crop geometry.
7542          */
7543          width=(unsigned int) (*image)->columns;
7544          height=(unsigned int) (*image)->rows;
7545          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7546            &width,&height);
7547          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7548            "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7549        }
7550      if (windows->image.orphan != MagickFalse)
7551        break;
7552      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7553      break;
7554    }
7555    case FlipCommand:
7556    {
7557      Image
7558        *flip_image;
7559
7560      /*
7561        Flip image scanlines.
7562      */
7563      XSetCursorState(display,windows,MagickTrue);
7564      XCheckRefreshWindows(display,windows);
7565      flip_image=FlipImage(*image,exception);
7566      if (flip_image != (Image *) NULL)
7567        {
7568          *image=DestroyImage(*image);
7569          *image=flip_image;
7570        }
7571      CatchException(exception);
7572      XSetCursorState(display,windows,MagickFalse);
7573      if (windows->image.crop_geometry != (char *) NULL)
7574        {
7575          /*
7576            Flip crop geometry.
7577          */
7578          width=(unsigned int) (*image)->columns;
7579          height=(unsigned int) (*image)->rows;
7580          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7581            &width,&height);
7582          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7583            "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7584        }
7585      if (windows->image.orphan != MagickFalse)
7586        break;
7587      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7588      break;
7589    }
7590    case RotateRightCommand:
7591    {
7592      /*
7593        Rotate image 90 degrees clockwise.
7594      */
7595      status=XRotateImage(display,resource_info,windows,90.0,image,exception);
7596      if (status == MagickFalse)
7597        {
7598          XNoticeWidget(display,windows,"Unable to rotate X image",
7599            (*image)->filename);
7600          break;
7601        }
7602      break;
7603    }
7604    case RotateLeftCommand:
7605    {
7606      /*
7607        Rotate image 90 degrees counter-clockwise.
7608      */
7609      status=XRotateImage(display,resource_info,windows,-90.0,image,exception);
7610      if (status == MagickFalse)
7611        {
7612          XNoticeWidget(display,windows,"Unable to rotate X image",
7613            (*image)->filename);
7614          break;
7615        }
7616      break;
7617    }
7618    case RotateCommand:
7619    {
7620      /*
7621        Rotate image.
7622      */
7623      status=XRotateImage(display,resource_info,windows,0.0,image,exception);
7624      if (status == MagickFalse)
7625        {
7626          XNoticeWidget(display,windows,"Unable to rotate X image",
7627            (*image)->filename);
7628          break;
7629        }
7630      break;
7631    }
7632    case ShearCommand:
7633    {
7634      Image
7635        *shear_image;
7636
7637      static char
7638        geometry[MaxTextExtent] = "45.0x45.0";
7639
7640      /*
7641        Query user for shear color and geometry.
7642      */
7643      XColorBrowserWidget(display,windows,"Select",color);
7644      if (*color == '\0')
7645        break;
7646      (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7647        geometry);
7648      if (*geometry == '\0')
7649        break;
7650      /*
7651        Shear image.
7652      */
7653      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7654        exception);
7655      XSetCursorState(display,windows,MagickTrue);
7656      XCheckRefreshWindows(display,windows);
7657      (void) QueryColorCompliance(color,AllCompliance,
7658        &(*image)->background_color,exception);
7659      flags=ParseGeometry(geometry,&geometry_info);
7660      if ((flags & SigmaValue) == 0)
7661        geometry_info.sigma=geometry_info.rho;
7662      shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7663        exception);
7664      if (shear_image != (Image *) NULL)
7665        {
7666          *image=DestroyImage(*image);
7667          *image=shear_image;
7668        }
7669      CatchException(exception);
7670      XSetCursorState(display,windows,MagickFalse);
7671      if (windows->image.orphan != MagickFalse)
7672        break;
7673      windows->image.window_changes.width=(int) (*image)->columns;
7674      windows->image.window_changes.height=(int) (*image)->rows;
7675      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7676      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7677      break;
7678    }
7679    case RollCommand:
7680    {
7681      Image
7682        *roll_image;
7683
7684      static char
7685        geometry[MaxTextExtent] = "+2+2";
7686
7687      /*
7688        Query user for the roll geometry.
7689      */
7690      (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7691        geometry);
7692      if (*geometry == '\0')
7693        break;
7694      /*
7695        Roll image.
7696      */
7697      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7698        exception);
7699      XSetCursorState(display,windows,MagickTrue);
7700      XCheckRefreshWindows(display,windows);
7701      (void) ParsePageGeometry(*image,geometry,&page_geometry,
7702        exception);
7703      roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7704        exception);
7705      if (roll_image != (Image *) NULL)
7706        {
7707          *image=DestroyImage(*image);
7708          *image=roll_image;
7709        }
7710      CatchException(exception);
7711      XSetCursorState(display,windows,MagickFalse);
7712      if (windows->image.orphan != MagickFalse)
7713        break;
7714      windows->image.window_changes.width=(int) (*image)->columns;
7715      windows->image.window_changes.height=(int) (*image)->rows;
7716      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7717      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7718      break;
7719    }
7720    case TrimCommand:
7721    {
7722      static char
7723        fuzz[MaxTextExtent];
7724
7725      /*
7726        Query user for the fuzz factor.
7727      */
7728      (void) FormatLocaleString(fuzz,MaxTextExtent,"%g%%",100.0*
7729        (*image)->fuzz/(QuantumRange+1.0));
7730      (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7731      if (*fuzz == '\0')
7732        break;
7733      (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+1.0);
7734      /*
7735        Trim image.
7736      */
7737      status=XTrimImage(display,resource_info,windows,*image,exception);
7738      if (status == MagickFalse)
7739        {
7740          XNoticeWidget(display,windows,"Unable to trim X image",
7741            (*image)->filename);
7742          break;
7743        }
7744      break;
7745    }
7746    case HueCommand:
7747    {
7748      static char
7749        hue_percent[MaxTextExtent] = "110";
7750
7751      /*
7752        Query user for percent hue change.
7753      */
7754      (void) XDialogWidget(display,windows,"Apply",
7755        "Enter percent change in image hue (0-200):",hue_percent);
7756      if (*hue_percent == '\0')
7757        break;
7758      /*
7759        Vary the image hue.
7760      */
7761      XSetCursorState(display,windows,MagickTrue);
7762      XCheckRefreshWindows(display,windows);
7763      (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7764      (void) ConcatenateMagickString(modulate_factors,hue_percent,
7765        MaxTextExtent);
7766      (void) ModulateImage(*image,modulate_factors,exception);
7767      XSetCursorState(display,windows,MagickFalse);
7768      if (windows->image.orphan != MagickFalse)
7769        break;
7770      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7771      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7772      break;
7773    }
7774    case SaturationCommand:
7775    {
7776      static char
7777        saturation_percent[MaxTextExtent] = "110";
7778
7779      /*
7780        Query user for percent saturation change.
7781      */
7782      (void) XDialogWidget(display,windows,"Apply",
7783        "Enter percent change in color saturation (0-200):",saturation_percent);
7784      if (*saturation_percent == '\0')
7785        break;
7786      /*
7787        Vary color saturation.
7788      */
7789      XSetCursorState(display,windows,MagickTrue);
7790      XCheckRefreshWindows(display,windows);
7791      (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7792      (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7793        MaxTextExtent);
7794      (void) ModulateImage(*image,modulate_factors,exception);
7795      XSetCursorState(display,windows,MagickFalse);
7796      if (windows->image.orphan != MagickFalse)
7797        break;
7798      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7799      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7800      break;
7801    }
7802    case BrightnessCommand:
7803    {
7804      static char
7805        brightness_percent[MaxTextExtent] = "110";
7806
7807      /*
7808        Query user for percent brightness change.
7809      */
7810      (void) XDialogWidget(display,windows,"Apply",
7811        "Enter percent change in color brightness (0-200):",brightness_percent);
7812      if (*brightness_percent == '\0')
7813        break;
7814      /*
7815        Vary the color brightness.
7816      */
7817      XSetCursorState(display,windows,MagickTrue);
7818      XCheckRefreshWindows(display,windows);
7819      (void) CopyMagickString(modulate_factors,brightness_percent,
7820        MaxTextExtent);
7821      (void) ModulateImage(*image,modulate_factors,exception);
7822      XSetCursorState(display,windows,MagickFalse);
7823      if (windows->image.orphan != MagickFalse)
7824        break;
7825      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7826      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7827      break;
7828    }
7829    case GammaCommand:
7830    {
7831      static char
7832        factor[MaxTextExtent] = "1.6";
7833
7834      /*
7835        Query user for gamma value.
7836      */
7837      (void) XDialogWidget(display,windows,"Gamma",
7838        "Enter gamma value (e.g. 1.2):",factor);
7839      if (*factor == '\0')
7840        break;
7841      /*
7842        Gamma correct image.
7843      */
7844      XSetCursorState(display,windows,MagickTrue);
7845      XCheckRefreshWindows(display,windows);
7846      (void) GammaImage(*image,atof(factor),exception);
7847      XSetCursorState(display,windows,MagickFalse);
7848      if (windows->image.orphan != MagickFalse)
7849        break;
7850      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7851      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7852      break;
7853    }
7854    case SpiffCommand:
7855    {
7856      /*
7857        Sharpen the image contrast.
7858      */
7859      XSetCursorState(display,windows,MagickTrue);
7860      XCheckRefreshWindows(display,windows);
7861      (void) ContrastImage(*image,MagickTrue,exception);
7862      XSetCursorState(display,windows,MagickFalse);
7863      if (windows->image.orphan != MagickFalse)
7864        break;
7865      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7866      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7867      break;
7868    }
7869    case DullCommand:
7870    {
7871      /*
7872        Dull the image contrast.
7873      */
7874      XSetCursorState(display,windows,MagickTrue);
7875      XCheckRefreshWindows(display,windows);
7876      (void) ContrastImage(*image,MagickFalse,exception);
7877      XSetCursorState(display,windows,MagickFalse);
7878      if (windows->image.orphan != MagickFalse)
7879        break;
7880      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7881      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7882      break;
7883    }
7884    case ContrastStretchCommand:
7885    {
7886      double
7887        black_point,
7888        white_point;
7889
7890      static char
7891        levels[MaxTextExtent] = "1%";
7892
7893      /*
7894        Query user for gamma value.
7895      */
7896      (void) XDialogWidget(display,windows,"Contrast Stretch",
7897        "Enter black and white points:",levels);
7898      if (*levels == '\0')
7899        break;
7900      /*
7901        Contrast stretch image.
7902      */
7903      XSetCursorState(display,windows,MagickTrue);
7904      XCheckRefreshWindows(display,windows);
7905      flags=ParseGeometry(levels,&geometry_info);
7906      black_point=geometry_info.rho;
7907      white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7908      if ((flags & PercentValue) != 0)
7909        {
7910          black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7911          white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7912        }
7913      white_point=(MagickRealType) (*image)->columns*(*image)->rows-white_point;
7914      (void) ContrastStretchImage(*image,black_point,white_point,
7915        exception);
7916      XSetCursorState(display,windows,MagickFalse);
7917      if (windows->image.orphan != MagickFalse)
7918        break;
7919      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7920      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7921      break;
7922    }
7923    case SigmoidalContrastCommand:
7924    {
7925      GeometryInfo
7926        geometry_info;
7927
7928      MagickStatusType
7929        flags;
7930
7931      static char
7932        levels[MaxTextExtent] = "3x50%";
7933
7934      /*
7935        Query user for gamma value.
7936      */
7937      (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7938        "Enter contrast and midpoint:",levels);
7939      if (*levels == '\0')
7940        break;
7941      /*
7942        Contrast stretch image.
7943      */
7944      XSetCursorState(display,windows,MagickTrue);
7945      XCheckRefreshWindows(display,windows);
7946      flags=ParseGeometry(levels,&geometry_info);
7947      if ((flags & SigmaValue) == 0)
7948        geometry_info.sigma=1.0*QuantumRange/2.0;
7949      if ((flags & PercentValue) != 0)
7950        geometry_info.sigma=1.0*QuantumRange*geometry_info.sigma/100.0;
7951      (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
7952        geometry_info.sigma,exception);
7953      XSetCursorState(display,windows,MagickFalse);
7954      if (windows->image.orphan != MagickFalse)
7955        break;
7956      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7957      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7958      break;
7959    }
7960    case NormalizeCommand:
7961    {
7962      /*
7963        Perform histogram normalization on the image.
7964      */
7965      XSetCursorState(display,windows,MagickTrue);
7966      XCheckRefreshWindows(display,windows);
7967      (void) NormalizeImage(*image,exception);
7968      XSetCursorState(display,windows,MagickFalse);
7969      if (windows->image.orphan != MagickFalse)
7970        break;
7971      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7972      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7973      break;
7974    }
7975    case EqualizeCommand:
7976    {
7977      /*
7978        Perform histogram equalization on the image.
7979      */
7980      XSetCursorState(display,windows,MagickTrue);
7981      XCheckRefreshWindows(display,windows);
7982      (void) EqualizeImage(*image,exception);
7983      XSetCursorState(display,windows,MagickFalse);
7984      if (windows->image.orphan != MagickFalse)
7985        break;
7986      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7987      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7988      break;
7989    }
7990    case NegateCommand:
7991    {
7992      /*
7993        Negate colors in image.
7994      */
7995      XSetCursorState(display,windows,MagickTrue);
7996      XCheckRefreshWindows(display,windows);
7997      (void) NegateImage(*image,MagickFalse,exception);
7998      XSetCursorState(display,windows,MagickFalse);
7999      if (windows->image.orphan != MagickFalse)
8000        break;
8001      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8002      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8003      break;
8004    }
8005    case GrayscaleCommand:
8006    {
8007      /*
8008        Convert image to grayscale.
8009      */
8010      XSetCursorState(display,windows,MagickTrue);
8011      XCheckRefreshWindows(display,windows);
8012      (void) SetImageType(*image,(*image)->matte == MagickFalse ?
8013        GrayscaleType : GrayscaleMatteType,exception);
8014      XSetCursorState(display,windows,MagickFalse);
8015      if (windows->image.orphan != MagickFalse)
8016        break;
8017      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8018      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8019      break;
8020    }
8021    case MapCommand:
8022    {
8023      Image
8024        *affinity_image;
8025
8026      static char
8027        filename[MaxTextExtent] = "\0";
8028
8029      /*
8030        Request image file name from user.
8031      */
8032      XFileBrowserWidget(display,windows,"Map",filename);
8033      if (*filename == '\0')
8034        break;
8035      /*
8036        Map image.
8037      */
8038      XSetCursorState(display,windows,MagickTrue);
8039      XCheckRefreshWindows(display,windows);
8040      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
8041      affinity_image=ReadImage(image_info,exception);
8042      if (affinity_image != (Image *) NULL)
8043        {
8044          (void) RemapImage(&quantize_info,*image,affinity_image,exception);
8045          affinity_image=DestroyImage(affinity_image);
8046        }
8047      CatchException(exception);
8048      XSetCursorState(display,windows,MagickFalse);
8049      if (windows->image.orphan != MagickFalse)
8050        break;
8051      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8052      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8053      break;
8054    }
8055    case QuantizeCommand:
8056    {
8057      int
8058        status;
8059
8060      static char
8061        colors[MaxTextExtent] = "256";
8062
8063      /*
8064        Query user for maximum number of colors.
8065      */
8066      status=XDialogWidget(display,windows,"Quantize",
8067        "Maximum number of colors:",colors);
8068      if (*colors == '\0')
8069        break;
8070      /*
8071        Color reduce the image.
8072      */
8073      XSetCursorState(display,windows,MagickTrue);
8074      XCheckRefreshWindows(display,windows);
8075      quantize_info.number_colors=StringToUnsignedLong(colors);
8076      quantize_info.dither=status != 0 ? MagickTrue : MagickFalse;
8077      (void) QuantizeImage(&quantize_info,*image,exception);
8078      XSetCursorState(display,windows,MagickFalse);
8079      if (windows->image.orphan != MagickFalse)
8080        break;
8081      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8082      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8083      break;
8084    }
8085    case DespeckleCommand:
8086    {
8087      Image
8088        *despeckle_image;
8089
8090      /*
8091        Despeckle image.
8092      */
8093      XSetCursorState(display,windows,MagickTrue);
8094      XCheckRefreshWindows(display,windows);
8095      despeckle_image=DespeckleImage(*image,exception);
8096      if (despeckle_image != (Image *) NULL)
8097        {
8098          *image=DestroyImage(*image);
8099          *image=despeckle_image;
8100        }
8101      CatchException(exception);
8102      XSetCursorState(display,windows,MagickFalse);
8103      if (windows->image.orphan != MagickFalse)
8104        break;
8105      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8106      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8107      break;
8108    }
8109    case EmbossCommand:
8110    {
8111      Image
8112        *emboss_image;
8113
8114      static char
8115        radius[MaxTextExtent] = "0.0x1.0";
8116
8117      /*
8118        Query user for emboss radius.
8119      */
8120      (void) XDialogWidget(display,windows,"Emboss",
8121        "Enter the emboss radius and standard deviation:",radius);
8122      if (*radius == '\0')
8123        break;
8124      /*
8125        Reduce noise in the image.
8126      */
8127      XSetCursorState(display,windows,MagickTrue);
8128      XCheckRefreshWindows(display,windows);
8129      flags=ParseGeometry(radius,&geometry_info);
8130      if ((flags & SigmaValue) == 0)
8131        geometry_info.sigma=1.0;
8132      emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8133        exception);
8134      if (emboss_image != (Image *) NULL)
8135        {
8136          *image=DestroyImage(*image);
8137          *image=emboss_image;
8138        }
8139      CatchException(exception);
8140      XSetCursorState(display,windows,MagickFalse);
8141      if (windows->image.orphan != MagickFalse)
8142        break;
8143      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8144      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8145      break;
8146    }
8147    case ReduceNoiseCommand:
8148    {
8149      Image
8150        *noise_image;
8151
8152      static char
8153        radius[MaxTextExtent] = "0";
8154
8155      /*
8156        Query user for noise radius.
8157      */
8158      (void) XDialogWidget(display,windows,"Reduce Noise",
8159        "Enter the noise radius:",radius);
8160      if (*radius == '\0')
8161        break;
8162      /*
8163        Reduce noise in the image.
8164      */
8165      XSetCursorState(display,windows,MagickTrue);
8166      XCheckRefreshWindows(display,windows);
8167      flags=ParseGeometry(radius,&geometry_info);
8168      noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8169        geometry_info.rho,(size_t) geometry_info.rho,exception);
8170      if (noise_image != (Image *) NULL)
8171        {
8172          *image=DestroyImage(*image);
8173          *image=noise_image;
8174        }
8175      CatchException(exception);
8176      XSetCursorState(display,windows,MagickFalse);
8177      if (windows->image.orphan != MagickFalse)
8178        break;
8179      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8180      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8181      break;
8182    }
8183    case AddNoiseCommand:
8184    {
8185      char
8186        **noises;
8187
8188      Image
8189        *noise_image;
8190
8191      static char
8192        noise_type[MaxTextExtent] = "Gaussian";
8193
8194      /*
8195        Add noise to the image.
8196      */
8197      noises=GetCommandOptions(MagickNoiseOptions);
8198      if (noises == (char **) NULL)
8199        break;
8200      XListBrowserWidget(display,windows,&windows->widget,
8201        (const char **) noises,"Add Noise",
8202        "Select a type of noise to add to your image:",noise_type);
8203      noises=DestroyStringList(noises);
8204      if (*noise_type == '\0')
8205        break;
8206      XSetCursorState(display,windows,MagickTrue);
8207      XCheckRefreshWindows(display,windows);
8208      noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8209        MagickNoiseOptions,MagickFalse,noise_type),1.0,exception);
8210      if (noise_image != (Image *) NULL)
8211        {
8212          *image=DestroyImage(*image);
8213          *image=noise_image;
8214        }
8215      CatchException(exception);
8216      XSetCursorState(display,windows,MagickFalse);
8217      if (windows->image.orphan != MagickFalse)
8218        break;
8219      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8220      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8221      break;
8222    }
8223    case SharpenCommand:
8224    {
8225      Image
8226        *sharp_image;
8227
8228      static char
8229        radius[MaxTextExtent] = "0.0x1.0";
8230
8231      /*
8232        Query user for sharpen radius.
8233      */
8234      (void) XDialogWidget(display,windows,"Sharpen",
8235        "Enter the sharpen radius and standard deviation:",radius);
8236      if (*radius == '\0')
8237        break;
8238      /*
8239        Sharpen image scanlines.
8240      */
8241      XSetCursorState(display,windows,MagickTrue);
8242      XCheckRefreshWindows(display,windows);
8243      flags=ParseGeometry(radius,&geometry_info);
8244      sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8245        geometry_info.xi,exception);
8246      if (sharp_image != (Image *) NULL)
8247        {
8248          *image=DestroyImage(*image);
8249          *image=sharp_image;
8250        }
8251      CatchException(exception);
8252      XSetCursorState(display,windows,MagickFalse);
8253      if (windows->image.orphan != MagickFalse)
8254        break;
8255      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8256      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8257      break;
8258    }
8259    case BlurCommand:
8260    {
8261      Image
8262        *blur_image;
8263
8264      static char
8265        radius[MaxTextExtent] = "0.0x1.0";
8266
8267      /*
8268        Query user for blur radius.
8269      */
8270      (void) XDialogWidget(display,windows,"Blur",
8271        "Enter the blur radius and standard deviation:",radius);
8272      if (*radius == '\0')
8273        break;
8274      /*
8275        Blur an image.
8276      */
8277      XSetCursorState(display,windows,MagickTrue);
8278      XCheckRefreshWindows(display,windows);
8279      flags=ParseGeometry(radius,&geometry_info);
8280      blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8281        geometry_info.xi,exception);
8282      if (blur_image != (Image *) NULL)
8283        {
8284          *image=DestroyImage(*image);
8285          *image=blur_image;
8286        }
8287      CatchException(exception);
8288      XSetCursorState(display,windows,MagickFalse);
8289      if (windows->image.orphan != MagickFalse)
8290        break;
8291      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8292      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8293      break;
8294    }
8295    case ThresholdCommand:
8296    {
8297      double
8298        threshold;
8299
8300      static char
8301        factor[MaxTextExtent] = "128";
8302
8303      /*
8304        Query user for threshold value.
8305      */
8306      (void) XDialogWidget(display,windows,"Threshold",
8307        "Enter threshold value:",factor);
8308      if (*factor == '\0')
8309        break;
8310      /*
8311        Gamma correct image.
8312      */
8313      XSetCursorState(display,windows,MagickTrue);
8314      XCheckRefreshWindows(display,windows);
8315      threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8316      (void) BilevelImage(*image,threshold,exception);
8317      XSetCursorState(display,windows,MagickFalse);
8318      if (windows->image.orphan != MagickFalse)
8319        break;
8320      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8321      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8322      break;
8323    }
8324    case EdgeDetectCommand:
8325    {
8326      Image
8327        *edge_image;
8328
8329      static char
8330        radius[MaxTextExtent] = "0";
8331
8332      /*
8333        Query user for edge factor.
8334      */
8335      (void) XDialogWidget(display,windows,"Detect Edges",
8336        "Enter the edge detect radius:",radius);
8337      if (*radius == '\0')
8338        break;
8339      /*
8340        Detect edge in image.
8341      */
8342      XSetCursorState(display,windows,MagickTrue);
8343      XCheckRefreshWindows(display,windows);
8344      flags=ParseGeometry(radius,&geometry_info);
8345      edge_image=EdgeImage(*image,geometry_info.rho,geometry_info.sigma,
8346        exception);
8347      if (edge_image != (Image *) NULL)
8348        {
8349          *image=DestroyImage(*image);
8350          *image=edge_image;
8351        }
8352      CatchException(exception);
8353      XSetCursorState(display,windows,MagickFalse);
8354      if (windows->image.orphan != MagickFalse)
8355        break;
8356      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8357      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8358      break;
8359    }
8360    case SpreadCommand:
8361    {
8362      Image
8363        *spread_image;
8364
8365      static char
8366        amount[MaxTextExtent] = "2";
8367
8368      /*
8369        Query user for spread amount.
8370      */
8371      (void) XDialogWidget(display,windows,"Spread",
8372        "Enter the displacement amount:",amount);
8373      if (*amount == '\0')
8374        break;
8375      /*
8376        Displace image pixels by a random amount.
8377      */
8378      XSetCursorState(display,windows,MagickTrue);
8379      XCheckRefreshWindows(display,windows);
8380      flags=ParseGeometry(amount,&geometry_info);
8381      spread_image=EdgeImage(*image,geometry_info.rho,geometry_info.sigma,
8382        exception);
8383      if (spread_image != (Image *) NULL)
8384        {
8385          *image=DestroyImage(*image);
8386          *image=spread_image;
8387        }
8388      CatchException(exception);
8389      XSetCursorState(display,windows,MagickFalse);
8390      if (windows->image.orphan != MagickFalse)
8391        break;
8392      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8393      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8394      break;
8395    }
8396    case ShadeCommand:
8397    {
8398      Image
8399        *shade_image;
8400
8401      int
8402        status;
8403
8404      static char
8405        geometry[MaxTextExtent] = "30x30";
8406
8407      /*
8408        Query user for the shade geometry.
8409      */
8410      status=XDialogWidget(display,windows,"Shade",
8411        "Enter the azimuth and elevation of the light source:",geometry);
8412      if (*geometry == '\0')
8413        break;
8414      /*
8415        Shade image pixels.
8416      */
8417      XSetCursorState(display,windows,MagickTrue);
8418      XCheckRefreshWindows(display,windows);
8419      flags=ParseGeometry(geometry,&geometry_info);
8420      if ((flags & SigmaValue) == 0)
8421        geometry_info.sigma=1.0;
8422      shade_image=ShadeImage(*image,status != 0 ? MagickFalse : MagickTrue,
8423        geometry_info.rho,geometry_info.sigma,exception);
8424      if (shade_image != (Image *) NULL)
8425        {
8426          *image=DestroyImage(*image);
8427          *image=shade_image;
8428        }
8429      CatchException(exception);
8430      XSetCursorState(display,windows,MagickFalse);
8431      if (windows->image.orphan != MagickFalse)
8432        break;
8433      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8434      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8435      break;
8436    }
8437    case RaiseCommand:
8438    {
8439      static char
8440        bevel_width[MaxTextExtent] = "10";
8441
8442      /*
8443        Query user for bevel width.
8444      */
8445      (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8446      if (*bevel_width == '\0')
8447        break;
8448      /*
8449        Raise an image.
8450      */
8451      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8452        exception);
8453      XSetCursorState(display,windows,MagickTrue);
8454      XCheckRefreshWindows(display,windows);
8455      (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8456        exception);
8457      (void) RaiseImage(*image,&page_geometry,MagickTrue,exception);
8458      XSetCursorState(display,windows,MagickFalse);
8459      if (windows->image.orphan != MagickFalse)
8460        break;
8461      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8462      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8463      break;
8464    }
8465    case SegmentCommand:
8466    {
8467      static char
8468        threshold[MaxTextExtent] = "1.0x1.5";
8469
8470      /*
8471        Query user for smoothing threshold.
8472      */
8473      (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8474        threshold);
8475      if (*threshold == '\0')
8476        break;
8477      /*
8478        Segment an image.
8479      */
8480      XSetCursorState(display,windows,MagickTrue);
8481      XCheckRefreshWindows(display,windows);
8482      flags=ParseGeometry(threshold,&geometry_info);
8483      if ((flags & SigmaValue) == 0)
8484        geometry_info.sigma=1.0;
8485      (void) SegmentImage(*image,RGBColorspace,MagickFalse,geometry_info.rho,
8486        geometry_info.sigma,exception);
8487      XSetCursorState(display,windows,MagickFalse);
8488      if (windows->image.orphan != MagickFalse)
8489        break;
8490      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8491      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8492      break;
8493    }
8494    case SepiaToneCommand:
8495    {
8496      double
8497        threshold;
8498
8499      Image
8500        *sepia_image;
8501
8502      static char
8503        factor[MaxTextExtent] = "80%";
8504
8505      /*
8506        Query user for sepia-tone factor.
8507      */
8508      (void) XDialogWidget(display,windows,"Sepia Tone",
8509        "Enter the sepia tone factor (0 - 99.9%):",factor);
8510      if (*factor == '\0')
8511        break;
8512      /*
8513        Sepia tone image pixels.
8514      */
8515      XSetCursorState(display,windows,MagickTrue);
8516      XCheckRefreshWindows(display,windows);
8517      threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8518      sepia_image=SepiaToneImage(*image,threshold,exception);
8519      if (sepia_image != (Image *) NULL)
8520        {
8521          *image=DestroyImage(*image);
8522          *image=sepia_image;
8523        }
8524      CatchException(exception);
8525      XSetCursorState(display,windows,MagickFalse);
8526      if (windows->image.orphan != MagickFalse)
8527        break;
8528      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8529      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8530      break;
8531    }
8532    case SolarizeCommand:
8533    {
8534      double
8535        threshold;
8536
8537      static char
8538        factor[MaxTextExtent] = "60%";
8539
8540      /*
8541        Query user for solarize factor.
8542      */
8543      (void) XDialogWidget(display,windows,"Solarize",
8544        "Enter the solarize factor (0 - 99.9%):",factor);
8545      if (*factor == '\0')
8546        break;
8547      /*
8548        Solarize image pixels.
8549      */
8550      XSetCursorState(display,windows,MagickTrue);
8551      XCheckRefreshWindows(display,windows);
8552      threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8553      (void) SolarizeImage(*image,threshold,exception);
8554      XSetCursorState(display,windows,MagickFalse);
8555      if (windows->image.orphan != MagickFalse)
8556        break;
8557      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8558      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8559      break;
8560    }
8561    case SwirlCommand:
8562    {
8563      Image
8564        *swirl_image;
8565
8566      static char
8567        degrees[MaxTextExtent] = "60";
8568
8569      /*
8570        Query user for swirl angle.
8571      */
8572      (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8573        degrees);
8574      if (*degrees == '\0')
8575        break;
8576      /*
8577        Swirl image pixels about the center.
8578      */
8579      XSetCursorState(display,windows,MagickTrue);
8580      XCheckRefreshWindows(display,windows);
8581      flags=ParseGeometry(degrees,&geometry_info);
8582      swirl_image=SwirlImage(*image,geometry_info.rho,(*image)->interpolate,
8583        exception);
8584      if (swirl_image != (Image *) NULL)
8585        {
8586          *image=DestroyImage(*image);
8587          *image=swirl_image;
8588        }
8589      CatchException(exception);
8590      XSetCursorState(display,windows,MagickFalse);
8591      if (windows->image.orphan != MagickFalse)
8592        break;
8593      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8594      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8595      break;
8596    }
8597    case ImplodeCommand:
8598    {
8599      Image
8600        *implode_image;
8601
8602      static char
8603        factor[MaxTextExtent] = "0.3";
8604
8605      /*
8606        Query user for implode factor.
8607      */
8608      (void) XDialogWidget(display,windows,"Implode",
8609        "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8610      if (*factor == '\0')
8611        break;
8612      /*
8613        Implode image pixels about the center.
8614      */
8615      XSetCursorState(display,windows,MagickTrue);
8616      XCheckRefreshWindows(display,windows);
8617      flags=ParseGeometry(factor,&geometry_info);
8618      implode_image=ImplodeImage(*image,geometry_info.rho,(*image)->interpolate,
8619        exception);
8620      if (implode_image != (Image *) NULL)
8621        {
8622          *image=DestroyImage(*image);
8623          *image=implode_image;
8624        }
8625      CatchException(exception);
8626      XSetCursorState(display,windows,MagickFalse);
8627      if (windows->image.orphan != MagickFalse)
8628        break;
8629      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8630      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8631      break;
8632    }
8633    case VignetteCommand:
8634    {
8635      Image
8636        *vignette_image;
8637
8638      static char
8639        geometry[MaxTextExtent] = "0x20";
8640
8641      /*
8642        Query user for the vignette geometry.
8643      */
8644      (void) XDialogWidget(display,windows,"Vignette",
8645        "Enter the radius, sigma, and x and y offsets:",geometry);
8646      if (*geometry == '\0')
8647        break;
8648      /*
8649        Soften the edges of the image in vignette style
8650      */
8651      XSetCursorState(display,windows,MagickTrue);
8652      XCheckRefreshWindows(display,windows);
8653      flags=ParseGeometry(geometry,&geometry_info);
8654      if ((flags & SigmaValue) == 0)
8655        geometry_info.sigma=1.0;
8656      if ((flags & XiValue) == 0)
8657        geometry_info.xi=0.1*(*image)->columns;
8658      if ((flags & PsiValue) == 0)
8659        geometry_info.psi=0.1*(*image)->rows;
8660      vignette_image=VignetteImage(*image,geometry_info.rho,geometry_info.sigma,
8661        (ssize_t) ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-
8662        0.5),exception);
8663      if (vignette_image != (Image *) NULL)
8664        {
8665          *image=DestroyImage(*image);
8666          *image=vignette_image;
8667        }
8668      CatchException(exception);
8669      XSetCursorState(display,windows,MagickFalse);
8670      if (windows->image.orphan != MagickFalse)
8671        break;
8672      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8673      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8674      break;
8675    }
8676    case WaveCommand:
8677    {
8678      Image
8679        *wave_image;
8680
8681      static char
8682        geometry[MaxTextExtent] = "25x150";
8683
8684      /*
8685        Query user for the wave geometry.
8686      */
8687      (void) XDialogWidget(display,windows,"Wave",
8688        "Enter the amplitude and length of the wave:",geometry);
8689      if (*geometry == '\0')
8690        break;
8691      /*
8692        Alter an image along a sine wave.
8693      */
8694      XSetCursorState(display,windows,MagickTrue);
8695      XCheckRefreshWindows(display,windows);
8696      flags=ParseGeometry(geometry,&geometry_info);
8697      if ((flags & SigmaValue) == 0)
8698        geometry_info.sigma=1.0;
8699      wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8700        (*image)->interpolate,exception);
8701      if (wave_image != (Image *) NULL)
8702        {
8703          *image=DestroyImage(*image);
8704          *image=wave_image;
8705        }
8706      CatchException(exception);
8707      XSetCursorState(display,windows,MagickFalse);
8708      if (windows->image.orphan != MagickFalse)
8709        break;
8710      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8711      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8712      break;
8713    }
8714    case OilPaintCommand:
8715    {
8716      Image
8717        *paint_image;
8718
8719      static char
8720        radius[MaxTextExtent] = "0";
8721
8722      /*
8723        Query user for circular neighborhood radius.
8724      */
8725      (void) XDialogWidget(display,windows,"Oil Paint",
8726        "Enter the mask radius:",radius);
8727      if (*radius == '\0')
8728        break;
8729      /*
8730        OilPaint image scanlines.
8731      */
8732      XSetCursorState(display,windows,MagickTrue);
8733      XCheckRefreshWindows(display,windows);
8734      flags=ParseGeometry(radius,&geometry_info);
8735      paint_image=OilPaintImage(*image,geometry_info.rho,geometry_info.sigma,
8736        exception);
8737      if (paint_image != (Image *) NULL)
8738        {
8739          *image=DestroyImage(*image);
8740          *image=paint_image;
8741        }
8742      CatchException(exception);
8743      XSetCursorState(display,windows,MagickFalse);
8744      if (windows->image.orphan != MagickFalse)
8745        break;
8746      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8747      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8748      break;
8749    }
8750    case CharcoalDrawCommand:
8751    {
8752      Image
8753        *charcoal_image;
8754
8755      static char
8756        radius[MaxTextExtent] = "0x1";
8757
8758      /*
8759        Query user for charcoal radius.
8760      */
8761      (void) XDialogWidget(display,windows,"Charcoal Draw",
8762        "Enter the charcoal radius and sigma:",radius);
8763      if (*radius == '\0')
8764        break;
8765      /*
8766        Charcoal the image.
8767      */
8768      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8769        exception);
8770      XSetCursorState(display,windows,MagickTrue);
8771      XCheckRefreshWindows(display,windows);
8772      flags=ParseGeometry(radius,&geometry_info);
8773      if ((flags & SigmaValue) == 0)
8774        geometry_info.sigma=geometry_info.rho;
8775      charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8776        geometry_info.xi,exception);
8777      if (charcoal_image != (Image *) NULL)
8778        {
8779          *image=DestroyImage(*image);
8780          *image=charcoal_image;
8781        }
8782      CatchException(exception);
8783      XSetCursorState(display,windows,MagickFalse);
8784      if (windows->image.orphan != MagickFalse)
8785        break;
8786      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8787      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8788      break;
8789    }
8790    case AnnotateCommand:
8791    {
8792      /*
8793        Annotate the image with text.
8794      */
8795      status=XAnnotateEditImage(display,resource_info,windows,*image,exception);
8796      if (status == MagickFalse)
8797        {
8798          XNoticeWidget(display,windows,"Unable to annotate X image",
8799            (*image)->filename);
8800          break;
8801        }
8802      break;
8803    }
8804    case DrawCommand:
8805    {
8806      /*
8807        Draw image.
8808      */
8809      status=XDrawEditImage(display,resource_info,windows,image,exception);
8810      if (status == MagickFalse)
8811        {
8812          XNoticeWidget(display,windows,"Unable to draw on the X image",
8813            (*image)->filename);
8814          break;
8815        }
8816      break;
8817    }
8818    case ColorCommand:
8819    {
8820      /*
8821        Color edit.
8822      */
8823      status=XColorEditImage(display,resource_info,windows,image,exception);
8824      if (status == MagickFalse)
8825        {
8826          XNoticeWidget(display,windows,"Unable to pixel edit X image",
8827            (*image)->filename);
8828          break;
8829        }
8830      break;
8831    }
8832    case MatteCommand:
8833    {
8834      /*
8835        Matte edit.
8836      */
8837      status=XMatteEditImage(display,resource_info,windows,image,exception);
8838      if (status == MagickFalse)
8839        {
8840          XNoticeWidget(display,windows,"Unable to matte edit X image",
8841            (*image)->filename);
8842          break;
8843        }
8844      break;
8845    }
8846    case CompositeCommand:
8847    {
8848      /*
8849        Composite image.
8850      */
8851      status=XCompositeImage(display,resource_info,windows,*image,
8852        exception);
8853      if (status == MagickFalse)
8854        {
8855          XNoticeWidget(display,windows,"Unable to composite X image",
8856            (*image)->filename);
8857          break;
8858        }
8859      break;
8860    }
8861    case AddBorderCommand:
8862    {
8863      Image
8864        *border_image;
8865
8866      static char
8867        geometry[MaxTextExtent] = "6x6";
8868
8869      /*
8870        Query user for border color and geometry.
8871      */
8872      XColorBrowserWidget(display,windows,"Select",color);
8873      if (*color == '\0')
8874        break;
8875      (void) XDialogWidget(display,windows,"Add Border",
8876        "Enter border geometry:",geometry);
8877      if (*geometry == '\0')
8878        break;
8879      /*
8880        Add a border to the image.
8881      */
8882      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8883        exception);
8884      XSetCursorState(display,windows,MagickTrue);
8885      XCheckRefreshWindows(display,windows);
8886      (void) QueryColorCompliance(color,AllCompliance,&(*image)->border_color,
8887        exception);
8888      (void) ParsePageGeometry(*image,geometry,&page_geometry,
8889        exception);
8890      border_image=BorderImage(*image,&page_geometry,(*image)->compose,
8891        exception);
8892      if (border_image != (Image *) NULL)
8893        {
8894          *image=DestroyImage(*image);
8895          *image=border_image;
8896        }
8897      CatchException(exception);
8898      XSetCursorState(display,windows,MagickFalse);
8899      if (windows->image.orphan != MagickFalse)
8900        break;
8901      windows->image.window_changes.width=(int) (*image)->columns;
8902      windows->image.window_changes.height=(int) (*image)->rows;
8903      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8904      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8905      break;
8906    }
8907    case AddFrameCommand:
8908    {
8909      FrameInfo
8910        frame_info;
8911
8912      Image
8913        *frame_image;
8914
8915      static char
8916        geometry[MaxTextExtent] = "6x6";
8917
8918      /*
8919        Query user for frame color and geometry.
8920      */
8921      XColorBrowserWidget(display,windows,"Select",color);
8922      if (*color == '\0')
8923        break;
8924      (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8925        geometry);
8926      if (*geometry == '\0')
8927        break;
8928      /*
8929        Surround image with an ornamental border.
8930      */
8931      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8932        exception);
8933      XSetCursorState(display,windows,MagickTrue);
8934      XCheckRefreshWindows(display,windows);
8935      (void) QueryColorCompliance(color,AllCompliance,&(*image)->matte_color,
8936        exception);
8937      (void) ParsePageGeometry(*image,geometry,&page_geometry,
8938        exception);
8939      frame_info.width=page_geometry.width;
8940      frame_info.height=page_geometry.height;
8941      frame_info.outer_bevel=page_geometry.x;
8942      frame_info.inner_bevel=page_geometry.y;
8943      frame_info.x=(ssize_t) frame_info.width;
8944      frame_info.y=(ssize_t) frame_info.height;
8945      frame_info.width=(*image)->columns+2*frame_info.width;
8946      frame_info.height=(*image)->rows+2*frame_info.height;
8947      frame_image=FrameImage(*image,&frame_info,(*image)->compose,exception);
8948      if (frame_image != (Image *) NULL)
8949        {
8950          *image=DestroyImage(*image);
8951          *image=frame_image;
8952        }
8953      CatchException(exception);
8954      XSetCursorState(display,windows,MagickFalse);
8955      if (windows->image.orphan != MagickFalse)
8956        break;
8957      windows->image.window_changes.width=(int) (*image)->columns;
8958      windows->image.window_changes.height=(int) (*image)->rows;
8959      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8960      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8961      break;
8962    }
8963    case CommentCommand:
8964    {
8965      const char
8966        *value;
8967
8968      FILE
8969        *file;
8970
8971      int
8972        unique_file;
8973
8974      /*
8975        Edit image comment.
8976      */
8977      unique_file=AcquireUniqueFileResource(image_info->filename);
8978      if (unique_file == -1)
8979        XNoticeWidget(display,windows,"Unable to edit image comment",
8980          image_info->filename);
8981      value=GetImageProperty(*image,"comment",exception);
8982      if (value == (char *) NULL)
8983        unique_file=close(unique_file)-1;
8984      else
8985        {
8986          register const char
8987            *p;
8988
8989          file=fdopen(unique_file,"w");
8990          if (file == (FILE *) NULL)
8991            {
8992              XNoticeWidget(display,windows,"Unable to edit image comment",
8993                image_info->filename);
8994              break;
8995            }
8996          for (p=value; *p != '\0'; p++)
8997            (void) fputc((int) *p,file);
8998          (void) fputc('\n',file);
8999          (void) fclose(file);
9000        }
9001      XSetCursorState(display,windows,MagickTrue);
9002      XCheckRefreshWindows(display,windows);
9003      status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
9004        exception);
9005      if (status == MagickFalse)
9006        XNoticeWidget(display,windows,"Unable to edit image comment",
9007          (char *) NULL);
9008      else
9009        {
9010          char
9011            *comment;
9012
9013          comment=FileToString(image_info->filename,~0UL,exception);
9014          if (comment != (char *) NULL)
9015            {
9016              (void) SetImageProperty(*image,"comment",comment,exception);
9017              (*image)->taint=MagickTrue;
9018            }
9019        }
9020      (void) RelinquishUniqueFileResource(image_info->filename);
9021      XSetCursorState(display,windows,MagickFalse);
9022      break;
9023    }
9024    case LaunchCommand:
9025    {
9026      /*
9027        Launch program.
9028      */
9029      XSetCursorState(display,windows,MagickTrue);
9030      XCheckRefreshWindows(display,windows);
9031      (void) AcquireUniqueFilename(filename);
9032      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"launch:%s",
9033        filename);
9034      status=WriteImage(image_info,*image,exception);
9035      if (status == MagickFalse)
9036        XNoticeWidget(display,windows,"Unable to launch image editor",
9037          (char *) NULL);
9038      else
9039        {
9040          nexus=ReadImage(resource_info->image_info,exception);
9041          CatchException(exception);
9042          XClientMessage(display,windows->image.id,windows->im_protocols,
9043            windows->im_next_image,CurrentTime);
9044        }
9045      (void) RelinquishUniqueFileResource(filename);
9046      XSetCursorState(display,windows,MagickFalse);
9047      break;
9048    }
9049    case RegionofInterestCommand:
9050    {
9051      /*
9052        Apply an image processing technique to a region of interest.
9053      */
9054      (void) XROIImage(display,resource_info,windows,image,exception);
9055      break;
9056    }
9057    case InfoCommand:
9058      break;
9059    case ZoomCommand:
9060    {
9061      /*
9062        Zoom image.
9063      */
9064      if (windows->magnify.mapped != MagickFalse)
9065        (void) XRaiseWindow(display,windows->magnify.id);
9066      else
9067        {
9068          /*
9069            Make magnify image.
9070          */
9071          XSetCursorState(display,windows,MagickTrue);
9072          (void) XMapRaised(display,windows->magnify.id);
9073          XSetCursorState(display,windows,MagickFalse);
9074        }
9075      break;
9076    }
9077    case ShowPreviewCommand:
9078    {
9079      char
9080        **previews;
9081
9082      Image
9083        *preview_image;
9084
9085      static char
9086        preview_type[MaxTextExtent] = "Gamma";
9087
9088      /*
9089        Select preview type from menu.
9090      */
9091      previews=GetCommandOptions(MagickPreviewOptions);
9092      if (previews == (char **) NULL)
9093        break;
9094      XListBrowserWidget(display,windows,&windows->widget,
9095        (const char **) previews,"Preview",
9096        "Select an enhancement, effect, or F/X:",preview_type);
9097      previews=DestroyStringList(previews);
9098      if (*preview_type == '\0')
9099        break;
9100      /*
9101        Show image preview.
9102      */
9103      XSetCursorState(display,windows,MagickTrue);
9104      XCheckRefreshWindows(display,windows);
9105      image_info->preview_type=(PreviewType)
9106        ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
9107      image_info->group=(ssize_t) windows->image.id;
9108      (void) DeleteImageProperty(*image,"label");
9109      (void) SetImageProperty(*image,"label","Preview",exception);
9110      (void) AcquireUniqueFilename(filename);
9111      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"preview:%s",
9112        filename);
9113      status=WriteImage(image_info,*image,exception);
9114      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9115      preview_image=ReadImage(image_info,exception);
9116      (void) RelinquishUniqueFileResource(filename);
9117      if (preview_image == (Image *) NULL)
9118        break;
9119      (void) FormatLocaleString(preview_image->filename,MaxTextExtent,"show:%s",
9120        filename);
9121      status=WriteImage(image_info,preview_image,exception);
9122      preview_image=DestroyImage(preview_image);
9123      if (status == MagickFalse)
9124        XNoticeWidget(display,windows,"Unable to show image preview",
9125          (*image)->filename);
9126      XDelay(display,1500);
9127      XSetCursorState(display,windows,MagickFalse);
9128      break;
9129    }
9130    case ShowHistogramCommand:
9131    {
9132      Image
9133        *histogram_image;
9134
9135      /*
9136        Show image histogram.
9137      */
9138      XSetCursorState(display,windows,MagickTrue);
9139      XCheckRefreshWindows(display,windows);
9140      image_info->group=(ssize_t) windows->image.id;
9141      (void) DeleteImageProperty(*image,"label");
9142      (void) SetImageProperty(*image,"label","Histogram",exception);
9143      (void) AcquireUniqueFilename(filename);
9144      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"histogram:%s",
9145        filename);
9146      status=WriteImage(image_info,*image,exception);
9147      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9148      histogram_image=ReadImage(image_info,exception);
9149      (void) RelinquishUniqueFileResource(filename);
9150      if (histogram_image == (Image *) NULL)
9151        break;
9152      (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
9153        "show:%s",filename);
9154      status=WriteImage(image_info,histogram_image,exception);
9155      histogram_image=DestroyImage(histogram_image);
9156      if (status == MagickFalse)
9157        XNoticeWidget(display,windows,"Unable to show histogram",
9158          (*image)->filename);
9159      XDelay(display,1500);
9160      XSetCursorState(display,windows,MagickFalse);
9161      break;
9162    }
9163    case ShowMatteCommand:
9164    {
9165      Image
9166        *matte_image;
9167
9168      if ((*image)->matte == MagickFalse)
9169        {
9170          XNoticeWidget(display,windows,
9171            "Image does not have any matte information",(*image)->filename);
9172          break;
9173        }
9174      /*
9175        Show image matte.
9176      */
9177      XSetCursorState(display,windows,MagickTrue);
9178      XCheckRefreshWindows(display,windows);
9179      image_info->group=(ssize_t) windows->image.id;
9180      (void) DeleteImageProperty(*image,"label");
9181      (void) SetImageProperty(*image,"label","Matte",exception);
9182      (void) AcquireUniqueFilename(filename);
9183      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"matte:%s",
9184        filename);
9185      status=WriteImage(image_info,*image,exception);
9186      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9187      matte_image=ReadImage(image_info,exception);
9188      (void) RelinquishUniqueFileResource(filename);
9189      if (matte_image == (Image *) NULL)
9190        break;
9191      (void) FormatLocaleString(matte_image->filename,MaxTextExtent,"show:%s",
9192        filename);
9193      status=WriteImage(image_info,matte_image,exception);
9194      matte_image=DestroyImage(matte_image);
9195      if (status == MagickFalse)
9196        XNoticeWidget(display,windows,"Unable to show matte",
9197          (*image)->filename);
9198      XDelay(display,1500);
9199      XSetCursorState(display,windows,MagickFalse);
9200      break;
9201    }
9202    case BackgroundCommand:
9203    {
9204      /*
9205        Background image.
9206      */
9207      status=XBackgroundImage(display,resource_info,windows,image,exception);
9208      if (status == MagickFalse)
9209        break;
9210      nexus=CloneImage(*image,0,0,MagickTrue,exception);
9211      if (nexus != (Image *) NULL)
9212        XClientMessage(display,windows->image.id,windows->im_protocols,
9213          windows->im_next_image,CurrentTime);
9214      break;
9215    }
9216    case SlideShowCommand:
9217    {
9218      static char
9219        delay[MaxTextExtent] = "5";
9220
9221      /*
9222        Display next image after pausing.
9223      */
9224      (void) XDialogWidget(display,windows,"Slide Show",
9225        "Pause how many 1/100ths of a second between images:",delay);
9226      if (*delay == '\0')
9227        break;
9228      resource_info->delay=StringToUnsignedLong(delay);
9229      XClientMessage(display,windows->image.id,windows->im_protocols,
9230        windows->im_next_image,CurrentTime);
9231      break;
9232    }
9233    case PreferencesCommand:
9234    {
9235      /*
9236        Set user preferences.
9237      */
9238      status=XPreferencesWidget(display,resource_info,windows);
9239      if (status == MagickFalse)
9240        break;
9241      nexus=CloneImage(*image,0,0,MagickTrue,exception);
9242      if (nexus != (Image *) NULL)
9243        XClientMessage(display,windows->image.id,windows->im_protocols,
9244          windows->im_next_image,CurrentTime);
9245      break;
9246    }
9247    case HelpCommand:
9248    {
9249      /*
9250        User requested help.
9251      */
9252      XTextViewWidget(display,resource_info,windows,MagickFalse,
9253        "Help Viewer - Display",DisplayHelp);
9254      break;
9255    }
9256    case BrowseDocumentationCommand:
9257    {
9258      Atom
9259        mozilla_atom;
9260
9261      Window
9262        mozilla_window,
9263        root_window;
9264
9265      /*
9266        Browse the ImageMagick documentation.
9267      */
9268      root_window=XRootWindow(display,XDefaultScreen(display));
9269      mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9270      mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9271      if (mozilla_window != (Window) NULL)
9272        {
9273          char
9274            command[MaxTextExtent],
9275            *url;
9276
9277          /*
9278            Display documentation using Netscape remote control.
9279          */
9280          url=GetMagickHomeURL();
9281          (void) FormatLocaleString(command,MaxTextExtent,
9282            "openurl(%s,new-tab)",url);
9283          url=DestroyString(url);
9284          mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9285          (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9286            8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9287          XSetCursorState(display,windows,MagickFalse);
9288          break;
9289        }
9290      XSetCursorState(display,windows,MagickTrue);
9291      XCheckRefreshWindows(display,windows);
9292      status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9293        exception);
9294      if (status == MagickFalse)
9295        XNoticeWidget(display,windows,"Unable to browse documentation",
9296          (char *) NULL);
9297      XDelay(display,1500);
9298      XSetCursorState(display,windows,MagickFalse);
9299      break;
9300    }
9301    case VersionCommand:
9302    {
9303      XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9304        GetMagickCopyright());
9305      break;
9306    }
9307    case SaveToUndoBufferCommand:
9308      break;
9309    default:
9310    {
9311      (void) XBell(display,0);
9312      break;
9313    }
9314  }
9315  image_info=DestroyImageInfo(image_info);
9316  return(nexus);
9317}
9318
9319/*
9320%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9321%                                                                             %
9322%                                                                             %
9323%                                                                             %
9324+   X M a g n i f y I m a g e                                                 %
9325%                                                                             %
9326%                                                                             %
9327%                                                                             %
9328%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9329%
9330%  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9331%  The magnified portion is displayed in a separate window.
9332%
9333%  The format of the XMagnifyImage method is:
9334%
9335%      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9336%        ExceptionInfo *exception)
9337%
9338%  A description of each parameter follows:
9339%
9340%    o display: Specifies a connection to an X server;  returned from
9341%      XOpenDisplay.
9342%
9343%    o windows: Specifies a pointer to a XWindows structure.
9344%
9345%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
9346%      the entire image is refreshed.
9347%
9348%    o exception: return any errors or warnings in this structure.
9349%
9350*/
9351static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9352  ExceptionInfo *exception)
9353{
9354  char
9355    text[MaxTextExtent];
9356
9357  register int
9358    x,
9359    y;
9360
9361  size_t
9362    state;
9363
9364  /*
9365    Update magnified image until the mouse button is released.
9366  */
9367  (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9368  state=DefaultState;
9369  x=event->xbutton.x;
9370  y=event->xbutton.y;
9371  windows->magnify.x=(int) windows->image.x+x;
9372  windows->magnify.y=(int) windows->image.y+y;
9373  do
9374  {
9375    /*
9376      Map and unmap Info widget as text cursor crosses its boundaries.
9377    */
9378    if (windows->info.mapped != MagickFalse)
9379      {
9380        if ((x < (int) (windows->info.x+windows->info.width)) &&
9381            (y < (int) (windows->info.y+windows->info.height)))
9382          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9383      }
9384    else
9385      if ((x > (int) (windows->info.x+windows->info.width)) ||
9386          (y > (int) (windows->info.y+windows->info.height)))
9387        (void) XMapWindow(display,windows->info.id);
9388    if (windows->info.mapped != MagickFalse)
9389      {
9390        /*
9391          Display pointer position.
9392        */
9393        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9394          windows->magnify.x,windows->magnify.y);
9395        XInfoWidget(display,windows,text);
9396      }
9397    /*
9398      Wait for next event.
9399    */
9400    XScreenEvent(display,windows,event,exception);
9401    switch (event->type)
9402    {
9403      case ButtonPress:
9404        break;
9405      case ButtonRelease:
9406      {
9407        /*
9408          User has finished magnifying image.
9409        */
9410        x=event->xbutton.x;
9411        y=event->xbutton.y;
9412        state|=ExitState;
9413        break;
9414      }
9415      case Expose:
9416        break;
9417      case MotionNotify:
9418      {
9419        x=event->xmotion.x;
9420        y=event->xmotion.y;
9421        break;
9422      }
9423      default:
9424        break;
9425    }
9426    /*
9427      Check boundary conditions.
9428    */
9429    if (x < 0)
9430      x=0;
9431    else
9432      if (x >= (int) windows->image.width)
9433        x=(int) windows->image.width-1;
9434    if (y < 0)
9435      y=0;
9436    else
9437     if (y >= (int) windows->image.height)
9438       y=(int) windows->image.height-1;
9439  } while ((state & ExitState) == 0);
9440  /*
9441    Display magnified image.
9442  */
9443  XSetCursorState(display,windows,MagickFalse);
9444}
9445
9446/*
9447%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9448%                                                                             %
9449%                                                                             %
9450%                                                                             %
9451+   X M a g n i f y W i n d o w C o m m a n d                                 %
9452%                                                                             %
9453%                                                                             %
9454%                                                                             %
9455%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9456%
9457%  XMagnifyWindowCommand() moves the image within an Magnify window by one
9458%  pixel as specified by the key symbol.
9459%
9460%  The format of the XMagnifyWindowCommand method is:
9461%
9462%      void XMagnifyWindowCommand(Display *display,XWindows *windows,
9463%        const MagickStatusType state,const KeySym key_symbol,
9464%        ExceptionInfo *exception)
9465%
9466%  A description of each parameter follows:
9467%
9468%    o display: Specifies a connection to an X server; returned from
9469%      XOpenDisplay.
9470%
9471%    o windows: Specifies a pointer to a XWindows structure.
9472%
9473%    o state: key mask.
9474%
9475%    o key_symbol: Specifies a KeySym which indicates which side of the image
9476%      to trim.
9477%
9478%    o exception: return any errors or warnings in this structure.
9479%
9480*/
9481static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9482  const MagickStatusType state,const KeySym key_symbol,ExceptionInfo *exception)
9483{
9484  unsigned int
9485    quantum;
9486
9487  /*
9488    User specified a magnify factor or position.
9489  */
9490  quantum=1;
9491  if ((state & Mod1Mask) != 0)
9492    quantum=10;
9493  switch ((int) key_symbol)
9494  {
9495    case QuitCommand:
9496    {
9497      (void) XWithdrawWindow(display,windows->magnify.id,
9498        windows->magnify.screen);
9499      break;
9500    }
9501    case XK_Home:
9502    case XK_KP_Home:
9503    {
9504      windows->magnify.x=(int) windows->image.width/2;
9505      windows->magnify.y=(int) windows->image.height/2;
9506      break;
9507    }
9508    case XK_Left:
9509    case XK_KP_Left:
9510    {
9511      if (windows->magnify.x > 0)
9512        windows->magnify.x-=quantum;
9513      break;
9514    }
9515    case XK_Up:
9516    case XK_KP_Up:
9517    {
9518      if (windows->magnify.y > 0)
9519        windows->magnify.y-=quantum;
9520      break;
9521    }
9522    case XK_Right:
9523    case XK_KP_Right:
9524    {
9525      if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9526        windows->magnify.x+=quantum;
9527      break;
9528    }
9529    case XK_Down:
9530    case XK_KP_Down:
9531    {
9532      if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9533        windows->magnify.y+=quantum;
9534      break;
9535    }
9536    case XK_0:
9537    case XK_1:
9538    case XK_2:
9539    case XK_3:
9540    case XK_4:
9541    case XK_5:
9542    case XK_6:
9543    case XK_7:
9544    case XK_8:
9545    case XK_9:
9546    {
9547      windows->magnify.data=(key_symbol-XK_0);
9548      break;
9549    }
9550    case XK_KP_0:
9551    case XK_KP_1:
9552    case XK_KP_2:
9553    case XK_KP_3:
9554    case XK_KP_4:
9555    case XK_KP_5:
9556    case XK_KP_6:
9557    case XK_KP_7:
9558    case XK_KP_8:
9559    case XK_KP_9:
9560    {
9561      windows->magnify.data=(key_symbol-XK_KP_0);
9562      break;
9563    }
9564    default:
9565      break;
9566  }
9567  XMakeMagnifyImage(display,windows,exception);
9568}
9569
9570/*
9571%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9572%                                                                             %
9573%                                                                             %
9574%                                                                             %
9575+   X M a k e P a n I m a g e                                                 %
9576%                                                                             %
9577%                                                                             %
9578%                                                                             %
9579%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9580%
9581%  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9582%  icon window.
9583%
9584%  The format of the XMakePanImage method is:
9585%
9586%        void XMakePanImage(Display *display,XResourceInfo *resource_info,
9587%          XWindows *windows,Image *image,ExceptionInfo *exception)
9588%
9589%  A description of each parameter follows:
9590%
9591%    o display: Specifies a connection to an X server;  returned from
9592%      XOpenDisplay.
9593%
9594%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9595%
9596%    o windows: Specifies a pointer to a XWindows structure.
9597%
9598%    o image: the image.
9599%
9600%    o exception: return any errors or warnings in this structure.
9601%
9602*/
9603static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9604  XWindows *windows,Image *image,ExceptionInfo *exception)
9605{
9606  MagickStatusType
9607    status;
9608
9609  /*
9610    Create and display image for panning icon.
9611  */
9612  XSetCursorState(display,windows,MagickTrue);
9613  XCheckRefreshWindows(display,windows);
9614  windows->pan.x=(int) windows->image.x;
9615  windows->pan.y=(int) windows->image.y;
9616  status=XMakeImage(display,resource_info,&windows->pan,image,
9617    windows->pan.width,windows->pan.height,exception);
9618  if (status == MagickFalse)
9619    ThrowXWindowFatalException(ResourceLimitError,
9620     "MemoryAllocationFailed",image->filename);
9621  (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9622    windows->pan.pixmap);
9623  (void) XClearWindow(display,windows->pan.id);
9624  XDrawPanRectangle(display,windows);
9625  XSetCursorState(display,windows,MagickFalse);
9626}
9627
9628/*
9629%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9630%                                                                             %
9631%                                                                             %
9632%                                                                             %
9633+   X M a t t a E d i t I m a g e                                             %
9634%                                                                             %
9635%                                                                             %
9636%                                                                             %
9637%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9638%
9639%  XMatteEditImage() allows the user to interactively change the Matte channel
9640%  of an image.  If the image is PseudoClass it is promoted to DirectClass
9641%  before the matte information is stored.
9642%
9643%  The format of the XMatteEditImage method is:
9644%
9645%      MagickBooleanType XMatteEditImage(Display *display,
9646%        XResourceInfo *resource_info,XWindows *windows,Image **image,
9647%        ExceptionInfo *exception)
9648%
9649%  A description of each parameter follows:
9650%
9651%    o display: Specifies a connection to an X server;  returned from
9652%      XOpenDisplay.
9653%
9654%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9655%
9656%    o windows: Specifies a pointer to a XWindows structure.
9657%
9658%    o image: the image; returned from ReadImage.
9659%
9660%    o exception: return any errors or warnings in this structure.
9661%
9662*/
9663static MagickBooleanType XMatteEditImage(Display *display,
9664  XResourceInfo *resource_info,XWindows *windows,Image **image,
9665  ExceptionInfo *exception)
9666{
9667  static char
9668    matte[MaxTextExtent] = "0";
9669
9670  static const char
9671    *MatteEditMenu[] =
9672    {
9673      "Method",
9674      "Border Color",
9675      "Fuzz",
9676      "Matte Value",
9677      "Undo",
9678      "Help",
9679      "Dismiss",
9680      (char *) NULL
9681    };
9682
9683  static const ModeType
9684    MatteEditCommands[] =
9685    {
9686      MatteEditMethod,
9687      MatteEditBorderCommand,
9688      MatteEditFuzzCommand,
9689      MatteEditValueCommand,
9690      MatteEditUndoCommand,
9691      MatteEditHelpCommand,
9692      MatteEditDismissCommand
9693    };
9694
9695  static PaintMethod
9696    method = PointMethod;
9697
9698  static XColor
9699    border_color = { 0, 0, 0, 0, 0, 0 };
9700
9701  char
9702    command[MaxTextExtent],
9703    text[MaxTextExtent];
9704
9705  Cursor
9706    cursor;
9707
9708  int
9709    entry,
9710    id,
9711    x,
9712    x_offset,
9713    y,
9714    y_offset;
9715
9716  register int
9717    i;
9718
9719  register Quantum
9720    *q;
9721
9722  unsigned int
9723    height,
9724    width;
9725
9726  size_t
9727    state;
9728
9729  XEvent
9730    event;
9731
9732  /*
9733    Map Command widget.
9734  */
9735  (void) CloneString(&windows->command.name,"Matte Edit");
9736  windows->command.data=4;
9737  (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9738  (void) XMapRaised(display,windows->command.id);
9739  XClientMessage(display,windows->image.id,windows->im_protocols,
9740    windows->im_update_widget,CurrentTime);
9741  /*
9742    Make cursor.
9743  */
9744  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9745    resource_info->background_color,resource_info->foreground_color);
9746  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9747  /*
9748    Track pointer until button 1 is pressed.
9749  */
9750  XQueryPosition(display,windows->image.id,&x,&y);
9751  (void) XSelectInput(display,windows->image.id,
9752    windows->image.attributes.event_mask | PointerMotionMask);
9753  state=DefaultState;
9754  do
9755  {
9756    if (windows->info.mapped != MagickFalse)
9757      {
9758        /*
9759          Display pointer position.
9760        */
9761        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9762          x+windows->image.x,y+windows->image.y);
9763        XInfoWidget(display,windows,text);
9764      }
9765    /*
9766      Wait for next event.
9767    */
9768    XScreenEvent(display,windows,&event,exception);
9769    if (event.xany.window == windows->command.id)
9770      {
9771        /*
9772          Select a command from the Command widget.
9773        */
9774        id=XCommandWidget(display,windows,MatteEditMenu,&event);
9775        if (id < 0)
9776          {
9777            (void) XCheckDefineCursor(display,windows->image.id,cursor);
9778            continue;
9779          }
9780        switch (MatteEditCommands[id])
9781        {
9782          case MatteEditMethod:
9783          {
9784            char
9785              **methods;
9786
9787            /*
9788              Select a method from the pop-up menu.
9789            */
9790            methods=GetCommandOptions(MagickMethodOptions);
9791            if (methods == (char **) NULL)
9792              break;
9793            entry=XMenuWidget(display,windows,MatteEditMenu[id],
9794              (const char **) methods,command);
9795            if (entry >= 0)
9796              method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9797                MagickFalse,methods[entry]);
9798            methods=DestroyStringList(methods);
9799            break;
9800          }
9801          case MatteEditBorderCommand:
9802          {
9803            const char
9804              *ColorMenu[MaxNumberPens];
9805
9806            int
9807              pen_number;
9808
9809            /*
9810              Initialize menu selections.
9811            */
9812            for (i=0; i < (int) (MaxNumberPens-2); i++)
9813              ColorMenu[i]=resource_info->pen_colors[i];
9814            ColorMenu[MaxNumberPens-2]="Browser...";
9815            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9816            /*
9817              Select a pen color from the pop-up menu.
9818            */
9819            pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9820              (const char **) ColorMenu,command);
9821            if (pen_number < 0)
9822              break;
9823            if (pen_number == (MaxNumberPens-2))
9824              {
9825                static char
9826                  color_name[MaxTextExtent] = "gray";
9827
9828                /*
9829                  Select a pen color from a dialog.
9830                */
9831                resource_info->pen_colors[pen_number]=color_name;
9832                XColorBrowserWidget(display,windows,"Select",color_name);
9833                if (*color_name == '\0')
9834                  break;
9835              }
9836            /*
9837              Set border color.
9838            */
9839            (void) XParseColor(display,windows->map_info->colormap,
9840              resource_info->pen_colors[pen_number],&border_color);
9841            break;
9842          }
9843          case MatteEditFuzzCommand:
9844          {
9845            static char
9846              fuzz[MaxTextExtent];
9847
9848            static const char
9849              *FuzzMenu[] =
9850              {
9851                "0%",
9852                "2%",
9853                "5%",
9854                "10%",
9855                "15%",
9856                "Dialog...",
9857                (char *) NULL,
9858              };
9859
9860            /*
9861              Select a command from the pop-up menu.
9862            */
9863            entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9864              command);
9865            if (entry < 0)
9866              break;
9867            if (entry != 5)
9868              {
9869                (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
9870                  QuantumRange+1.0);
9871                break;
9872              }
9873            (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9874            (void) XDialogWidget(display,windows,"Ok",
9875              "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9876            if (*fuzz == '\0')
9877              break;
9878            (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
9879            (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
9880              1.0);
9881            break;
9882          }
9883          case MatteEditValueCommand:
9884          {
9885            static char
9886              message[MaxTextExtent];
9887
9888            static const char
9889              *MatteMenu[] =
9890              {
9891                "Opaque",
9892                "Transparent",
9893                "Dialog...",
9894                (char *) NULL,
9895              };
9896
9897            /*
9898              Select a command from the pop-up menu.
9899            */
9900            entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9901              command);
9902            if (entry < 0)
9903              break;
9904            if (entry != 2)
9905              {
9906                (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9907                  OpaqueAlpha);
9908                if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9909                  (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9910                    (Quantum) TransparentAlpha);
9911                break;
9912              }
9913            (void) FormatLocaleString(message,MaxTextExtent,
9914              "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9915              QuantumRange);
9916            (void) XDialogWidget(display,windows,"Matte",message,matte);
9917            if (*matte == '\0')
9918              break;
9919            break;
9920          }
9921          case MatteEditUndoCommand:
9922          {
9923            (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9924              image,exception);
9925            break;
9926          }
9927          case MatteEditHelpCommand:
9928          {
9929            XTextViewWidget(display,resource_info,windows,MagickFalse,
9930              "Help Viewer - Matte Edit",ImageMatteEditHelp);
9931            break;
9932          }
9933          case MatteEditDismissCommand:
9934          {
9935            /*
9936              Prematurely exit.
9937            */
9938            state|=EscapeState;
9939            state|=ExitState;
9940            break;
9941          }
9942          default:
9943            break;
9944        }
9945        (void) XCheckDefineCursor(display,windows->image.id,cursor);
9946        continue;
9947      }
9948    switch (event.type)
9949    {
9950      case ButtonPress:
9951      {
9952        if (event.xbutton.button != Button1)
9953          break;
9954        if ((event.xbutton.window != windows->image.id) &&
9955            (event.xbutton.window != windows->magnify.id))
9956          break;
9957        /*
9958          Update matte data.
9959        */
9960        x=event.xbutton.x;
9961        y=event.xbutton.y;
9962        (void) XMagickCommand(display,resource_info,windows,
9963          SaveToUndoBufferCommand,image,exception);
9964        state|=UpdateConfigurationState;
9965        break;
9966      }
9967      case ButtonRelease:
9968      {
9969        if (event.xbutton.button != Button1)
9970          break;
9971        if ((event.xbutton.window != windows->image.id) &&
9972            (event.xbutton.window != windows->magnify.id))
9973          break;
9974        /*
9975          Update colormap information.
9976        */
9977        x=event.xbutton.x;
9978        y=event.xbutton.y;
9979        XConfigureImageColormap(display,resource_info,windows,*image,exception);
9980        (void) XConfigureImage(display,resource_info,windows,*image,exception);
9981        XInfoWidget(display,windows,text);
9982        (void) XCheckDefineCursor(display,windows->image.id,cursor);
9983        state&=(~UpdateConfigurationState);
9984        break;
9985      }
9986      case Expose:
9987        break;
9988      case KeyPress:
9989      {
9990        char
9991          command[MaxTextExtent];
9992
9993        KeySym
9994          key_symbol;
9995
9996        if (event.xkey.window == windows->magnify.id)
9997          {
9998            Window
9999              window;
10000
10001            window=windows->magnify.id;
10002            while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
10003          }
10004        if (event.xkey.window != windows->image.id)
10005          break;
10006        /*
10007          Respond to a user key press.
10008        */
10009        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
10010          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10011        switch ((int) key_symbol)
10012        {
10013          case XK_Escape:
10014          case XK_F20:
10015          {
10016            /*
10017              Prematurely exit.
10018            */
10019            state|=ExitState;
10020            break;
10021          }
10022          case XK_F1:
10023          case XK_Help:
10024          {
10025            XTextViewWidget(display,resource_info,windows,MagickFalse,
10026              "Help Viewer - Matte Edit",ImageMatteEditHelp);
10027            break;
10028          }
10029          default:
10030          {
10031            (void) XBell(display,0);
10032            break;
10033          }
10034        }
10035        break;
10036      }
10037      case MotionNotify:
10038      {
10039        /*
10040          Map and unmap Info widget as cursor crosses its boundaries.
10041        */
10042        x=event.xmotion.x;
10043        y=event.xmotion.y;
10044        if (windows->info.mapped != MagickFalse)
10045          {
10046            if ((x < (int) (windows->info.x+windows->info.width)) &&
10047                (y < (int) (windows->info.y+windows->info.height)))
10048              (void) XWithdrawWindow(display,windows->info.id,
10049                windows->info.screen);
10050          }
10051        else
10052          if ((x > (int) (windows->info.x+windows->info.width)) ||
10053              (y > (int) (windows->info.y+windows->info.height)))
10054            (void) XMapWindow(display,windows->info.id);
10055        break;
10056      }
10057      default:
10058        break;
10059    }
10060    if (event.xany.window == windows->magnify.id)
10061      {
10062        x=windows->magnify.x-windows->image.x;
10063        y=windows->magnify.y-windows->image.y;
10064      }
10065    x_offset=x;
10066    y_offset=y;
10067    if ((state & UpdateConfigurationState) != 0)
10068      {
10069        CacheView
10070          *image_view;
10071
10072        int
10073          x,
10074          y;
10075
10076        /*
10077          Matte edit is relative to image configuration.
10078        */
10079        (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
10080          MagickTrue);
10081        XPutPixel(windows->image.ximage,x_offset,y_offset,
10082          windows->pixel_info->background_color.pixel);
10083        width=(unsigned int) (*image)->columns;
10084        height=(unsigned int) (*image)->rows;
10085        x=0;
10086        y=0;
10087        if (windows->image.crop_geometry != (char *) NULL)
10088          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
10089            &height);
10090        x_offset=(int) (width*(windows->image.x+x_offset)/
10091          windows->image.ximage->width+x);
10092        y_offset=(int) (height*(windows->image.y+y_offset)/
10093          windows->image.ximage->height+y);
10094        if ((x_offset < 0) || (y_offset < 0))
10095          continue;
10096        if ((x_offset >= (int) (*image)->columns) ||
10097            (y_offset >= (int) (*image)->rows))
10098          continue;
10099        if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10100          return(MagickFalse);
10101        (*image)->matte=MagickTrue;
10102        image_view=AcquireCacheView(*image);
10103        switch (method)
10104        {
10105          case PointMethod:
10106          default:
10107          {
10108            /*
10109              Update matte information using point algorithm.
10110            */
10111            q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10112              (ssize_t) y_offset,1,1,exception);
10113            if (q == (Quantum *) NULL)
10114              break;
10115            SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10116            (void) SyncCacheViewAuthenticPixels(image_view,exception);
10117            break;
10118          }
10119          case ReplaceMethod:
10120          {
10121            PixelInfo
10122              pixel,
10123              target;
10124
10125            Quantum
10126              virtual_pixel[CompositePixelChannel];
10127
10128            /*
10129              Update matte information using replace algorithm.
10130            */
10131            (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
10132              (ssize_t) y_offset,virtual_pixel,exception);
10133            target.red=virtual_pixel[RedPixelChannel];
10134            target.green=virtual_pixel[GreenPixelChannel];
10135            target.blue=virtual_pixel[BluePixelChannel];
10136            target.alpha=virtual_pixel[AlphaPixelChannel];
10137            for (y=0; y < (int) (*image)->rows; y++)
10138            {
10139              q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10140                (*image)->columns,1,exception);
10141              if (q == (Quantum *) NULL)
10142                break;
10143              for (x=0; x < (int) (*image)->columns; x++)
10144              {
10145                GetPixelInfoPixel(*image,q,&pixel);
10146                if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
10147                  SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10148                q+=GetPixelChannels(*image);
10149              }
10150              if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10151                break;
10152            }
10153            break;
10154          }
10155          case FloodfillMethod:
10156          case FillToBorderMethod:
10157          {
10158            ChannelType
10159              channel_mask;
10160
10161            DrawInfo
10162              *draw_info;
10163
10164            PixelInfo
10165              target;
10166
10167            /*
10168              Update matte information using floodfill algorithm.
10169            */
10170            (void) GetOneVirtualMagickPixel(*image,
10171              GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
10172              y_offset,&target,exception);
10173            if (method == FillToBorderMethod)
10174              {
10175                target.red=(MagickRealType) ScaleShortToQuantum(
10176                  border_color.red);
10177                target.green=(MagickRealType) ScaleShortToQuantum(
10178                  border_color.green);
10179                target.blue=(MagickRealType) ScaleShortToQuantum(
10180                  border_color.blue);
10181              }
10182            draw_info=CloneDrawInfo(resource_info->image_info,
10183              (DrawInfo *) NULL);
10184            draw_info->fill.alpha=ClampToQuantum(StringToDouble(matte,
10185              (char **) NULL));
10186            channel_mask=SetPixelChannelMask(*image,AlphaChannel);
10187            (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10188              x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
10189              MagickFalse : MagickTrue,exception);
10190            (void) SetPixelChannelMapMask(*image,channel_mask);
10191            draw_info=DestroyDrawInfo(draw_info);
10192            break;
10193          }
10194          case ResetMethod:
10195          {
10196            /*
10197              Update matte information using reset algorithm.
10198            */
10199            if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10200              return(MagickFalse);
10201            for (y=0; y < (int) (*image)->rows; y++)
10202            {
10203              q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10204                (*image)->columns,1,exception);
10205              if (q == (Quantum *) NULL)
10206                break;
10207              for (x=0; x < (int) (*image)->columns; x++)
10208              {
10209                SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10210                q+=GetPixelChannels(*image);
10211              }
10212              if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10213                break;
10214            }
10215            if (StringToLong(matte) == (long) OpaqueAlpha)
10216              (*image)->matte=MagickFalse;
10217            break;
10218          }
10219        }
10220        image_view=DestroyCacheView(image_view);
10221        state&=(~UpdateConfigurationState);
10222      }
10223  } while ((state & ExitState) == 0);
10224  (void) XSelectInput(display,windows->image.id,
10225    windows->image.attributes.event_mask);
10226  XSetCursorState(display,windows,MagickFalse);
10227  (void) XFreeCursor(display,cursor);
10228  return(MagickTrue);
10229}
10230
10231/*
10232%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10233%                                                                             %
10234%                                                                             %
10235%                                                                             %
10236+   X O p e n I m a g e                                                       %
10237%                                                                             %
10238%                                                                             %
10239%                                                                             %
10240%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10241%
10242%  XOpenImage() loads an image from a file.
10243%
10244%  The format of the XOpenImage method is:
10245%
10246%     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10247%       XWindows *windows,const unsigned int command)
10248%
10249%  A description of each parameter follows:
10250%
10251%    o display: Specifies a connection to an X server; returned from
10252%      XOpenDisplay.
10253%
10254%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10255%
10256%    o windows: Specifies a pointer to a XWindows structure.
10257%
10258%    o command: A value other than zero indicates that the file is selected
10259%      from the command line argument list.
10260%
10261*/
10262static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10263  XWindows *windows,const MagickBooleanType command)
10264{
10265  const MagickInfo
10266    *magick_info;
10267
10268  ExceptionInfo
10269    *exception;
10270
10271  Image
10272    *nexus;
10273
10274  ImageInfo
10275    *image_info;
10276
10277  static char
10278    filename[MaxTextExtent] = "\0";
10279
10280  /*
10281    Request file name from user.
10282  */
10283  if (command == MagickFalse)
10284    XFileBrowserWidget(display,windows,"Open",filename);
10285  else
10286    {
10287      char
10288        **filelist,
10289        **files;
10290
10291      int
10292        count,
10293        status;
10294
10295      register int
10296        i,
10297        j;
10298
10299      /*
10300        Select next image from the command line.
10301      */
10302      status=XGetCommand(display,windows->image.id,&files,&count);
10303      if (status == 0)
10304        {
10305          ThrowXWindowFatalException(XServerError,"UnableToGetProperty","...");
10306          return((Image *) NULL);
10307        }
10308      filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10309      if (filelist == (char **) NULL)
10310        {
10311          ThrowXWindowFatalException(ResourceLimitError,
10312            "MemoryAllocationFailed","...");
10313          (void) XFreeStringList(files);
10314          return((Image *) NULL);
10315        }
10316      j=0;
10317      for (i=1; i < count; i++)
10318        if (*files[i] != '-')
10319          filelist[j++]=files[i];
10320      filelist[j]=(char *) NULL;
10321      XListBrowserWidget(display,windows,&windows->widget,
10322        (const char **) filelist,"Load","Select Image to Load:",filename);
10323      filelist=(char **) RelinquishMagickMemory(filelist);
10324      (void) XFreeStringList(files);
10325    }
10326  if (*filename == '\0')
10327    return((Image *) NULL);
10328  image_info=CloneImageInfo(resource_info->image_info);
10329  (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10330    (void *) NULL);
10331  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10332  exception=AcquireExceptionInfo();
10333  (void) SetImageInfo(image_info,0,exception);
10334  if (LocaleCompare(image_info->magick,"X") == 0)
10335    {
10336      char
10337        seconds[MaxTextExtent];
10338
10339      /*
10340        User may want to delay the X server screen grab.
10341      */
10342      (void) CopyMagickString(seconds,"0",MaxTextExtent);
10343      (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10344        seconds);
10345      if (*seconds == '\0')
10346        return((Image *) NULL);
10347      XDelay(display,(size_t) (1000*StringToLong(seconds)));
10348    }
10349  magick_info=GetMagickInfo(image_info->magick,exception);
10350  if ((magick_info != (const MagickInfo *) NULL) &&
10351      (magick_info->raw != MagickFalse))
10352    {
10353      char
10354        geometry[MaxTextExtent];
10355
10356      /*
10357        Request image size from the user.
10358      */
10359      (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10360      if (image_info->size != (char *) NULL)
10361        (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10362      (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10363        geometry);
10364      (void) CloneString(&image_info->size,geometry);
10365    }
10366  /*
10367    Load the image.
10368  */
10369  XSetCursorState(display,windows,MagickTrue);
10370  XCheckRefreshWindows(display,windows);
10371  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10372  nexus=ReadImage(image_info,exception);
10373  CatchException(exception);
10374  XSetCursorState(display,windows,MagickFalse);
10375  if (nexus != (Image *) NULL)
10376    XClientMessage(display,windows->image.id,windows->im_protocols,
10377      windows->im_next_image,CurrentTime);
10378  else
10379    {
10380      char
10381        *text,
10382        **textlist;
10383
10384      /*
10385        Unknown image format.
10386      */
10387      text=FileToString(filename,~0,exception);
10388      if (text == (char *) NULL)
10389        return((Image *) NULL);
10390      textlist=StringToList(text);
10391      if (textlist != (char **) NULL)
10392        {
10393          char
10394            title[MaxTextExtent];
10395
10396          register int
10397            i;
10398
10399          (void) FormatLocaleString(title,MaxTextExtent,
10400            "Unknown format: %s",filename);
10401          XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10402            (const char **) textlist);
10403          for (i=0; textlist[i] != (char *) NULL; i++)
10404            textlist[i]=DestroyString(textlist[i]);
10405          textlist=(char **) RelinquishMagickMemory(textlist);
10406        }
10407      text=DestroyString(text);
10408    }
10409  exception=DestroyExceptionInfo(exception);
10410  image_info=DestroyImageInfo(image_info);
10411  return(nexus);
10412}
10413
10414/*
10415%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10416%                                                                             %
10417%                                                                             %
10418%                                                                             %
10419+   X P a n I m a g e                                                         %
10420%                                                                             %
10421%                                                                             %
10422%                                                                             %
10423%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10424%
10425%  XPanImage() pans the image until the mouse button is released.
10426%
10427%  The format of the XPanImage method is:
10428%
10429%      void XPanImage(Display *display,XWindows *windows,XEvent *event,
10430%        ExceptionInfo *exception)
10431%
10432%  A description of each parameter follows:
10433%
10434%    o display: Specifies a connection to an X server;  returned from
10435%      XOpenDisplay.
10436%
10437%    o windows: Specifies a pointer to a XWindows structure.
10438%
10439%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10440%      the entire image is refreshed.
10441%
10442%    o exception: return any errors or warnings in this structure.
10443%
10444*/
10445static void XPanImage(Display *display,XWindows *windows,XEvent *event,
10446  ExceptionInfo *exception)
10447{
10448  char
10449    text[MaxTextExtent];
10450
10451  Cursor
10452    cursor;
10453
10454  MagickRealType
10455    x_factor,
10456    y_factor;
10457
10458  RectangleInfo
10459    pan_info;
10460
10461  size_t
10462    state;
10463
10464  /*
10465    Define cursor.
10466  */
10467  if ((windows->image.ximage->width > (int) windows->image.width) &&
10468      (windows->image.ximage->height > (int) windows->image.height))
10469    cursor=XCreateFontCursor(display,XC_fleur);
10470  else
10471    if (windows->image.ximage->width > (int) windows->image.width)
10472      cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10473    else
10474      if (windows->image.ximage->height > (int) windows->image.height)
10475        cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10476      else
10477        cursor=XCreateFontCursor(display,XC_arrow);
10478  (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10479  /*
10480    Pan image as pointer moves until the mouse button is released.
10481  */
10482  x_factor=(MagickRealType) windows->image.ximage->width/windows->pan.width;
10483  y_factor=(MagickRealType) windows->image.ximage->height/windows->pan.height;
10484  pan_info.width=windows->pan.width*windows->image.width/
10485    windows->image.ximage->width;
10486  pan_info.height=windows->pan.height*windows->image.height/
10487    windows->image.ximage->height;
10488  pan_info.x=0;
10489  pan_info.y=0;
10490  state=UpdateConfigurationState;
10491  do
10492  {
10493    switch (event->type)
10494    {
10495      case ButtonPress:
10496      {
10497        /*
10498          User choose an initial pan location.
10499        */
10500        pan_info.x=(ssize_t) event->xbutton.x;
10501        pan_info.y=(ssize_t) event->xbutton.y;
10502        state|=UpdateConfigurationState;
10503        break;
10504      }
10505      case ButtonRelease:
10506      {
10507        /*
10508          User has finished panning the image.
10509        */
10510        pan_info.x=(ssize_t) event->xbutton.x;
10511        pan_info.y=(ssize_t) event->xbutton.y;
10512        state|=UpdateConfigurationState | ExitState;
10513        break;
10514      }
10515      case MotionNotify:
10516      {
10517        pan_info.x=(ssize_t) event->xmotion.x;
10518        pan_info.y=(ssize_t) event->xmotion.y;
10519        state|=UpdateConfigurationState;
10520      }
10521      default:
10522        break;
10523    }
10524    if ((state & UpdateConfigurationState) != 0)
10525      {
10526        /*
10527          Check boundary conditions.
10528        */
10529        if (pan_info.x < (ssize_t) (pan_info.width/2))
10530          pan_info.x=0;
10531        else
10532          pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10533        if (pan_info.x < 0)
10534          pan_info.x=0;
10535        else
10536          if ((int) (pan_info.x+windows->image.width) >
10537              windows->image.ximage->width)
10538            pan_info.x=(ssize_t)
10539              (windows->image.ximage->width-windows->image.width);
10540        if (pan_info.y < (ssize_t) (pan_info.height/2))
10541          pan_info.y=0;
10542        else
10543          pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10544        if (pan_info.y < 0)
10545          pan_info.y=0;
10546        else
10547          if ((int) (pan_info.y+windows->image.height) >
10548              windows->image.ximage->height)
10549            pan_info.y=(ssize_t)
10550              (windows->image.ximage->height-windows->image.height);
10551        if ((windows->image.x != (int) pan_info.x) ||
10552            (windows->image.y != (int) pan_info.y))
10553          {
10554            /*
10555              Display image pan offset.
10556            */
10557            windows->image.x=(int) pan_info.x;
10558            windows->image.y=(int) pan_info.y;
10559            (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
10560              windows->image.width,windows->image.height,windows->image.x,
10561              windows->image.y);
10562            XInfoWidget(display,windows,text);
10563            /*
10564              Refresh Image window.
10565            */
10566            XDrawPanRectangle(display,windows);
10567            XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10568          }
10569        state&=(~UpdateConfigurationState);
10570      }
10571    /*
10572      Wait for next event.
10573    */
10574    if ((state & ExitState) == 0)
10575      XScreenEvent(display,windows,event,exception);
10576  } while ((state & ExitState) == 0);
10577  /*
10578    Restore cursor.
10579  */
10580  (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10581  (void) XFreeCursor(display,cursor);
10582  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10583}
10584
10585/*
10586%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10587%                                                                             %
10588%                                                                             %
10589%                                                                             %
10590+   X P a s t e I m a g e                                                     %
10591%                                                                             %
10592%                                                                             %
10593%                                                                             %
10594%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10595%
10596%  XPasteImage() pastes an image previously saved with XCropImage in the X
10597%  window image at a location the user chooses with the pointer.
10598%
10599%  The format of the XPasteImage method is:
10600%
10601%      MagickBooleanType XPasteImage(Display *display,
10602%        XResourceInfo *resource_info,XWindows *windows,Image *image,
10603%        ExceptionInfo *exception)
10604%
10605%  A description of each parameter follows:
10606%
10607%    o display: Specifies a connection to an X server;  returned from
10608%      XOpenDisplay.
10609%
10610%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10611%
10612%    o windows: Specifies a pointer to a XWindows structure.
10613%
10614%    o image: the image; returned from ReadImage.
10615%
10616%    o exception: return any errors or warnings in this structure.
10617%
10618*/
10619static MagickBooleanType XPasteImage(Display *display,
10620  XResourceInfo *resource_info,XWindows *windows,Image *image,
10621  ExceptionInfo *exception)
10622{
10623  static const char
10624    *PasteMenu[] =
10625    {
10626      "Operator",
10627      "Help",
10628      "Dismiss",
10629      (char *) NULL
10630    };
10631
10632  static const ModeType
10633    PasteCommands[] =
10634    {
10635      PasteOperatorsCommand,
10636      PasteHelpCommand,
10637      PasteDismissCommand
10638    };
10639
10640  static CompositeOperator
10641    compose = CopyCompositeOp;
10642
10643  char
10644    text[MaxTextExtent];
10645
10646  Cursor
10647    cursor;
10648
10649  Image
10650    *paste_image;
10651
10652  int
10653    entry,
10654    id,
10655    x,
10656    y;
10657
10658  MagickRealType
10659    scale_factor;
10660
10661  RectangleInfo
10662    highlight_info,
10663    paste_info;
10664
10665  unsigned int
10666    height,
10667    width;
10668
10669  size_t
10670    state;
10671
10672  XEvent
10673    event;
10674
10675  /*
10676    Copy image.
10677  */
10678  if (resource_info->copy_image == (Image *) NULL)
10679    return(MagickFalse);
10680  paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,exception);
10681  /*
10682    Map Command widget.
10683  */
10684  (void) CloneString(&windows->command.name,"Paste");
10685  windows->command.data=1;
10686  (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10687  (void) XMapRaised(display,windows->command.id);
10688  XClientMessage(display,windows->image.id,windows->im_protocols,
10689    windows->im_update_widget,CurrentTime);
10690  /*
10691    Track pointer until button 1 is pressed.
10692  */
10693  XSetCursorState(display,windows,MagickFalse);
10694  XQueryPosition(display,windows->image.id,&x,&y);
10695  (void) XSelectInput(display,windows->image.id,
10696    windows->image.attributes.event_mask | PointerMotionMask);
10697  paste_info.x=(ssize_t) windows->image.x+x;
10698  paste_info.y=(ssize_t) windows->image.y+y;
10699  paste_info.width=0;
10700  paste_info.height=0;
10701  cursor=XCreateFontCursor(display,XC_ul_angle);
10702  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10703  state=DefaultState;
10704  do
10705  {
10706    if (windows->info.mapped != MagickFalse)
10707      {
10708        /*
10709          Display pointer position.
10710        */
10711        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
10712          (long) paste_info.x,(long) paste_info.y);
10713        XInfoWidget(display,windows,text);
10714      }
10715    highlight_info=paste_info;
10716    highlight_info.x=paste_info.x-windows->image.x;
10717    highlight_info.y=paste_info.y-windows->image.y;
10718    XHighlightRectangle(display,windows->image.id,
10719      windows->image.highlight_context,&highlight_info);
10720    /*
10721      Wait for next event.
10722    */
10723    XScreenEvent(display,windows,&event,exception);
10724    XHighlightRectangle(display,windows->image.id,
10725      windows->image.highlight_context,&highlight_info);
10726    if (event.xany.window == windows->command.id)
10727      {
10728        /*
10729          Select a command from the Command widget.
10730        */
10731        id=XCommandWidget(display,windows,PasteMenu,&event);
10732        if (id < 0)
10733          continue;
10734        switch (PasteCommands[id])
10735        {
10736          case PasteOperatorsCommand:
10737          {
10738            char
10739              command[MaxTextExtent],
10740              **operators;
10741
10742            /*
10743              Select a command from the pop-up menu.
10744            */
10745            operators=GetCommandOptions(MagickComposeOptions);
10746            if (operators == (char **) NULL)
10747              break;
10748            entry=XMenuWidget(display,windows,PasteMenu[id],
10749              (const char **) operators,command);
10750            if (entry >= 0)
10751              compose=(CompositeOperator) ParseCommandOption(
10752                MagickComposeOptions,MagickFalse,operators[entry]);
10753            operators=DestroyStringList(operators);
10754            break;
10755          }
10756          case PasteHelpCommand:
10757          {
10758            XTextViewWidget(display,resource_info,windows,MagickFalse,
10759              "Help Viewer - Image Composite",ImagePasteHelp);
10760            break;
10761          }
10762          case PasteDismissCommand:
10763          {
10764            /*
10765              Prematurely exit.
10766            */
10767            state|=EscapeState;
10768            state|=ExitState;
10769            break;
10770          }
10771          default:
10772            break;
10773        }
10774        continue;
10775      }
10776    switch (event.type)
10777    {
10778      case ButtonPress:
10779      {
10780        if (image->debug != MagickFalse)
10781          (void) LogMagickEvent(X11Event,GetMagickModule(),
10782            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10783            event.xbutton.button,event.xbutton.x,event.xbutton.y);
10784        if (event.xbutton.button != Button1)
10785          break;
10786        if (event.xbutton.window != windows->image.id)
10787          break;
10788        /*
10789          Paste rectangle is relative to image configuration.
10790        */
10791        width=(unsigned int) image->columns;
10792        height=(unsigned int) image->rows;
10793        x=0;
10794        y=0;
10795        if (windows->image.crop_geometry != (char *) NULL)
10796          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10797            &width,&height);
10798        scale_factor=(MagickRealType) windows->image.ximage->width/width;
10799        paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10800        scale_factor=(MagickRealType) windows->image.ximage->height/height;
10801        paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10802        (void) XCheckDefineCursor(display,windows->image.id,cursor);
10803        paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10804        paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10805        break;
10806      }
10807      case ButtonRelease:
10808      {
10809        if (image->debug != MagickFalse)
10810          (void) LogMagickEvent(X11Event,GetMagickModule(),
10811            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10812            event.xbutton.button,event.xbutton.x,event.xbutton.y);
10813        if (event.xbutton.button != Button1)
10814          break;
10815        if (event.xbutton.window != windows->image.id)
10816          break;
10817        if ((paste_info.width != 0) && (paste_info.height != 0))
10818          {
10819            /*
10820              User has selected the location of the paste image.
10821            */
10822            paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10823            paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10824            state|=ExitState;
10825          }
10826        break;
10827      }
10828      case Expose:
10829        break;
10830      case KeyPress:
10831      {
10832        char
10833          command[MaxTextExtent];
10834
10835        KeySym
10836          key_symbol;
10837
10838        int
10839          length;
10840
10841        if (event.xkey.window != windows->image.id)
10842          break;
10843        /*
10844          Respond to a user key press.
10845        */
10846        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10847          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10848        *(command+length)='\0';
10849        if (image->debug != MagickFalse)
10850          (void) LogMagickEvent(X11Event,GetMagickModule(),
10851            "Key press: 0x%lx (%s)",(long) key_symbol,command);
10852        switch ((int) key_symbol)
10853        {
10854          case XK_Escape:
10855          case XK_F20:
10856          {
10857            /*
10858              Prematurely exit.
10859            */
10860            paste_image=DestroyImage(paste_image);
10861            state|=EscapeState;
10862            state|=ExitState;
10863            break;
10864          }
10865          case XK_F1:
10866          case XK_Help:
10867          {
10868            (void) XSetFunction(display,windows->image.highlight_context,
10869              GXcopy);
10870            XTextViewWidget(display,resource_info,windows,MagickFalse,
10871              "Help Viewer - Image Composite",ImagePasteHelp);
10872            (void) XSetFunction(display,windows->image.highlight_context,
10873              GXinvert);
10874            break;
10875          }
10876          default:
10877          {
10878            (void) XBell(display,0);
10879            break;
10880          }
10881        }
10882        break;
10883      }
10884      case MotionNotify:
10885      {
10886        /*
10887          Map and unmap Info widget as text cursor crosses its boundaries.
10888        */
10889        x=event.xmotion.x;
10890        y=event.xmotion.y;
10891        if (windows->info.mapped != MagickFalse)
10892          {
10893            if ((x < (int) (windows->info.x+windows->info.width)) &&
10894                (y < (int) (windows->info.y+windows->info.height)))
10895              (void) XWithdrawWindow(display,windows->info.id,
10896                windows->info.screen);
10897          }
10898        else
10899          if ((x > (int) (windows->info.x+windows->info.width)) ||
10900              (y > (int) (windows->info.y+windows->info.height)))
10901            (void) XMapWindow(display,windows->info.id);
10902        paste_info.x=(ssize_t) windows->image.x+x;
10903        paste_info.y=(ssize_t) windows->image.y+y;
10904        break;
10905      }
10906      default:
10907      {
10908        if (image->debug != MagickFalse)
10909          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10910            event.type);
10911        break;
10912      }
10913    }
10914  } while ((state & ExitState) == 0);
10915  (void) XSelectInput(display,windows->image.id,
10916    windows->image.attributes.event_mask);
10917  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10918  XSetCursorState(display,windows,MagickFalse);
10919  (void) XFreeCursor(display,cursor);
10920  if ((state & EscapeState) != 0)
10921    return(MagickTrue);
10922  /*
10923    Image pasting is relative to image configuration.
10924  */
10925  XSetCursorState(display,windows,MagickTrue);
10926  XCheckRefreshWindows(display,windows);
10927  width=(unsigned int) image->columns;
10928  height=(unsigned int) image->rows;
10929  x=0;
10930  y=0;
10931  if (windows->image.crop_geometry != (char *) NULL)
10932    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10933  scale_factor=(MagickRealType) width/windows->image.ximage->width;
10934  paste_info.x+=x;
10935  paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10936  paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10937  scale_factor=(MagickRealType) height/windows->image.ximage->height;
10938  paste_info.y+=y;
10939  paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10940  paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10941  /*
10942    Paste image with X Image window.
10943  */
10944  (void) CompositeImage(image,compose,paste_image,paste_info.x,paste_info.y,
10945    exception);
10946  paste_image=DestroyImage(paste_image);
10947  XSetCursorState(display,windows,MagickFalse);
10948  /*
10949    Update image colormap.
10950  */
10951  XConfigureImageColormap(display,resource_info,windows,image,exception);
10952  (void) XConfigureImage(display,resource_info,windows,image,exception);
10953  return(MagickTrue);
10954}
10955
10956/*
10957%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10958%                                                                             %
10959%                                                                             %
10960%                                                                             %
10961+   X P r i n t I m a g e                                                     %
10962%                                                                             %
10963%                                                                             %
10964%                                                                             %
10965%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10966%
10967%  XPrintImage() prints an image to a Postscript printer.
10968%
10969%  The format of the XPrintImage method is:
10970%
10971%      MagickBooleanType XPrintImage(Display *display,
10972%        XResourceInfo *resource_info,XWindows *windows,Image *image,
10973%        ExceptionInfo *exception)
10974%
10975%  A description of each parameter follows:
10976%
10977%    o display: Specifies a connection to an X server; returned from
10978%      XOpenDisplay.
10979%
10980%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10981%
10982%    o windows: Specifies a pointer to a XWindows structure.
10983%
10984%    o image: the image.
10985%
10986%    o exception: return any errors or warnings in this structure.
10987%
10988*/
10989static MagickBooleanType XPrintImage(Display *display,
10990  XResourceInfo *resource_info,XWindows *windows,Image *image,
10991  ExceptionInfo *exception)
10992{
10993  char
10994    filename[MaxTextExtent],
10995    geometry[MaxTextExtent];
10996
10997  Image
10998    *print_image;
10999
11000  ImageInfo
11001    *image_info;
11002
11003  MagickStatusType
11004    status;
11005
11006  /*
11007    Request Postscript page geometry from user.
11008  */
11009  image_info=CloneImageInfo(resource_info->image_info);
11010  (void) FormatLocaleString(geometry,MaxTextExtent,"Letter");
11011  if (image_info->page != (char *) NULL)
11012    (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
11013  XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
11014    "Select Postscript Page Geometry:",geometry);
11015  if (*geometry == '\0')
11016    return(MagickTrue);
11017  image_info->page=GetPageGeometry(geometry);
11018  /*
11019    Apply image transforms.
11020  */
11021  XSetCursorState(display,windows,MagickTrue);
11022  XCheckRefreshWindows(display,windows);
11023  print_image=CloneImage(image,0,0,MagickTrue,exception);
11024  if (print_image == (Image *) NULL)
11025    return(MagickFalse);
11026  (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
11027    windows->image.ximage->width,windows->image.ximage->height);
11028  (void) TransformImage(&print_image,windows->image.crop_geometry,geometry,
11029    exception);
11030  /*
11031    Print image.
11032  */
11033  (void) AcquireUniqueFilename(filename);
11034  (void) FormatLocaleString(print_image->filename,MaxTextExtent,"print:%s",
11035    filename);
11036  status=WriteImage(image_info,print_image,exception);
11037  (void) RelinquishUniqueFileResource(filename);
11038  print_image=DestroyImage(print_image);
11039  image_info=DestroyImageInfo(image_info);
11040  XSetCursorState(display,windows,MagickFalse);
11041  return(status != 0 ? MagickTrue : MagickFalse);
11042}
11043
11044/*
11045%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11046%                                                                             %
11047%                                                                             %
11048%                                                                             %
11049+   X R O I I m a g e                                                         %
11050%                                                                             %
11051%                                                                             %
11052%                                                                             %
11053%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11054%
11055%  XROIImage() applies an image processing technique to a region of interest.
11056%
11057%  The format of the XROIImage method is:
11058%
11059%      MagickBooleanType XROIImage(Display *display,
11060%        XResourceInfo *resource_info,XWindows *windows,Image **image,
11061%        ExceptionInfo *exception)
11062%
11063%  A description of each parameter follows:
11064%
11065%    o display: Specifies a connection to an X server; returned from
11066%      XOpenDisplay.
11067%
11068%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11069%
11070%    o windows: Specifies a pointer to a XWindows structure.
11071%
11072%    o image: the image; returned from ReadImage.
11073%
11074%    o exception: return any errors or warnings in this structure.
11075%
11076*/
11077static MagickBooleanType XROIImage(Display *display,
11078  XResourceInfo *resource_info,XWindows *windows,Image **image,
11079  ExceptionInfo *exception)
11080{
11081#define ApplyMenus  7
11082
11083  static const char
11084    *ROIMenu[] =
11085    {
11086      "Help",
11087      "Dismiss",
11088      (char *) NULL
11089    },
11090    *ApplyMenu[] =
11091    {
11092      "File",
11093      "Edit",
11094      "Transform",
11095      "Enhance",
11096      "Effects",
11097      "F/X",
11098      "Miscellany",
11099      "Help",
11100      "Dismiss",
11101      (char *) NULL
11102    },
11103    *FileMenu[] =
11104    {
11105      "Save...",
11106      "Print...",
11107      (char *) NULL
11108    },
11109    *EditMenu[] =
11110    {
11111      "Undo",
11112      "Redo",
11113      (char *) NULL
11114    },
11115    *TransformMenu[] =
11116    {
11117      "Flop",
11118      "Flip",
11119      "Rotate Right",
11120      "Rotate Left",
11121      (char *) NULL
11122    },
11123    *EnhanceMenu[] =
11124    {
11125      "Hue...",
11126      "Saturation...",
11127      "Brightness...",
11128      "Gamma...",
11129      "Spiff",
11130      "Dull",
11131      "Contrast Stretch...",
11132      "Sigmoidal Contrast...",
11133      "Normalize",
11134      "Equalize",
11135      "Negate",
11136      "Grayscale",
11137      "Map...",
11138      "Quantize...",
11139      (char *) NULL
11140    },
11141    *EffectsMenu[] =
11142    {
11143      "Despeckle",
11144      "Emboss",
11145      "Reduce Noise",
11146      "Add Noise",
11147      "Sharpen...",
11148      "Blur...",
11149      "Threshold...",
11150      "Edge Detect...",
11151      "Spread...",
11152      "Shade...",
11153      "Raise...",
11154      "Segment...",
11155      (char *) NULL
11156    },
11157    *FXMenu[] =
11158    {
11159      "Solarize...",
11160      "Sepia Tone...",
11161      "Swirl...",
11162      "Implode...",
11163      "Vignette...",
11164      "Wave...",
11165      "Oil Paint...",
11166      "Charcoal Draw...",
11167      (char *) NULL
11168    },
11169    *MiscellanyMenu[] =
11170    {
11171      "Image Info",
11172      "Zoom Image",
11173      "Show Preview...",
11174      "Show Histogram",
11175      "Show Matte",
11176      (char *) NULL
11177    };
11178
11179  static const char
11180    **Menus[ApplyMenus] =
11181    {
11182      FileMenu,
11183      EditMenu,
11184      TransformMenu,
11185      EnhanceMenu,
11186      EffectsMenu,
11187      FXMenu,
11188      MiscellanyMenu
11189    };
11190
11191  static const CommandType
11192    ApplyCommands[] =
11193    {
11194      NullCommand,
11195      NullCommand,
11196      NullCommand,
11197      NullCommand,
11198      NullCommand,
11199      NullCommand,
11200      NullCommand,
11201      HelpCommand,
11202      QuitCommand
11203    },
11204    FileCommands[] =
11205    {
11206      SaveCommand,
11207      PrintCommand
11208    },
11209    EditCommands[] =
11210    {
11211      UndoCommand,
11212      RedoCommand
11213    },
11214    TransformCommands[] =
11215    {
11216      FlopCommand,
11217      FlipCommand,
11218      RotateRightCommand,
11219      RotateLeftCommand
11220    },
11221    EnhanceCommands[] =
11222    {
11223      HueCommand,
11224      SaturationCommand,
11225      BrightnessCommand,
11226      GammaCommand,
11227      SpiffCommand,
11228      DullCommand,
11229      ContrastStretchCommand,
11230      SigmoidalContrastCommand,
11231      NormalizeCommand,
11232      EqualizeCommand,
11233      NegateCommand,
11234      GrayscaleCommand,
11235      MapCommand,
11236      QuantizeCommand
11237    },
11238    EffectsCommands[] =
11239    {
11240      DespeckleCommand,
11241      EmbossCommand,
11242      ReduceNoiseCommand,
11243      AddNoiseCommand,
11244      SharpenCommand,
11245      BlurCommand,
11246      EdgeDetectCommand,
11247      SpreadCommand,
11248      ShadeCommand,
11249      RaiseCommand,
11250      SegmentCommand
11251    },
11252    FXCommands[] =
11253    {
11254      SolarizeCommand,
11255      SepiaToneCommand,
11256      SwirlCommand,
11257      ImplodeCommand,
11258      VignetteCommand,
11259      WaveCommand,
11260      OilPaintCommand,
11261      CharcoalDrawCommand
11262    },
11263    MiscellanyCommands[] =
11264    {
11265      InfoCommand,
11266      ZoomCommand,
11267      ShowPreviewCommand,
11268      ShowHistogramCommand,
11269      ShowMatteCommand
11270    },
11271    ROICommands[] =
11272    {
11273      ROIHelpCommand,
11274      ROIDismissCommand
11275    };
11276
11277  static const CommandType
11278    *Commands[ApplyMenus] =
11279    {
11280      FileCommands,
11281      EditCommands,
11282      TransformCommands,
11283      EnhanceCommands,
11284      EffectsCommands,
11285      FXCommands,
11286      MiscellanyCommands
11287    };
11288
11289  char
11290    command[MaxTextExtent],
11291    text[MaxTextExtent];
11292
11293  CommandType
11294    command_type;
11295
11296  Cursor
11297    cursor;
11298
11299  Image
11300    *roi_image;
11301
11302  int
11303    entry,
11304    id,
11305    x,
11306    y;
11307
11308  MagickRealType
11309    scale_factor;
11310
11311  MagickProgressMonitor
11312    progress_monitor;
11313
11314  RectangleInfo
11315    crop_info,
11316    highlight_info,
11317    roi_info;
11318
11319  unsigned int
11320    height,
11321    width;
11322
11323  size_t
11324    state;
11325
11326  XEvent
11327    event;
11328
11329  /*
11330    Map Command widget.
11331  */
11332  (void) CloneString(&windows->command.name,"ROI");
11333  windows->command.data=0;
11334  (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11335  (void) XMapRaised(display,windows->command.id);
11336  XClientMessage(display,windows->image.id,windows->im_protocols,
11337    windows->im_update_widget,CurrentTime);
11338  /*
11339    Track pointer until button 1 is pressed.
11340  */
11341  XQueryPosition(display,windows->image.id,&x,&y);
11342  (void) XSelectInput(display,windows->image.id,
11343    windows->image.attributes.event_mask | PointerMotionMask);
11344  roi_info.x=(ssize_t) windows->image.x+x;
11345  roi_info.y=(ssize_t) windows->image.y+y;
11346  roi_info.width=0;
11347  roi_info.height=0;
11348  cursor=XCreateFontCursor(display,XC_fleur);
11349  state=DefaultState;
11350  do
11351  {
11352    if (windows->info.mapped != MagickFalse)
11353      {
11354        /*
11355          Display pointer position.
11356        */
11357        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
11358          (long) roi_info.x,(long) roi_info.y);
11359        XInfoWidget(display,windows,text);
11360      }
11361    /*
11362      Wait for next event.
11363    */
11364    XScreenEvent(display,windows,&event,exception);
11365    if (event.xany.window == windows->command.id)
11366      {
11367        /*
11368          Select a command from the Command widget.
11369        */
11370        id=XCommandWidget(display,windows,ROIMenu,&event);
11371        if (id < 0)
11372          continue;
11373        switch (ROICommands[id])
11374        {
11375          case ROIHelpCommand:
11376          {
11377            XTextViewWidget(display,resource_info,windows,MagickFalse,
11378              "Help Viewer - Region of Interest",ImageROIHelp);
11379            break;
11380          }
11381          case ROIDismissCommand:
11382          {
11383            /*
11384              Prematurely exit.
11385            */
11386            state|=EscapeState;
11387            state|=ExitState;
11388            break;
11389          }
11390          default:
11391            break;
11392        }
11393        continue;
11394      }
11395    switch (event.type)
11396    {
11397      case ButtonPress:
11398      {
11399        if (event.xbutton.button != Button1)
11400          break;
11401        if (event.xbutton.window != windows->image.id)
11402          break;
11403        /*
11404          Note first corner of region of interest rectangle-- exit loop.
11405        */
11406        (void) XCheckDefineCursor(display,windows->image.id,cursor);
11407        roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11408        roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11409        state|=ExitState;
11410        break;
11411      }
11412      case ButtonRelease:
11413        break;
11414      case Expose:
11415        break;
11416      case KeyPress:
11417      {
11418        KeySym
11419          key_symbol;
11420
11421        if (event.xkey.window != windows->image.id)
11422          break;
11423        /*
11424          Respond to a user key press.
11425        */
11426        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11427          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11428        switch ((int) key_symbol)
11429        {
11430          case XK_Escape:
11431          case XK_F20:
11432          {
11433            /*
11434              Prematurely exit.
11435            */
11436            state|=EscapeState;
11437            state|=ExitState;
11438            break;
11439          }
11440          case XK_F1:
11441          case XK_Help:
11442          {
11443            XTextViewWidget(display,resource_info,windows,MagickFalse,
11444              "Help Viewer - Region of Interest",ImageROIHelp);
11445            break;
11446          }
11447          default:
11448          {
11449            (void) XBell(display,0);
11450            break;
11451          }
11452        }
11453        break;
11454      }
11455      case MotionNotify:
11456      {
11457        /*
11458          Map and unmap Info widget as text cursor crosses its boundaries.
11459        */
11460        x=event.xmotion.x;
11461        y=event.xmotion.y;
11462        if (windows->info.mapped != MagickFalse)
11463          {
11464            if ((x < (int) (windows->info.x+windows->info.width)) &&
11465                (y < (int) (windows->info.y+windows->info.height)))
11466              (void) XWithdrawWindow(display,windows->info.id,
11467                windows->info.screen);
11468          }
11469        else
11470          if ((x > (int) (windows->info.x+windows->info.width)) ||
11471              (y > (int) (windows->info.y+windows->info.height)))
11472            (void) XMapWindow(display,windows->info.id);
11473        roi_info.x=(ssize_t) windows->image.x+x;
11474        roi_info.y=(ssize_t) windows->image.y+y;
11475        break;
11476      }
11477      default:
11478        break;
11479    }
11480  } while ((state & ExitState) == 0);
11481  (void) XSelectInput(display,windows->image.id,
11482    windows->image.attributes.event_mask);
11483  if ((state & EscapeState) != 0)
11484    {
11485      /*
11486        User want to exit without region of interest.
11487      */
11488      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11489      (void) XFreeCursor(display,cursor);
11490      return(MagickTrue);
11491    }
11492  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11493  do
11494  {
11495    /*
11496      Size rectangle as pointer moves until the mouse button is released.
11497    */
11498    x=(int) roi_info.x;
11499    y=(int) roi_info.y;
11500    roi_info.width=0;
11501    roi_info.height=0;
11502    state=DefaultState;
11503    do
11504    {
11505      highlight_info=roi_info;
11506      highlight_info.x=roi_info.x-windows->image.x;
11507      highlight_info.y=roi_info.y-windows->image.y;
11508      if ((highlight_info.width > 3) && (highlight_info.height > 3))
11509        {
11510          /*
11511            Display info and draw region of interest rectangle.
11512          */
11513          if (windows->info.mapped == MagickFalse)
11514            (void) XMapWindow(display,windows->info.id);
11515          (void) FormatLocaleString(text,MaxTextExtent,
11516            " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11517            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11518          XInfoWidget(display,windows,text);
11519          XHighlightRectangle(display,windows->image.id,
11520            windows->image.highlight_context,&highlight_info);
11521        }
11522      else
11523        if (windows->info.mapped != MagickFalse)
11524          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11525      /*
11526        Wait for next event.
11527      */
11528      XScreenEvent(display,windows,&event,exception);
11529      if ((highlight_info.width > 3) && (highlight_info.height > 3))
11530        XHighlightRectangle(display,windows->image.id,
11531          windows->image.highlight_context,&highlight_info);
11532      switch (event.type)
11533      {
11534        case ButtonPress:
11535        {
11536          roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11537          roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11538          break;
11539        }
11540        case ButtonRelease:
11541        {
11542          /*
11543            User has committed to region of interest rectangle.
11544          */
11545          roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11546          roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11547          XSetCursorState(display,windows,MagickFalse);
11548          state|=ExitState;
11549          if (LocaleCompare(windows->command.name,"Apply") == 0)
11550            break;
11551          (void) CloneString(&windows->command.name,"Apply");
11552          windows->command.data=ApplyMenus;
11553          (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11554          break;
11555        }
11556        case Expose:
11557          break;
11558        case MotionNotify:
11559        {
11560          roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11561          roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11562        }
11563        default:
11564          break;
11565      }
11566      if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11567          ((state & ExitState) != 0))
11568        {
11569          /*
11570            Check boundary conditions.
11571          */
11572          if (roi_info.x < 0)
11573            roi_info.x=0;
11574          else
11575            if (roi_info.x > (ssize_t) windows->image.ximage->width)
11576              roi_info.x=(ssize_t) windows->image.ximage->width;
11577          if ((int) roi_info.x < x)
11578            roi_info.width=(unsigned int) (x-roi_info.x);
11579          else
11580            {
11581              roi_info.width=(unsigned int) (roi_info.x-x);
11582              roi_info.x=(ssize_t) x;
11583            }
11584          if (roi_info.y < 0)
11585            roi_info.y=0;
11586          else
11587            if (roi_info.y > (ssize_t) windows->image.ximage->height)
11588              roi_info.y=(ssize_t) windows->image.ximage->height;
11589          if ((int) roi_info.y < y)
11590            roi_info.height=(unsigned int) (y-roi_info.y);
11591          else
11592            {
11593              roi_info.height=(unsigned int) (roi_info.y-y);
11594              roi_info.y=(ssize_t) y;
11595            }
11596        }
11597    } while ((state & ExitState) == 0);
11598    /*
11599      Wait for user to grab a corner of the rectangle or press return.
11600    */
11601    state=DefaultState;
11602    command_type=NullCommand;
11603    (void) XMapWindow(display,windows->info.id);
11604    do
11605    {
11606      if (windows->info.mapped != MagickFalse)
11607        {
11608          /*
11609            Display pointer position.
11610          */
11611          (void) FormatLocaleString(text,MaxTextExtent,
11612            " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11613            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11614          XInfoWidget(display,windows,text);
11615        }
11616      highlight_info=roi_info;
11617      highlight_info.x=roi_info.x-windows->image.x;
11618      highlight_info.y=roi_info.y-windows->image.y;
11619      if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11620        {
11621          state|=EscapeState;
11622          state|=ExitState;
11623          break;
11624        }
11625      if ((state & UpdateRegionState) != 0)
11626        {
11627          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11628          switch (command_type)
11629          {
11630            case UndoCommand:
11631            case RedoCommand:
11632            {
11633              (void) XMagickCommand(display,resource_info,windows,command_type,
11634                image,exception);
11635              break;
11636            }
11637            default:
11638            {
11639              /*
11640                Region of interest is relative to image configuration.
11641              */
11642              progress_monitor=SetImageProgressMonitor(*image,
11643                (MagickProgressMonitor) NULL,(*image)->client_data);
11644              crop_info=roi_info;
11645              width=(unsigned int) (*image)->columns;
11646              height=(unsigned int) (*image)->rows;
11647              x=0;
11648              y=0;
11649              if (windows->image.crop_geometry != (char *) NULL)
11650                (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11651                  &width,&height);
11652              scale_factor=(MagickRealType) width/windows->image.ximage->width;
11653              crop_info.x+=x;
11654              crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11655              crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11656              scale_factor=(MagickRealType)
11657                height/windows->image.ximage->height;
11658              crop_info.y+=y;
11659              crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11660              crop_info.height=(unsigned int)
11661                (scale_factor*crop_info.height+0.5);
11662              roi_image=CropImage(*image,&crop_info,exception);
11663              (void) SetImageProgressMonitor(*image,progress_monitor,
11664                (*image)->client_data);
11665              if (roi_image == (Image *) NULL)
11666                continue;
11667              /*
11668                Apply image processing technique to the region of interest.
11669              */
11670              windows->image.orphan=MagickTrue;
11671              (void) XMagickCommand(display,resource_info,windows,command_type,
11672                &roi_image,exception);
11673              progress_monitor=SetImageProgressMonitor(*image,
11674                (MagickProgressMonitor) NULL,(*image)->client_data);
11675              (void) XMagickCommand(display,resource_info,windows,
11676                SaveToUndoBufferCommand,image,exception);
11677              windows->image.orphan=MagickFalse;
11678              (void) CompositeImage(*image,CopyCompositeOp,roi_image,
11679                crop_info.x,crop_info.y,exception);
11680              roi_image=DestroyImage(roi_image);
11681              (void) SetImageProgressMonitor(*image,progress_monitor,
11682                (*image)->client_data);
11683              break;
11684            }
11685          }
11686          if (command_type != InfoCommand)
11687            {
11688              XConfigureImageColormap(display,resource_info,windows,*image,
11689                exception);
11690              (void) XConfigureImage(display,resource_info,windows,*image,
11691                exception);
11692            }
11693          XCheckRefreshWindows(display,windows);
11694          XInfoWidget(display,windows,text);
11695          (void) XSetFunction(display,windows->image.highlight_context,
11696            GXinvert);
11697          state&=(~UpdateRegionState);
11698        }
11699      XHighlightRectangle(display,windows->image.id,
11700        windows->image.highlight_context,&highlight_info);
11701      XScreenEvent(display,windows,&event,exception);
11702      if (event.xany.window == windows->command.id)
11703        {
11704          /*
11705            Select a command from the Command widget.
11706          */
11707          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11708          command_type=NullCommand;
11709          id=XCommandWidget(display,windows,ApplyMenu,&event);
11710          if (id >= 0)
11711            {
11712              (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11713              command_type=ApplyCommands[id];
11714              if (id < ApplyMenus)
11715                {
11716                  /*
11717                    Select a command from a pop-up menu.
11718                  */
11719                  entry=XMenuWidget(display,windows,ApplyMenu[id],
11720                    (const char **) Menus[id],command);
11721                  if (entry >= 0)
11722                    {
11723                      (void) CopyMagickString(command,Menus[id][entry],
11724                        MaxTextExtent);
11725                      command_type=Commands[id][entry];
11726                    }
11727                }
11728            }
11729          (void) XSetFunction(display,windows->image.highlight_context,
11730            GXinvert);
11731          XHighlightRectangle(display,windows->image.id,
11732            windows->image.highlight_context,&highlight_info);
11733          if (command_type == HelpCommand)
11734            {
11735              (void) XSetFunction(display,windows->image.highlight_context,
11736                GXcopy);
11737              XTextViewWidget(display,resource_info,windows,MagickFalse,
11738                "Help Viewer - Region of Interest",ImageROIHelp);
11739              (void) XSetFunction(display,windows->image.highlight_context,
11740                GXinvert);
11741              continue;
11742            }
11743          if (command_type == QuitCommand)
11744            {
11745              /*
11746                exit.
11747              */
11748              state|=EscapeState;
11749              state|=ExitState;
11750              continue;
11751            }
11752          if (command_type != NullCommand)
11753            state|=UpdateRegionState;
11754          continue;
11755        }
11756      XHighlightRectangle(display,windows->image.id,
11757        windows->image.highlight_context,&highlight_info);
11758      switch (event.type)
11759      {
11760        case ButtonPress:
11761        {
11762          x=windows->image.x;
11763          y=windows->image.y;
11764          if (event.xbutton.button != Button1)
11765            break;
11766          if (event.xbutton.window != windows->image.id)
11767            break;
11768          x=windows->image.x+event.xbutton.x;
11769          y=windows->image.y+event.xbutton.y;
11770          if ((x < (int) (roi_info.x+RoiDelta)) &&
11771              (x > (int) (roi_info.x-RoiDelta)) &&
11772              (y < (int) (roi_info.y+RoiDelta)) &&
11773              (y > (int) (roi_info.y-RoiDelta)))
11774            {
11775              roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11776              roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11777              state|=UpdateConfigurationState;
11778              break;
11779            }
11780          if ((x < (int) (roi_info.x+RoiDelta)) &&
11781              (x > (int) (roi_info.x-RoiDelta)) &&
11782              (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11783              (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11784            {
11785              roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11786              state|=UpdateConfigurationState;
11787              break;
11788            }
11789          if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11790              (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11791              (y < (int) (roi_info.y+RoiDelta)) &&
11792              (y > (int) (roi_info.y-RoiDelta)))
11793            {
11794              roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11795              state|=UpdateConfigurationState;
11796              break;
11797            }
11798          if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11799              (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11800              (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11801              (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11802            {
11803              state|=UpdateConfigurationState;
11804              break;
11805            }
11806        }
11807        case ButtonRelease:
11808        {
11809          if (event.xbutton.window == windows->pan.id)
11810            if ((highlight_info.x != crop_info.x-windows->image.x) ||
11811                (highlight_info.y != crop_info.y-windows->image.y))
11812              XHighlightRectangle(display,windows->image.id,
11813                windows->image.highlight_context,&highlight_info);
11814          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11815            event.xbutton.time);
11816          break;
11817        }
11818        case Expose:
11819        {
11820          if (event.xexpose.window == windows->image.id)
11821            if (event.xexpose.count == 0)
11822              {
11823                event.xexpose.x=(int) highlight_info.x;
11824                event.xexpose.y=(int) highlight_info.y;
11825                event.xexpose.width=(int) highlight_info.width;
11826                event.xexpose.height=(int) highlight_info.height;
11827                XRefreshWindow(display,&windows->image,&event);
11828              }
11829          if (event.xexpose.window == windows->info.id)
11830            if (event.xexpose.count == 0)
11831              XInfoWidget(display,windows,text);
11832          break;
11833        }
11834        case KeyPress:
11835        {
11836          KeySym
11837            key_symbol;
11838
11839          if (event.xkey.window != windows->image.id)
11840            break;
11841          /*
11842            Respond to a user key press.
11843          */
11844          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11845            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11846          switch ((int) key_symbol)
11847          {
11848            case XK_Shift_L:
11849            case XK_Shift_R:
11850              break;
11851            case XK_Escape:
11852            case XK_F20:
11853              state|=EscapeState;
11854            case XK_Return:
11855            {
11856              state|=ExitState;
11857              break;
11858            }
11859            case XK_Home:
11860            case XK_KP_Home:
11861            {
11862              roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11863              roi_info.y=(ssize_t) (windows->image.height/2L-
11864                roi_info.height/2L);
11865              break;
11866            }
11867            case XK_Left:
11868            case XK_KP_Left:
11869            {
11870              roi_info.x--;
11871              break;
11872            }
11873            case XK_Up:
11874            case XK_KP_Up:
11875            case XK_Next:
11876            {
11877              roi_info.y--;
11878              break;
11879            }
11880            case XK_Right:
11881            case XK_KP_Right:
11882            {
11883              roi_info.x++;
11884              break;
11885            }
11886            case XK_Prior:
11887            case XK_Down:
11888            case XK_KP_Down:
11889            {
11890              roi_info.y++;
11891              break;
11892            }
11893            case XK_F1:
11894            case XK_Help:
11895            {
11896              (void) XSetFunction(display,windows->image.highlight_context,
11897                GXcopy);
11898              XTextViewWidget(display,resource_info,windows,MagickFalse,
11899                "Help Viewer - Region of Interest",ImageROIHelp);
11900              (void) XSetFunction(display,windows->image.highlight_context,
11901                GXinvert);
11902              break;
11903            }
11904            default:
11905            {
11906              command_type=XImageWindowCommand(display,resource_info,windows,
11907                event.xkey.state,key_symbol,image,exception);
11908              if (command_type != NullCommand)
11909                state|=UpdateRegionState;
11910              break;
11911            }
11912          }
11913          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11914            event.xkey.time);
11915          break;
11916        }
11917        case KeyRelease:
11918          break;
11919        case MotionNotify:
11920        {
11921          if (event.xbutton.window != windows->image.id)
11922            break;
11923          /*
11924            Map and unmap Info widget as text cursor crosses its boundaries.
11925          */
11926          x=event.xmotion.x;
11927          y=event.xmotion.y;
11928          if (windows->info.mapped != MagickFalse)
11929            {
11930              if ((x < (int) (windows->info.x+windows->info.width)) &&
11931                  (y < (int) (windows->info.y+windows->info.height)))
11932                (void) XWithdrawWindow(display,windows->info.id,
11933                  windows->info.screen);
11934            }
11935          else
11936            if ((x > (int) (windows->info.x+windows->info.width)) ||
11937                (y > (int) (windows->info.y+windows->info.height)))
11938              (void) XMapWindow(display,windows->info.id);
11939          roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11940          roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11941          break;
11942        }
11943        case SelectionRequest:
11944        {
11945          XSelectionEvent
11946            notify;
11947
11948          XSelectionRequestEvent
11949            *request;
11950
11951          /*
11952            Set primary selection.
11953          */
11954          (void) FormatLocaleString(text,MaxTextExtent,
11955            "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11956            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11957          request=(&(event.xselectionrequest));
11958          (void) XChangeProperty(request->display,request->requestor,
11959            request->property,request->target,8,PropModeReplace,
11960            (unsigned char *) text,(int) strlen(text));
11961          notify.type=SelectionNotify;
11962          notify.display=request->display;
11963          notify.requestor=request->requestor;
11964          notify.selection=request->selection;
11965          notify.target=request->target;
11966          notify.time=request->time;
11967          if (request->property == None)
11968            notify.property=request->target;
11969          else
11970            notify.property=request->property;
11971          (void) XSendEvent(request->display,request->requestor,False,0,
11972            (XEvent *) &notify);
11973        }
11974        default:
11975          break;
11976      }
11977      if ((state & UpdateConfigurationState) != 0)
11978        {
11979          (void) XPutBackEvent(display,&event);
11980          (void) XCheckDefineCursor(display,windows->image.id,cursor);
11981          break;
11982        }
11983    } while ((state & ExitState) == 0);
11984  } while ((state & ExitState) == 0);
11985  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11986  XSetCursorState(display,windows,MagickFalse);
11987  if ((state & EscapeState) != 0)
11988    return(MagickTrue);
11989  return(MagickTrue);
11990}
11991
11992/*
11993%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11994%                                                                             %
11995%                                                                             %
11996%                                                                             %
11997+   X R o t a t e I m a g e                                                   %
11998%                                                                             %
11999%                                                                             %
12000%                                                                             %
12001%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12002%
12003%  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
12004%  rotation angle is computed from the slope of a line drawn by the user.
12005%
12006%  The format of the XRotateImage method is:
12007%
12008%      MagickBooleanType XRotateImage(Display *display,
12009%        XResourceInfo *resource_info,XWindows *windows,double degrees,
12010%        Image **image,ExceptionInfo *exception)
12011%
12012%  A description of each parameter follows:
12013%
12014%    o display: Specifies a connection to an X server; returned from
12015%      XOpenDisplay.
12016%
12017%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12018%
12019%    o windows: Specifies a pointer to a XWindows structure.
12020%
12021%    o degrees: Specifies the number of degrees to rotate the image.
12022%
12023%    o image: the image.
12024%
12025%    o exception: return any errors or warnings in this structure.
12026%
12027*/
12028static MagickBooleanType XRotateImage(Display *display,
12029  XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
12030  ExceptionInfo *exception)
12031{
12032  static const char
12033    *RotateMenu[] =
12034    {
12035      "Pixel Color",
12036      "Direction",
12037      "Help",
12038      "Dismiss",
12039      (char *) NULL
12040    };
12041
12042  static ModeType
12043    direction = HorizontalRotateCommand;
12044
12045  static const ModeType
12046    DirectionCommands[] =
12047    {
12048      HorizontalRotateCommand,
12049      VerticalRotateCommand
12050    },
12051    RotateCommands[] =
12052    {
12053      RotateColorCommand,
12054      RotateDirectionCommand,
12055      RotateHelpCommand,
12056      RotateDismissCommand
12057    };
12058
12059  static unsigned int
12060    pen_id = 0;
12061
12062  char
12063    command[MaxTextExtent],
12064    text[MaxTextExtent];
12065
12066  Image
12067    *rotate_image;
12068
12069  int
12070    id,
12071    x,
12072    y;
12073
12074  MagickRealType
12075    normalized_degrees;
12076
12077  register int
12078    i;
12079
12080  unsigned int
12081    height,
12082    rotations,
12083    width;
12084
12085  if (degrees == 0.0)
12086    {
12087      unsigned int
12088        distance;
12089
12090      size_t
12091        state;
12092
12093      XEvent
12094        event;
12095
12096      XSegment
12097        rotate_info;
12098
12099      /*
12100        Map Command widget.
12101      */
12102      (void) CloneString(&windows->command.name,"Rotate");
12103      windows->command.data=2;
12104      (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12105      (void) XMapRaised(display,windows->command.id);
12106      XClientMessage(display,windows->image.id,windows->im_protocols,
12107        windows->im_update_widget,CurrentTime);
12108      /*
12109        Wait for first button press.
12110      */
12111      (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12112      XQueryPosition(display,windows->image.id,&x,&y);
12113      rotate_info.x1=x;
12114      rotate_info.y1=y;
12115      rotate_info.x2=x;
12116      rotate_info.y2=y;
12117      state=DefaultState;
12118      do
12119      {
12120        XHighlightLine(display,windows->image.id,
12121          windows->image.highlight_context,&rotate_info);
12122        /*
12123          Wait for next event.
12124        */
12125        XScreenEvent(display,windows,&event,exception);
12126        XHighlightLine(display,windows->image.id,
12127          windows->image.highlight_context,&rotate_info);
12128        if (event.xany.window == windows->command.id)
12129          {
12130            /*
12131              Select a command from the Command widget.
12132            */
12133            id=XCommandWidget(display,windows,RotateMenu,&event);
12134            if (id < 0)
12135              continue;
12136            (void) XSetFunction(display,windows->image.highlight_context,
12137              GXcopy);
12138            switch (RotateCommands[id])
12139            {
12140              case RotateColorCommand:
12141              {
12142                const char
12143                  *ColorMenu[MaxNumberPens];
12144
12145                int
12146                  pen_number;
12147
12148                XColor
12149                  color;
12150
12151                /*
12152                  Initialize menu selections.
12153                */
12154                for (i=0; i < (int) (MaxNumberPens-2); i++)
12155                  ColorMenu[i]=resource_info->pen_colors[i];
12156                ColorMenu[MaxNumberPens-2]="Browser...";
12157                ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12158                /*
12159                  Select a pen color from the pop-up menu.
12160                */
12161                pen_number=XMenuWidget(display,windows,RotateMenu[id],
12162                  (const char **) ColorMenu,command);
12163                if (pen_number < 0)
12164                  break;
12165                if (pen_number == (MaxNumberPens-2))
12166                  {
12167                    static char
12168                      color_name[MaxTextExtent] = "gray";
12169
12170                    /*
12171                      Select a pen color from a dialog.
12172                    */
12173                    resource_info->pen_colors[pen_number]=color_name;
12174                    XColorBrowserWidget(display,windows,"Select",color_name);
12175                    if (*color_name == '\0')
12176                      break;
12177                  }
12178                /*
12179                  Set pen color.
12180                */
12181                (void) XParseColor(display,windows->map_info->colormap,
12182                  resource_info->pen_colors[pen_number],&color);
12183                XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12184                  (unsigned int) MaxColors,&color);
12185                windows->pixel_info->pen_colors[pen_number]=color;
12186                pen_id=(unsigned int) pen_number;
12187                break;
12188              }
12189              case RotateDirectionCommand:
12190              {
12191                static const char
12192                  *Directions[] =
12193                  {
12194                    "horizontal",
12195                    "vertical",
12196                    (char *) NULL,
12197                  };
12198
12199                /*
12200                  Select a command from the pop-up menu.
12201                */
12202                id=XMenuWidget(display,windows,RotateMenu[id],
12203                  Directions,command);
12204                if (id >= 0)
12205                  direction=DirectionCommands[id];
12206                break;
12207              }
12208              case RotateHelpCommand:
12209              {
12210                XTextViewWidget(display,resource_info,windows,MagickFalse,
12211                  "Help Viewer - Image Rotation",ImageRotateHelp);
12212                break;
12213              }
12214              case RotateDismissCommand:
12215              {
12216                /*
12217                  Prematurely exit.
12218                */
12219                state|=EscapeState;
12220                state|=ExitState;
12221                break;
12222              }
12223              default:
12224                break;
12225            }
12226            (void) XSetFunction(display,windows->image.highlight_context,
12227              GXinvert);
12228            continue;
12229          }
12230        switch (event.type)
12231        {
12232          case ButtonPress:
12233          {
12234            if (event.xbutton.button != Button1)
12235              break;
12236            if (event.xbutton.window != windows->image.id)
12237              break;
12238            /*
12239              exit loop.
12240            */
12241            (void) XSetFunction(display,windows->image.highlight_context,
12242              GXcopy);
12243            rotate_info.x1=event.xbutton.x;
12244            rotate_info.y1=event.xbutton.y;
12245            state|=ExitState;
12246            break;
12247          }
12248          case ButtonRelease:
12249            break;
12250          case Expose:
12251            break;
12252          case KeyPress:
12253          {
12254            char
12255              command[MaxTextExtent];
12256
12257            KeySym
12258              key_symbol;
12259
12260            if (event.xkey.window != windows->image.id)
12261              break;
12262            /*
12263              Respond to a user key press.
12264            */
12265            (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12266              sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12267            switch ((int) key_symbol)
12268            {
12269              case XK_Escape:
12270              case XK_F20:
12271              {
12272                /*
12273                  Prematurely exit.
12274                */
12275                state|=EscapeState;
12276                state|=ExitState;
12277                break;
12278              }
12279              case XK_F1:
12280              case XK_Help:
12281              {
12282                (void) XSetFunction(display,windows->image.highlight_context,
12283                  GXcopy);
12284                XTextViewWidget(display,resource_info,windows,MagickFalse,
12285                  "Help Viewer - Image Rotation",ImageRotateHelp);
12286                (void) XSetFunction(display,windows->image.highlight_context,
12287                  GXinvert);
12288                break;
12289              }
12290              default:
12291              {
12292                (void) XBell(display,0);
12293                break;
12294              }
12295            }
12296            break;
12297          }
12298          case MotionNotify:
12299          {
12300            rotate_info.x1=event.xmotion.x;
12301            rotate_info.y1=event.xmotion.y;
12302          }
12303        }
12304        rotate_info.x2=rotate_info.x1;
12305        rotate_info.y2=rotate_info.y1;
12306        if (direction == HorizontalRotateCommand)
12307          rotate_info.x2+=32;
12308        else
12309          rotate_info.y2-=32;
12310      } while ((state & ExitState) == 0);
12311      (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12312      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12313      if ((state & EscapeState) != 0)
12314        return(MagickTrue);
12315      /*
12316        Draw line as pointer moves until the mouse button is released.
12317      */
12318      distance=0;
12319      (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12320      state=DefaultState;
12321      do
12322      {
12323        if (distance > 9)
12324          {
12325            /*
12326              Display info and draw rotation line.
12327            */
12328            if (windows->info.mapped == MagickFalse)
12329              (void) XMapWindow(display,windows->info.id);
12330            (void) FormatLocaleString(text,MaxTextExtent," %g",
12331              direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12332            XInfoWidget(display,windows,text);
12333            XHighlightLine(display,windows->image.id,
12334              windows->image.highlight_context,&rotate_info);
12335          }
12336        else
12337          if (windows->info.mapped != MagickFalse)
12338            (void) XWithdrawWindow(display,windows->info.id,
12339              windows->info.screen);
12340        /*
12341          Wait for next event.
12342        */
12343        XScreenEvent(display,windows,&event,exception);
12344        if (distance > 9)
12345          XHighlightLine(display,windows->image.id,
12346            windows->image.highlight_context,&rotate_info);
12347        switch (event.type)
12348        {
12349          case ButtonPress:
12350            break;
12351          case ButtonRelease:
12352          {
12353            /*
12354              User has committed to rotation line.
12355            */
12356            rotate_info.x2=event.xbutton.x;
12357            rotate_info.y2=event.xbutton.y;
12358            state|=ExitState;
12359            break;
12360          }
12361          case Expose:
12362            break;
12363          case MotionNotify:
12364          {
12365            rotate_info.x2=event.xmotion.x;
12366            rotate_info.y2=event.xmotion.y;
12367          }
12368          default:
12369            break;
12370        }
12371        /*
12372          Check boundary conditions.
12373        */
12374        if (rotate_info.x2 < 0)
12375          rotate_info.x2=0;
12376        else
12377          if (rotate_info.x2 > (int) windows->image.width)
12378            rotate_info.x2=(short) windows->image.width;
12379        if (rotate_info.y2 < 0)
12380          rotate_info.y2=0;
12381        else
12382          if (rotate_info.y2 > (int) windows->image.height)
12383            rotate_info.y2=(short) windows->image.height;
12384        /*
12385          Compute rotation angle from the slope of the line.
12386        */
12387        degrees=0.0;
12388        distance=(unsigned int)
12389          ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12390          ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12391        if (distance > 9)
12392          degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12393            rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12394      } while ((state & ExitState) == 0);
12395      (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12396      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12397      if (distance <= 9)
12398        return(MagickTrue);
12399    }
12400  if (direction == VerticalRotateCommand)
12401    degrees-=90.0;
12402  if (degrees == 0.0)
12403    return(MagickTrue);
12404  /*
12405    Rotate image.
12406  */
12407  normalized_degrees=degrees;
12408  while (normalized_degrees < -45.0)
12409    normalized_degrees+=360.0;
12410  for (rotations=0; normalized_degrees > 45.0; rotations++)
12411    normalized_degrees-=90.0;
12412  if (normalized_degrees != 0.0)
12413    (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12414      exception);
12415  XSetCursorState(display,windows,MagickTrue);
12416  XCheckRefreshWindows(display,windows);
12417  (*image)->background_color.red=ScaleShortToQuantum(
12418    windows->pixel_info->pen_colors[pen_id].red);
12419  (*image)->background_color.green=ScaleShortToQuantum(
12420    windows->pixel_info->pen_colors[pen_id].green);
12421  (*image)->background_color.blue=ScaleShortToQuantum(
12422    windows->pixel_info->pen_colors[pen_id].blue);
12423  rotate_image=RotateImage(*image,degrees,exception);
12424  XSetCursorState(display,windows,MagickFalse);
12425  if (rotate_image == (Image *) NULL)
12426    return(MagickFalse);
12427  *image=DestroyImage(*image);
12428  *image=rotate_image;
12429  if (windows->image.crop_geometry != (char *) NULL)
12430    {
12431      /*
12432        Rotate crop geometry.
12433      */
12434      width=(unsigned int) (*image)->columns;
12435      height=(unsigned int) (*image)->rows;
12436      (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12437      switch (rotations % 4)
12438      {
12439        default:
12440        case 0:
12441          break;
12442        case 1:
12443        {
12444          /*
12445            Rotate 90 degrees.
12446          */
12447          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12448            "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12449            (int) height-y,x);
12450          break;
12451        }
12452        case 2:
12453        {
12454          /*
12455            Rotate 180 degrees.
12456          */
12457          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12458            "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12459          break;
12460        }
12461        case 3:
12462        {
12463          /*
12464            Rotate 270 degrees.
12465          */
12466          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12467            "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12468          break;
12469        }
12470      }
12471    }
12472  if (windows->image.orphan != MagickFalse)
12473    return(MagickTrue);
12474  if (normalized_degrees != 0.0)
12475    {
12476      /*
12477        Update image colormap.
12478      */
12479      windows->image.window_changes.width=(int) (*image)->columns;
12480      windows->image.window_changes.height=(int) (*image)->rows;
12481      if (windows->image.crop_geometry != (char *) NULL)
12482        {
12483          /*
12484            Obtain dimensions of image from crop geometry.
12485          */
12486          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12487            &width,&height);
12488          windows->image.window_changes.width=(int) width;
12489          windows->image.window_changes.height=(int) height;
12490        }
12491      XConfigureImageColormap(display,resource_info,windows,*image,exception);
12492    }
12493  else
12494    if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12495      {
12496        windows->image.window_changes.width=windows->image.ximage->height;
12497        windows->image.window_changes.height=windows->image.ximage->width;
12498      }
12499  /*
12500    Update image configuration.
12501  */
12502  (void) XConfigureImage(display,resource_info,windows,*image,exception);
12503  return(MagickTrue);
12504}
12505
12506/*
12507%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12508%                                                                             %
12509%                                                                             %
12510%                                                                             %
12511+   X S a v e I m a g e                                                       %
12512%                                                                             %
12513%                                                                             %
12514%                                                                             %
12515%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12516%
12517%  XSaveImage() saves an image to a file.
12518%
12519%  The format of the XSaveImage method is:
12520%
12521%      MagickBooleanType XSaveImage(Display *display,
12522%        XResourceInfo *resource_info,XWindows *windows,Image *image,
12523%        ExceptionInfo *exception)
12524%
12525%  A description of each parameter follows:
12526%
12527%    o display: Specifies a connection to an X server; returned from
12528%      XOpenDisplay.
12529%
12530%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12531%
12532%    o windows: Specifies a pointer to a XWindows structure.
12533%
12534%    o image: the image.
12535%
12536%    o exception: return any errors or warnings in this structure.
12537%
12538*/
12539static MagickBooleanType XSaveImage(Display *display,
12540  XResourceInfo *resource_info,XWindows *windows,Image *image,
12541  ExceptionInfo *exception)
12542{
12543  char
12544    filename[MaxTextExtent],
12545    geometry[MaxTextExtent];
12546
12547  Image
12548    *save_image;
12549
12550  ImageInfo
12551    *image_info;
12552
12553  MagickStatusType
12554    status;
12555
12556  /*
12557    Request file name from user.
12558  */
12559  if (resource_info->write_filename != (char *) NULL)
12560    (void) CopyMagickString(filename,resource_info->write_filename,
12561      MaxTextExtent);
12562  else
12563    {
12564      char
12565        path[MaxTextExtent];
12566
12567      int
12568        status;
12569
12570      GetPathComponent(image->filename,HeadPath,path);
12571      GetPathComponent(image->filename,TailPath,filename);
12572      if (*path != '\0')
12573        {
12574          status=chdir(path);
12575          if (status == -1)
12576            (void) ThrowMagickException(exception,GetMagickModule(),
12577              FileOpenError,"UnableToOpenFile","%s",path);
12578        }
12579    }
12580  XFileBrowserWidget(display,windows,"Save",filename);
12581  if (*filename == '\0')
12582    return(MagickTrue);
12583  if (IsPathAccessible(filename) != MagickFalse)
12584    {
12585      int
12586        status;
12587
12588      /*
12589        File exists-- seek user's permission before overwriting.
12590      */
12591      status=XConfirmWidget(display,windows,"Overwrite",filename);
12592      if (status <= 0)
12593        return(MagickTrue);
12594    }
12595  image_info=CloneImageInfo(resource_info->image_info);
12596  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12597  (void) SetImageInfo(image_info,1,exception);
12598  if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12599      (LocaleCompare(image_info->magick,"JPG") == 0))
12600    {
12601      char
12602        quality[MaxTextExtent];
12603
12604      int
12605        status;
12606
12607      /*
12608        Request JPEG quality from user.
12609      */
12610      (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
12611        image->quality);
12612      status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12613        quality);
12614      if (*quality == '\0')
12615        return(MagickTrue);
12616      image->quality=StringToUnsignedLong(quality);
12617      image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12618    }
12619  if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12620      (LocaleCompare(image_info->magick,"PDF") == 0) ||
12621      (LocaleCompare(image_info->magick,"PS") == 0) ||
12622      (LocaleCompare(image_info->magick,"PS2") == 0))
12623    {
12624      char
12625        geometry[MaxTextExtent];
12626
12627      /*
12628        Request page geometry from user.
12629      */
12630      (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12631      if (LocaleCompare(image_info->magick,"PDF") == 0)
12632        (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12633      if (image_info->page != (char *) NULL)
12634        (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12635      XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12636        "Select page geometry:",geometry);
12637      if (*geometry != '\0')
12638        image_info->page=GetPageGeometry(geometry);
12639    }
12640  /*
12641    Apply image transforms.
12642  */
12643  XSetCursorState(display,windows,MagickTrue);
12644  XCheckRefreshWindows(display,windows);
12645  save_image=CloneImage(image,0,0,MagickTrue,exception);
12646  if (save_image == (Image *) NULL)
12647    return(MagickFalse);
12648  (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
12649    windows->image.ximage->width,windows->image.ximage->height);
12650  (void) TransformImage(&save_image,windows->image.crop_geometry,geometry,
12651    exception);
12652  /*
12653    Write image.
12654  */
12655  (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12656  status=WriteImage(image_info,save_image,exception);
12657  if (status != MagickFalse)
12658    image->taint=MagickFalse;
12659  save_image=DestroyImage(save_image);
12660  image_info=DestroyImageInfo(image_info);
12661  XSetCursorState(display,windows,MagickFalse);
12662  return(status != 0 ? MagickTrue : MagickFalse);
12663}
12664
12665/*
12666%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12667%                                                                             %
12668%                                                                             %
12669%                                                                             %
12670+   X S c r e e n E v e n t                                                   %
12671%                                                                             %
12672%                                                                             %
12673%                                                                             %
12674%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12675%
12676%  XScreenEvent() handles global events associated with the Pan and Magnify
12677%  windows.
12678%
12679%  The format of the XScreenEvent function is:
12680%
12681%      void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12682%        ExceptionInfo *exception)
12683%
12684%  A description of each parameter follows:
12685%
12686%    o display: Specifies a pointer to the Display structure;  returned from
12687%      XOpenDisplay.
12688%
12689%    o windows: Specifies a pointer to a XWindows structure.
12690%
12691%    o event: Specifies a pointer to a X11 XEvent structure.
12692%
12693%    o exception: return any errors or warnings in this structure.
12694%
12695*/
12696
12697#if defined(__cplusplus) || defined(c_plusplus)
12698extern "C" {
12699#endif
12700
12701static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12702{
12703  register XWindows
12704    *windows;
12705
12706  windows=(XWindows *) data;
12707  if ((event->type == ClientMessage) &&
12708      (event->xclient.window == windows->image.id))
12709    return(MagickFalse);
12710  return(MagickTrue);
12711}
12712
12713#if defined(__cplusplus) || defined(c_plusplus)
12714}
12715#endif
12716
12717static void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12718  ExceptionInfo *exception)
12719{
12720  register int
12721    x,
12722    y;
12723
12724  (void) XIfEvent(display,event,XPredicate,(char *) windows);
12725  if (event->xany.window == windows->command.id)
12726    return;
12727  switch (event->type)
12728  {
12729    case ButtonPress:
12730    case ButtonRelease:
12731    {
12732      if ((event->xbutton.button == Button3) &&
12733          (event->xbutton.state & Mod1Mask))
12734        {
12735          /*
12736            Convert Alt-Button3 to Button2.
12737          */
12738          event->xbutton.button=Button2;
12739          event->xbutton.state&=(~Mod1Mask);
12740        }
12741      if (event->xbutton.window == windows->backdrop.id)
12742        {
12743          (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12744            event->xbutton.time);
12745          break;
12746        }
12747      if (event->xbutton.window == windows->pan.id)
12748        {
12749          XPanImage(display,windows,event,exception);
12750          break;
12751        }
12752      if (event->xbutton.window == windows->image.id)
12753        if (event->xbutton.button == Button2)
12754          {
12755            /*
12756              Update magnified image.
12757            */
12758            x=event->xbutton.x;
12759            y=event->xbutton.y;
12760            if (x < 0)
12761              x=0;
12762            else
12763              if (x >= (int) windows->image.width)
12764                x=(int) (windows->image.width-1);
12765            windows->magnify.x=(int) windows->image.x+x;
12766            if (y < 0)
12767              y=0;
12768            else
12769             if (y >= (int) windows->image.height)
12770               y=(int) (windows->image.height-1);
12771            windows->magnify.y=windows->image.y+y;
12772            if (windows->magnify.mapped == MagickFalse)
12773              (void) XMapRaised(display,windows->magnify.id);
12774            XMakeMagnifyImage(display,windows,exception);
12775            if (event->type == ButtonRelease)
12776              (void) XWithdrawWindow(display,windows->info.id,
12777                windows->info.screen);
12778            break;
12779          }
12780      break;
12781    }
12782    case ClientMessage:
12783    {
12784      /*
12785        If client window delete message, exit.
12786      */
12787      if (event->xclient.message_type != windows->wm_protocols)
12788        break;
12789      if (*event->xclient.data.l != (long) windows->wm_delete_window)
12790        break;
12791      if (event->xclient.window == windows->magnify.id)
12792        {
12793          (void) XWithdrawWindow(display,windows->magnify.id,
12794            windows->magnify.screen);
12795          break;
12796        }
12797      break;
12798    }
12799    case ConfigureNotify:
12800    {
12801      if (event->xconfigure.window == windows->magnify.id)
12802        {
12803          unsigned int
12804            magnify;
12805
12806          /*
12807            Magnify window has a new configuration.
12808          */
12809          windows->magnify.width=(unsigned int) event->xconfigure.width;
12810          windows->magnify.height=(unsigned int) event->xconfigure.height;
12811          if (windows->magnify.mapped == MagickFalse)
12812            break;
12813          magnify=1;
12814          while ((int) magnify <= event->xconfigure.width)
12815            magnify<<=1;
12816          while ((int) magnify <= event->xconfigure.height)
12817            magnify<<=1;
12818          magnify>>=1;
12819          if (((int) magnify != event->xconfigure.width) ||
12820              ((int) magnify != event->xconfigure.height))
12821            {
12822              XWindowChanges
12823                window_changes;
12824
12825              window_changes.width=(int) magnify;
12826              window_changes.height=(int) magnify;
12827              (void) XReconfigureWMWindow(display,windows->magnify.id,
12828                windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12829                &window_changes);
12830              break;
12831            }
12832          XMakeMagnifyImage(display,windows,exception);
12833          break;
12834        }
12835      break;
12836    }
12837    case Expose:
12838    {
12839      if (event->xexpose.window == windows->image.id)
12840        {
12841          XRefreshWindow(display,&windows->image,event);
12842          break;
12843        }
12844      if (event->xexpose.window == windows->pan.id)
12845        if (event->xexpose.count == 0)
12846          {
12847            XDrawPanRectangle(display,windows);
12848            break;
12849          }
12850      if (event->xexpose.window == windows->magnify.id)
12851        if (event->xexpose.count == 0)
12852          {
12853            XMakeMagnifyImage(display,windows,exception);
12854            break;
12855          }
12856      break;
12857    }
12858    case KeyPress:
12859    {
12860      char
12861        command[MaxTextExtent];
12862
12863      KeySym
12864        key_symbol;
12865
12866      if (event->xkey.window != windows->magnify.id)
12867        break;
12868      /*
12869        Respond to a user key press.
12870      */
12871      (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12872        sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12873      XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol,
12874        exception);
12875      break;
12876    }
12877    case MapNotify:
12878    {
12879      if (event->xmap.window == windows->magnify.id)
12880        {
12881          windows->magnify.mapped=MagickTrue;
12882          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12883          break;
12884        }
12885      if (event->xmap.window == windows->info.id)
12886        {
12887          windows->info.mapped=MagickTrue;
12888          break;
12889        }
12890      break;
12891    }
12892    case MotionNotify:
12893    {
12894      while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12895      if (event->xmotion.window == windows->image.id)
12896        if (windows->magnify.mapped != MagickFalse)
12897          {
12898            /*
12899              Update magnified image.
12900            */
12901            x=event->xmotion.x;
12902            y=event->xmotion.y;
12903            if (x < 0)
12904              x=0;
12905            else
12906              if (x >= (int) windows->image.width)
12907                x=(int) (windows->image.width-1);
12908            windows->magnify.x=(int) windows->image.x+x;
12909            if (y < 0)
12910              y=0;
12911            else
12912             if (y >= (int) windows->image.height)
12913               y=(int) (windows->image.height-1);
12914            windows->magnify.y=windows->image.y+y;
12915            XMakeMagnifyImage(display,windows,exception);
12916          }
12917      break;
12918    }
12919    case UnmapNotify:
12920    {
12921      if (event->xunmap.window == windows->magnify.id)
12922        {
12923          windows->magnify.mapped=MagickFalse;
12924          break;
12925        }
12926      if (event->xunmap.window == windows->info.id)
12927        {
12928          windows->info.mapped=MagickFalse;
12929          break;
12930        }
12931      break;
12932    }
12933    default:
12934      break;
12935  }
12936}
12937
12938/*
12939%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12940%                                                                             %
12941%                                                                             %
12942%                                                                             %
12943+   X S e t C r o p G e o m e t r y                                           %
12944%                                                                             %
12945%                                                                             %
12946%                                                                             %
12947%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12948%
12949%  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12950%  and translates it to a cropping geometry relative to the image.
12951%
12952%  The format of the XSetCropGeometry method is:
12953%
12954%      void XSetCropGeometry(Display *display,XWindows *windows,
12955%        RectangleInfo *crop_info,Image *image)
12956%
12957%  A description of each parameter follows:
12958%
12959%    o display: Specifies a connection to an X server; returned from
12960%      XOpenDisplay.
12961%
12962%    o windows: Specifies a pointer to a XWindows structure.
12963%
12964%    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12965%      Image window to crop.
12966%
12967%    o image: the image.
12968%
12969*/
12970static void XSetCropGeometry(Display *display,XWindows *windows,
12971  RectangleInfo *crop_info,Image *image)
12972{
12973  char
12974    text[MaxTextExtent];
12975
12976  int
12977    x,
12978    y;
12979
12980  MagickRealType
12981    scale_factor;
12982
12983  unsigned int
12984    height,
12985    width;
12986
12987  if (windows->info.mapped != MagickFalse)
12988    {
12989      /*
12990        Display info on cropping rectangle.
12991      */
12992      (void) FormatLocaleString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
12993        (double) crop_info->width,(double) crop_info->height,(double)
12994        crop_info->x,(double) crop_info->y);
12995      XInfoWidget(display,windows,text);
12996    }
12997  /*
12998    Cropping geometry is relative to any previous crop geometry.
12999  */
13000  x=0;
13001  y=0;
13002  width=(unsigned int) image->columns;
13003  height=(unsigned int) image->rows;
13004  if (windows->image.crop_geometry != (char *) NULL)
13005    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13006  else
13007    windows->image.crop_geometry=AcquireString((char *) NULL);
13008  /*
13009    Define the crop geometry string from the cropping rectangle.
13010  */
13011  scale_factor=(MagickRealType) width/windows->image.ximage->width;
13012  if (crop_info->x > 0)
13013    x+=(int) (scale_factor*crop_info->x+0.5);
13014  width=(unsigned int) (scale_factor*crop_info->width+0.5);
13015  if (width == 0)
13016    width=1;
13017  scale_factor=(MagickRealType) height/windows->image.ximage->height;
13018  if (crop_info->y > 0)
13019    y+=(int) (scale_factor*crop_info->y+0.5);
13020  height=(unsigned int) (scale_factor*crop_info->height+0.5);
13021  if (height == 0)
13022    height=1;
13023  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
13024    "%ux%u%+d%+d",width,height,x,y);
13025}
13026
13027/*
13028%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13029%                                                                             %
13030%                                                                             %
13031%                                                                             %
13032+   X T i l e I m a g e                                                       %
13033%                                                                             %
13034%                                                                             %
13035%                                                                             %
13036%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13037%
13038%  XTileImage() loads or deletes a selected tile from a visual image directory.
13039%  The load or delete command is chosen from a menu.
13040%
13041%  The format of the XTileImage method is:
13042%
13043%      Image *XTileImage(Display *display,XResourceInfo *resource_info,
13044%        XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13045%
13046%  A description of each parameter follows:
13047%
13048%    o tile_image:  XTileImage reads or deletes the tile image
13049%      and returns it.  A null image is returned if an error occurs.
13050%
13051%    o display: Specifies a connection to an X server;  returned from
13052%      XOpenDisplay.
13053%
13054%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13055%
13056%    o windows: Specifies a pointer to a XWindows structure.
13057%
13058%    o image: the image; returned from ReadImage.
13059%
13060%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
13061%      the entire image is refreshed.
13062%
13063%    o exception: return any errors or warnings in this structure.
13064%
13065*/
13066static Image *XTileImage(Display *display,XResourceInfo *resource_info,
13067  XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13068{
13069  static const char
13070    *VerbMenu[] =
13071    {
13072      "Load",
13073      "Next",
13074      "Former",
13075      "Delete",
13076      "Update",
13077      (char *) NULL,
13078    };
13079
13080  static const ModeType
13081    TileCommands[] =
13082    {
13083      TileLoadCommand,
13084      TileNextCommand,
13085      TileFormerCommand,
13086      TileDeleteCommand,
13087      TileUpdateCommand
13088    };
13089
13090  char
13091    command[MaxTextExtent],
13092    filename[MaxTextExtent];
13093
13094  Image
13095    *tile_image;
13096
13097  int
13098    id,
13099    status,
13100    tile,
13101    x,
13102    y;
13103
13104  MagickRealType
13105    scale_factor;
13106
13107  register char
13108    *p,
13109    *q;
13110
13111  register int
13112    i;
13113
13114  unsigned int
13115    height,
13116    width;
13117
13118  /*
13119    Tile image is relative to montage image configuration.
13120  */
13121  x=0;
13122  y=0;
13123  width=(unsigned int) image->columns;
13124  height=(unsigned int) image->rows;
13125  if (windows->image.crop_geometry != (char *) NULL)
13126    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13127  scale_factor=(MagickRealType) width/windows->image.ximage->width;
13128  event->xbutton.x+=windows->image.x;
13129  event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13130  scale_factor=(MagickRealType) height/windows->image.ximage->height;
13131  event->xbutton.y+=windows->image.y;
13132  event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13133  /*
13134    Determine size and location of each tile in the visual image directory.
13135  */
13136  width=(unsigned int) image->columns;
13137  height=(unsigned int) image->rows;
13138  x=0;
13139  y=0;
13140  (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13141  tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13142    (event->xbutton.x-x)/width;
13143  if (tile < 0)
13144    {
13145      /*
13146        Button press is outside any tile.
13147      */
13148      (void) XBell(display,0);
13149      return((Image *) NULL);
13150    }
13151  /*
13152    Determine file name from the tile directory.
13153  */
13154  p=image->directory;
13155  for (i=tile; (i != 0) && (*p != '\0'); )
13156  {
13157    if (*p == '\n')
13158      i--;
13159    p++;
13160  }
13161  if (*p == '\0')
13162    {
13163      /*
13164        Button press is outside any tile.
13165      */
13166      (void) XBell(display,0);
13167      return((Image *) NULL);
13168    }
13169  /*
13170    Select a command from the pop-up menu.
13171  */
13172  id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13173  if (id < 0)
13174    return((Image *) NULL);
13175  q=p;
13176  while ((*q != '\n') && (*q != '\0'))
13177    q++;
13178  (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13179  /*
13180    Perform command for the selected tile.
13181  */
13182  XSetCursorState(display,windows,MagickTrue);
13183  XCheckRefreshWindows(display,windows);
13184  tile_image=NewImageList();
13185  switch (TileCommands[id])
13186  {
13187    case TileLoadCommand:
13188    {
13189      /*
13190        Load tile image.
13191      */
13192      XCheckRefreshWindows(display,windows);
13193      (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13194        MaxTextExtent);
13195      (void) CopyMagickString(resource_info->image_info->filename,filename,
13196        MaxTextExtent);
13197      tile_image=ReadImage(resource_info->image_info,exception);
13198      CatchException(exception);
13199      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13200      break;
13201    }
13202    case TileNextCommand:
13203    {
13204      /*
13205        Display next image.
13206      */
13207      XClientMessage(display,windows->image.id,windows->im_protocols,
13208        windows->im_next_image,CurrentTime);
13209      break;
13210    }
13211    case TileFormerCommand:
13212    {
13213      /*
13214        Display former image.
13215      */
13216      XClientMessage(display,windows->image.id,windows->im_protocols,
13217        windows->im_former_image,CurrentTime);
13218      break;
13219    }
13220    case TileDeleteCommand:
13221    {
13222      /*
13223        Delete tile image.
13224      */
13225      if (IsPathAccessible(filename) == MagickFalse)
13226        {
13227          XNoticeWidget(display,windows,"Image file does not exist:",filename);
13228          break;
13229        }
13230      status=XConfirmWidget(display,windows,"Really delete tile",filename);
13231      if (status <= 0)
13232        break;
13233      status=remove_utf8(filename) != 0 ? MagickTrue : MagickFalse;
13234      if (status != MagickFalse)
13235        {
13236          XNoticeWidget(display,windows,"Unable to delete image file:",
13237            filename);
13238          break;
13239        }
13240    }
13241    case TileUpdateCommand:
13242    {
13243      int
13244        x_offset,
13245        y_offset;
13246
13247      PixelInfo
13248        pixel;
13249
13250      Quantum
13251        virtual_pixel[CompositePixelChannel];
13252
13253      register int
13254        j;
13255
13256      register Quantum
13257        *s;
13258
13259      /*
13260        Ensure all the images exist.
13261      */
13262      tile=0;
13263      GetPixelInfo(image,&pixel);
13264      for (p=image->directory; *p != '\0'; p++)
13265      {
13266        CacheView
13267          *image_view;
13268
13269        q=p;
13270        while ((*q != '\n') && (*q != '\0'))
13271          q++;
13272        (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13273        p=q;
13274        if (IsPathAccessible(filename) != MagickFalse)
13275          {
13276            tile++;
13277            continue;
13278          }
13279        /*
13280          Overwrite tile with background color.
13281        */
13282        x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13283        y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13284        image_view=AcquireCacheView(image);
13285        (void) GetOneCacheViewVirtualPixel(image_view,0,0,virtual_pixel,
13286          exception);
13287        pixel.red=virtual_pixel[RedPixelChannel];
13288        pixel.green=virtual_pixel[GreenPixelChannel];
13289        pixel.blue=virtual_pixel[BluePixelChannel];
13290        pixel.alpha=virtual_pixel[AlphaPixelChannel];
13291        for (i=0; i < (int) height; i++)
13292        {
13293          s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13294            y_offset+i,width,1,exception);
13295          if (s == (Quantum *) NULL)
13296            break;
13297          for (j=0; j < (int) width; j++)
13298          {
13299            SetPixelInfoPixel(image,&pixel,s);
13300            s+=GetPixelChannels(image);
13301          }
13302          if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13303            break;
13304        }
13305        image_view=DestroyCacheView(image_view);
13306        tile++;
13307      }
13308      windows->image.window_changes.width=(int) image->columns;
13309      windows->image.window_changes.height=(int) image->rows;
13310      XConfigureImageColormap(display,resource_info,windows,image,exception);
13311      (void) XConfigureImage(display,resource_info,windows,image,exception);
13312      break;
13313    }
13314    default:
13315      break;
13316  }
13317  XSetCursorState(display,windows,MagickFalse);
13318  return(tile_image);
13319}
13320
13321/*
13322%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13323%                                                                             %
13324%                                                                             %
13325%                                                                             %
13326+   X T r a n s l a t e I m a g e                                             %
13327%                                                                             %
13328%                                                                             %
13329%                                                                             %
13330%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13331%
13332%  XTranslateImage() translates the image within an Image window by one pixel
13333%  as specified by the key symbol.  If the image has a `montage string the
13334%  translation is respect to the width and height contained within the string.
13335%
13336%  The format of the XTranslateImage method is:
13337%
13338%      void XTranslateImage(Display *display,XWindows *windows,
13339%        Image *image,const KeySym key_symbol)
13340%
13341%  A description of each parameter follows:
13342%
13343%    o display: Specifies a connection to an X server; returned from
13344%      XOpenDisplay.
13345%
13346%    o windows: Specifies a pointer to a XWindows structure.
13347%
13348%    o image: the image.
13349%
13350%    o key_symbol: Specifies a KeySym which indicates which side of the image
13351%      to trim.
13352%
13353*/
13354static void XTranslateImage(Display *display,XWindows *windows,
13355  Image *image,const KeySym key_symbol)
13356{
13357  char
13358    text[MaxTextExtent];
13359
13360  int
13361    x,
13362    y;
13363
13364  unsigned int
13365    x_offset,
13366    y_offset;
13367
13368  /*
13369    User specified a pan position offset.
13370  */
13371  x_offset=windows->image.width;
13372  y_offset=windows->image.height;
13373  if (image->montage != (char *) NULL)
13374    (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13375  switch ((int) key_symbol)
13376  {
13377    case XK_Home:
13378    case XK_KP_Home:
13379    {
13380      windows->image.x=(int) windows->image.width/2;
13381      windows->image.y=(int) windows->image.height/2;
13382      break;
13383    }
13384    case XK_Left:
13385    case XK_KP_Left:
13386    {
13387      windows->image.x-=x_offset;
13388      break;
13389    }
13390    case XK_Next:
13391    case XK_Up:
13392    case XK_KP_Up:
13393    {
13394      windows->image.y-=y_offset;
13395      break;
13396    }
13397    case XK_Right:
13398    case XK_KP_Right:
13399    {
13400      windows->image.x+=x_offset;
13401      break;
13402    }
13403    case XK_Prior:
13404    case XK_Down:
13405    case XK_KP_Down:
13406    {
13407      windows->image.y+=y_offset;
13408      break;
13409    }
13410    default:
13411      return;
13412  }
13413  /*
13414    Check boundary conditions.
13415  */
13416  if (windows->image.x < 0)
13417    windows->image.x=0;
13418  else
13419    if ((int) (windows->image.x+windows->image.width) >
13420        windows->image.ximage->width)
13421      windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13422  if (windows->image.y < 0)
13423    windows->image.y=0;
13424  else
13425    if ((int) (windows->image.y+windows->image.height) >
13426        windows->image.ximage->height)
13427      windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13428  /*
13429    Refresh Image window.
13430  */
13431  (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
13432    windows->image.width,windows->image.height,windows->image.x,
13433    windows->image.y);
13434  XInfoWidget(display,windows,text);
13435  XCheckRefreshWindows(display,windows);
13436  XDrawPanRectangle(display,windows);
13437  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13438  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13439}
13440
13441/*
13442%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13443%                                                                             %
13444%                                                                             %
13445%                                                                             %
13446+   X T r i m I m a g e                                                       %
13447%                                                                             %
13448%                                                                             %
13449%                                                                             %
13450%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13451%
13452%  XTrimImage() trims the edges from the Image window.
13453%
13454%  The format of the XTrimImage method is:
13455%
13456%      MagickBooleanType XTrimImage(Display *display,
13457%        XResourceInfo *resource_info,XWindows *windows,Image *image,
13458%        ExceptionInfo *exception)
13459%
13460%  A description of each parameter follows:
13461%
13462%    o display: Specifies a connection to an X server; returned from
13463%      XOpenDisplay.
13464%
13465%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13466%
13467%    o windows: Specifies a pointer to a XWindows structure.
13468%
13469%    o image: the image.
13470%
13471%    o exception: return any errors or warnings in this structure.
13472%
13473*/
13474static MagickBooleanType XTrimImage(Display *display,
13475  XResourceInfo *resource_info,XWindows *windows,Image *image,
13476  ExceptionInfo *exception)
13477{
13478  RectangleInfo
13479    trim_info;
13480
13481  register int
13482    x,
13483    y;
13484
13485  size_t
13486    background,
13487    pixel;
13488
13489  /*
13490    Trim edges from image.
13491  */
13492  XSetCursorState(display,windows,MagickTrue);
13493  XCheckRefreshWindows(display,windows);
13494  /*
13495    Crop the left edge.
13496  */
13497  background=XGetPixel(windows->image.ximage,0,0);
13498  trim_info.width=(size_t) windows->image.ximage->width;
13499  for (x=0; x < windows->image.ximage->width; x++)
13500  {
13501    for (y=0; y < windows->image.ximage->height; y++)
13502    {
13503      pixel=XGetPixel(windows->image.ximage,x,y);
13504      if (pixel != background)
13505        break;
13506    }
13507    if (y < windows->image.ximage->height)
13508      break;
13509  }
13510  trim_info.x=(ssize_t) x;
13511  if (trim_info.x == (ssize_t) windows->image.ximage->width)
13512    {
13513      XSetCursorState(display,windows,MagickFalse);
13514      return(MagickFalse);
13515    }
13516  /*
13517    Crop the right edge.
13518  */
13519  background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13520  for (x=windows->image.ximage->width-1; x != 0; x--)
13521  {
13522    for (y=0; y < windows->image.ximage->height; y++)
13523    {
13524      pixel=XGetPixel(windows->image.ximage,x,y);
13525      if (pixel != background)
13526        break;
13527    }
13528    if (y < windows->image.ximage->height)
13529      break;
13530  }
13531  trim_info.width=(size_t) (x-trim_info.x+1);
13532  /*
13533    Crop the top edge.
13534  */
13535  background=XGetPixel(windows->image.ximage,0,0);
13536  trim_info.height=(size_t) windows->image.ximage->height;
13537  for (y=0; y < windows->image.ximage->height; y++)
13538  {
13539    for (x=0; x < windows->image.ximage->width; x++)
13540    {
13541      pixel=XGetPixel(windows->image.ximage,x,y);
13542      if (pixel != background)
13543        break;
13544    }
13545    if (x < windows->image.ximage->width)
13546      break;
13547  }
13548  trim_info.y=(ssize_t) y;
13549  /*
13550    Crop the bottom edge.
13551  */
13552  background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13553  for (y=windows->image.ximage->height-1; y != 0; y--)
13554  {
13555    for (x=0; x < windows->image.ximage->width; x++)
13556    {
13557      pixel=XGetPixel(windows->image.ximage,x,y);
13558      if (pixel != background)
13559        break;
13560    }
13561    if (x < windows->image.ximage->width)
13562      break;
13563  }
13564  trim_info.height=(size_t) y-trim_info.y+1;
13565  if (((unsigned int) trim_info.width != windows->image.width) ||
13566      ((unsigned int) trim_info.height != windows->image.height))
13567    {
13568      /*
13569        Reconfigure Image window as defined by the trimming rectangle.
13570      */
13571      XSetCropGeometry(display,windows,&trim_info,image);
13572      windows->image.window_changes.width=(int) trim_info.width;
13573      windows->image.window_changes.height=(int) trim_info.height;
13574      (void) XConfigureImage(display,resource_info,windows,image,exception);
13575    }
13576  XSetCursorState(display,windows,MagickFalse);
13577  return(MagickTrue);
13578}
13579
13580/*
13581%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13582%                                                                             %
13583%                                                                             %
13584%                                                                             %
13585+   X V i s u a l D i r e c t o r y I m a g e                                 %
13586%                                                                             %
13587%                                                                             %
13588%                                                                             %
13589%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13590%
13591%  XVisualDirectoryImage() creates a Visual Image Directory.
13592%
13593%  The format of the XVisualDirectoryImage method is:
13594%
13595%      Image *XVisualDirectoryImage(Display *display,
13596%        XResourceInfo *resource_info,XWindows *windows,
13597%        ExceptionInfo *exception)
13598%
13599%  A description of each parameter follows:
13600%
13601%    o display: Specifies a connection to an X server; returned from
13602%      XOpenDisplay.
13603%
13604%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13605%
13606%    o windows: Specifies a pointer to a XWindows structure.
13607%
13608%    o exception: return any errors or warnings in this structure.
13609%
13610*/
13611static Image *XVisualDirectoryImage(Display *display,
13612  XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
13613{
13614#define TileImageTag  "Scale/Image"
13615#define XClientName  "montage"
13616
13617  char
13618    **filelist;
13619
13620  Image
13621    *images,
13622    *montage_image,
13623    *next_image,
13624    *thumbnail_image;
13625
13626  ImageInfo
13627    *read_info;
13628
13629  int
13630    number_files;
13631
13632  MagickBooleanType
13633    backdrop;
13634
13635  MagickStatusType
13636    status;
13637
13638  MontageInfo
13639    *montage_info;
13640
13641  RectangleInfo
13642    geometry;
13643
13644  register int
13645    i;
13646
13647  static char
13648    filename[MaxTextExtent] = "\0",
13649    filenames[MaxTextExtent] = "*";
13650
13651  XResourceInfo
13652    background_resources;
13653
13654  /*
13655    Request file name from user.
13656  */
13657  XFileBrowserWidget(display,windows,"Directory",filenames);
13658  if (*filenames == '\0')
13659    return((Image *) NULL);
13660  /*
13661    Expand the filenames.
13662  */
13663  filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13664  if (filelist == (char **) NULL)
13665    {
13666      ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13667        filenames);
13668      return((Image *) NULL);
13669    }
13670  number_files=1;
13671  filelist[0]=filenames;
13672  status=ExpandFilenames(&number_files,&filelist);
13673  if ((status == MagickFalse) || (number_files == 0))
13674    {
13675      if (number_files == 0)
13676        ThrowXWindowFatalException(ImageError,"NoImagesWereFound",filenames)
13677      else
13678        ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13679          filenames);
13680      return((Image *) NULL);
13681    }
13682  /*
13683    Set image background resources.
13684  */
13685  background_resources=(*resource_info);
13686  background_resources.window_id=AcquireString("");
13687  (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
13688    "0x%lx",windows->image.id);
13689  background_resources.backdrop=MagickTrue;
13690  /*
13691    Read each image and convert them to a tile.
13692  */
13693  backdrop=(windows->visual_info->klass == TrueColor) ||
13694    (windows->visual_info->klass == DirectColor) ? MagickTrue : MagickFalse;
13695  read_info=CloneImageInfo(resource_info->image_info);
13696  (void) SetImageOption(read_info,"jpeg:size","120x120");
13697  (void) CloneString(&read_info->size,DefaultTileGeometry);
13698  (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13699    (void *) NULL);
13700  images=NewImageList();
13701  XSetCursorState(display,windows,MagickTrue);
13702  XCheckRefreshWindows(display,windows);
13703  for (i=0; i < (int) number_files; i++)
13704  {
13705    (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13706    filelist[i]=DestroyString(filelist[i]);
13707    *read_info->magick='\0';
13708    next_image=ReadImage(read_info,exception);
13709    CatchException(exception);
13710    if (next_image != (Image *) NULL)
13711      {
13712        (void) DeleteImageProperty(next_image,"label");
13713        (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13714          read_info,next_image,DefaultTileLabel,exception),exception);
13715        (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13716          exception);
13717        thumbnail_image=ThumbnailImage(next_image,geometry.width,
13718          geometry.height,exception);
13719        if (thumbnail_image != (Image *) NULL)
13720          {
13721            next_image=DestroyImage(next_image);
13722            next_image=thumbnail_image;
13723          }
13724        if (backdrop)
13725          {
13726            (void) XDisplayBackgroundImage(display,&background_resources,
13727              next_image,exception);
13728            XSetCursorState(display,windows,MagickTrue);
13729          }
13730        AppendImageToList(&images,next_image);
13731        if (images->progress_monitor != (MagickProgressMonitor) NULL)
13732          {
13733            MagickBooleanType
13734              proceed;
13735
13736            proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13737              (MagickSizeType) number_files);
13738            if (proceed == MagickFalse)
13739              break;
13740          }
13741      }
13742  }
13743  filelist=(char **) RelinquishMagickMemory(filelist);
13744  if (images == (Image *) NULL)
13745    {
13746      read_info=DestroyImageInfo(read_info);
13747      XSetCursorState(display,windows,MagickFalse);
13748      ThrowXWindowFatalException(ImageError,"NoImagesWereLoaded",filenames);
13749      return((Image *) NULL);
13750    }
13751  /*
13752    Create the Visual Image Directory.
13753  */
13754  montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13755  montage_info->pointsize=10;
13756  if (resource_info->font != (char *) NULL)
13757    (void) CloneString(&montage_info->font,resource_info->font);
13758  (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13759  montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13760    images),exception);
13761  images=DestroyImageList(images);
13762  montage_info=DestroyMontageInfo(montage_info);
13763  read_info=DestroyImageInfo(read_info);
13764  XSetCursorState(display,windows,MagickFalse);
13765  if (montage_image == (Image *) NULL)
13766    return(montage_image);
13767  XClientMessage(display,windows->image.id,windows->im_protocols,
13768    windows->im_next_image,CurrentTime);
13769  return(montage_image);
13770}
13771
13772/*
13773%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13774%                                                                             %
13775%                                                                             %
13776%                                                                             %
13777%   X D i s p l a y B a c k g r o u n d I m a g e                             %
13778%                                                                             %
13779%                                                                             %
13780%                                                                             %
13781%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13782%
13783%  XDisplayBackgroundImage() displays an image in the background of a window.
13784%
13785%  The format of the XDisplayBackgroundImage method is:
13786%
13787%      MagickBooleanType XDisplayBackgroundImage(Display *display,
13788%        XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13789%
13790%  A description of each parameter follows:
13791%
13792%    o display: Specifies a connection to an X server;  returned from
13793%      XOpenDisplay.
13794%
13795%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13796%
13797%    o image: the image.
13798%
13799%    o exception: return any errors or warnings in this structure.
13800%
13801*/
13802MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13803  XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13804{
13805  char
13806    geometry[MaxTextExtent],
13807    visual_type[MaxTextExtent];
13808
13809  int
13810    height,
13811    status,
13812    width;
13813
13814  RectangleInfo
13815    geometry_info;
13816
13817  static XPixelInfo
13818    pixel;
13819
13820  static XStandardColormap
13821    *map_info;
13822
13823  static XVisualInfo
13824    *visual_info = (XVisualInfo *) NULL;
13825
13826  static XWindowInfo
13827    window_info;
13828
13829  size_t
13830    delay;
13831
13832  Window
13833    root_window;
13834
13835  XGCValues
13836    context_values;
13837
13838  XResourceInfo
13839    resources;
13840
13841  XWindowAttributes
13842    window_attributes;
13843
13844  /*
13845    Determine target window.
13846  */
13847  assert(image != (Image *) NULL);
13848  assert(image->signature == MagickSignature);
13849  if (image->debug != MagickFalse)
13850    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13851  resources=(*resource_info);
13852  window_info.id=(Window) NULL;
13853  root_window=XRootWindow(display,XDefaultScreen(display));
13854  if (LocaleCompare(resources.window_id,"root") == 0)
13855    window_info.id=root_window;
13856  else
13857    {
13858      if (isdigit((unsigned char) *resources.window_id) != 0)
13859        window_info.id=XWindowByID(display,root_window,
13860          (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13861      if (window_info.id == (Window) NULL)
13862        window_info.id=XWindowByName(display,root_window,resources.window_id);
13863    }
13864  if (window_info.id == (Window) NULL)
13865    {
13866      ThrowXWindowFatalException(XServerError,"NoWindowWithSpecifiedIDExists",
13867        resources.window_id);
13868      return(MagickFalse);
13869    }
13870  /*
13871    Determine window visual id.
13872  */
13873  window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13874  window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13875  (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13876  status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13877  if (status != 0)
13878    (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
13879      XVisualIDFromVisual(window_attributes.visual));
13880  if (visual_info == (XVisualInfo *) NULL)
13881    {
13882      /*
13883        Allocate standard colormap.
13884      */
13885      map_info=XAllocStandardColormap();
13886      if (map_info == (XStandardColormap *) NULL)
13887        ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13888          image->filename);
13889      map_info->colormap=(Colormap) NULL;
13890      pixel.pixels=(unsigned long *) NULL;
13891      /*
13892        Initialize visual info.
13893      */
13894      resources.map_type=(char *) NULL;
13895      resources.visual_type=visual_type;
13896      visual_info=XBestVisualInfo(display,map_info,&resources);
13897      if (visual_info == (XVisualInfo *) NULL)
13898        ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13899          resources.visual_type);
13900      /*
13901        Initialize window info.
13902      */
13903      window_info.ximage=(XImage *) NULL;
13904      window_info.matte_image=(XImage *) NULL;
13905      window_info.pixmap=(Pixmap) NULL;
13906      window_info.matte_pixmap=(Pixmap) NULL;
13907    }
13908  /*
13909    Free previous root colors.
13910  */
13911  if (window_info.id == root_window)
13912    (void) XDestroyWindowColors(display,root_window);
13913  /*
13914    Initialize Standard Colormap.
13915  */
13916  resources.colormap=SharedColormap;
13917  XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel,
13918    exception);
13919  /*
13920    Graphic context superclass.
13921  */
13922  context_values.background=pixel.background_color.pixel;
13923  context_values.foreground=pixel.foreground_color.pixel;
13924  pixel.annotate_context=XCreateGC(display,window_info.id,
13925    (size_t) (GCBackground | GCForeground),&context_values);
13926  if (pixel.annotate_context == (GC) NULL)
13927    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13928      image->filename);
13929  /*
13930    Initialize Image window attributes.
13931  */
13932  window_info.name=AcquireString("\0");
13933  window_info.icon_name=AcquireString("\0");
13934  XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13935    &resources,&window_info);
13936  /*
13937    Create the X image.
13938  */
13939  window_info.width=(unsigned int) image->columns;
13940  window_info.height=(unsigned int) image->rows;
13941  if ((image->columns != window_info.width) ||
13942      (image->rows != window_info.height))
13943    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13944      image->filename);
13945  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
13946    window_attributes.width,window_attributes.height);
13947  geometry_info.width=window_info.width;
13948  geometry_info.height=window_info.height;
13949  geometry_info.x=(ssize_t) window_info.x;
13950  geometry_info.y=(ssize_t) window_info.y;
13951  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13952    &geometry_info.width,&geometry_info.height);
13953  window_info.width=(unsigned int) geometry_info.width;
13954  window_info.height=(unsigned int) geometry_info.height;
13955  window_info.x=(int) geometry_info.x;
13956  window_info.y=(int) geometry_info.y;
13957  status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13958    window_info.height,exception);
13959  if (status == MagickFalse)
13960    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13961      image->filename);
13962  window_info.x=0;
13963  window_info.y=0;
13964  if (image->debug != MagickFalse)
13965    {
13966      (void) LogMagickEvent(X11Event,GetMagickModule(),
13967        "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13968        (double) image->columns,(double) image->rows);
13969      if (image->colors != 0)
13970        (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13971          image->colors);
13972      (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13973    }
13974  /*
13975    Adjust image dimensions as specified by backdrop or geometry options.
13976  */
13977  width=(int) window_info.width;
13978  height=(int) window_info.height;
13979  if (resources.backdrop != MagickFalse)
13980    {
13981      /*
13982        Center image on window.
13983      */
13984      window_info.x=(window_attributes.width/2)-
13985        (window_info.ximage->width/2);
13986      window_info.y=(window_attributes.height/2)-
13987        (window_info.ximage->height/2);
13988      width=window_attributes.width;
13989      height=window_attributes.height;
13990    }
13991  if ((resources.image_geometry != (char *) NULL) &&
13992      (*resources.image_geometry != '\0'))
13993    {
13994      char
13995        default_geometry[MaxTextExtent];
13996
13997      int
13998        flags,
13999        gravity;
14000
14001      XSizeHints
14002        *size_hints;
14003
14004      /*
14005        User specified geometry.
14006      */
14007      size_hints=XAllocSizeHints();
14008      if (size_hints == (XSizeHints *) NULL)
14009        ThrowXWindowFatalException(ResourceLimitFatalError,
14010          "MemoryAllocationFailed",image->filename);
14011      size_hints->flags=0L;
14012      (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
14013        width,height);
14014      flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
14015        default_geometry,window_info.border_width,size_hints,&window_info.x,
14016        &window_info.y,&width,&height,&gravity);
14017      if (flags & (XValue | YValue))
14018        {
14019          width=window_attributes.width;
14020          height=window_attributes.height;
14021        }
14022      (void) XFree((void *) size_hints);
14023    }
14024  /*
14025    Create the X pixmap.
14026  */
14027  window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
14028    (unsigned int) height,window_info.depth);
14029  if (window_info.pixmap == (Pixmap) NULL)
14030    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
14031      image->filename);
14032  /*
14033    Display pixmap on the window.
14034  */
14035  if (((unsigned int) width > window_info.width) ||
14036      ((unsigned int) height > window_info.height))
14037    (void) XFillRectangle(display,window_info.pixmap,
14038      window_info.annotate_context,0,0,(unsigned int) width,
14039      (unsigned int) height);
14040  (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
14041    window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
14042    window_info.width,(unsigned int) window_info.height);
14043  (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
14044  (void) XClearWindow(display,window_info.id);
14045  delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
14046  XDelay(display,delay == 0UL ? 10UL : delay);
14047  (void) XSync(display,MagickFalse);
14048  return(window_info.id == root_window ? MagickTrue : MagickFalse);
14049}
14050
14051/*
14052%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14053%                                                                             %
14054%                                                                             %
14055%                                                                             %
14056+   X D i s p l a y I m a g e                                                 %
14057%                                                                             %
14058%                                                                             %
14059%                                                                             %
14060%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14061%
14062%  XDisplayImage() displays an image via X11.  A new image is created and
14063%  returned if the user interactively transforms the displayed image.
14064%
14065%  The format of the XDisplayImage method is:
14066%
14067%      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14068%        char **argv,int argc,Image **image,size_t *state,
14069%        ExceptionInfo *exception)
14070%
14071%  A description of each parameter follows:
14072%
14073%    o nexus:  Method XDisplayImage returns an image when the
14074%      user chooses 'Open Image' from the command menu or picks a tile
14075%      from the image directory.  Otherwise a null image is returned.
14076%
14077%    o display: Specifies a connection to an X server;  returned from
14078%      XOpenDisplay.
14079%
14080%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14081%
14082%    o argv: Specifies the application's argument list.
14083%
14084%    o argc: Specifies the number of arguments.
14085%
14086%    o image: Specifies an address to an address of an Image structure;
14087%
14088%    o exception: return any errors or warnings in this structure.
14089%
14090*/
14091MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14092  char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
14093{
14094#define MagnifySize  256  /* must be a power of 2 */
14095#define MagickMenus  10
14096#define MagickTitle  "Commands"
14097
14098  static const char
14099    *CommandMenu[] =
14100    {
14101      "File",
14102      "Edit",
14103      "View",
14104      "Transform",
14105      "Enhance",
14106      "Effects",
14107      "F/X",
14108      "Image Edit",
14109      "Miscellany",
14110      "Help",
14111      (char *) NULL
14112    },
14113    *FileMenu[] =
14114    {
14115      "Open...",
14116      "Next",
14117      "Former",
14118      "Select...",
14119      "Save...",
14120      "Print...",
14121      "Delete...",
14122      "New...",
14123      "Visual Directory...",
14124      "Quit",
14125      (char *) NULL
14126    },
14127    *EditMenu[] =
14128    {
14129      "Undo",
14130      "Redo",
14131      "Cut",
14132      "Copy",
14133      "Paste",
14134      (char *) NULL
14135    },
14136    *ViewMenu[] =
14137    {
14138      "Half Size",
14139      "Original Size",
14140      "Double Size",
14141      "Resize...",
14142      "Apply",
14143      "Refresh",
14144      "Restore",
14145      (char *) NULL
14146    },
14147    *TransformMenu[] =
14148    {
14149      "Crop",
14150      "Chop",
14151      "Flop",
14152      "Flip",
14153      "Rotate Right",
14154      "Rotate Left",
14155      "Rotate...",
14156      "Shear...",
14157      "Roll...",
14158      "Trim Edges",
14159      (char *) NULL
14160    },
14161    *EnhanceMenu[] =
14162    {
14163      "Hue...",
14164      "Saturation...",
14165      "Brightness...",
14166      "Gamma...",
14167      "Spiff",
14168      "Dull",
14169      "Contrast Stretch...",
14170      "Sigmoidal Contrast...",
14171      "Normalize",
14172      "Equalize",
14173      "Negate",
14174      "Grayscale",
14175      "Map...",
14176      "Quantize...",
14177      (char *) NULL
14178    },
14179    *EffectsMenu[] =
14180    {
14181      "Despeckle",
14182      "Emboss",
14183      "Reduce Noise",
14184      "Add Noise...",
14185      "Sharpen...",
14186      "Blur...",
14187      "Threshold...",
14188      "Edge Detect...",
14189      "Spread...",
14190      "Shade...",
14191      "Raise...",
14192      "Segment...",
14193      (char *) NULL
14194    },
14195    *FXMenu[] =
14196    {
14197      "Solarize...",
14198      "Sepia Tone...",
14199      "Swirl...",
14200      "Implode...",
14201      "Vignette...",
14202      "Wave...",
14203      "Oil Paint...",
14204      "Charcoal Draw...",
14205      (char *) NULL
14206    },
14207    *ImageEditMenu[] =
14208    {
14209      "Annotate...",
14210      "Draw...",
14211      "Color...",
14212      "Matte...",
14213      "Composite...",
14214      "Add Border...",
14215      "Add Frame...",
14216      "Comment...",
14217      "Launch...",
14218      "Region of Interest...",
14219      (char *) NULL
14220    },
14221    *MiscellanyMenu[] =
14222    {
14223      "Image Info",
14224      "Zoom Image",
14225      "Show Preview...",
14226      "Show Histogram",
14227      "Show Matte",
14228      "Background...",
14229      "Slide Show...",
14230      "Preferences...",
14231      (char *) NULL
14232    },
14233    *HelpMenu[] =
14234    {
14235      "Overview",
14236      "Browse Documentation",
14237      "About Display",
14238      (char *) NULL
14239    },
14240    *ShortCutsMenu[] =
14241    {
14242      "Next",
14243      "Former",
14244      "Open...",
14245      "Save...",
14246      "Print...",
14247      "Undo",
14248      "Restore",
14249      "Image Info",
14250      "Quit",
14251      (char *) NULL
14252    },
14253    *VirtualMenu[] =
14254    {
14255      "Image Info",
14256      "Print",
14257      "Next",
14258      "Quit",
14259      (char *) NULL
14260    };
14261
14262  static const char
14263    **Menus[MagickMenus] =
14264    {
14265      FileMenu,
14266      EditMenu,
14267      ViewMenu,
14268      TransformMenu,
14269      EnhanceMenu,
14270      EffectsMenu,
14271      FXMenu,
14272      ImageEditMenu,
14273      MiscellanyMenu,
14274      HelpMenu
14275    };
14276
14277  static CommandType
14278    CommandMenus[] =
14279    {
14280      NullCommand,
14281      NullCommand,
14282      NullCommand,
14283      NullCommand,
14284      NullCommand,
14285      NullCommand,
14286      NullCommand,
14287      NullCommand,
14288      NullCommand,
14289      NullCommand,
14290    },
14291    FileCommands[] =
14292    {
14293      OpenCommand,
14294      NextCommand,
14295      FormerCommand,
14296      SelectCommand,
14297      SaveCommand,
14298      PrintCommand,
14299      DeleteCommand,
14300      NewCommand,
14301      VisualDirectoryCommand,
14302      QuitCommand
14303    },
14304    EditCommands[] =
14305    {
14306      UndoCommand,
14307      RedoCommand,
14308      CutCommand,
14309      CopyCommand,
14310      PasteCommand
14311    },
14312    ViewCommands[] =
14313    {
14314      HalfSizeCommand,
14315      OriginalSizeCommand,
14316      DoubleSizeCommand,
14317      ResizeCommand,
14318      ApplyCommand,
14319      RefreshCommand,
14320      RestoreCommand
14321    },
14322    TransformCommands[] =
14323    {
14324      CropCommand,
14325      ChopCommand,
14326      FlopCommand,
14327      FlipCommand,
14328      RotateRightCommand,
14329      RotateLeftCommand,
14330      RotateCommand,
14331      ShearCommand,
14332      RollCommand,
14333      TrimCommand
14334    },
14335    EnhanceCommands[] =
14336    {
14337      HueCommand,
14338      SaturationCommand,
14339      BrightnessCommand,
14340      GammaCommand,
14341      SpiffCommand,
14342      DullCommand,
14343      ContrastStretchCommand,
14344      SigmoidalContrastCommand,
14345      NormalizeCommand,
14346      EqualizeCommand,
14347      NegateCommand,
14348      GrayscaleCommand,
14349      MapCommand,
14350      QuantizeCommand
14351    },
14352    EffectsCommands[] =
14353    {
14354      DespeckleCommand,
14355      EmbossCommand,
14356      ReduceNoiseCommand,
14357      AddNoiseCommand,
14358      SharpenCommand,
14359      BlurCommand,
14360      ThresholdCommand,
14361      EdgeDetectCommand,
14362      SpreadCommand,
14363      ShadeCommand,
14364      RaiseCommand,
14365      SegmentCommand
14366    },
14367    FXCommands[] =
14368    {
14369      SolarizeCommand,
14370      SepiaToneCommand,
14371      SwirlCommand,
14372      ImplodeCommand,
14373      VignetteCommand,
14374      WaveCommand,
14375      OilPaintCommand,
14376      CharcoalDrawCommand
14377    },
14378    ImageEditCommands[] =
14379    {
14380      AnnotateCommand,
14381      DrawCommand,
14382      ColorCommand,
14383      MatteCommand,
14384      CompositeCommand,
14385      AddBorderCommand,
14386      AddFrameCommand,
14387      CommentCommand,
14388      LaunchCommand,
14389      RegionofInterestCommand
14390    },
14391    MiscellanyCommands[] =
14392    {
14393      InfoCommand,
14394      ZoomCommand,
14395      ShowPreviewCommand,
14396      ShowHistogramCommand,
14397      ShowMatteCommand,
14398      BackgroundCommand,
14399      SlideShowCommand,
14400      PreferencesCommand
14401    },
14402    HelpCommands[] =
14403    {
14404      HelpCommand,
14405      BrowseDocumentationCommand,
14406      VersionCommand
14407    },
14408    ShortCutsCommands[] =
14409    {
14410      NextCommand,
14411      FormerCommand,
14412      OpenCommand,
14413      SaveCommand,
14414      PrintCommand,
14415      UndoCommand,
14416      RestoreCommand,
14417      InfoCommand,
14418      QuitCommand
14419    },
14420    VirtualCommands[] =
14421    {
14422      InfoCommand,
14423      PrintCommand,
14424      NextCommand,
14425      QuitCommand
14426    };
14427
14428  static CommandType
14429    *Commands[MagickMenus] =
14430    {
14431      FileCommands,
14432      EditCommands,
14433      ViewCommands,
14434      TransformCommands,
14435      EnhanceCommands,
14436      EffectsCommands,
14437      FXCommands,
14438      ImageEditCommands,
14439      MiscellanyCommands,
14440      HelpCommands
14441    };
14442
14443  char
14444    command[MaxTextExtent],
14445    *directory,
14446    geometry[MaxTextExtent],
14447    resource_name[MaxTextExtent];
14448
14449  CommandType
14450    command_type;
14451
14452  Image
14453    *display_image,
14454    *nexus;
14455
14456  int
14457    entry,
14458    id;
14459
14460  KeySym
14461    key_symbol;
14462
14463  MagickStatusType
14464    context_mask,
14465    status;
14466
14467  RectangleInfo
14468    geometry_info;
14469
14470  register int
14471    i;
14472
14473  static char
14474    working_directory[MaxTextExtent];
14475
14476  static XPoint
14477    vid_info;
14478
14479  static XWindowInfo
14480    *magick_windows[MaxXWindows];
14481
14482  static unsigned int
14483    number_windows;
14484
14485  struct stat
14486    attributes;
14487
14488  time_t
14489    timer,
14490    timestamp,
14491    update_time;
14492
14493  unsigned int
14494    height,
14495    width;
14496
14497  size_t
14498    delay;
14499
14500  WarningHandler
14501    warning_handler;
14502
14503  Window
14504    root_window;
14505
14506  XClassHint
14507    *class_hints;
14508
14509  XEvent
14510    event;
14511
14512  XFontStruct
14513    *font_info;
14514
14515  XGCValues
14516    context_values;
14517
14518  XPixelInfo
14519    *icon_pixel,
14520    *pixel;
14521
14522  XResourceInfo
14523    *icon_resources;
14524
14525  XStandardColormap
14526    *icon_map,
14527    *map_info;
14528
14529  XVisualInfo
14530    *icon_visual,
14531    *visual_info;
14532
14533  XWindowChanges
14534    window_changes;
14535
14536  XWindows
14537    *windows;
14538
14539  XWMHints
14540    *manager_hints;
14541
14542  assert(image != (Image **) NULL);
14543  assert((*image)->signature == MagickSignature);
14544  if ((*image)->debug != MagickFalse)
14545    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14546  display_image=(*image);
14547  warning_handler=(WarningHandler) NULL;
14548  windows=XSetWindows((XWindows *) ~0);
14549  if (windows != (XWindows *) NULL)
14550    {
14551      int
14552        status;
14553
14554      status=chdir(working_directory);
14555      if (status == -1)
14556        (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14557          "UnableToOpenFile","%s",working_directory);
14558      warning_handler=resource_info->display_warnings ?
14559        SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14560      warning_handler=resource_info->display_warnings ?
14561        SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14562    }
14563  else
14564    {
14565      /*
14566        Allocate windows structure.
14567      */
14568      resource_info->colors=display_image->colors;
14569      windows=XSetWindows(XInitializeWindows(display,resource_info));
14570      if (windows == (XWindows *) NULL)
14571        ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14572          (*image)->filename);
14573      /*
14574        Initialize window id's.
14575      */
14576      number_windows=0;
14577      magick_windows[number_windows++]=(&windows->icon);
14578      magick_windows[number_windows++]=(&windows->backdrop);
14579      magick_windows[number_windows++]=(&windows->image);
14580      magick_windows[number_windows++]=(&windows->info);
14581      magick_windows[number_windows++]=(&windows->command);
14582      magick_windows[number_windows++]=(&windows->widget);
14583      magick_windows[number_windows++]=(&windows->popup);
14584      magick_windows[number_windows++]=(&windows->magnify);
14585      magick_windows[number_windows++]=(&windows->pan);
14586      for (i=0; i < (int) number_windows; i++)
14587        magick_windows[i]->id=(Window) NULL;
14588      vid_info.x=0;
14589      vid_info.y=0;
14590    }
14591  /*
14592    Initialize font info.
14593  */
14594  if (windows->font_info != (XFontStruct *) NULL)
14595    (void) XFreeFont(display,windows->font_info);
14596  windows->font_info=XBestFont(display,resource_info,MagickFalse);
14597  if (windows->font_info == (XFontStruct *) NULL)
14598    ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14599      resource_info->font);
14600  /*
14601    Initialize Standard Colormap.
14602  */
14603  map_info=windows->map_info;
14604  icon_map=windows->icon_map;
14605  visual_info=windows->visual_info;
14606  icon_visual=windows->icon_visual;
14607  pixel=windows->pixel_info;
14608  icon_pixel=windows->icon_pixel;
14609  font_info=windows->font_info;
14610  icon_resources=windows->icon_resources;
14611  class_hints=windows->class_hints;
14612  manager_hints=windows->manager_hints;
14613  root_window=XRootWindow(display,visual_info->screen);
14614  nexus=NewImageList();
14615  if (display_image->debug != MagickFalse)
14616    {
14617      (void) LogMagickEvent(X11Event,GetMagickModule(),
14618        "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14619        (double) display_image->scene,(double) display_image->columns,
14620        (double) display_image->rows);
14621      if (display_image->colors != 0)
14622        (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14623          display_image->colors);
14624      (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14625        display_image->magick);
14626    }
14627  XMakeStandardColormap(display,visual_info,resource_info,display_image,
14628    map_info,pixel,exception);
14629  display_image->taint=MagickFalse;
14630  /*
14631    Initialize graphic context.
14632  */
14633  windows->context.id=(Window) NULL;
14634  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14635    resource_info,&windows->context);
14636  (void) CloneString(&class_hints->res_name,resource_info->client_name);
14637  (void) CloneString(&class_hints->res_class,resource_info->client_name);
14638  class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14639  manager_hints->flags=InputHint | StateHint;
14640  manager_hints->input=MagickFalse;
14641  manager_hints->initial_state=WithdrawnState;
14642  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14643    &windows->context);
14644  if (display_image->debug != MagickFalse)
14645    (void) LogMagickEvent(X11Event,GetMagickModule(),
14646      "Window id: 0x%lx (context)",windows->context.id);
14647  context_values.background=pixel->background_color.pixel;
14648  context_values.font=font_info->fid;
14649  context_values.foreground=pixel->foreground_color.pixel;
14650  context_values.graphics_exposures=MagickFalse;
14651  context_mask=(MagickStatusType)
14652    (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14653  if (pixel->annotate_context != (GC) NULL)
14654    (void) XFreeGC(display,pixel->annotate_context);
14655  pixel->annotate_context=XCreateGC(display,windows->context.id,
14656    context_mask,&context_values);
14657  if (pixel->annotate_context == (GC) NULL)
14658    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14659      display_image->filename);
14660  context_values.background=pixel->depth_color.pixel;
14661  if (pixel->widget_context != (GC) NULL)
14662    (void) XFreeGC(display,pixel->widget_context);
14663  pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14664    &context_values);
14665  if (pixel->widget_context == (GC) NULL)
14666    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14667      display_image->filename);
14668  context_values.background=pixel->foreground_color.pixel;
14669  context_values.foreground=pixel->background_color.pixel;
14670  context_values.plane_mask=context_values.background ^
14671    context_values.foreground;
14672  if (pixel->highlight_context != (GC) NULL)
14673    (void) XFreeGC(display,pixel->highlight_context);
14674  pixel->highlight_context=XCreateGC(display,windows->context.id,
14675    (size_t) (context_mask | GCPlaneMask),&context_values);
14676  if (pixel->highlight_context == (GC) NULL)
14677    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14678      display_image->filename);
14679  (void) XDestroyWindow(display,windows->context.id);
14680  /*
14681    Initialize icon window.
14682  */
14683  XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14684    icon_resources,&windows->icon);
14685  windows->icon.geometry=resource_info->icon_geometry;
14686  XBestIconSize(display,&windows->icon,display_image);
14687  windows->icon.attributes.colormap=XDefaultColormap(display,
14688    icon_visual->screen);
14689  windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14690  manager_hints->flags=InputHint | StateHint;
14691  manager_hints->input=MagickFalse;
14692  manager_hints->initial_state=IconicState;
14693  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14694    &windows->icon);
14695  if (display_image->debug != MagickFalse)
14696    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14697      windows->icon.id);
14698  /*
14699    Initialize graphic context for icon window.
14700  */
14701  if (icon_pixel->annotate_context != (GC) NULL)
14702    (void) XFreeGC(display,icon_pixel->annotate_context);
14703  context_values.background=icon_pixel->background_color.pixel;
14704  context_values.foreground=icon_pixel->foreground_color.pixel;
14705  icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14706    (size_t) (GCBackground | GCForeground),&context_values);
14707  if (icon_pixel->annotate_context == (GC) NULL)
14708    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14709      display_image->filename);
14710  windows->icon.annotate_context=icon_pixel->annotate_context;
14711  /*
14712    Initialize Image window.
14713  */
14714  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14715    &windows->image);
14716  windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14717  if (resource_info->use_shared_memory == MagickFalse)
14718    windows->image.shared_memory=MagickFalse;
14719  if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14720    {
14721      char
14722        *title;
14723
14724      title=InterpretImageProperties(resource_info->image_info,display_image,
14725        resource_info->title,exception);
14726      (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
14727      (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
14728      title=DestroyString(title);
14729    }
14730  else
14731    {
14732      char
14733        filename[MaxTextExtent];
14734
14735      /*
14736        Window name is the base of the filename.
14737      */
14738      GetPathComponent(display_image->magick_filename,TailPath,filename);
14739      if (display_image->scene == 0)
14740        (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14741          "%s: %s",MagickPackageName,filename);
14742      else
14743        (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14744          "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14745          (double) display_image->scene,(double) GetImageListLength(
14746          display_image));
14747      (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
14748    }
14749  if (resource_info->immutable)
14750    windows->image.immutable=MagickTrue;
14751  windows->image.use_pixmap=resource_info->use_pixmap;
14752  windows->image.geometry=resource_info->image_geometry;
14753  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14754    XDisplayWidth(display,visual_info->screen),
14755    XDisplayHeight(display,visual_info->screen));
14756  geometry_info.width=display_image->columns;
14757  geometry_info.height=display_image->rows;
14758  geometry_info.x=0;
14759  geometry_info.y=0;
14760  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14761    &geometry_info.width,&geometry_info.height);
14762  windows->image.width=(unsigned int) geometry_info.width;
14763  windows->image.height=(unsigned int) geometry_info.height;
14764  windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14765    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14766    KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14767    PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14768  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14769    resource_info,&windows->backdrop);
14770  if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14771    {
14772      /*
14773        Initialize backdrop window.
14774      */
14775      windows->backdrop.x=0;
14776      windows->backdrop.y=0;
14777      (void) CloneString(&windows->backdrop.name,"Backdrop");
14778      windows->backdrop.flags=(size_t) (USSize | USPosition);
14779      windows->backdrop.width=(unsigned int)
14780        XDisplayWidth(display,visual_info->screen);
14781      windows->backdrop.height=(unsigned int)
14782        XDisplayHeight(display,visual_info->screen);
14783      windows->backdrop.border_width=0;
14784      windows->backdrop.immutable=MagickTrue;
14785      windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14786        ButtonReleaseMask;
14787      windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14788        StructureNotifyMask;
14789      manager_hints->flags=IconWindowHint | InputHint | StateHint;
14790      manager_hints->icon_window=windows->icon.id;
14791      manager_hints->input=MagickTrue;
14792      manager_hints->initial_state=resource_info->iconic ? IconicState :
14793        NormalState;
14794      XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14795        &windows->backdrop);
14796      if (display_image->debug != MagickFalse)
14797        (void) LogMagickEvent(X11Event,GetMagickModule(),
14798          "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14799      (void) XMapWindow(display,windows->backdrop.id);
14800      (void) XClearWindow(display,windows->backdrop.id);
14801      if (windows->image.id != (Window) NULL)
14802        {
14803          (void) XDestroyWindow(display,windows->image.id);
14804          windows->image.id=(Window) NULL;
14805        }
14806      /*
14807        Position image in the center the backdrop.
14808      */
14809      windows->image.flags|=USPosition;
14810      windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14811        (windows->image.width/2);
14812      windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14813        (windows->image.height/2);
14814    }
14815  manager_hints->flags=IconWindowHint | InputHint | StateHint;
14816  manager_hints->icon_window=windows->icon.id;
14817  manager_hints->input=MagickTrue;
14818  manager_hints->initial_state=resource_info->iconic ? IconicState :
14819    NormalState;
14820  if (windows->group_leader.id != (Window) NULL)
14821    {
14822      /*
14823        Follow the leader.
14824      */
14825      manager_hints->flags|=WindowGroupHint;
14826      manager_hints->window_group=windows->group_leader.id;
14827      (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14828      if (display_image->debug != MagickFalse)
14829        (void) LogMagickEvent(X11Event,GetMagickModule(),
14830          "Window id: 0x%lx (group leader)",windows->group_leader.id);
14831    }
14832  XMakeWindow(display,
14833    (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14834    argv,argc,class_hints,manager_hints,&windows->image);
14835  (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14836    XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14837  if (windows->group_leader.id != (Window) NULL)
14838    (void) XSetTransientForHint(display,windows->image.id,
14839      windows->group_leader.id);
14840  if (display_image->debug != MagickFalse)
14841    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14842      windows->image.id);
14843  /*
14844    Initialize Info widget.
14845  */
14846  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14847    &windows->info);
14848  (void) CloneString(&windows->info.name,"Info");
14849  (void) CloneString(&windows->info.icon_name,"Info");
14850  windows->info.border_width=1;
14851  windows->info.x=2;
14852  windows->info.y=2;
14853  windows->info.flags|=PPosition;
14854  windows->info.attributes.win_gravity=UnmapGravity;
14855  windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14856    StructureNotifyMask;
14857  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14858  manager_hints->input=MagickFalse;
14859  manager_hints->initial_state=NormalState;
14860  manager_hints->window_group=windows->image.id;
14861  XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14862    &windows->info);
14863  windows->info.highlight_stipple=XCreateBitmapFromData(display,
14864    windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14865  windows->info.shadow_stipple=XCreateBitmapFromData(display,
14866    windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14867  (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14868  if (windows->image.mapped != MagickFalse)
14869    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14870  if (display_image->debug != MagickFalse)
14871    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14872      windows->info.id);
14873  /*
14874    Initialize Command widget.
14875  */
14876  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14877    resource_info,&windows->command);
14878  windows->command.data=MagickMenus;
14879  (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14880  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
14881    resource_info->client_name);
14882  windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14883    resource_name,"geometry",(char *) NULL);
14884  (void) CloneString(&windows->command.name,MagickTitle);
14885  windows->command.border_width=0;
14886  windows->command.flags|=PPosition;
14887  windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14888    ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14889    OwnerGrabButtonMask | StructureNotifyMask;
14890  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14891  manager_hints->input=MagickTrue;
14892  manager_hints->initial_state=NormalState;
14893  manager_hints->window_group=windows->image.id;
14894  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14895    &windows->command);
14896  windows->command.highlight_stipple=XCreateBitmapFromData(display,
14897    windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14898    HighlightHeight);
14899  windows->command.shadow_stipple=XCreateBitmapFromData(display,
14900    windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14901  (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14902  if (windows->command.mapped != MagickFalse)
14903    (void) XMapRaised(display,windows->command.id);
14904  if (display_image->debug != MagickFalse)
14905    (void) LogMagickEvent(X11Event,GetMagickModule(),
14906      "Window id: 0x%lx (command)",windows->command.id);
14907  /*
14908    Initialize Widget window.
14909  */
14910  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14911    resource_info,&windows->widget);
14912  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
14913    resource_info->client_name);
14914  windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14915    resource_name,"geometry",(char *) NULL);
14916  windows->widget.border_width=0;
14917  windows->widget.flags|=PPosition;
14918  windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14919    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14920    KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14921    StructureNotifyMask;
14922  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14923  manager_hints->input=MagickTrue;
14924  manager_hints->initial_state=NormalState;
14925  manager_hints->window_group=windows->image.id;
14926  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14927    &windows->widget);
14928  windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14929    windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14930  windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14931    windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14932  (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14933  if (display_image->debug != MagickFalse)
14934    (void) LogMagickEvent(X11Event,GetMagickModule(),
14935      "Window id: 0x%lx (widget)",windows->widget.id);
14936  /*
14937    Initialize popup window.
14938  */
14939  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14940    resource_info,&windows->popup);
14941  windows->popup.border_width=0;
14942  windows->popup.flags|=PPosition;
14943  windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14944    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14945    KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14946  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14947  manager_hints->input=MagickTrue;
14948  manager_hints->initial_state=NormalState;
14949  manager_hints->window_group=windows->image.id;
14950  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14951    &windows->popup);
14952  windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14953    windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14954  windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14955    windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14956  (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14957  if (display_image->debug != MagickFalse)
14958    (void) LogMagickEvent(X11Event,GetMagickModule(),
14959      "Window id: 0x%lx (pop up)",windows->popup.id);
14960  /*
14961    Initialize Magnify window and cursor.
14962  */
14963  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14964    resource_info,&windows->magnify);
14965  if (resource_info->use_shared_memory == MagickFalse)
14966    windows->magnify.shared_memory=MagickFalse;
14967  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
14968    resource_info->client_name);
14969  windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14970    resource_name,"geometry",(char *) NULL);
14971  (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14972    resource_info->magnify);
14973  if (windows->magnify.cursor != (Cursor) NULL)
14974    (void) XFreeCursor(display,windows->magnify.cursor);
14975  windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14976    map_info->colormap,resource_info->background_color,
14977    resource_info->foreground_color);
14978  if (windows->magnify.cursor == (Cursor) NULL)
14979    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14980      display_image->filename);
14981  windows->magnify.width=MagnifySize;
14982  windows->magnify.height=MagnifySize;
14983  windows->magnify.flags|=PPosition;
14984  windows->magnify.min_width=MagnifySize;
14985  windows->magnify.min_height=MagnifySize;
14986  windows->magnify.width_inc=MagnifySize;
14987  windows->magnify.height_inc=MagnifySize;
14988  windows->magnify.data=resource_info->magnify;
14989  windows->magnify.attributes.cursor=windows->magnify.cursor;
14990  windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14991    ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14992    StructureNotifyMask;
14993  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14994  manager_hints->input=MagickTrue;
14995  manager_hints->initial_state=NormalState;
14996  manager_hints->window_group=windows->image.id;
14997  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14998    &windows->magnify);
14999  if (display_image->debug != MagickFalse)
15000    (void) LogMagickEvent(X11Event,GetMagickModule(),
15001      "Window id: 0x%lx (magnify)",windows->magnify.id);
15002  (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
15003  /*
15004    Initialize panning window.
15005  */
15006  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
15007    resource_info,&windows->pan);
15008  (void) CloneString(&windows->pan.name,"Pan Icon");
15009  windows->pan.width=windows->icon.width;
15010  windows->pan.height=windows->icon.height;
15011  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
15012    resource_info->client_name);
15013  windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
15014    resource_name,"geometry",(char *) NULL);
15015  (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
15016    &windows->pan.width,&windows->pan.height);
15017  windows->pan.flags|=PPosition;
15018  windows->pan.immutable=MagickTrue;
15019  windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
15020    ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
15021    StructureNotifyMask;
15022  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
15023  manager_hints->input=MagickFalse;
15024  manager_hints->initial_state=NormalState;
15025  manager_hints->window_group=windows->image.id;
15026  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
15027    &windows->pan);
15028  if (display_image->debug != MagickFalse)
15029    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
15030      windows->pan.id);
15031  (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
15032  if (windows->info.mapped != MagickFalse)
15033    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15034  if ((windows->image.mapped == MagickFalse) ||
15035      (windows->backdrop.id != (Window) NULL))
15036    (void) XMapWindow(display,windows->image.id);
15037  /*
15038    Set our progress monitor and warning handlers.
15039  */
15040  if (warning_handler == (WarningHandler) NULL)
15041    {
15042      warning_handler=resource_info->display_warnings ?
15043        SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
15044      warning_handler=resource_info->display_warnings ?
15045        SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
15046    }
15047  /*
15048    Initialize Image and Magnify X images.
15049  */
15050  windows->image.x=0;
15051  windows->image.y=0;
15052  windows->magnify.shape=MagickFalse;
15053  width=(unsigned int) display_image->columns;
15054  height=(unsigned int) display_image->rows;
15055  if ((display_image->columns != width) || (display_image->rows != height))
15056    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15057      display_image->filename);
15058  status=XMakeImage(display,resource_info,&windows->image,display_image,
15059    width,height,exception);
15060  if (status == MagickFalse)
15061    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15062      display_image->filename);
15063  status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
15064    windows->magnify.width,windows->magnify.height,exception);
15065  if (status == MagickFalse)
15066    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15067      display_image->filename);
15068  if (windows->magnify.mapped != MagickFalse)
15069    (void) XMapRaised(display,windows->magnify.id);
15070  if (windows->pan.mapped != MagickFalse)
15071    (void) XMapRaised(display,windows->pan.id);
15072  windows->image.window_changes.width=(int) display_image->columns;
15073  windows->image.window_changes.height=(int) display_image->rows;
15074  (void) XConfigureImage(display,resource_info,windows,display_image,exception);
15075  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15076  (void) XSync(display,MagickFalse);
15077  /*
15078    Respond to events.
15079  */
15080  delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
15081  timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15082  update_time=0;
15083  if (resource_info->update != MagickFalse)
15084    {
15085      MagickBooleanType
15086        status;
15087
15088      /*
15089        Determine when file data was last modified.
15090      */
15091      status=GetPathAttributes(display_image->filename,&attributes);
15092      if (status != MagickFalse)
15093        update_time=attributes.st_mtime;
15094    }
15095  *state&=(~FormerImageState);
15096  *state&=(~MontageImageState);
15097  *state&=(~NextImageState);
15098  do
15099  {
15100    /*
15101      Handle a window event.
15102    */
15103    if (windows->image.mapped != MagickFalse)
15104      if ((display_image->delay != 0) || (resource_info->update != 0))
15105        {
15106          if (timer < time((time_t *) NULL))
15107            {
15108              if (resource_info->update == MagickFalse)
15109                *state|=NextImageState | ExitState;
15110              else
15111                {
15112                  MagickBooleanType
15113                    status;
15114
15115                  /*
15116                    Determine if image file was modified.
15117                  */
15118                  status=GetPathAttributes(display_image->filename,&attributes);
15119                  if (status != MagickFalse)
15120                    if (update_time != attributes.st_mtime)
15121                      {
15122                        /*
15123                          Redisplay image.
15124                        */
15125                        (void) FormatLocaleString(
15126                          resource_info->image_info->filename,MaxTextExtent,
15127                          "%s:%s",display_image->magick,
15128                          display_image->filename);
15129                        nexus=ReadImage(resource_info->image_info,exception);
15130                        if (nexus != (Image *) NULL)
15131                          {
15132                            nexus=DestroyImage(nexus);
15133                            *state|=NextImageState | ExitState;
15134                          }
15135                      }
15136                  delay=display_image->delay/MagickMax(
15137                    display_image->ticks_per_second,1L);
15138                  timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15139                }
15140            }
15141          if (XEventsQueued(display,QueuedAfterFlush) == 0)
15142            {
15143              /*
15144                Do not block if delay > 0.
15145              */
15146              XDelay(display,SuspendTime << 2);
15147              continue;
15148            }
15149        }
15150    timestamp=time((time_t *) NULL);
15151    (void) XNextEvent(display,&event);
15152    if (windows->image.stasis == MagickFalse)
15153      windows->image.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15154        MagickTrue : MagickFalse;
15155    if (windows->magnify.stasis == MagickFalse)
15156      windows->magnify.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15157        MagickTrue : MagickFalse;
15158    if (event.xany.window == windows->command.id)
15159      {
15160        /*
15161          Select a command from the Command widget.
15162        */
15163        id=XCommandWidget(display,windows,CommandMenu,&event);
15164        if (id < 0)
15165          continue;
15166        (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15167        command_type=CommandMenus[id];
15168        if (id < MagickMenus)
15169          {
15170            /*
15171              Select a command from a pop-up menu.
15172            */
15173            entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15174              command);
15175            if (entry < 0)
15176              continue;
15177            (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15178            command_type=Commands[id][entry];
15179          }
15180        if (command_type != NullCommand)
15181          nexus=XMagickCommand(display,resource_info,windows,command_type,
15182            &display_image,exception);
15183        continue;
15184      }
15185    switch (event.type)
15186    {
15187      case ButtonPress:
15188      {
15189        if (display_image->debug != MagickFalse)
15190          (void) LogMagickEvent(X11Event,GetMagickModule(),
15191            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15192            event.xbutton.button,event.xbutton.x,event.xbutton.y);
15193        if ((event.xbutton.button == Button3) &&
15194            (event.xbutton.state & Mod1Mask))
15195          {
15196            /*
15197              Convert Alt-Button3 to Button2.
15198            */
15199            event.xbutton.button=Button2;
15200            event.xbutton.state&=(~Mod1Mask);
15201          }
15202        if (event.xbutton.window == windows->backdrop.id)
15203          {
15204            (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15205              event.xbutton.time);
15206            break;
15207          }
15208        if (event.xbutton.window == windows->image.id)
15209          {
15210            switch (event.xbutton.button)
15211            {
15212              case Button1:
15213              {
15214                if (resource_info->immutable)
15215                  {
15216                    /*
15217                      Select a command from the Virtual menu.
15218                    */
15219                    entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15220                      command);
15221                    if (entry >= 0)
15222                      nexus=XMagickCommand(display,resource_info,windows,
15223                        VirtualCommands[entry],&display_image,exception);
15224                    break;
15225                  }
15226                /*
15227                  Map/unmap Command widget.
15228                */
15229                if (windows->command.mapped != MagickFalse)
15230                  (void) XWithdrawWindow(display,windows->command.id,
15231                    windows->command.screen);
15232                else
15233                  {
15234                    (void) XCommandWidget(display,windows,CommandMenu,
15235                      (XEvent *) NULL);
15236                    (void) XMapRaised(display,windows->command.id);
15237                  }
15238                break;
15239              }
15240              case Button2:
15241              {
15242                /*
15243                  User pressed the image magnify button.
15244                */
15245                (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15246                  &display_image,exception);
15247                XMagnifyImage(display,windows,&event,exception);
15248                break;
15249              }
15250              case Button3:
15251              {
15252                if (resource_info->immutable)
15253                  {
15254                    /*
15255                      Select a command from the Virtual menu.
15256                    */
15257                    entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15258                      command);
15259                    if (entry >= 0)
15260                      nexus=XMagickCommand(display,resource_info,windows,
15261                        VirtualCommands[entry],&display_image,exception);
15262                    break;
15263                  }
15264                if (display_image->montage != (char *) NULL)
15265                  {
15266                    /*
15267                      Open or delete a tile from a visual image directory.
15268                    */
15269                    nexus=XTileImage(display,resource_info,windows,
15270                      display_image,&event,exception);
15271                    if (nexus != (Image *) NULL)
15272                      *state|=MontageImageState | NextImageState | ExitState;
15273                    vid_info.x=(short int) windows->image.x;
15274                    vid_info.y=(short int) windows->image.y;
15275                    break;
15276                  }
15277                /*
15278                  Select a command from the Short Cuts menu.
15279                */
15280                entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15281                  command);
15282                if (entry >= 0)
15283                  nexus=XMagickCommand(display,resource_info,windows,
15284                    ShortCutsCommands[entry],&display_image,exception);
15285                break;
15286              }
15287              case Button4:
15288              {
15289                /*
15290                  Wheel up.
15291                */
15292                XTranslateImage(display,windows,*image,XK_Up);
15293                break;
15294              }
15295              case Button5:
15296              {
15297                /*
15298                  Wheel down.
15299                */
15300                XTranslateImage(display,windows,*image,XK_Down);
15301                break;
15302              }
15303              default:
15304                break;
15305            }
15306            break;
15307          }
15308        if (event.xbutton.window == windows->magnify.id)
15309          {
15310            int
15311              factor;
15312
15313            static const char
15314              *MagnifyMenu[] =
15315              {
15316                "2",
15317                "4",
15318                "5",
15319                "6",
15320                "7",
15321                "8",
15322                "9",
15323                "3",
15324                (char *) NULL,
15325              };
15326
15327            static KeySym
15328              MagnifyCommands[] =
15329              {
15330                XK_2,
15331                XK_4,
15332                XK_5,
15333                XK_6,
15334                XK_7,
15335                XK_8,
15336                XK_9,
15337                XK_3
15338              };
15339
15340            /*
15341              Select a magnify factor from the pop-up menu.
15342            */
15343            factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15344            if (factor >= 0)
15345              XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor],
15346                exception);
15347            break;
15348          }
15349        if (event.xbutton.window == windows->pan.id)
15350          {
15351            switch (event.xbutton.button)
15352            {
15353              case Button4:
15354              {
15355                /*
15356                  Wheel up.
15357                */
15358                XTranslateImage(display,windows,*image,XK_Up);
15359                break;
15360              }
15361              case Button5:
15362              {
15363                /*
15364                  Wheel down.
15365                */
15366                XTranslateImage(display,windows,*image,XK_Down);
15367                break;
15368              }
15369              default:
15370              {
15371                XPanImage(display,windows,&event,exception);
15372                break;
15373              }
15374            }
15375            break;
15376          }
15377        delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15378          1L);
15379        timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15380        break;
15381      }
15382      case ButtonRelease:
15383      {
15384        if (display_image->debug != MagickFalse)
15385          (void) LogMagickEvent(X11Event,GetMagickModule(),
15386            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15387            event.xbutton.button,event.xbutton.x,event.xbutton.y);
15388        break;
15389      }
15390      case ClientMessage:
15391      {
15392        if (display_image->debug != MagickFalse)
15393          (void) LogMagickEvent(X11Event,GetMagickModule(),
15394            "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15395            event.xclient.message_type,event.xclient.format,(unsigned long)
15396            event.xclient.data.l[0]);
15397        if (event.xclient.message_type == windows->im_protocols)
15398          {
15399            if (*event.xclient.data.l == (long) windows->im_update_widget)
15400              {
15401                (void) CloneString(&windows->command.name,MagickTitle);
15402                windows->command.data=MagickMenus;
15403                (void) XCommandWidget(display,windows,CommandMenu,
15404                  (XEvent *) NULL);
15405                break;
15406              }
15407            if (*event.xclient.data.l == (long) windows->im_update_colormap)
15408              {
15409                /*
15410                  Update graphic context and window colormap.
15411                */
15412                for (i=0; i < (int) number_windows; i++)
15413                {
15414                  if (magick_windows[i]->id == windows->icon.id)
15415                    continue;
15416                  context_values.background=pixel->background_color.pixel;
15417                  context_values.foreground=pixel->foreground_color.pixel;
15418                  (void) XChangeGC(display,magick_windows[i]->annotate_context,
15419                    context_mask,&context_values);
15420                  (void) XChangeGC(display,magick_windows[i]->widget_context,
15421                    context_mask,&context_values);
15422                  context_values.background=pixel->foreground_color.pixel;
15423                  context_values.foreground=pixel->background_color.pixel;
15424                  context_values.plane_mask=context_values.background ^
15425                    context_values.foreground;
15426                  (void) XChangeGC(display,magick_windows[i]->highlight_context,
15427                    (size_t) (context_mask | GCPlaneMask),
15428                    &context_values);
15429                  magick_windows[i]->attributes.background_pixel=
15430                    pixel->background_color.pixel;
15431                  magick_windows[i]->attributes.border_pixel=
15432                    pixel->border_color.pixel;
15433                  magick_windows[i]->attributes.colormap=map_info->colormap;
15434                  (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15435                    (unsigned long) magick_windows[i]->mask,
15436                    &magick_windows[i]->attributes);
15437                }
15438                if (windows->pan.mapped != MagickFalse)
15439                  {
15440                    (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15441                      windows->pan.pixmap);
15442                    (void) XClearWindow(display,windows->pan.id);
15443                    XDrawPanRectangle(display,windows);
15444                  }
15445                if (windows->backdrop.id != (Window) NULL)
15446                  (void) XInstallColormap(display,map_info->colormap);
15447                break;
15448              }
15449            if (*event.xclient.data.l == (long) windows->im_former_image)
15450              {
15451                *state|=FormerImageState | ExitState;
15452                break;
15453              }
15454            if (*event.xclient.data.l == (long) windows->im_next_image)
15455              {
15456                *state|=NextImageState | ExitState;
15457                break;
15458              }
15459            if (*event.xclient.data.l == (long) windows->im_retain_colors)
15460              {
15461                *state|=RetainColorsState;
15462                break;
15463              }
15464            if (*event.xclient.data.l == (long) windows->im_exit)
15465              {
15466                *state|=ExitState;
15467                break;
15468              }
15469            break;
15470          }
15471        if (event.xclient.message_type == windows->dnd_protocols)
15472          {
15473            Atom
15474              selection,
15475              type;
15476
15477            int
15478              format,
15479              status;
15480
15481            unsigned char
15482              *data;
15483
15484            unsigned long
15485              after,
15486              length;
15487
15488            /*
15489              Display image named by the Drag-and-Drop selection.
15490            */
15491            if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15492              break;
15493            selection=XInternAtom(display,"DndSelection",MagickFalse);
15494            status=XGetWindowProperty(display,root_window,selection,0L,(long)
15495              MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15496              &length,&after,&data);
15497            if ((status != Success) || (length == 0))
15498              break;
15499            if (*event.xclient.data.l == 2)
15500              {
15501                /*
15502                  Offix DND.
15503                */
15504                (void) CopyMagickString(resource_info->image_info->filename,
15505                  (char *) data,MaxTextExtent);
15506              }
15507            else
15508              {
15509                /*
15510                  XDND.
15511                */
15512                if (strncmp((char *) data, "file:", 5) != 0)
15513                  {
15514                    (void) XFree((void *) data);
15515                    break;
15516                  }
15517                (void) CopyMagickString(resource_info->image_info->filename,
15518                  ((char *) data)+5,MaxTextExtent);
15519              }
15520            nexus=ReadImage(resource_info->image_info,exception);
15521            CatchException(exception);
15522            if (nexus != (Image *) NULL)
15523              *state|=NextImageState | ExitState;
15524            (void) XFree((void *) data);
15525            break;
15526          }
15527        /*
15528          If client window delete message, exit.
15529        */
15530        if (event.xclient.message_type != windows->wm_protocols)
15531          break;
15532        if (*event.xclient.data.l != (long) windows->wm_delete_window)
15533          break;
15534        (void) XWithdrawWindow(display,event.xclient.window,
15535          visual_info->screen);
15536        if (event.xclient.window == windows->image.id)
15537          {
15538            *state|=ExitState;
15539            break;
15540          }
15541        if (event.xclient.window == windows->pan.id)
15542          {
15543            /*
15544              Restore original image size when pan window is deleted.
15545            */
15546            windows->image.window_changes.width=windows->image.ximage->width;
15547            windows->image.window_changes.height=windows->image.ximage->height;
15548            (void) XConfigureImage(display,resource_info,windows,
15549              display_image,exception);
15550          }
15551        break;
15552      }
15553      case ConfigureNotify:
15554      {
15555        if (display_image->debug != MagickFalse)
15556          (void) LogMagickEvent(X11Event,GetMagickModule(),
15557            "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15558            event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15559            event.xconfigure.y,event.xconfigure.send_event);
15560        if (event.xconfigure.window == windows->image.id)
15561          {
15562            /*
15563              Image window has a new configuration.
15564            */
15565            if (event.xconfigure.send_event != 0)
15566              {
15567                XWindowChanges
15568                  window_changes;
15569
15570                /*
15571                  Position the transient windows relative of the Image window.
15572                */
15573                if (windows->command.geometry == (char *) NULL)
15574                  if (windows->command.mapped == MagickFalse)
15575                    {
15576                      windows->command.x=event.xconfigure.x-
15577                        windows->command.width-25;
15578                      windows->command.y=event.xconfigure.y;
15579                      XConstrainWindowPosition(display,&windows->command);
15580                      window_changes.x=windows->command.x;
15581                      window_changes.y=windows->command.y;
15582                      (void) XReconfigureWMWindow(display,windows->command.id,
15583                        windows->command.screen,(unsigned int) (CWX | CWY),
15584                        &window_changes);
15585                    }
15586                if (windows->widget.geometry == (char *) NULL)
15587                  if (windows->widget.mapped == MagickFalse)
15588                    {
15589                      windows->widget.x=event.xconfigure.x+
15590                        event.xconfigure.width/10;
15591                      windows->widget.y=event.xconfigure.y+
15592                        event.xconfigure.height/10;
15593                      XConstrainWindowPosition(display,&windows->widget);
15594                      window_changes.x=windows->widget.x;
15595                      window_changes.y=windows->widget.y;
15596                      (void) XReconfigureWMWindow(display,windows->widget.id,
15597                        windows->widget.screen,(unsigned int) (CWX | CWY),
15598                        &window_changes);
15599                    }
15600                if (windows->magnify.geometry == (char *) NULL)
15601                  if (windows->magnify.mapped == MagickFalse)
15602                    {
15603                      windows->magnify.x=event.xconfigure.x+
15604                        event.xconfigure.width+25;
15605                      windows->magnify.y=event.xconfigure.y;
15606                      XConstrainWindowPosition(display,&windows->magnify);
15607                      window_changes.x=windows->magnify.x;
15608                      window_changes.y=windows->magnify.y;
15609                      (void) XReconfigureWMWindow(display,windows->magnify.id,
15610                        windows->magnify.screen,(unsigned int) (CWX | CWY),
15611                        &window_changes);
15612                    }
15613                if (windows->pan.geometry == (char *) NULL)
15614                  if (windows->pan.mapped == MagickFalse)
15615                    {
15616                      windows->pan.x=event.xconfigure.x+
15617                        event.xconfigure.width+25;
15618                      windows->pan.y=event.xconfigure.y+
15619                        windows->magnify.height+50;
15620                      XConstrainWindowPosition(display,&windows->pan);
15621                      window_changes.x=windows->pan.x;
15622                      window_changes.y=windows->pan.y;
15623                      (void) XReconfigureWMWindow(display,windows->pan.id,
15624                        windows->pan.screen,(unsigned int) (CWX | CWY),
15625                        &window_changes);
15626                    }
15627              }
15628            if ((event.xconfigure.width == (int) windows->image.width) &&
15629                (event.xconfigure.height == (int) windows->image.height))
15630              break;
15631            windows->image.width=(unsigned int) event.xconfigure.width;
15632            windows->image.height=(unsigned int) event.xconfigure.height;
15633            windows->image.x=0;
15634            windows->image.y=0;
15635            if (display_image->montage != (char *) NULL)
15636              {
15637                windows->image.x=vid_info.x;
15638                windows->image.y=vid_info.y;
15639              }
15640            if ((windows->image.mapped != MagickFalse) &&
15641                (windows->image.stasis != MagickFalse))
15642              {
15643                /*
15644                  Update image window configuration.
15645                */
15646                windows->image.window_changes.width=event.xconfigure.width;
15647                windows->image.window_changes.height=event.xconfigure.height;
15648                (void) XConfigureImage(display,resource_info,windows,
15649                  display_image,exception);
15650              }
15651            /*
15652              Update pan window configuration.
15653            */
15654            if ((event.xconfigure.width < windows->image.ximage->width) ||
15655                (event.xconfigure.height < windows->image.ximage->height))
15656              {
15657                (void) XMapRaised(display,windows->pan.id);
15658                XDrawPanRectangle(display,windows);
15659              }
15660            else
15661              if (windows->pan.mapped != MagickFalse)
15662                (void) XWithdrawWindow(display,windows->pan.id,
15663                  windows->pan.screen);
15664            break;
15665          }
15666        if (event.xconfigure.window == windows->magnify.id)
15667          {
15668            unsigned int
15669              magnify;
15670
15671            /*
15672              Magnify window has a new configuration.
15673            */
15674            windows->magnify.width=(unsigned int) event.xconfigure.width;
15675            windows->magnify.height=(unsigned int) event.xconfigure.height;
15676            if (windows->magnify.mapped == MagickFalse)
15677              break;
15678            magnify=1;
15679            while ((int) magnify <= event.xconfigure.width)
15680              magnify<<=1;
15681            while ((int) magnify <= event.xconfigure.height)
15682              magnify<<=1;
15683            magnify>>=1;
15684            if (((int) magnify != event.xconfigure.width) ||
15685                ((int) magnify != event.xconfigure.height))
15686              {
15687                window_changes.width=(int) magnify;
15688                window_changes.height=(int) magnify;
15689                (void) XReconfigureWMWindow(display,windows->magnify.id,
15690                  windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15691                  &window_changes);
15692                break;
15693              }
15694            if ((windows->magnify.mapped != MagickFalse) &&
15695                (windows->magnify.stasis != MagickFalse))
15696              {
15697                status=XMakeImage(display,resource_info,&windows->magnify,
15698                  display_image,windows->magnify.width,windows->magnify.height,
15699                  exception);
15700                XMakeMagnifyImage(display,windows,exception);
15701              }
15702            break;
15703          }
15704        if ((windows->magnify.mapped != MagickFalse) &&
15705            (event.xconfigure.window == windows->pan.id))
15706          {
15707            /*
15708              Pan icon window has a new configuration.
15709            */
15710            if (event.xconfigure.send_event != 0)
15711              {
15712                windows->pan.x=event.xconfigure.x;
15713                windows->pan.y=event.xconfigure.y;
15714              }
15715            windows->pan.width=(unsigned int) event.xconfigure.width;
15716            windows->pan.height=(unsigned int) event.xconfigure.height;
15717            break;
15718          }
15719        if (event.xconfigure.window == windows->icon.id)
15720          {
15721            /*
15722              Icon window has a new configuration.
15723            */
15724            windows->icon.width=(unsigned int) event.xconfigure.width;
15725            windows->icon.height=(unsigned int) event.xconfigure.height;
15726            break;
15727          }
15728        break;
15729      }
15730      case DestroyNotify:
15731      {
15732        /*
15733          Group leader has exited.
15734        */
15735        if (display_image->debug != MagickFalse)
15736          (void) LogMagickEvent(X11Event,GetMagickModule(),
15737            "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15738        if (event.xdestroywindow.window == windows->group_leader.id)
15739          {
15740            *state|=ExitState;
15741            break;
15742          }
15743        break;
15744      }
15745      case EnterNotify:
15746      {
15747        /*
15748          Selectively install colormap.
15749        */
15750        if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15751          if (event.xcrossing.mode != NotifyUngrab)
15752            XInstallColormap(display,map_info->colormap);
15753        break;
15754      }
15755      case Expose:
15756      {
15757        if (display_image->debug != MagickFalse)
15758          (void) LogMagickEvent(X11Event,GetMagickModule(),
15759            "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15760            event.xexpose.width,event.xexpose.height,event.xexpose.x,
15761            event.xexpose.y);
15762        /*
15763          Refresh windows that are now exposed.
15764        */
15765        if ((event.xexpose.window == windows->image.id) &&
15766            (windows->image.mapped != MagickFalse))
15767          {
15768            XRefreshWindow(display,&windows->image,&event);
15769            delay=display_image->delay/MagickMax(
15770              display_image->ticks_per_second,1L);
15771            timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15772            break;
15773          }
15774        if ((event.xexpose.window == windows->magnify.id) &&
15775            (windows->magnify.mapped != MagickFalse))
15776          {
15777            XMakeMagnifyImage(display,windows,exception);
15778            break;
15779          }
15780        if (event.xexpose.window == windows->pan.id)
15781          {
15782            XDrawPanRectangle(display,windows);
15783            break;
15784          }
15785        if (event.xexpose.window == windows->icon.id)
15786          {
15787            XRefreshWindow(display,&windows->icon,&event);
15788            break;
15789          }
15790        break;
15791      }
15792      case KeyPress:
15793      {
15794        int
15795          length;
15796
15797        /*
15798          Respond to a user key press.
15799        */
15800        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15801          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15802        *(command+length)='\0';
15803        if (display_image->debug != MagickFalse)
15804          (void) LogMagickEvent(X11Event,GetMagickModule(),
15805            "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15806            key_symbol,command);
15807        if (event.xkey.window == windows->image.id)
15808          {
15809            command_type=XImageWindowCommand(display,resource_info,windows,
15810              event.xkey.state,key_symbol,&display_image,exception);
15811            if (command_type != NullCommand)
15812              nexus=XMagickCommand(display,resource_info,windows,command_type,
15813                &display_image,exception);
15814          }
15815        if (event.xkey.window == windows->magnify.id)
15816          XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol,
15817            exception);
15818        if (event.xkey.window == windows->pan.id)
15819          {
15820            if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15821              (void) XWithdrawWindow(display,windows->pan.id,
15822                windows->pan.screen);
15823            else
15824              if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15825                XTextViewWidget(display,resource_info,windows,MagickFalse,
15826                  "Help Viewer - Image Pan",ImagePanHelp);
15827              else
15828                XTranslateImage(display,windows,*image,key_symbol);
15829          }
15830        delay=display_image->delay/MagickMax(
15831          display_image->ticks_per_second,1L);
15832        timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15833        break;
15834      }
15835      case KeyRelease:
15836      {
15837        /*
15838          Respond to a user key release.
15839        */
15840        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15841          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15842        if (display_image->debug != MagickFalse)
15843          (void) LogMagickEvent(X11Event,GetMagickModule(),
15844            "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15845        break;
15846      }
15847      case LeaveNotify:
15848      {
15849        /*
15850          Selectively uninstall colormap.
15851        */
15852        if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15853          if (event.xcrossing.mode != NotifyUngrab)
15854            XUninstallColormap(display,map_info->colormap);
15855        break;
15856      }
15857      case MapNotify:
15858      {
15859        if (display_image->debug != MagickFalse)
15860          (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15861            event.xmap.window);
15862        if (event.xmap.window == windows->backdrop.id)
15863          {
15864            (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15865              CurrentTime);
15866            windows->backdrop.mapped=MagickTrue;
15867            break;
15868          }
15869        if (event.xmap.window == windows->image.id)
15870          {
15871            if (windows->backdrop.id != (Window) NULL)
15872              (void) XInstallColormap(display,map_info->colormap);
15873            if (LocaleCompare(display_image->magick,"LOGO") == 0)
15874              {
15875                if (LocaleCompare(display_image->filename,"LOGO") == 0)
15876                  nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15877              }
15878            if (((int) windows->image.width < windows->image.ximage->width) ||
15879                ((int) windows->image.height < windows->image.ximage->height))
15880              (void) XMapRaised(display,windows->pan.id);
15881            windows->image.mapped=MagickTrue;
15882            break;
15883          }
15884        if (event.xmap.window == windows->magnify.id)
15885          {
15886            XMakeMagnifyImage(display,windows,exception);
15887            windows->magnify.mapped=MagickTrue;
15888            (void) XWithdrawWindow(display,windows->info.id,
15889              windows->info.screen);
15890            break;
15891          }
15892        if (event.xmap.window == windows->pan.id)
15893          {
15894            XMakePanImage(display,resource_info,windows,display_image,
15895              exception);
15896            windows->pan.mapped=MagickTrue;
15897            break;
15898          }
15899        if (event.xmap.window == windows->info.id)
15900          {
15901            windows->info.mapped=MagickTrue;
15902            break;
15903          }
15904        if (event.xmap.window == windows->icon.id)
15905          {
15906            MagickBooleanType
15907              taint;
15908
15909            /*
15910              Create an icon image.
15911            */
15912            taint=display_image->taint;
15913            XMakeStandardColormap(display,icon_visual,icon_resources,
15914              display_image,icon_map,icon_pixel,exception);
15915            (void) XMakeImage(display,icon_resources,&windows->icon,
15916              display_image,windows->icon.width,windows->icon.height,
15917              exception);
15918            display_image->taint=taint;
15919            (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15920              windows->icon.pixmap);
15921            (void) XClearWindow(display,windows->icon.id);
15922            (void) XWithdrawWindow(display,windows->info.id,
15923              windows->info.screen);
15924            windows->icon.mapped=MagickTrue;
15925            break;
15926          }
15927        if (event.xmap.window == windows->command.id)
15928          {
15929            windows->command.mapped=MagickTrue;
15930            break;
15931          }
15932        if (event.xmap.window == windows->popup.id)
15933          {
15934            windows->popup.mapped=MagickTrue;
15935            break;
15936          }
15937        if (event.xmap.window == windows->widget.id)
15938          {
15939            windows->widget.mapped=MagickTrue;
15940            break;
15941          }
15942        break;
15943      }
15944      case MappingNotify:
15945      {
15946        (void) XRefreshKeyboardMapping(&event.xmapping);
15947        break;
15948      }
15949      case NoExpose:
15950        break;
15951      case PropertyNotify:
15952      {
15953        Atom
15954          type;
15955
15956        int
15957          format,
15958          status;
15959
15960        unsigned char
15961          *data;
15962
15963        unsigned long
15964          after,
15965          length;
15966
15967        if (display_image->debug != MagickFalse)
15968          (void) LogMagickEvent(X11Event,GetMagickModule(),
15969            "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15970            event.xproperty.atom,event.xproperty.state);
15971        if (event.xproperty.atom != windows->im_remote_command)
15972          break;
15973        /*
15974          Display image named by the remote command protocol.
15975        */
15976        status=XGetWindowProperty(display,event.xproperty.window,
15977          event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15978          AnyPropertyType,&type,&format,&length,&after,&data);
15979        if ((status != Success) || (length == 0))
15980          break;
15981        if (LocaleCompare((char *) data,"-quit") == 0)
15982          {
15983            XClientMessage(display,windows->image.id,windows->im_protocols,
15984              windows->im_exit,CurrentTime);
15985            (void) XFree((void *) data);
15986            break;
15987          }
15988        (void) CopyMagickString(resource_info->image_info->filename,
15989          (char *) data,MaxTextExtent);
15990        (void) XFree((void *) data);
15991        nexus=ReadImage(resource_info->image_info,exception);
15992        CatchException(exception);
15993        if (nexus != (Image *) NULL)
15994          *state|=NextImageState | ExitState;
15995        break;
15996      }
15997      case ReparentNotify:
15998      {
15999        if (display_image->debug != MagickFalse)
16000          (void) LogMagickEvent(X11Event,GetMagickModule(),
16001            "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
16002            event.xreparent.window);
16003        break;
16004      }
16005      case UnmapNotify:
16006      {
16007        if (display_image->debug != MagickFalse)
16008          (void) LogMagickEvent(X11Event,GetMagickModule(),
16009            "Unmap Notify: 0x%lx",event.xunmap.window);
16010        if (event.xunmap.window == windows->backdrop.id)
16011          {
16012            windows->backdrop.mapped=MagickFalse;
16013            break;
16014          }
16015        if (event.xunmap.window == windows->image.id)
16016          {
16017            windows->image.mapped=MagickFalse;
16018            break;
16019          }
16020        if (event.xunmap.window == windows->magnify.id)
16021          {
16022            windows->magnify.mapped=MagickFalse;
16023            break;
16024          }
16025        if (event.xunmap.window == windows->pan.id)
16026          {
16027            windows->pan.mapped=MagickFalse;
16028            break;
16029          }
16030        if (event.xunmap.window == windows->info.id)
16031          {
16032            windows->info.mapped=MagickFalse;
16033            break;
16034          }
16035        if (event.xunmap.window == windows->icon.id)
16036          {
16037            if (map_info->colormap == icon_map->colormap)
16038              XConfigureImageColormap(display,resource_info,windows,
16039                display_image,exception);
16040            (void) XFreeStandardColormap(display,icon_visual,icon_map,
16041              icon_pixel);
16042            windows->icon.mapped=MagickFalse;
16043            break;
16044          }
16045        if (event.xunmap.window == windows->command.id)
16046          {
16047            windows->command.mapped=MagickFalse;
16048            break;
16049          }
16050        if (event.xunmap.window == windows->popup.id)
16051          {
16052            if (windows->backdrop.id != (Window) NULL)
16053              (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16054                CurrentTime);
16055            windows->popup.mapped=MagickFalse;
16056            break;
16057          }
16058        if (event.xunmap.window == windows->widget.id)
16059          {
16060            if (windows->backdrop.id != (Window) NULL)
16061              (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16062                CurrentTime);
16063            windows->widget.mapped=MagickFalse;
16064            break;
16065          }
16066        break;
16067      }
16068      default:
16069      {
16070        if (display_image->debug != MagickFalse)
16071          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
16072            event.type);
16073        break;
16074      }
16075    }
16076  } while (!(*state & ExitState));
16077  if ((*state & ExitState) == 0)
16078    (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
16079      &display_image,exception);
16080  else
16081    if (resource_info->confirm_edit != MagickFalse)
16082      {
16083        /*
16084          Query user if image has changed.
16085        */
16086        if ((resource_info->immutable == MagickFalse) &&
16087            (display_image->taint != MagickFalse))
16088          {
16089            int
16090              status;
16091
16092            status=XConfirmWidget(display,windows,"Your image changed.",
16093              "Do you want to save it");
16094            if (status == 0)
16095              *state&=(~ExitState);
16096            else
16097              if (status > 0)
16098                (void) XMagickCommand(display,resource_info,windows,SaveCommand,
16099                  &display_image,exception);
16100          }
16101      }
16102  if ((windows->visual_info->klass == GrayScale) ||
16103      (windows->visual_info->klass == PseudoColor) ||
16104      (windows->visual_info->klass == DirectColor))
16105    {
16106      /*
16107        Withdraw pan and Magnify window.
16108      */
16109      if (windows->info.mapped != MagickFalse)
16110        (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16111      if (windows->magnify.mapped != MagickFalse)
16112        (void) XWithdrawWindow(display,windows->magnify.id,
16113          windows->magnify.screen);
16114      if (windows->command.mapped != MagickFalse)
16115        (void) XWithdrawWindow(display,windows->command.id,
16116          windows->command.screen);
16117    }
16118  if (windows->pan.mapped != MagickFalse)
16119    (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16120  if (resource_info->backdrop == MagickFalse)
16121    if (windows->backdrop.mapped)
16122      {
16123        (void) XWithdrawWindow(display,windows->backdrop.id,
16124          windows->backdrop.screen);
16125        (void) XDestroyWindow(display,windows->backdrop.id);
16126        windows->backdrop.id=(Window) NULL;
16127        (void) XWithdrawWindow(display,windows->image.id,
16128          windows->image.screen);
16129        (void) XDestroyWindow(display,windows->image.id);
16130        windows->image.id=(Window) NULL;
16131      }
16132  XSetCursorState(display,windows,MagickTrue);
16133  XCheckRefreshWindows(display,windows);
16134  if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16135    *state&=(~ExitState);
16136  if (*state & ExitState)
16137    {
16138      /*
16139        Free Standard Colormap.
16140      */
16141      (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16142      if (resource_info->map_type == (char *) NULL)
16143        (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16144      /*
16145        Free X resources.
16146      */
16147      if (resource_info->copy_image != (Image *) NULL)
16148        {
16149          resource_info->copy_image=DestroyImage(resource_info->copy_image);
16150          resource_info->copy_image=NewImageList();
16151        }
16152      DestroyXResources();
16153    }
16154  (void) XSync(display,MagickFalse);
16155  /*
16156    Restore our progress monitor and warning handlers.
16157  */
16158  (void) SetErrorHandler(warning_handler);
16159  (void) SetWarningHandler(warning_handler);
16160  /*
16161    Change to home directory.
16162  */
16163  directory=getcwd(working_directory,MaxTextExtent);
16164  (void) directory;
16165  {
16166    int
16167      status;
16168
16169    status=chdir(resource_info->home_directory);
16170    if (status == -1)
16171      (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
16172        "UnableToOpenFile","%s",resource_info->home_directory);
16173  }
16174  *image=display_image;
16175  return(nexus);
16176}
16177#else
16178
16179/*
16180%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16181%                                                                             %
16182%                                                                             %
16183%                                                                             %
16184+   D i s p l a y I m a g e s                                                 %
16185%                                                                             %
16186%                                                                             %
16187%                                                                             %
16188%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16189%
16190%  DisplayImages() displays an image sequence to any X window screen.  It
16191%  returns a value other than 0 if successful.  Check the exception member
16192%  of image to determine the reason for any failure.
16193%
16194%  The format of the DisplayImages method is:
16195%
16196%      MagickBooleanType DisplayImages(const ImageInfo *image_info,
16197%        Image *images,ExceptionInfo *exception)
16198%
16199%  A description of each parameter follows:
16200%
16201%    o image_info: the image info.
16202%
16203%    o image: the image.
16204%
16205%    o exception: return any errors or warnings in this structure.
16206%
16207*/
16208MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16209  Image *image,ExceptionInfo *exception)
16210{
16211  assert(image_info != (const ImageInfo *) NULL);
16212  assert(image_info->signature == MagickSignature);
16213  assert(image != (Image *) NULL);
16214  assert(image->signature == MagickSignature);
16215  if (image->debug != MagickFalse)
16216    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16217  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16218    "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image->filename);
16219  return(MagickFalse);
16220}
16221
16222/*
16223%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16224%                                                                             %
16225%                                                                             %
16226%                                                                             %
16227+   R e m o t e D i s p l a y C o m m a n d                                   %
16228%                                                                             %
16229%                                                                             %
16230%                                                                             %
16231%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16232%
16233%  RemoteDisplayCommand() encourages a remote display program to display the
16234%  specified image filename.
16235%
16236%  The format of the RemoteDisplayCommand method is:
16237%
16238%      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16239%        const char *window,const char *filename,ExceptionInfo *exception)
16240%
16241%  A description of each parameter follows:
16242%
16243%    o image_info: the image info.
16244%
16245%    o window: Specifies the name or id of an X window.
16246%
16247%    o filename: the name of the image filename to display.
16248%
16249%    o exception: return any errors or warnings in this structure.
16250%
16251*/
16252MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16253  const char *window,const char *filename,ExceptionInfo *exception)
16254{
16255  assert(image_info != (const ImageInfo *) NULL);
16256  assert(image_info->signature == MagickSignature);
16257  assert(filename != (char *) NULL);
16258  (void) window;
16259  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16260  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16261    "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image_info->filename);
16262  return(MagickFalse);
16263}
16264#endif
16265