display.c revision 5f95f4f77efc46ff53593d750491c8f60698c983
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/draw.h"
57#include "MagickCore/effect.h"
58#include "MagickCore/enhance.h"
59#include "MagickCore/exception.h"
60#include "MagickCore/exception-private.h"
61#include "MagickCore/fx.h"
62#include "MagickCore/geometry.h"
63#include "MagickCore/image.h"
64#include "MagickCore/image-private.h"
65#include "MagickCore/list.h"
66#include "MagickCore/log.h"
67#include "MagickCore/magick.h"
68#include "MagickCore/memory_.h"
69#include "MagickCore/monitor.h"
70#include "MagickCore/monitor-private.h"
71#include "MagickCore/montage.h"
72#include "MagickCore/option.h"
73#include "MagickCore/paint.h"
74#include "MagickCore/pixel.h"
75#include "MagickCore/pixel-accessor.h"
76#include "MagickCore/PreRvIcccm.h"
77#include "MagickCore/property.h"
78#include "MagickCore/quantum.h"
79#include "MagickCore/quantum-private.h"
80#include "MagickCore/resize.h"
81#include "MagickCore/resource_.h"
82#include "MagickCore/shear.h"
83#include "MagickCore/segment.h"
84#include "MagickCore/string_.h"
85#include "MagickCore/string-private.h"
86#include "MagickCore/transform.h"
87#include "MagickCore/threshold.h"
88#include "MagickCore/utility.h"
89#include "MagickCore/utility-private.h"
90#include "MagickCore/version.h"
91#include "MagickCore/widget.h"
92#include "MagickCore/widget-private.h"
93#include "MagickCore/xwindow.h"
94#include "MagickCore/xwindow-private.h"
95
96#if defined(MAGICKCORE_X11_DELEGATE)
97/*
98  Define declarations.
99*/
100#define MaxColors  MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
101
102/*
103  Constant declarations.
104*/
105static const unsigned char
106  HighlightBitmap[8] =
107  {
108    0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
109  },
110  OpaqueBitmap[8] =
111  {
112    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
113  },
114  ShadowBitmap[8] =
115  {
116    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
117  };
118
119static const char
120  *PageSizes[] =
121  {
122    "Letter",
123    "Tabloid",
124    "Ledger",
125    "Legal",
126    "Statement",
127    "Executive",
128    "A3",
129    "A4",
130    "A5",
131    "B4",
132    "B5",
133    "Folio",
134    "Quarto",
135    "10x14",
136    (char *) NULL
137  };
138
139/*
140  Help widget declarations.
141*/
142static const char
143  *ImageAnnotateHelp[] =
144  {
145    "In annotate mode, the Command widget has these options:",
146    "",
147    "    Font Name",
148    "      fixed",
149    "      variable",
150    "      5x8",
151    "      6x10",
152    "      7x13bold",
153    "      8x13bold",
154    "      9x15bold",
155    "      10x20",
156    "      12x24",
157    "      Browser...",
158    "    Font Color",
159    "      black",
160    "      blue",
161    "      cyan",
162    "      green",
163    "      gray",
164    "      red",
165    "      magenta",
166    "      yellow",
167    "      white",
168    "      transparent",
169    "      Browser...",
170    "    Font Color",
171    "      black",
172    "      blue",
173    "      cyan",
174    "      green",
175    "      gray",
176    "      red",
177    "      magenta",
178    "      yellow",
179    "      white",
180    "      transparent",
181    "      Browser...",
182    "    Rotate Text",
183    "      -90",
184    "      -45",
185    "      -30",
186    "      0",
187    "      30",
188    "      45",
189    "      90",
190    "      180",
191    "      Dialog...",
192    "    Help",
193    "    Dismiss",
194    "",
195    "Choose a font name from the Font Name sub-menu.  Additional",
196    "font names can be specified with the font browser.  You can",
197    "change the menu names by setting the X resources font1",
198    "through font9.",
199    "",
200    "Choose a font color from the Font Color sub-menu.",
201    "Additional font colors can be specified with the color",
202    "browser.  You can change the menu colors by setting the X",
203    "resources pen1 through pen9.",
204    "",
205    "If you select the color browser and press Grab, you can",
206    "choose the font color by moving the pointer to the desired",
207    "color on the screen and press any button.",
208    "",
209    "If you choose to rotate the text, choose Rotate Text from the",
210    "menu and select an angle.  Typically you will only want to",
211    "rotate one line of text at a time.  Depending on the angle you",
212    "choose, subsequent lines may end up overwriting each other.",
213    "",
214    "Choosing a font and its color is optional.  The default font",
215    "is fixed and the default color is black.  However, you must",
216    "choose a location to begin entering text and press button 1.",
217    "An underscore character will appear at the location of the",
218    "pointer.  The cursor changes to a pencil to indicate you are",
219    "in text mode.  To exit immediately, press Dismiss.",
220    "",
221    "In text mode, any key presses will display the character at",
222    "the location of the underscore and advance the underscore",
223    "cursor.  Enter your text and once completed press Apply to",
224    "finish your image annotation.  To correct errors press BACK",
225    "SPACE.  To delete an entire line of text, press DELETE.  Any",
226    "text that exceeds the boundaries of the image window is",
227    "automagically continued onto the next line.",
228    "",
229    "The actual color you request for the font is saved in the",
230    "image.  However, the color that appears in your image window",
231    "may be different.  For example, on a monochrome screen the",
232    "text will appear black or white even if you choose the color",
233    "red as the font color.  However, the image saved to a file",
234    "with -write is written with red lettering.  To assure the",
235    "correct color text in the final image, any PseudoClass image",
236    "is promoted to DirectClass (see miff(5)).  To force a",
237    "PseudoClass image to remain PseudoClass, use -colors.",
238    (char *) NULL,
239  },
240  *ImageChopHelp[] =
241  {
242    "In chop mode, the Command widget has these options:",
243    "",
244    "    Direction",
245    "      horizontal",
246    "      vertical",
247    "    Help",
248    "    Dismiss",
249    "",
250    "If the you choose the horizontal direction (this the",
251    "default), the area of the image between the two horizontal",
252    "endpoints of the chop line is removed.  Otherwise, the area",
253    "of the image between the two vertical endpoints of the chop",
254    "line is removed.",
255    "",
256    "Select a location within the image window to begin your chop,",
257    "press and hold any button.  Next, move the pointer to",
258    "another location in the image.  As you move a line will",
259    "connect the initial location and the pointer.  When you",
260    "release the button, the area within the image to chop is",
261    "determined by which direction you choose from the Command",
262    "widget.",
263    "",
264    "To cancel the image chopping, move the pointer back to the",
265    "starting point of the line and release the button.",
266    (char *) NULL,
267  },
268  *ImageColorEditHelp[] =
269  {
270    "In color edit mode, the Command widget has these options:",
271    "",
272    "    Method",
273    "      point",
274    "      replace",
275    "      floodfill",
276    "      filltoborder",
277    "      reset",
278    "    Pixel Color",
279    "      black",
280    "      blue",
281    "      cyan",
282    "      green",
283    "      gray",
284    "      red",
285    "      magenta",
286    "      yellow",
287    "      white",
288    "      Browser...",
289    "    Border Color",
290    "      black",
291    "      blue",
292    "      cyan",
293    "      green",
294    "      gray",
295    "      red",
296    "      magenta",
297    "      yellow",
298    "      white",
299    "      Browser...",
300    "    Fuzz",
301    "      0%",
302    "      2%",
303    "      5%",
304    "      10%",
305    "      15%",
306    "      Dialog...",
307    "    Undo",
308    "    Help",
309    "    Dismiss",
310    "",
311    "Choose a color editing method from the Method sub-menu",
312    "of the Command widget.  The point method recolors any pixel",
313    "selected with the pointer until the button is released.  The",
314    "replace method recolors any pixel that matches the color of",
315    "the pixel you select with a button press.  Floodfill recolors",
316    "any pixel that matches the color of the pixel you select with",
317    "a button press and is a neighbor.  Whereas filltoborder recolors",
318    "any neighbor pixel that is not the border color.  Finally reset",
319    "changes the entire image to the designated color.",
320    "",
321    "Next, choose a pixel color from the Pixel Color sub-menu.",
322    "Additional pixel colors can be specified with the color",
323    "browser.  You can change the menu colors by setting the X",
324    "resources pen1 through pen9.",
325    "",
326    "Now press button 1 to select a pixel within the image window",
327    "to change its color.  Additional pixels may be recolored as",
328    "prescribed by the method you choose.",
329    "",
330    "If the Magnify widget is mapped, it can be helpful in positioning",
331    "your pointer within the image (refer to button 2).",
332    "",
333    "The actual color you request for the pixels is saved in the",
334    "image.  However, the color that appears in your image window",
335    "may be different.  For example, on a monochrome screen the",
336    "pixel will appear black or white even if you choose the",
337    "color red as the pixel color.  However, the image saved to a",
338    "file with -write is written with red pixels.  To assure the",
339    "correct color text in the final image, any PseudoClass image",
340    "is promoted to DirectClass (see miff(5)).  To force a",
341    "PseudoClass image to remain PseudoClass, use -colors.",
342    (char *) NULL,
343  },
344  *ImageCompositeHelp[] =
345  {
346    "First a widget window is displayed requesting you to enter an",
347    "image name. Press Composite, Grab or type a file name.",
348    "Press Cancel if you choose not to create a composite image.",
349    "When you choose Grab, move the pointer to the desired window",
350    "and press any button.",
351    "",
352    "If the Composite image does not have any matte information,",
353    "you are informed and the file browser is displayed again.",
354    "Enter the name of a mask image.  The image is typically",
355    "grayscale and the same size as the composite image.  If the",
356    "image is not grayscale, it is converted to grayscale and the",
357    "resulting intensities are used as matte information.",
358    "",
359    "A small window appears showing the location of the cursor in",
360    "the image window. You are now in composite mode.  To exit",
361    "immediately, press Dismiss.  In composite mode, the Command",
362    "widget has these options:",
363    "",
364    "    Operators",
365    "      Over",
366    "      In",
367    "      Out",
368    "      Atop",
369    "      Xor",
370    "      Plus",
371    "      Minus",
372    "      Add",
373    "      Subtract",
374    "      Difference",
375    "      Multiply",
376    "      Bumpmap",
377    "      Copy",
378    "      CopyRed",
379    "      CopyGreen",
380    "      CopyBlue",
381    "      CopyOpacity",
382    "      Clear",
383    "    Dissolve",
384    "    Displace",
385    "    Help",
386    "    Dismiss",
387    "",
388    "Choose a composite operation from the Operators sub-menu of",
389    "the Command widget.  How each operator behaves is described",
390    "below.  Image window is the image currently displayed on",
391    "your X server and image is the image obtained with the File",
392    "Browser widget.",
393    "",
394    "Over     The result is the union of the two image shapes,",
395    "         with image obscuring image window in the region of",
396    "         overlap.",
397    "",
398    "In       The result is simply image cut by the shape of",
399    "         image window.  None of the image data of image",
400    "         window is in the result.",
401    "",
402    "Out      The resulting image is image with the shape of",
403    "         image window cut out.",
404    "",
405    "Atop     The result is the same shape as image image window,",
406    "         with image obscuring image window where the image",
407    "         shapes overlap.  Note this differs from over",
408    "         because the portion of image outside image window's",
409    "         shape does not appear in the result.",
410    "",
411    "Xor      The result is the image data from both image and",
412    "         image window that is outside the overlap region.",
413    "         The overlap region is blank.",
414    "",
415    "Plus     The result is just the sum of the image data.",
416    "         Output values are cropped to QuantumRange (no overflow).",
417    "",
418    "Minus    The result of image - image window, with underflow",
419    "         cropped to zero.",
420    "",
421    "Add      The result of image + image window, with overflow",
422    "         wrapping around (mod 256).",
423    "",
424    "Subtract The result of image - image window, with underflow",
425    "         wrapping around (mod 256).  The add and subtract",
426    "         operators can be used to perform reversible",
427    "         transformations.",
428    "",
429    "Difference",
430    "         The result of abs(image - image window).  This",
431    "         useful for comparing two very similar images.",
432    "",
433    "Multiply",
434    "         The result of image * image window.  This",
435    "         useful for the creation of drop-shadows.",
436    "",
437    "Bumpmap  The result of surface normals from image * image",
438    "         window.",
439    "",
440    "Copy     The resulting image is image window replaced with",
441    "         image.  Here the matte information is ignored.",
442    "",
443    "CopyRed  The red layer of the image window is replace with",
444    "         the red layer of the image.  The other layers are",
445    "         untouched.",
446    "",
447    "CopyGreen",
448    "         The green layer of the image window is replace with",
449    "         the green layer of the image.  The other layers are",
450    "         untouched.",
451    "",
452    "CopyBlue The blue layer of the image window is replace with",
453    "         the blue layer of the image.  The other layers are",
454    "         untouched.",
455    "",
456    "CopyOpacity",
457    "         The matte layer of the image window is replace with",
458    "         the matte layer of the image.  The other layers are",
459    "         untouched.",
460    "",
461    "The image compositor requires a matte, or alpha channel in",
462    "the image for some operations.  This extra channel usually",
463    "defines a mask which represents a sort of a cookie-cutter",
464    "for the image.  This the case when matte is opaque (full",
465    "coverage) for pixels inside the shape, zero outside, and",
466    "between 0 and QuantumRange on the boundary.  If image does not",
467    "have a matte channel, it is initialized with 0 for any pixel",
468    "matching in color to pixel location (0,0), otherwise QuantumRange.",
469    "",
470    "If you choose Dissolve, the composite operator becomes Over.  The",
471    "image matte channel percent transparency is initialized to factor.",
472    "The image window is initialized to (100-factor). Where factor is the",
473    "value you specify in the Dialog widget.",
474    "",
475    "Displace shifts the image pixels as defined by a displacement",
476    "map.  With this option, image is used as a displacement map.",
477    "Black, within the displacement map, is a maximum positive",
478    "displacement.  White is a maximum negative displacement and",
479    "middle gray is neutral.  The displacement is scaled to determine",
480    "the pixel shift.  By default, the displacement applies in both the",
481    "horizontal and vertical directions.  However, if you specify a mask,",
482    "image is the horizontal X displacement and mask the vertical Y",
483    "displacement.",
484    "",
485    "Note that matte information for image window is not retained",
486    "for colormapped X server visuals (e.g. StaticColor,",
487    "StaticColor, GrayScale, PseudoColor).  Correct compositing",
488    "behavior may require a TrueColor or DirectColor visual or a",
489    "Standard Colormap.",
490    "",
491    "Choosing a composite operator is optional.  The default",
492    "operator is replace.  However, you must choose a location to",
493    "composite your image and press button 1.  Press and hold the",
494    "button before releasing and an outline of the image will",
495    "appear to help you identify your location.",
496    "",
497    "The actual colors of the composite image is saved.  However,",
498    "the color that appears in image window may be different.",
499    "For example, on a monochrome screen image window will appear",
500    "black or white even though your composited image may have",
501    "many colors.  If the image is saved to a file it is written",
502    "with the correct colors.  To assure the correct colors are",
503    "saved in the final image, any PseudoClass image is promoted",
504    "to DirectClass (see miff(5)).  To force a PseudoClass image",
505    "to remain PseudoClass, use -colors.",
506    (char *) NULL,
507  },
508  *ImageCutHelp[] =
509  {
510    "In cut mode, the Command widget has these options:",
511    "",
512    "    Help",
513    "    Dismiss",
514    "",
515    "To define a cut region, press button 1 and drag.  The",
516    "cut region is defined by a highlighted rectangle that",
517    "expands or contracts as it follows the pointer.  Once you",
518    "are satisfied with the cut region, release the button.",
519    "You are now in rectify mode.  In rectify mode, the Command",
520    "widget has these options:",
521    "",
522    "    Cut",
523    "    Help",
524    "    Dismiss",
525    "",
526    "You can make adjustments by moving the pointer to one of the",
527    "cut rectangle corners, pressing a button, and dragging.",
528    "Finally, press Cut to commit your copy region.  To",
529    "exit without cutting the image, press Dismiss.",
530    (char *) NULL,
531  },
532  *ImageCopyHelp[] =
533  {
534    "In copy mode, the Command widget has these options:",
535    "",
536    "    Help",
537    "    Dismiss",
538    "",
539    "To define a copy region, press button 1 and drag.  The",
540    "copy region is defined by a highlighted rectangle that",
541    "expands or contracts as it follows the pointer.  Once you",
542    "are satisfied with the copy region, release the button.",
543    "You are now in rectify mode.  In rectify mode, the Command",
544    "widget has these options:",
545    "",
546    "    Copy",
547    "    Help",
548    "    Dismiss",
549    "",
550    "You can make adjustments by moving the pointer to one of the",
551    "copy rectangle corners, pressing a button, and dragging.",
552    "Finally, press Copy to commit your copy region.  To",
553    "exit without copying the image, press Dismiss.",
554    (char *) NULL,
555  },
556  *ImageCropHelp[] =
557  {
558    "In crop mode, the Command widget has these options:",
559    "",
560    "    Help",
561    "    Dismiss",
562    "",
563    "To define a cropping region, press button 1 and drag.  The",
564    "cropping region is defined by a highlighted rectangle that",
565    "expands or contracts as it follows the pointer.  Once you",
566    "are satisfied with the cropping region, release the button.",
567    "You are now in rectify mode.  In rectify mode, the Command",
568    "widget has these options:",
569    "",
570    "    Crop",
571    "    Help",
572    "    Dismiss",
573    "",
574    "You can make adjustments by moving the pointer to one of the",
575    "cropping rectangle corners, pressing a button, and dragging.",
576    "Finally, press Crop to commit your cropping region.  To",
577    "exit without cropping the image, press Dismiss.",
578    (char *) NULL,
579  },
580  *ImageDrawHelp[] =
581  {
582    "The cursor changes to a crosshair to indicate you are in",
583    "draw mode.  To exit immediately, press Dismiss.  In draw mode,",
584    "the Command widget has these options:",
585    "",
586    "    Element",
587    "      point",
588    "      line",
589    "      rectangle",
590    "      fill rectangle",
591    "      circle",
592    "      fill circle",
593    "      ellipse",
594    "      fill ellipse",
595    "      polygon",
596    "      fill polygon",
597    "    Color",
598    "      black",
599    "      blue",
600    "      cyan",
601    "      green",
602    "      gray",
603    "      red",
604    "      magenta",
605    "      yellow",
606    "      white",
607    "      transparent",
608    "      Browser...",
609    "    Stipple",
610    "      Brick",
611    "      Diagonal",
612    "      Scales",
613    "      Vertical",
614    "      Wavy",
615    "      Translucent",
616    "      Opaque",
617    "      Open...",
618    "    Width",
619    "      1",
620    "      2",
621    "      4",
622    "      8",
623    "      16",
624    "      Dialog...",
625    "    Undo",
626    "    Help",
627    "    Dismiss",
628    "",
629    "Choose a drawing primitive from the Element sub-menu.",
630    "",
631    "Choose a color from the Color sub-menu.  Additional",
632    "colors can be specified with the color browser.",
633    "",
634    "If you choose the color browser and press Grab, you can",
635    "select the color by moving the pointer to the desired",
636    "color on the screen and press any button.  The transparent",
637    "color updates the image matte channel and is useful for",
638    "image compositing.",
639    "",
640    "Choose a stipple, if appropriate, from the Stipple sub-menu.",
641    "Additional stipples can be specified with the file browser.",
642    "Stipples obtained from the file browser must be on disk in the",
643    "X11 bitmap format.",
644    "",
645    "Choose a width, if appropriate, from the Width sub-menu.  To",
646    "choose a specific width select the Dialog widget.",
647    "",
648    "Choose a point in the Image window and press button 1 and",
649    "hold.  Next, move the pointer to another location in the",
650    "image.  As you move, a line connects the initial location and",
651    "the pointer.  When you release the button, the image is",
652    "updated with the primitive you just drew.  For polygons, the",
653    "image is updated when you press and release the button without",
654    "moving the pointer.",
655    "",
656    "To cancel image drawing, move the pointer back to the",
657    "starting point of the line and release the button.",
658    (char *) NULL,
659  },
660  *DisplayHelp[] =
661  {
662    "BUTTONS",
663    "  The effects of each button press is described below.  Three",
664    "  buttons are required.  If you have a two button mouse,",
665    "  button 1 and 3 are returned.  Press ALT and button 3 to",
666    "  simulate button 2.",
667    "",
668    "  1    Press this button to map or unmap the Command widget.",
669    "",
670    "  2    Press and drag to define a region of the image to",
671    "       magnify.",
672    "",
673    "  3    Press and drag to choose from a select set of commands.",
674    "       This button behaves differently if the image being",
675    "       displayed is a visual image directory.  Here, choose a",
676    "       particular tile of the directory and press this button and",
677    "       drag to select a command from a pop-up menu.  Choose from",
678    "       these menu items:",
679    "",
680    "           Open",
681    "           Next",
682    "           Former",
683    "           Delete",
684    "           Update",
685    "",
686    "       If you choose Open, the image represented by the tile is",
687    "       displayed.  To return to the visual image directory, choose",
688    "       Next from the Command widget.  Next and Former moves to the",
689    "       next or former image respectively.  Choose Delete to delete",
690    "       a particular image tile.  Finally, choose Update to",
691    "       synchronize all the image tiles with their respective",
692    "       images.",
693    "",
694    "COMMAND WIDGET",
695    "  The Command widget lists a number of sub-menus and commands.",
696    "  They are",
697    "",
698    "      File",
699    "        Open...",
700    "        Next",
701    "        Former",
702    "        Select...",
703    "        Save...",
704    "        Print...",
705    "        Delete...",
706    "        New...",
707    "        Visual Directory...",
708    "        Quit",
709    "      Edit",
710    "        Undo",
711    "        Redo",
712    "        Cut",
713    "        Copy",
714    "        Paste",
715    "      View",
716    "        Half Size",
717    "        Original Size",
718    "        Double Size",
719    "        Resize...",
720    "        Apply",
721    "        Refresh",
722    "        Restore",
723    "      Transform",
724    "        Crop",
725    "        Chop",
726    "        Flop",
727    "        Flip",
728    "        Rotate Right",
729    "        Rotate Left",
730    "        Rotate...",
731    "        Shear...",
732    "        Roll...",
733    "        Trim Edges",
734    "      Enhance",
735    "        Brightness...",
736    "        Saturation...",
737    "        Hue...",
738    "        Gamma...",
739    "        Sharpen...",
740    "        Dull",
741    "        Contrast Stretch...",
742    "        Sigmoidal Contrast...",
743    "        Normalize",
744    "        Equalize",
745    "        Negate",
746    "        Grayscale",
747    "        Map...",
748    "        Quantize...",
749    "      Effects",
750    "        Despeckle",
751    "        Emboss",
752    "        Reduce Noise",
753    "        Add Noise",
754    "        Sharpen...",
755    "        Blur...",
756    "        Threshold...",
757    "        Edge Detect...",
758    "        Spread...",
759    "        Shade...",
760    "        Painting...",
761    "        Segment...",
762    "      F/X",
763    "        Solarize...",
764    "        Sepia Tone...",
765    "        Swirl...",
766    "        Implode...",
767    "        Vignette...",
768    "        Wave...",
769    "        Oil Painting...",
770    "        Charcoal Drawing...",
771    "      Image Edit",
772    "        Annotate...",
773    "        Draw...",
774    "        Color...",
775    "        Matte...",
776    "        Composite...",
777    "        Add Border...",
778    "        Add Frame...",
779    "        Comment...",
780    "        Launch...",
781    "        Region of Interest...",
782    "      Miscellany",
783    "        Image Info",
784    "        Zoom Image",
785    "        Show Preview...",
786    "        Show Histogram",
787    "        Show Matte",
788    "        Background...",
789    "        Slide Show",
790    "        Preferences...",
791    "      Help",
792    "        Overview",
793    "        Browse Documentation",
794    "        About Display",
795    "",
796    "  Menu items with a indented triangle have a sub-menu.  They",
797    "  are represented above as the indented items.  To access a",
798    "  sub-menu item, move the pointer to the appropriate menu and",
799    "  press a button and drag.  When you find the desired sub-menu",
800    "  item, release the button and the command is executed.  Move",
801    "  the pointer away from the sub-menu if you decide not to",
802    "  execute a particular command.",
803    "",
804    "KEYBOARD ACCELERATORS",
805    "  Accelerators are one or two key presses that effect a",
806    "  particular command.  The keyboard accelerators that",
807    "  display(1) understands is:",
808    "",
809    "  Ctl+O     Press to open an image from a file.",
810    "",
811    "  space     Press to display the next image.",
812    "",
813    "            If the image is a multi-paged document such as a Postscript",
814    "            document, you can skip ahead several pages by preceding",
815    "            this command with a number.  For example to display the",
816    "            third page beyond the current page, press 3<space>.",
817    "",
818    "  backspace Press to display the former image.",
819    "",
820    "            If the image is a multi-paged document such as a Postscript",
821    "            document, you can skip behind several pages by preceding",
822    "            this command with a number.  For example to display the",
823    "            third page preceding the current page, press 3<backspace>.",
824    "",
825    "  Ctl+S     Press to write the image to a file.",
826    "",
827    "  Ctl+P     Press to print the image to a Postscript printer.",
828    "",
829    "  Ctl+D     Press to delete an image file.",
830    "",
831    "  Ctl+N     Press to create a blank canvas.",
832    "",
833    "  Ctl+Q     Press to discard all images and exit program.",
834    "",
835    "  Ctl+Z     Press to undo last image transformation.",
836    "",
837    "  Ctl+R     Press to redo last image transformation.",
838    "",
839    "  Ctl+X     Press to cut a region of the image.",
840    "",
841    "  Ctl+C     Press to copy a region of the image.",
842    "",
843    "  Ctl+V     Press to paste a region to the image.",
844    "",
845    "  <         Press to half the image size.",
846    "",
847    "  -         Press to return to the original image size.",
848    "",
849    "  >         Press to double the image size.",
850    "",
851    "  %         Press to resize the image to a width and height you",
852    "            specify.",
853    "",
854    "Cmd-A       Press to make any image transformations permanent."
855    "",
856    "            By default, any image size transformations are applied",
857    "            to the original image to create the image displayed on",
858    "            the X server.  However, the transformations are not",
859    "            permanent (i.e. the original image does not change",
860    "            size only the X image does).  For example, if you",
861    "            press > the X image will appear to double in size,",
862    "            but the original image will in fact remain the same size.",
863    "            To force the original image to double in size, press >",
864    "            followed by Cmd-A.",
865    "",
866    "  @         Press to refresh the image window.",
867    "",
868    "  C         Press to cut out a rectangular region of the image.",
869    "",
870    "  [         Press to chop the image.",
871    "",
872    "  H         Press to flop image in the horizontal direction.",
873    "",
874    "  V         Press to flip image in the vertical direction.",
875    "",
876    "  /         Press to rotate the image 90 degrees clockwise.",
877    "",
878    " \\         Press to rotate the image 90 degrees counter-clockwise.",
879    "",
880    "  *         Press to rotate the image the number of degrees you",
881    "            specify.",
882    "",
883    "  S         Press to shear the image the number of degrees you",
884    "            specify.",
885    "",
886    "  R         Press to roll the image.",
887    "",
888    "  T         Press to trim the image edges.",
889    "",
890    "  Shft-H    Press to vary the image hue.",
891    "",
892    "  Shft-S    Press to vary the color saturation.",
893    "",
894    "  Shft-L    Press to vary the color brightness.",
895    "",
896    "  Shft-G    Press to gamma correct the image.",
897    "",
898    "  Shft-C    Press to sharpen the image contrast.",
899    "",
900    "  Shft-Z    Press to dull the image contrast.",
901    "",
902    "  =         Press to perform histogram equalization on the image.",
903    "",
904    "  Shft-N    Press to perform histogram normalization on the image.",
905    "",
906    "  Shft-~    Press to negate the colors of the image.",
907    "",
908    "  .         Press to convert the image colors to gray.",
909    "",
910    "  Shft-#    Press to set the maximum number of unique colors in the",
911    "            image.",
912    "",
913    "  F2        Press to reduce the speckles in an image.",
914    "",
915    "  F3        Press to eliminate peak noise from an image.",
916    "",
917    "  F4        Press to add noise to an image.",
918    "",
919    "  F5        Press to sharpen an image.",
920    "",
921    "  F6        Press to delete an image file.",
922    "",
923    "  F7        Press to threshold the image.",
924    "",
925    "  F8        Press to detect edges within an image.",
926    "",
927    "  F9        Press to emboss an image.",
928    "",
929    "  F10       Press to displace pixels by a random amount.",
930    "",
931    "  F11       Press to negate all pixels above the threshold level.",
932    "",
933    "  F12       Press to shade the image using a distant light source.",
934    "",
935    "  F13       Press to lighten or darken image edges to create a 3-D effect.",
936    "",
937    "  F14       Press to segment the image by color.",
938    "",
939    "  Meta-S    Press to swirl image pixels about the center.",
940    "",
941    "  Meta-I    Press to implode image pixels about the center.",
942    "",
943    "  Meta-W    Press to alter an image along a sine wave.",
944    "",
945    "  Meta-P    Press to simulate an oil painting.",
946    "",
947    "  Meta-C    Press to simulate a charcoal drawing.",
948    "",
949    "  Alt-A     Press to annotate the image with text.",
950    "",
951    "  Alt-D     Press to draw on an image.",
952    "",
953    "  Alt-P     Press to edit an image pixel color.",
954    "",
955    "  Alt-M     Press to edit the image matte information.",
956    "",
957    "  Alt-V     Press to composite the image with another.",
958    "",
959    "  Alt-B     Press to add a border to the image.",
960    "",
961    "  Alt-F     Press to add an ornamental border to the image.",
962    "",
963    "  Alt-Shft-!",
964    "            Press to add an image comment.",
965    "",
966    "  Ctl-A     Press to apply image processing techniques to a region",
967    "            of interest.",
968    "",
969    "  Shft-?    Press to display information about the image.",
970    "",
971    "  Shft-+    Press to map the zoom image window.",
972    "",
973    "  Shft-P    Press to preview an image enhancement, effect, or f/x.",
974    "",
975    "  F1        Press to display helpful information about display(1).",
976    "",
977    "  Find      Press to browse documentation about ImageMagick.",
978    "",
979    "  1-9       Press to change the level of magnification.",
980    "",
981    "  Use the arrow keys to move the image one pixel up, down,",
982    "  left, or right within the magnify window.  Be sure to first",
983    "  map the magnify window by pressing button 2.",
984    "",
985    "  Press ALT and one of the arrow keys to trim off one pixel",
986    "  from any side of the image.",
987    (char *) NULL,
988  },
989  *ImageMatteEditHelp[] =
990  {
991    "Matte information within an image is useful for some",
992    "operations such as image compositing (See IMAGE",
993    "COMPOSITING).  This extra channel usually defines a mask",
994    "which represents a sort of a cookie-cutter for the image.",
995    "This the case when matte is opaque (full coverage) for",
996    "pixels inside the shape, zero outside, and between 0 and",
997    "QuantumRange on the boundary.",
998    "",
999    "A small window appears showing the location of the cursor in",
1000    "the image window. You are now in matte edit mode.  To exit",
1001    "immediately, press Dismiss.  In matte edit mode, the Command",
1002    "widget has these options:",
1003    "",
1004    "    Method",
1005    "      point",
1006    "      replace",
1007    "      floodfill",
1008    "      filltoborder",
1009    "      reset",
1010    "    Border Color",
1011    "      black",
1012    "      blue",
1013    "      cyan",
1014    "      green",
1015    "      gray",
1016    "      red",
1017    "      magenta",
1018    "      yellow",
1019    "      white",
1020    "      Browser...",
1021    "    Fuzz",
1022    "      0%",
1023    "      2%",
1024    "      5%",
1025    "      10%",
1026    "      15%",
1027    "      Dialog...",
1028    "    Matte",
1029    "      Opaque",
1030    "      Transparent",
1031    "      Dialog...",
1032    "    Undo",
1033    "    Help",
1034    "    Dismiss",
1035    "",
1036    "Choose a matte editing method from the Method sub-menu of",
1037    "the Command widget.  The point method changes the matte value",
1038    "of any pixel selected with the pointer until the button is",
1039    "is released.  The replace method changes the matte value of",
1040    "any pixel that matches the color of the pixel you select with",
1041    "a button press.  Floodfill changes the matte value of any pixel",
1042    "that matches the color of the pixel you select with a button",
1043    "press and is a neighbor.  Whereas filltoborder changes the matte",
1044    "value any neighbor pixel that is not the border color.  Finally",
1045    "reset changes the entire image to the designated matte value.",
1046    "",
1047    "Choose Matte Value and pick Opaque or Transarent.  For other values",
1048    "select the Dialog entry.  Here a dialog appears requesting a matte",
1049    "value.  The value you select is assigned as the opacity value of the",
1050    "selected pixel or pixels.",
1051    "",
1052    "Now, press any button to select a pixel within the image",
1053    "window to change its matte value.",
1054    "",
1055    "If the Magnify widget is mapped, it can be helpful in positioning",
1056    "your pointer within the image (refer to button 2).",
1057    "",
1058    "Matte information is only valid in a DirectClass image.",
1059    "Therefore, any PseudoClass image is promoted to DirectClass",
1060    "(see miff(5)).  Note that matte information for PseudoClass",
1061    "is not retained for colormapped X server visuals (e.g.",
1062    "StaticColor, StaticColor, GrayScale, PseudoColor) unless you",
1063    "immediately save your image to a file (refer to Write).",
1064    "Correct matte editing behavior may require a TrueColor or",
1065    "DirectColor visual or a Standard Colormap.",
1066    (char *) NULL,
1067  },
1068  *ImagePanHelp[] =
1069  {
1070    "When an image exceeds the width or height of the X server",
1071    "screen, display maps a small panning icon.  The rectangle",
1072    "within the panning icon shows the area that is currently",
1073    "displayed in the image window.  To pan about the image,",
1074    "press any button and drag the pointer within the panning",
1075    "icon.  The pan rectangle moves with the pointer and the",
1076    "image window is updated to reflect the location of the",
1077    "rectangle within the panning icon.  When you have selected",
1078    "the area of the image you wish to view, release the button.",
1079    "",
1080    "Use the arrow keys to pan the image one pixel up, down,",
1081    "left, or right within the image window.",
1082    "",
1083    "The panning icon is withdrawn if the image becomes smaller",
1084    "than the dimensions of the X server screen.",
1085    (char *) NULL,
1086  },
1087  *ImagePasteHelp[] =
1088  {
1089    "A small window appears showing the location of the cursor in",
1090    "the image window. You are now in paste mode.  To exit",
1091    "immediately, press Dismiss.  In paste mode, the Command",
1092    "widget has these options:",
1093    "",
1094    "    Operators",
1095    "      over",
1096    "      in",
1097    "      out",
1098    "      atop",
1099    "      xor",
1100    "      plus",
1101    "      minus",
1102    "      add",
1103    "      subtract",
1104    "      difference",
1105    "      replace",
1106    "    Help",
1107    "    Dismiss",
1108    "",
1109    "Choose a composite operation from the Operators sub-menu of",
1110    "the Command widget.  How each operator behaves is described",
1111    "below.  Image window is the image currently displayed on",
1112    "your X server and image is the image obtained with the File",
1113    "Browser widget.",
1114    "",
1115    "Over     The result is the union of the two image shapes,",
1116    "         with image obscuring image window in the region of",
1117    "         overlap.",
1118    "",
1119    "In       The result is simply image cut by the shape of",
1120    "         image window.  None of the image data of image",
1121    "         window is in the result.",
1122    "",
1123    "Out      The resulting image is image with the shape of",
1124    "         image window cut out.",
1125    "",
1126    "Atop     The result is the same shape as image image window,",
1127    "         with image obscuring image window where the image",
1128    "         shapes overlap.  Note this differs from over",
1129    "         because the portion of image outside image window's",
1130    "         shape does not appear in the result.",
1131    "",
1132    "Xor      The result is the image data from both image and",
1133    "         image window that is outside the overlap region.",
1134    "         The overlap region is blank.",
1135    "",
1136    "Plus     The result is just the sum of the image data.",
1137    "         Output values are cropped to QuantumRange (no overflow).",
1138    "         This operation is independent of the matte",
1139    "         channels.",
1140    "",
1141    "Minus    The result of image - image window, with underflow",
1142    "         cropped to zero.",
1143    "",
1144    "Add      The result of image + image window, with overflow",
1145    "         wrapping around (mod 256).",
1146    "",
1147    "Subtract The result of image - image window, with underflow",
1148    "         wrapping around (mod 256).  The add and subtract",
1149    "         operators can be used to perform reversible",
1150    "         transformations.",
1151    "",
1152    "Difference",
1153    "         The result of abs(image - image window).  This",
1154    "         useful for comparing two very similar images.",
1155    "",
1156    "Copy     The resulting image is image window replaced with",
1157    "         image.  Here the matte information is ignored.",
1158    "",
1159    "CopyRed  The red layer of the image window is replace with",
1160    "         the red layer of the image.  The other layers are",
1161    "         untouched.",
1162    "",
1163    "CopyGreen",
1164    "         The green layer of the image window is replace with",
1165    "         the green layer of the image.  The other layers are",
1166    "         untouched.",
1167    "",
1168    "CopyBlue The blue layer of the image window is replace with",
1169    "         the blue layer of the image.  The other layers are",
1170    "         untouched.",
1171    "",
1172    "CopyOpacity",
1173    "         The matte layer of the image window is replace with",
1174    "         the matte layer of the image.  The other layers are",
1175    "         untouched.",
1176    "",
1177    "The image compositor requires a matte, or alpha channel in",
1178    "the image for some operations.  This extra channel usually",
1179    "defines a mask which represents a sort of a cookie-cutter",
1180    "for the image.  This the case when matte is opaque (full",
1181    "coverage) for pixels inside the shape, zero outside, and",
1182    "between 0 and QuantumRange on the boundary.  If image does not",
1183    "have a matte channel, it is initialized with 0 for any pixel",
1184    "matching in color to pixel location (0,0), otherwise QuantumRange.",
1185    "",
1186    "Note that matte information for image window is not retained",
1187    "for colormapped X server visuals (e.g. StaticColor,",
1188    "StaticColor, GrayScale, PseudoColor).  Correct compositing",
1189    "behavior may require a TrueColor or DirectColor visual or a",
1190    "Standard Colormap.",
1191    "",
1192    "Choosing a composite operator is optional.  The default",
1193    "operator is replace.  However, you must choose a location to",
1194    "paste your image and press button 1.  Press and hold the",
1195    "button before releasing and an outline of the image will",
1196    "appear to help you identify your location.",
1197    "",
1198    "The actual colors of the pasted image is saved.  However,",
1199    "the color that appears in image window may be different.",
1200    "For example, on a monochrome screen image window will appear",
1201    "black or white even though your pasted image may have",
1202    "many colors.  If the image is saved to a file it is written",
1203    "with the correct colors.  To assure the correct colors are",
1204    "saved in the final image, any PseudoClass image is promoted",
1205    "to DirectClass (see miff(5)).  To force a PseudoClass image",
1206    "to remain PseudoClass, use -colors.",
1207    (char *) NULL,
1208  },
1209  *ImageROIHelp[] =
1210  {
1211    "In region of interest mode, the Command widget has these",
1212    "options:",
1213    "",
1214    "    Help",
1215    "    Dismiss",
1216    "",
1217    "To define a region of interest, press button 1 and drag.",
1218    "The region of interest is defined by a highlighted rectangle",
1219    "that expands or contracts as it follows the pointer.  Once",
1220    "you are satisfied with the region of interest, release the",
1221    "button.  You are now in apply mode.  In apply mode the",
1222    "Command widget has these options:",
1223    "",
1224    "      File",
1225    "        Save...",
1226    "        Print...",
1227    "      Edit",
1228    "        Undo",
1229    "        Redo",
1230    "      Transform",
1231    "        Flop",
1232    "        Flip",
1233    "        Rotate Right",
1234    "        Rotate Left",
1235    "      Enhance",
1236    "        Hue...",
1237    "        Saturation...",
1238    "        Brightness...",
1239    "        Gamma...",
1240    "        Spiff",
1241    "        Dull",
1242    "        Contrast Stretch",
1243    "        Sigmoidal Contrast...",
1244    "        Normalize",
1245    "        Equalize",
1246    "        Negate",
1247    "        Grayscale",
1248    "        Map...",
1249    "        Quantize...",
1250    "      Effects",
1251    "        Despeckle",
1252    "        Emboss",
1253    "        Reduce Noise",
1254    "        Sharpen...",
1255    "        Blur...",
1256    "        Threshold...",
1257    "        Edge Detect...",
1258    "        Spread...",
1259    "        Shade...",
1260    "        Raise...",
1261    "        Segment...",
1262    "      F/X",
1263    "        Solarize...",
1264    "        Sepia Tone...",
1265    "        Swirl...",
1266    "        Implode...",
1267    "        Vignette...",
1268    "        Wave...",
1269    "        Oil Painting...",
1270    "        Charcoal Drawing...",
1271    "      Miscellany",
1272    "        Image Info",
1273    "        Zoom Image",
1274    "        Show Preview...",
1275    "        Show Histogram",
1276    "        Show Matte",
1277    "      Help",
1278    "      Dismiss",
1279    "",
1280    "You can make adjustments to the region of interest by moving",
1281    "the pointer to one of the rectangle corners, pressing a",
1282    "button, and dragging.  Finally, choose an image processing",
1283    "technique from the Command widget.  You can choose more than",
1284    "one image processing technique to apply to an area.",
1285    "Alternatively, you can move the region of interest before",
1286    "applying another image processing technique.  To exit, press",
1287    "Dismiss.",
1288    (char *) NULL,
1289  },
1290  *ImageRotateHelp[] =
1291  {
1292    "In rotate mode, the Command widget has these options:",
1293    "",
1294    "    Pixel Color",
1295    "      black",
1296    "      blue",
1297    "      cyan",
1298    "      green",
1299    "      gray",
1300    "      red",
1301    "      magenta",
1302    "      yellow",
1303    "      white",
1304    "      Browser...",
1305    "    Direction",
1306    "      horizontal",
1307    "      vertical",
1308    "    Help",
1309    "    Dismiss",
1310    "",
1311    "Choose a background color from the Pixel Color sub-menu.",
1312    "Additional background colors can be specified with the color",
1313    "browser.  You can change the menu colors by setting the X",
1314    "resources pen1 through pen9.",
1315    "",
1316    "If you choose the color browser and press Grab, you can",
1317    "select the background color by moving the pointer to the",
1318    "desired color on the screen and press any button.",
1319    "",
1320    "Choose a point in the image window and press this button and",
1321    "hold.  Next, move the pointer to another location in the",
1322    "image.  As you move a line connects the initial location and",
1323    "the pointer.  When you release the button, the degree of",
1324    "image rotation is determined by the slope of the line you",
1325    "just drew.  The slope is relative to the direction you",
1326    "choose from the Direction sub-menu of the Command widget.",
1327    "",
1328    "To cancel the image rotation, move the pointer back to the",
1329    "starting point of the line and release the button.",
1330    (char *) NULL,
1331  };
1332
1333/*
1334  Enumeration declarations.
1335*/
1336typedef enum
1337{
1338  CopyMode,
1339  CropMode,
1340  CutMode
1341} ClipboardMode;
1342
1343typedef enum
1344{
1345  OpenCommand,
1346  NextCommand,
1347  FormerCommand,
1348  SelectCommand,
1349  SaveCommand,
1350  PrintCommand,
1351  DeleteCommand,
1352  NewCommand,
1353  VisualDirectoryCommand,
1354  QuitCommand,
1355  UndoCommand,
1356  RedoCommand,
1357  CutCommand,
1358  CopyCommand,
1359  PasteCommand,
1360  HalfSizeCommand,
1361  OriginalSizeCommand,
1362  DoubleSizeCommand,
1363  ResizeCommand,
1364  ApplyCommand,
1365  RefreshCommand,
1366  RestoreCommand,
1367  CropCommand,
1368  ChopCommand,
1369  FlopCommand,
1370  FlipCommand,
1371  RotateRightCommand,
1372  RotateLeftCommand,
1373  RotateCommand,
1374  ShearCommand,
1375  RollCommand,
1376  TrimCommand,
1377  HueCommand,
1378  SaturationCommand,
1379  BrightnessCommand,
1380  GammaCommand,
1381  SpiffCommand,
1382  DullCommand,
1383  ContrastStretchCommand,
1384  SigmoidalContrastCommand,
1385  NormalizeCommand,
1386  EqualizeCommand,
1387  NegateCommand,
1388  GrayscaleCommand,
1389  MapCommand,
1390  QuantizeCommand,
1391  DespeckleCommand,
1392  EmbossCommand,
1393  ReduceNoiseCommand,
1394  AddNoiseCommand,
1395  SharpenCommand,
1396  BlurCommand,
1397  ThresholdCommand,
1398  EdgeDetectCommand,
1399  SpreadCommand,
1400  ShadeCommand,
1401  RaiseCommand,
1402  SegmentCommand,
1403  SolarizeCommand,
1404  SepiaToneCommand,
1405  SwirlCommand,
1406  ImplodeCommand,
1407  VignetteCommand,
1408  WaveCommand,
1409  OilPaintCommand,
1410  CharcoalDrawCommand,
1411  AnnotateCommand,
1412  DrawCommand,
1413  ColorCommand,
1414  MatteCommand,
1415  CompositeCommand,
1416  AddBorderCommand,
1417  AddFrameCommand,
1418  CommentCommand,
1419  LaunchCommand,
1420  RegionofInterestCommand,
1421  ROIHelpCommand,
1422  ROIDismissCommand,
1423  InfoCommand,
1424  ZoomCommand,
1425  ShowPreviewCommand,
1426  ShowHistogramCommand,
1427  ShowMatteCommand,
1428  BackgroundCommand,
1429  SlideShowCommand,
1430  PreferencesCommand,
1431  HelpCommand,
1432  BrowseDocumentationCommand,
1433  VersionCommand,
1434  SaveToUndoBufferCommand,
1435  FreeBuffersCommand,
1436  NullCommand
1437} CommandType;
1438
1439typedef enum
1440{
1441  AnnotateNameCommand,
1442  AnnotateFontColorCommand,
1443  AnnotateBackgroundColorCommand,
1444  AnnotateRotateCommand,
1445  AnnotateHelpCommand,
1446  AnnotateDismissCommand,
1447  TextHelpCommand,
1448  TextApplyCommand,
1449  ChopDirectionCommand,
1450  ChopHelpCommand,
1451  ChopDismissCommand,
1452  HorizontalChopCommand,
1453  VerticalChopCommand,
1454  ColorEditMethodCommand,
1455  ColorEditColorCommand,
1456  ColorEditBorderCommand,
1457  ColorEditFuzzCommand,
1458  ColorEditUndoCommand,
1459  ColorEditHelpCommand,
1460  ColorEditDismissCommand,
1461  CompositeOperatorsCommand,
1462  CompositeDissolveCommand,
1463  CompositeDisplaceCommand,
1464  CompositeHelpCommand,
1465  CompositeDismissCommand,
1466  CropHelpCommand,
1467  CropDismissCommand,
1468  RectifyCopyCommand,
1469  RectifyHelpCommand,
1470  RectifyDismissCommand,
1471  DrawElementCommand,
1472  DrawColorCommand,
1473  DrawStippleCommand,
1474  DrawWidthCommand,
1475  DrawUndoCommand,
1476  DrawHelpCommand,
1477  DrawDismissCommand,
1478  MatteEditMethod,
1479  MatteEditBorderCommand,
1480  MatteEditFuzzCommand,
1481  MatteEditValueCommand,
1482  MatteEditUndoCommand,
1483  MatteEditHelpCommand,
1484  MatteEditDismissCommand,
1485  PasteOperatorsCommand,
1486  PasteHelpCommand,
1487  PasteDismissCommand,
1488  RotateColorCommand,
1489  RotateDirectionCommand,
1490  RotateCropCommand,
1491  RotateSharpenCommand,
1492  RotateHelpCommand,
1493  RotateDismissCommand,
1494  HorizontalRotateCommand,
1495  VerticalRotateCommand,
1496  TileLoadCommand,
1497  TileNextCommand,
1498  TileFormerCommand,
1499  TileDeleteCommand,
1500  TileUpdateCommand
1501} ModeType;
1502
1503/*
1504  Stipples.
1505*/
1506#define BricksWidth  20
1507#define BricksHeight  20
1508#define DiagonalWidth  16
1509#define DiagonalHeight  16
1510#define HighlightWidth  8
1511#define HighlightHeight  8
1512#define OpaqueWidth  8
1513#define OpaqueHeight  8
1514#define ScalesWidth  16
1515#define ScalesHeight  16
1516#define ShadowWidth  8
1517#define ShadowHeight  8
1518#define VerticalWidth  16
1519#define VerticalHeight  16
1520#define WavyWidth  16
1521#define WavyHeight  16
1522
1523/*
1524  Constant declaration.
1525*/
1526static const int
1527  RoiDelta = 8;
1528
1529static const unsigned char
1530  BricksBitmap[] =
1531  {
1532    0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1533    0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1534    0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1535    0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1536    0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1537  },
1538  DiagonalBitmap[] =
1539  {
1540    0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1541    0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1542    0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1543  },
1544  ScalesBitmap[] =
1545  {
1546    0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1547    0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1548    0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1549  },
1550  VerticalBitmap[] =
1551  {
1552    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1553    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1554    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1555  },
1556  WavyBitmap[] =
1557  {
1558    0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1559    0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1560    0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1561  };
1562
1563/*
1564  Function prototypes.
1565*/
1566static CommandType
1567  XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1568    const MagickStatusType,KeySym,Image **,ExceptionInfo *);
1569
1570static Image
1571  *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
1572    Image **,ExceptionInfo *),
1573  *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1574  *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *,
1575    ExceptionInfo *),
1576  *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *,
1577    ExceptionInfo *);
1578
1579static MagickBooleanType
1580  XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *,
1581    ExceptionInfo *),
1582  XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **,
1583    ExceptionInfo *),
1584  XChopImage(Display *,XResourceInfo *,XWindows *,Image **,
1585    ExceptionInfo *),
1586  XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode,
1587    ExceptionInfo *),
1588  XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1589    ExceptionInfo *),
1590  XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *,
1591    ExceptionInfo *),
1592  XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1593  XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1594    ExceptionInfo *),
1595  XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1596    ExceptionInfo *),
1597  XPasteImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1598  XPrintImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1599  XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **,
1600    ExceptionInfo *),
1601  XROIImage(Display *,XResourceInfo *,XWindows *,Image **,ExceptionInfo *),
1602  XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1603  XTrimImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
1604
1605static void
1606  XDrawPanRectangle(Display *,XWindows *),
1607  XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **,
1608    ExceptionInfo *),
1609  XMagnifyImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1610  XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1611  XPanImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1612  XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1613    const KeySym,ExceptionInfo *),
1614  XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1615  XScreenEvent(Display *,XWindows *,XEvent *,ExceptionInfo *),
1616  XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1617
1618/*
1619%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1620%                                                                             %
1621%                                                                             %
1622%                                                                             %
1623%   D i s p l a y I m a g e s                                                 %
1624%                                                                             %
1625%                                                                             %
1626%                                                                             %
1627%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1628%
1629%  DisplayImages() displays an image sequence to any X window screen.  It
1630%  returns a value other than 0 if successful.  Check the exception member
1631%  of image to determine the reason for any failure.
1632%
1633%  The format of the DisplayImages method is:
1634%
1635%      MagickBooleanType DisplayImages(const ImageInfo *image_info,
1636%        Image *images,ExceptionInfo *exception)
1637%
1638%  A description of each parameter follows:
1639%
1640%    o image_info: the image info.
1641%
1642%    o image: the image.
1643%
1644%    o exception: return any errors or warnings in this structure.
1645%
1646*/
1647MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1648  Image *images,ExceptionInfo *exception)
1649{
1650  char
1651    *argv[1];
1652
1653  Display
1654    *display;
1655
1656  Image
1657    *image;
1658
1659  register ssize_t
1660    i;
1661
1662  size_t
1663    state;
1664
1665  XrmDatabase
1666    resource_database;
1667
1668  XResourceInfo
1669    resource_info;
1670
1671  assert(image_info != (const ImageInfo *) NULL);
1672  assert(image_info->signature == MagickSignature);
1673  assert(images != (Image *) NULL);
1674  assert(images->signature == MagickSignature);
1675  if (images->debug != MagickFalse)
1676    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1677  display=XOpenDisplay(image_info->server_name);
1678  if (display == (Display *) NULL)
1679    {
1680      (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1681        "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1682      return(MagickFalse);
1683    }
1684  if (exception->severity != UndefinedException)
1685    CatchException(exception);
1686  (void) XSetErrorHandler(XError);
1687  resource_database=XGetResourceDatabase(display,GetClientName());
1688  (void) ResetMagickMemory(&resource_info,0,sizeof(resource_info));
1689  XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1690  if (image_info->page != (char *) NULL)
1691    resource_info.image_geometry=AcquireString(image_info->page);
1692  resource_info.immutable=MagickTrue;
1693  argv[0]=AcquireString(GetClientName());
1694  state=DefaultState;
1695  for (i=0; (state & ExitState) == 0; i++)
1696  {
1697    if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1698      break;
1699    image=GetImageFromList(images,i % GetImageListLength(images));
1700    (void) XDisplayImage(display,&resource_info,argv,1,&image,&state,exception);
1701  }
1702  SetErrorHandler((ErrorHandler) NULL);
1703  SetWarningHandler((WarningHandler) NULL);
1704  argv[0]=DestroyString(argv[0]);
1705  (void) XCloseDisplay(display);
1706  XDestroyResourceInfo(&resource_info);
1707  if (exception->severity != UndefinedException)
1708    return(MagickFalse);
1709  return(MagickTrue);
1710}
1711
1712/*
1713%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1714%                                                                             %
1715%                                                                             %
1716%                                                                             %
1717%   R e m o t e D i s p l a y C o m m a n d                                   %
1718%                                                                             %
1719%                                                                             %
1720%                                                                             %
1721%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1722%
1723%  RemoteDisplayCommand() encourages a remote display program to display the
1724%  specified image filename.
1725%
1726%  The format of the RemoteDisplayCommand method is:
1727%
1728%      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1729%        const char *window,const char *filename,ExceptionInfo *exception)
1730%
1731%  A description of each parameter follows:
1732%
1733%    o image_info: the image info.
1734%
1735%    o window: Specifies the name or id of an X window.
1736%
1737%    o filename: the name of the image filename to display.
1738%
1739%    o exception: return any errors or warnings in this structure.
1740%
1741*/
1742MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1743  const char *window,const char *filename,ExceptionInfo *exception)
1744{
1745  Display
1746    *display;
1747
1748  MagickStatusType
1749    status;
1750
1751  assert(image_info != (const ImageInfo *) NULL);
1752  assert(image_info->signature == MagickSignature);
1753  assert(filename != (char *) NULL);
1754  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1755  display=XOpenDisplay(image_info->server_name);
1756  if (display == (Display *) NULL)
1757    {
1758      (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1759        "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1760      return(MagickFalse);
1761    }
1762  (void) XSetErrorHandler(XError);
1763  status=XRemoteCommand(display,window,filename);
1764  (void) XCloseDisplay(display);
1765  return(status != 0 ? MagickTrue : MagickFalse);
1766}
1767
1768/*
1769%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1770%                                                                             %
1771%                                                                             %
1772%                                                                             %
1773+   X A n n o t a t e E d i t I m a g e                                       %
1774%                                                                             %
1775%                                                                             %
1776%                                                                             %
1777%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1778%
1779%  XAnnotateEditImage() annotates the image with text.
1780%
1781%  The format of the XAnnotateEditImage method is:
1782%
1783%      MagickBooleanType XAnnotateEditImage(Display *display,
1784%        XResourceInfo *resource_info,XWindows *windows,Image *image,
1785%        ExceptionInfo *exception)
1786%
1787%  A description of each parameter follows:
1788%
1789%    o display: Specifies a connection to an X server;  returned from
1790%      XOpenDisplay.
1791%
1792%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1793%
1794%    o windows: Specifies a pointer to a XWindows structure.
1795%
1796%    o image: the image; returned from ReadImage.
1797%
1798*/
1799
1800static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
1801{
1802  if (x > y)
1803    return(x);
1804  return(y);
1805}
1806
1807static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
1808{
1809  if (x < y)
1810    return(x);
1811  return(y);
1812}
1813
1814static MagickBooleanType XAnnotateEditImage(Display *display,
1815  XResourceInfo *resource_info,XWindows *windows,Image *image,
1816  ExceptionInfo *exception)
1817{
1818  static const char
1819    *AnnotateMenu[] =
1820    {
1821      "Font Name",
1822      "Font Color",
1823      "Box Color",
1824      "Rotate Text",
1825      "Help",
1826      "Dismiss",
1827      (char *) NULL
1828    },
1829    *TextMenu[] =
1830    {
1831      "Help",
1832      "Apply",
1833      (char *) NULL
1834    };
1835
1836  static const ModeType
1837    AnnotateCommands[] =
1838    {
1839      AnnotateNameCommand,
1840      AnnotateFontColorCommand,
1841      AnnotateBackgroundColorCommand,
1842      AnnotateRotateCommand,
1843      AnnotateHelpCommand,
1844      AnnotateDismissCommand
1845    },
1846    TextCommands[] =
1847    {
1848      TextHelpCommand,
1849      TextApplyCommand
1850    };
1851
1852  static MagickBooleanType
1853    transparent_box = MagickTrue,
1854    transparent_pen = MagickFalse;
1855
1856  static MagickRealType
1857    degrees = 0.0;
1858
1859  static unsigned int
1860    box_id = MaxNumberPens-2,
1861    font_id = 0,
1862    pen_id = 0;
1863
1864  char
1865    command[MaxTextExtent],
1866    text[MaxTextExtent];
1867
1868  const char
1869    *ColorMenu[MaxNumberPens+1];
1870
1871  Cursor
1872    cursor;
1873
1874  GC
1875    annotate_context;
1876
1877  int
1878    id,
1879    pen_number,
1880    status,
1881    x,
1882    y;
1883
1884  KeySym
1885    key_symbol;
1886
1887  register char
1888    *p;
1889
1890  register ssize_t
1891    i;
1892
1893  unsigned int
1894    height,
1895    width;
1896
1897  size_t
1898    state;
1899
1900  XAnnotateInfo
1901    *annotate_info,
1902    *previous_info;
1903
1904  XColor
1905    color;
1906
1907  XFontStruct
1908    *font_info;
1909
1910  XEvent
1911    event,
1912    text_event;
1913
1914  /*
1915    Map Command widget.
1916  */
1917  (void) CloneString(&windows->command.name,"Annotate");
1918  windows->command.data=4;
1919  (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1920  (void) XMapRaised(display,windows->command.id);
1921  XClientMessage(display,windows->image.id,windows->im_protocols,
1922    windows->im_update_widget,CurrentTime);
1923  /*
1924    Track pointer until button 1 is pressed.
1925  */
1926  XQueryPosition(display,windows->image.id,&x,&y);
1927  (void) XSelectInput(display,windows->image.id,
1928    windows->image.attributes.event_mask | PointerMotionMask);
1929  cursor=XCreateFontCursor(display,XC_left_side);
1930  (void) XCheckDefineCursor(display,windows->image.id,cursor);
1931  state=DefaultState;
1932  do
1933  {
1934    if (windows->info.mapped != MagickFalse)
1935      {
1936        /*
1937          Display pointer position.
1938        */
1939        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
1940          x+windows->image.x,y+windows->image.y);
1941        XInfoWidget(display,windows,text);
1942      }
1943    /*
1944      Wait for next event.
1945    */
1946    XScreenEvent(display,windows,&event,exception);
1947    if (event.xany.window == windows->command.id)
1948      {
1949        /*
1950          Select a command from the Command widget.
1951        */
1952        id=XCommandWidget(display,windows,AnnotateMenu,&event);
1953        (void) XCheckDefineCursor(display,windows->image.id,cursor);
1954        if (id < 0)
1955          continue;
1956        switch (AnnotateCommands[id])
1957        {
1958          case AnnotateNameCommand:
1959          {
1960            const char
1961              *FontMenu[MaxNumberFonts];
1962
1963            int
1964              font_number;
1965
1966            /*
1967              Initialize menu selections.
1968            */
1969            for (i=0; i < MaxNumberFonts; i++)
1970              FontMenu[i]=resource_info->font_name[i];
1971            FontMenu[MaxNumberFonts-2]="Browser...";
1972            FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1973            /*
1974              Select a font name from the pop-up menu.
1975            */
1976            font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1977              (const char **) FontMenu,command);
1978            if (font_number < 0)
1979              break;
1980            if (font_number == (MaxNumberFonts-2))
1981              {
1982                static char
1983                  font_name[MaxTextExtent] = "fixed";
1984
1985                /*
1986                  Select a font name from a browser.
1987                */
1988                resource_info->font_name[font_number]=font_name;
1989                XFontBrowserWidget(display,windows,"Select",font_name);
1990                if (*font_name == '\0')
1991                  break;
1992              }
1993            /*
1994              Initialize font info.
1995            */
1996            font_info=XLoadQueryFont(display,resource_info->font_name[
1997              font_number]);
1998            if (font_info == (XFontStruct *) NULL)
1999              {
2000                XNoticeWidget(display,windows,"Unable to load font:",
2001                  resource_info->font_name[font_number]);
2002                break;
2003              }
2004            font_id=(unsigned int) font_number;
2005            (void) XFreeFont(display,font_info);
2006            break;
2007          }
2008          case AnnotateFontColorCommand:
2009          {
2010            /*
2011              Initialize menu selections.
2012            */
2013            for (i=0; i < (int) (MaxNumberPens-2); i++)
2014              ColorMenu[i]=resource_info->pen_colors[i];
2015            ColorMenu[MaxNumberPens-2]="transparent";
2016            ColorMenu[MaxNumberPens-1]="Browser...";
2017            ColorMenu[MaxNumberPens]=(const char *) NULL;
2018            /*
2019              Select a pen color from the pop-up menu.
2020            */
2021            pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2022              (const char **) ColorMenu,command);
2023            if (pen_number < 0)
2024              break;
2025            transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
2026              MagickFalse;
2027            if (transparent_pen != MagickFalse)
2028              break;
2029            if (pen_number == (MaxNumberPens-1))
2030              {
2031                static char
2032                  color_name[MaxTextExtent] = "gray";
2033
2034                /*
2035                  Select a pen color from a dialog.
2036                */
2037                resource_info->pen_colors[pen_number]=color_name;
2038                XColorBrowserWidget(display,windows,"Select",color_name);
2039                if (*color_name == '\0')
2040                  break;
2041              }
2042            /*
2043              Set pen color.
2044            */
2045            (void) XParseColor(display,windows->map_info->colormap,
2046              resource_info->pen_colors[pen_number],&color);
2047            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2048              (unsigned int) MaxColors,&color);
2049            windows->pixel_info->pen_colors[pen_number]=color;
2050            pen_id=(unsigned int) pen_number;
2051            break;
2052          }
2053          case AnnotateBackgroundColorCommand:
2054          {
2055            /*
2056              Initialize menu selections.
2057            */
2058            for (i=0; i < (int) (MaxNumberPens-2); i++)
2059              ColorMenu[i]=resource_info->pen_colors[i];
2060            ColorMenu[MaxNumberPens-2]="transparent";
2061            ColorMenu[MaxNumberPens-1]="Browser...";
2062            ColorMenu[MaxNumberPens]=(const char *) NULL;
2063            /*
2064              Select a pen color from the pop-up menu.
2065            */
2066            pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2067              (const char **) ColorMenu,command);
2068            if (pen_number < 0)
2069              break;
2070            transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2071              MagickFalse;
2072            if (transparent_box != MagickFalse)
2073              break;
2074            if (pen_number == (MaxNumberPens-1))
2075              {
2076                static char
2077                  color_name[MaxTextExtent] = "gray";
2078
2079                /*
2080                  Select a pen color from a dialog.
2081                */
2082                resource_info->pen_colors[pen_number]=color_name;
2083                XColorBrowserWidget(display,windows,"Select",color_name);
2084                if (*color_name == '\0')
2085                  break;
2086              }
2087            /*
2088              Set pen color.
2089            */
2090            (void) XParseColor(display,windows->map_info->colormap,
2091              resource_info->pen_colors[pen_number],&color);
2092            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2093              (unsigned int) MaxColors,&color);
2094            windows->pixel_info->pen_colors[pen_number]=color;
2095            box_id=(unsigned int) pen_number;
2096            break;
2097          }
2098          case AnnotateRotateCommand:
2099          {
2100            int
2101              entry;
2102
2103            static char
2104              angle[MaxTextExtent] = "30.0";
2105
2106            static const char
2107              *RotateMenu[] =
2108              {
2109                "-90",
2110                "-45",
2111                "-30",
2112                "0",
2113                "30",
2114                "45",
2115                "90",
2116                "180",
2117                "Dialog...",
2118                (char *) NULL,
2119              };
2120
2121            /*
2122              Select a command from the pop-up menu.
2123            */
2124            entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2125              command);
2126            if (entry < 0)
2127              break;
2128            if (entry != 8)
2129              {
2130                degrees=InterpretLocaleValue(RotateMenu[entry],(char **) NULL);
2131                break;
2132              }
2133            (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2134              angle);
2135            if (*angle == '\0')
2136              break;
2137            degrees=InterpretLocaleValue(angle,(char **) NULL);
2138            break;
2139          }
2140          case AnnotateHelpCommand:
2141          {
2142            XTextViewWidget(display,resource_info,windows,MagickFalse,
2143              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2144            break;
2145          }
2146          case AnnotateDismissCommand:
2147          {
2148            /*
2149              Prematurely exit.
2150            */
2151            state|=EscapeState;
2152            state|=ExitState;
2153            break;
2154          }
2155          default:
2156            break;
2157        }
2158        continue;
2159      }
2160    switch (event.type)
2161    {
2162      case ButtonPress:
2163      {
2164        if (event.xbutton.button != Button1)
2165          break;
2166        if (event.xbutton.window != windows->image.id)
2167          break;
2168        /*
2169          Change to text entering mode.
2170        */
2171        x=event.xbutton.x;
2172        y=event.xbutton.y;
2173        state|=ExitState;
2174        break;
2175      }
2176      case ButtonRelease:
2177        break;
2178      case Expose:
2179        break;
2180      case KeyPress:
2181      {
2182        if (event.xkey.window != windows->image.id)
2183          break;
2184        /*
2185          Respond to a user key press.
2186        */
2187        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2188          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2189        switch ((int) key_symbol)
2190        {
2191          case XK_Escape:
2192          case XK_F20:
2193          {
2194            /*
2195              Prematurely exit.
2196            */
2197            state|=EscapeState;
2198            state|=ExitState;
2199            break;
2200          }
2201          case XK_F1:
2202          case XK_Help:
2203          {
2204            XTextViewWidget(display,resource_info,windows,MagickFalse,
2205              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2206            break;
2207          }
2208          default:
2209          {
2210            (void) XBell(display,0);
2211            break;
2212          }
2213        }
2214        break;
2215      }
2216      case MotionNotify:
2217      {
2218        /*
2219          Map and unmap Info widget as cursor crosses its boundaries.
2220        */
2221        x=event.xmotion.x;
2222        y=event.xmotion.y;
2223        if (windows->info.mapped != MagickFalse)
2224          {
2225            if ((x < (int) (windows->info.x+windows->info.width)) &&
2226                (y < (int) (windows->info.y+windows->info.height)))
2227              (void) XWithdrawWindow(display,windows->info.id,
2228                windows->info.screen);
2229          }
2230        else
2231          if ((x > (int) (windows->info.x+windows->info.width)) ||
2232              (y > (int) (windows->info.y+windows->info.height)))
2233            (void) XMapWindow(display,windows->info.id);
2234        break;
2235      }
2236      default:
2237        break;
2238    }
2239  } while ((state & ExitState) == 0);
2240  (void) XSelectInput(display,windows->image.id,
2241    windows->image.attributes.event_mask);
2242  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2243  if ((state & EscapeState) != 0)
2244    return(MagickTrue);
2245  /*
2246    Set font info and check boundary conditions.
2247  */
2248  font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2249  if (font_info == (XFontStruct *) NULL)
2250    {
2251      XNoticeWidget(display,windows,"Unable to load font:",
2252        resource_info->font_name[font_id]);
2253      font_info=windows->font_info;
2254    }
2255  if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2256    x=(int) windows->image.width-font_info->max_bounds.width;
2257  if (y < (int) (font_info->ascent+font_info->descent))
2258    y=(int) font_info->ascent+font_info->descent;
2259  if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2260      ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2261    return(MagickFalse);
2262  /*
2263    Initialize annotate structure.
2264  */
2265  annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2266  if (annotate_info == (XAnnotateInfo *) NULL)
2267    return(MagickFalse);
2268  XGetAnnotateInfo(annotate_info);
2269  annotate_info->x=x;
2270  annotate_info->y=y;
2271  if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
2272    annotate_info->stencil=OpaqueStencil;
2273  else
2274    if (transparent_box == MagickFalse)
2275      annotate_info->stencil=BackgroundStencil;
2276    else
2277      annotate_info->stencil=ForegroundStencil;
2278  annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2279  annotate_info->degrees=degrees;
2280  annotate_info->font_info=font_info;
2281  annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2282    windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
2283    sizeof(*annotate_info->text));
2284  if (annotate_info->text == (char *) NULL)
2285    return(MagickFalse);
2286  /*
2287    Create cursor and set graphic context.
2288  */
2289  cursor=XCreateFontCursor(display,XC_pencil);
2290  (void) XCheckDefineCursor(display,windows->image.id,cursor);
2291  annotate_context=windows->image.annotate_context;
2292  (void) XSetFont(display,annotate_context,font_info->fid);
2293  (void) XSetBackground(display,annotate_context,
2294    windows->pixel_info->pen_colors[box_id].pixel);
2295  (void) XSetForeground(display,annotate_context,
2296    windows->pixel_info->pen_colors[pen_id].pixel);
2297  /*
2298    Begin annotating the image with text.
2299  */
2300  (void) CloneString(&windows->command.name,"Text");
2301  windows->command.data=0;
2302  (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2303  state=DefaultState;
2304  (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2305  text_event.xexpose.width=(int) font_info->max_bounds.width;
2306  text_event.xexpose.height=font_info->max_bounds.ascent+
2307    font_info->max_bounds.descent;
2308  p=annotate_info->text;
2309  do
2310  {
2311    /*
2312      Display text cursor.
2313    */
2314    *p='\0';
2315    (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2316    /*
2317      Wait for next event.
2318    */
2319    XScreenEvent(display,windows,&event,exception);
2320    if (event.xany.window == windows->command.id)
2321      {
2322        /*
2323          Select a command from the Command widget.
2324        */
2325        (void) XSetBackground(display,annotate_context,
2326          windows->pixel_info->background_color.pixel);
2327        (void) XSetForeground(display,annotate_context,
2328          windows->pixel_info->foreground_color.pixel);
2329        id=XCommandWidget(display,windows,AnnotateMenu,&event);
2330        (void) XSetBackground(display,annotate_context,
2331          windows->pixel_info->pen_colors[box_id].pixel);
2332        (void) XSetForeground(display,annotate_context,
2333          windows->pixel_info->pen_colors[pen_id].pixel);
2334        if (id < 0)
2335          continue;
2336        switch (TextCommands[id])
2337        {
2338          case TextHelpCommand:
2339          {
2340            XTextViewWidget(display,resource_info,windows,MagickFalse,
2341              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2342            (void) XCheckDefineCursor(display,windows->image.id,cursor);
2343            break;
2344          }
2345          case TextApplyCommand:
2346          {
2347            /*
2348              Finished annotating.
2349            */
2350            annotate_info->width=(unsigned int) XTextWidth(font_info,
2351              annotate_info->text,(int) strlen(annotate_info->text));
2352            XRefreshWindow(display,&windows->image,&text_event);
2353            state|=ExitState;
2354            break;
2355          }
2356          default:
2357            break;
2358        }
2359        continue;
2360      }
2361    /*
2362      Erase text cursor.
2363    */
2364    text_event.xexpose.x=x;
2365    text_event.xexpose.y=y-font_info->max_bounds.ascent;
2366    (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2367      (unsigned int) text_event.xexpose.width,(unsigned int)
2368      text_event.xexpose.height,MagickFalse);
2369    XRefreshWindow(display,&windows->image,&text_event);
2370    switch (event.type)
2371    {
2372      case ButtonPress:
2373      {
2374        if (event.xbutton.window != windows->image.id)
2375          break;
2376        if (event.xbutton.button == Button2)
2377          {
2378            /*
2379              Request primary selection.
2380            */
2381            (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2382              windows->image.id,CurrentTime);
2383            break;
2384          }
2385        break;
2386      }
2387      case Expose:
2388      {
2389        if (event.xexpose.count == 0)
2390          {
2391            XAnnotateInfo
2392              *text_info;
2393
2394            /*
2395              Refresh Image window.
2396            */
2397            XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2398            text_info=annotate_info;
2399            while (text_info != (XAnnotateInfo *) NULL)
2400            {
2401              if (annotate_info->stencil == ForegroundStencil)
2402                (void) XDrawString(display,windows->image.id,annotate_context,
2403                  text_info->x,text_info->y,text_info->text,
2404                  (int) strlen(text_info->text));
2405              else
2406                (void) XDrawImageString(display,windows->image.id,
2407                  annotate_context,text_info->x,text_info->y,text_info->text,
2408                  (int) strlen(text_info->text));
2409              text_info=text_info->previous;
2410            }
2411            (void) XDrawString(display,windows->image.id,annotate_context,
2412              x,y,"_",1);
2413          }
2414        break;
2415      }
2416      case KeyPress:
2417      {
2418        int
2419          length;
2420
2421        if (event.xkey.window != windows->image.id)
2422          break;
2423        /*
2424          Respond to a user key press.
2425        */
2426        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2427          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2428        *(command+length)='\0';
2429        if (((event.xkey.state & ControlMask) != 0) ||
2430            ((event.xkey.state & Mod1Mask) != 0))
2431          state|=ModifierState;
2432        if ((state & ModifierState) != 0)
2433          switch ((int) key_symbol)
2434          {
2435            case XK_u:
2436            case XK_U:
2437            {
2438              key_symbol=DeleteCommand;
2439              break;
2440            }
2441            default:
2442              break;
2443          }
2444        switch ((int) key_symbol)
2445        {
2446          case XK_BackSpace:
2447          {
2448            /*
2449              Erase one character.
2450            */
2451            if (p == annotate_info->text)
2452              {
2453                if (annotate_info->previous == (XAnnotateInfo *) NULL)
2454                  break;
2455                else
2456                  {
2457                    /*
2458                      Go to end of the previous line of text.
2459                    */
2460                    annotate_info=annotate_info->previous;
2461                    p=annotate_info->text;
2462                    x=annotate_info->x+annotate_info->width;
2463                    y=annotate_info->y;
2464                    if (annotate_info->width != 0)
2465                      p+=strlen(annotate_info->text);
2466                    break;
2467                  }
2468              }
2469            p--;
2470            x-=XTextWidth(font_info,p,1);
2471            text_event.xexpose.x=x;
2472            text_event.xexpose.y=y-font_info->max_bounds.ascent;
2473            XRefreshWindow(display,&windows->image,&text_event);
2474            break;
2475          }
2476          case XK_bracketleft:
2477          {
2478            key_symbol=XK_Escape;
2479            break;
2480          }
2481          case DeleteCommand:
2482          {
2483            /*
2484              Erase the entire line of text.
2485            */
2486            while (p != annotate_info->text)
2487            {
2488              p--;
2489              x-=XTextWidth(font_info,p,1);
2490              text_event.xexpose.x=x;
2491              XRefreshWindow(display,&windows->image,&text_event);
2492            }
2493            break;
2494          }
2495          case XK_Escape:
2496          case XK_F20:
2497          {
2498            /*
2499              Finished annotating.
2500            */
2501            annotate_info->width=(unsigned int) XTextWidth(font_info,
2502              annotate_info->text,(int) strlen(annotate_info->text));
2503            XRefreshWindow(display,&windows->image,&text_event);
2504            state|=ExitState;
2505            break;
2506          }
2507          default:
2508          {
2509            /*
2510              Draw a single character on the Image window.
2511            */
2512            if ((state & ModifierState) != 0)
2513              break;
2514            if (*command == '\0')
2515              break;
2516            *p=(*command);
2517            if (annotate_info->stencil == ForegroundStencil)
2518              (void) XDrawString(display,windows->image.id,annotate_context,
2519                x,y,p,1);
2520            else
2521              (void) XDrawImageString(display,windows->image.id,
2522                annotate_context,x,y,p,1);
2523            x+=XTextWidth(font_info,p,1);
2524            p++;
2525            if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2526              break;
2527          }
2528          case XK_Return:
2529          case XK_KP_Enter:
2530          {
2531            /*
2532              Advance to the next line of text.
2533            */
2534            *p='\0';
2535            annotate_info->width=(unsigned int) XTextWidth(font_info,
2536              annotate_info->text,(int) strlen(annotate_info->text));
2537            if (annotate_info->next != (XAnnotateInfo *) NULL)
2538              {
2539                /*
2540                  Line of text already exists.
2541                */
2542                annotate_info=annotate_info->next;
2543                x=annotate_info->x;
2544                y=annotate_info->y;
2545                p=annotate_info->text;
2546                break;
2547              }
2548            annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2549              sizeof(*annotate_info->next));
2550            if (annotate_info->next == (XAnnotateInfo *) NULL)
2551              return(MagickFalse);
2552            *annotate_info->next=(*annotate_info);
2553            annotate_info->next->previous=annotate_info;
2554            annotate_info=annotate_info->next;
2555            annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2556              windows->image.width/MagickMax((ssize_t)
2557              font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2558            if (annotate_info->text == (char *) NULL)
2559              return(MagickFalse);
2560            annotate_info->y+=annotate_info->height;
2561            if (annotate_info->y > (int) windows->image.height)
2562              annotate_info->y=(int) annotate_info->height;
2563            annotate_info->next=(XAnnotateInfo *) NULL;
2564            x=annotate_info->x;
2565            y=annotate_info->y;
2566            p=annotate_info->text;
2567            break;
2568          }
2569        }
2570        break;
2571      }
2572      case KeyRelease:
2573      {
2574        /*
2575          Respond to a user key release.
2576        */
2577        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2578          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2579        state&=(~ModifierState);
2580        break;
2581      }
2582      case SelectionNotify:
2583      {
2584        Atom
2585          type;
2586
2587        int
2588          format;
2589
2590        unsigned char
2591          *data;
2592
2593        unsigned long
2594          after,
2595          length;
2596
2597        /*
2598          Obtain response from primary selection.
2599        */
2600        if (event.xselection.property == (Atom) None)
2601          break;
2602        status=XGetWindowProperty(display,event.xselection.requestor,
2603          event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
2604          &type,&format,&length,&after,&data);
2605        if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2606            (length == 0))
2607          break;
2608        /*
2609          Annotate Image window with primary selection.
2610        */
2611        for (i=0; i < (ssize_t) length; i++)
2612        {
2613          if ((char) data[i] != '\n')
2614            {
2615              /*
2616                Draw a single character on the Image window.
2617              */
2618              *p=(char) data[i];
2619              (void) XDrawString(display,windows->image.id,annotate_context,
2620                x,y,p,1);
2621              x+=XTextWidth(font_info,p,1);
2622              p++;
2623              if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2624                continue;
2625            }
2626          /*
2627            Advance to the next line of text.
2628          */
2629          *p='\0';
2630          annotate_info->width=(unsigned int) XTextWidth(font_info,
2631            annotate_info->text,(int) strlen(annotate_info->text));
2632          if (annotate_info->next != (XAnnotateInfo *) NULL)
2633            {
2634              /*
2635                Line of text already exists.
2636              */
2637              annotate_info=annotate_info->next;
2638              x=annotate_info->x;
2639              y=annotate_info->y;
2640              p=annotate_info->text;
2641              continue;
2642            }
2643          annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2644            sizeof(*annotate_info->next));
2645          if (annotate_info->next == (XAnnotateInfo *) NULL)
2646            return(MagickFalse);
2647          *annotate_info->next=(*annotate_info);
2648          annotate_info->next->previous=annotate_info;
2649          annotate_info=annotate_info->next;
2650          annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2651            windows->image.width/MagickMax((ssize_t)
2652            font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2653          if (annotate_info->text == (char *) NULL)
2654            return(MagickFalse);
2655          annotate_info->y+=annotate_info->height;
2656          if (annotate_info->y > (int) windows->image.height)
2657            annotate_info->y=(int) annotate_info->height;
2658          annotate_info->next=(XAnnotateInfo *) NULL;
2659          x=annotate_info->x;
2660          y=annotate_info->y;
2661          p=annotate_info->text;
2662        }
2663        (void) XFree((void *) data);
2664        break;
2665      }
2666      default:
2667        break;
2668    }
2669  } while ((state & ExitState) == 0);
2670  (void) XFreeCursor(display,cursor);
2671  /*
2672    Annotation is relative to image configuration.
2673  */
2674  width=(unsigned int) image->columns;
2675  height=(unsigned int) image->rows;
2676  x=0;
2677  y=0;
2678  if (windows->image.crop_geometry != (char *) NULL)
2679    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2680  /*
2681    Initialize annotated image.
2682  */
2683  XSetCursorState(display,windows,MagickTrue);
2684  XCheckRefreshWindows(display,windows);
2685  while (annotate_info != (XAnnotateInfo *) NULL)
2686  {
2687    if (annotate_info->width == 0)
2688      {
2689        /*
2690          No text on this line--  go to the next line of text.
2691        */
2692        previous_info=annotate_info->previous;
2693        annotate_info->text=(char *)
2694          RelinquishMagickMemory(annotate_info->text);
2695        annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2696        annotate_info=previous_info;
2697        continue;
2698      }
2699    /*
2700      Determine pixel index for box and pen color.
2701    */
2702    windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2703    if (windows->pixel_info->colors != 0)
2704      for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2705        if (windows->pixel_info->pixels[i] ==
2706            windows->pixel_info->pen_colors[box_id].pixel)
2707          {
2708            windows->pixel_info->box_index=(unsigned short) i;
2709            break;
2710          }
2711    windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2712    if (windows->pixel_info->colors != 0)
2713      for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2714        if (windows->pixel_info->pixels[i] ==
2715            windows->pixel_info->pen_colors[pen_id].pixel)
2716          {
2717            windows->pixel_info->pen_index=(unsigned short) i;
2718            break;
2719          }
2720    /*
2721      Define the annotate geometry string.
2722    */
2723    annotate_info->x=(int)
2724      width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2725    annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2726      windows->image.y)/windows->image.ximage->height;
2727    (void) FormatLocaleString(annotate_info->geometry,MaxTextExtent,
2728      "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2729      height*annotate_info->height/windows->image.ximage->height,
2730      annotate_info->x+x,annotate_info->y+y);
2731    /*
2732      Annotate image with text.
2733    */
2734    status=XAnnotateImage(display,windows->pixel_info,annotate_info,image,
2735      exception);
2736    if (status == 0)
2737      return(MagickFalse);
2738    /*
2739      Free up memory.
2740    */
2741    previous_info=annotate_info->previous;
2742    annotate_info->text=DestroyString(annotate_info->text);
2743    annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2744    annotate_info=previous_info;
2745  }
2746  (void) XSetForeground(display,annotate_context,
2747    windows->pixel_info->foreground_color.pixel);
2748  (void) XSetBackground(display,annotate_context,
2749    windows->pixel_info->background_color.pixel);
2750  (void) XSetFont(display,annotate_context,windows->font_info->fid);
2751  XSetCursorState(display,windows,MagickFalse);
2752  (void) XFreeFont(display,font_info);
2753  /*
2754    Update image configuration.
2755  */
2756  XConfigureImageColormap(display,resource_info,windows,image,exception);
2757  (void) XConfigureImage(display,resource_info,windows,image,exception);
2758  return(MagickTrue);
2759}
2760
2761/*
2762%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2763%                                                                             %
2764%                                                                             %
2765%                                                                             %
2766+   X B a c k g r o u n d I m a g e                                           %
2767%                                                                             %
2768%                                                                             %
2769%                                                                             %
2770%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2771%
2772%  XBackgroundImage() displays the image in the background of a window.
2773%
2774%  The format of the XBackgroundImage method is:
2775%
2776%      MagickBooleanType XBackgroundImage(Display *display,
2777%        XResourceInfo *resource_info,XWindows *windows,Image **image,
2778%        ExceptionInfo *exception)
2779%
2780%  A description of each parameter follows:
2781%
2782%    o display: Specifies a connection to an X server; returned from
2783%      XOpenDisplay.
2784%
2785%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2786%
2787%    o windows: Specifies a pointer to a XWindows structure.
2788%
2789%    o image: the image.
2790%
2791%    o exception: return any errors or warnings in this structure.
2792%
2793*/
2794static MagickBooleanType XBackgroundImage(Display *display,
2795  XResourceInfo *resource_info,XWindows *windows,Image **image,
2796  ExceptionInfo *exception)
2797{
2798#define BackgroundImageTag  "Background/Image"
2799
2800  int
2801    status;
2802
2803  static char
2804    window_id[MaxTextExtent] = "root";
2805
2806  XResourceInfo
2807    background_resources;
2808
2809  /*
2810    Put image in background.
2811  */
2812  status=XDialogWidget(display,windows,"Background",
2813    "Enter window id (id 0x00 selects window with pointer):",window_id);
2814  if (*window_id == '\0')
2815    return(MagickFalse);
2816  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
2817    exception);
2818  XInfoWidget(display,windows,BackgroundImageTag);
2819  XSetCursorState(display,windows,MagickTrue);
2820  XCheckRefreshWindows(display,windows);
2821  background_resources=(*resource_info);
2822  background_resources.window_id=window_id;
2823  background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2824  status=XDisplayBackgroundImage(display,&background_resources,*image,
2825    exception);
2826  if (status != MagickFalse)
2827    XClientMessage(display,windows->image.id,windows->im_protocols,
2828      windows->im_retain_colors,CurrentTime);
2829  XSetCursorState(display,windows,MagickFalse);
2830  (void) XMagickCommand(display,resource_info,windows,UndoCommand,image,
2831    exception);
2832  return(MagickTrue);
2833}
2834
2835/*
2836%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2837%                                                                             %
2838%                                                                             %
2839%                                                                             %
2840+   X C h o p I m a g e                                                       %
2841%                                                                             %
2842%                                                                             %
2843%                                                                             %
2844%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2845%
2846%  XChopImage() chops the X image.
2847%
2848%  The format of the XChopImage method is:
2849%
2850%    MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2851%      XWindows *windows,Image **image,ExceptionInfo *exception)
2852%
2853%  A description of each parameter follows:
2854%
2855%    o display: Specifies a connection to an X server; returned from
2856%      XOpenDisplay.
2857%
2858%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2859%
2860%    o windows: Specifies a pointer to a XWindows structure.
2861%
2862%    o image: the image.
2863%
2864%    o exception: return any errors or warnings in this structure.
2865%
2866*/
2867static MagickBooleanType XChopImage(Display *display,
2868  XResourceInfo *resource_info,XWindows *windows,Image **image,
2869  ExceptionInfo *exception)
2870{
2871  static const char
2872    *ChopMenu[] =
2873    {
2874      "Direction",
2875      "Help",
2876      "Dismiss",
2877      (char *) NULL
2878    };
2879
2880  static ModeType
2881    direction = HorizontalChopCommand;
2882
2883  static const ModeType
2884    ChopCommands[] =
2885    {
2886      ChopDirectionCommand,
2887      ChopHelpCommand,
2888      ChopDismissCommand
2889    },
2890    DirectionCommands[] =
2891    {
2892      HorizontalChopCommand,
2893      VerticalChopCommand
2894    };
2895
2896  char
2897    text[MaxTextExtent];
2898
2899  Image
2900    *chop_image;
2901
2902  int
2903    id,
2904    x,
2905    y;
2906
2907  MagickRealType
2908    scale_factor;
2909
2910  RectangleInfo
2911    chop_info;
2912
2913  unsigned int
2914    distance,
2915    height,
2916    width;
2917
2918  size_t
2919    state;
2920
2921  XEvent
2922    event;
2923
2924  XSegment
2925    segment_info;
2926
2927  /*
2928    Map Command widget.
2929  */
2930  (void) CloneString(&windows->command.name,"Chop");
2931  windows->command.data=1;
2932  (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2933  (void) XMapRaised(display,windows->command.id);
2934  XClientMessage(display,windows->image.id,windows->im_protocols,
2935    windows->im_update_widget,CurrentTime);
2936  /*
2937    Track pointer until button 1 is pressed.
2938  */
2939  XQueryPosition(display,windows->image.id,&x,&y);
2940  (void) XSelectInput(display,windows->image.id,
2941    windows->image.attributes.event_mask | PointerMotionMask);
2942  state=DefaultState;
2943  do
2944  {
2945    if (windows->info.mapped != MagickFalse)
2946      {
2947        /*
2948          Display pointer position.
2949        */
2950        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
2951          x+windows->image.x,y+windows->image.y);
2952        XInfoWidget(display,windows,text);
2953      }
2954    /*
2955      Wait for next event.
2956    */
2957    XScreenEvent(display,windows,&event,exception);
2958    if (event.xany.window == windows->command.id)
2959      {
2960        /*
2961          Select a command from the Command widget.
2962        */
2963        id=XCommandWidget(display,windows,ChopMenu,&event);
2964        if (id < 0)
2965          continue;
2966        switch (ChopCommands[id])
2967        {
2968          case ChopDirectionCommand:
2969          {
2970            char
2971              command[MaxTextExtent];
2972
2973            static const char
2974              *Directions[] =
2975              {
2976                "horizontal",
2977                "vertical",
2978                (char *) NULL,
2979              };
2980
2981            /*
2982              Select a command from the pop-up menu.
2983            */
2984            id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2985            if (id >= 0)
2986              direction=DirectionCommands[id];
2987            break;
2988          }
2989          case ChopHelpCommand:
2990          {
2991            XTextViewWidget(display,resource_info,windows,MagickFalse,
2992              "Help Viewer - Image Chop",ImageChopHelp);
2993            break;
2994          }
2995          case ChopDismissCommand:
2996          {
2997            /*
2998              Prematurely exit.
2999            */
3000            state|=EscapeState;
3001            state|=ExitState;
3002            break;
3003          }
3004          default:
3005            break;
3006        }
3007        continue;
3008      }
3009    switch (event.type)
3010    {
3011      case ButtonPress:
3012      {
3013        if (event.xbutton.button != Button1)
3014          break;
3015        if (event.xbutton.window != windows->image.id)
3016          break;
3017        /*
3018          User has committed to start point of chopping line.
3019        */
3020        segment_info.x1=(short int) event.xbutton.x;
3021        segment_info.x2=(short int) event.xbutton.x;
3022        segment_info.y1=(short int) event.xbutton.y;
3023        segment_info.y2=(short int) event.xbutton.y;
3024        state|=ExitState;
3025        break;
3026      }
3027      case ButtonRelease:
3028        break;
3029      case Expose:
3030        break;
3031      case KeyPress:
3032      {
3033        char
3034          command[MaxTextExtent];
3035
3036        KeySym
3037          key_symbol;
3038
3039        if (event.xkey.window != windows->image.id)
3040          break;
3041        /*
3042          Respond to a user key press.
3043        */
3044        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3045          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3046        switch ((int) key_symbol)
3047        {
3048          case XK_Escape:
3049          case XK_F20:
3050          {
3051            /*
3052              Prematurely exit.
3053            */
3054            state|=EscapeState;
3055            state|=ExitState;
3056            break;
3057          }
3058          case XK_F1:
3059          case XK_Help:
3060          {
3061            (void) XSetFunction(display,windows->image.highlight_context,
3062              GXcopy);
3063            XTextViewWidget(display,resource_info,windows,MagickFalse,
3064              "Help Viewer - Image Chop",ImageChopHelp);
3065            (void) XSetFunction(display,windows->image.highlight_context,
3066              GXinvert);
3067            break;
3068          }
3069          default:
3070          {
3071            (void) XBell(display,0);
3072            break;
3073          }
3074        }
3075        break;
3076      }
3077      case MotionNotify:
3078      {
3079        /*
3080          Map and unmap Info widget as text cursor crosses its boundaries.
3081        */
3082        x=event.xmotion.x;
3083        y=event.xmotion.y;
3084        if (windows->info.mapped != MagickFalse)
3085          {
3086            if ((x < (int) (windows->info.x+windows->info.width)) &&
3087                (y < (int) (windows->info.y+windows->info.height)))
3088              (void) XWithdrawWindow(display,windows->info.id,
3089                windows->info.screen);
3090          }
3091        else
3092          if ((x > (int) (windows->info.x+windows->info.width)) ||
3093              (y > (int) (windows->info.y+windows->info.height)))
3094            (void) XMapWindow(display,windows->info.id);
3095      }
3096    }
3097  } while ((state & ExitState) == 0);
3098  (void) XSelectInput(display,windows->image.id,
3099    windows->image.attributes.event_mask);
3100  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3101  if ((state & EscapeState) != 0)
3102    return(MagickTrue);
3103  /*
3104    Draw line as pointer moves until the mouse button is released.
3105  */
3106  chop_info.width=0;
3107  chop_info.height=0;
3108  chop_info.x=0;
3109  chop_info.y=0;
3110  distance=0;
3111  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3112  state=DefaultState;
3113  do
3114  {
3115    if (distance > 9)
3116      {
3117        /*
3118          Display info and draw chopping line.
3119        */
3120        if (windows->info.mapped == MagickFalse)
3121          (void) XMapWindow(display,windows->info.id);
3122        (void) FormatLocaleString(text,MaxTextExtent,
3123          " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3124          chop_info.height,(double) chop_info.x,(double) chop_info.y);
3125        XInfoWidget(display,windows,text);
3126        XHighlightLine(display,windows->image.id,
3127          windows->image.highlight_context,&segment_info);
3128      }
3129    else
3130      if (windows->info.mapped != MagickFalse)
3131        (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3132    /*
3133      Wait for next event.
3134    */
3135    XScreenEvent(display,windows,&event,exception);
3136    if (distance > 9)
3137      XHighlightLine(display,windows->image.id,
3138        windows->image.highlight_context,&segment_info);
3139    switch (event.type)
3140    {
3141      case ButtonPress:
3142      {
3143        segment_info.x2=(short int) event.xmotion.x;
3144        segment_info.y2=(short int) event.xmotion.y;
3145        break;
3146      }
3147      case ButtonRelease:
3148      {
3149        /*
3150          User has committed to chopping line.
3151        */
3152        segment_info.x2=(short int) event.xbutton.x;
3153        segment_info.y2=(short int) event.xbutton.y;
3154        state|=ExitState;
3155        break;
3156      }
3157      case Expose:
3158        break;
3159      case MotionNotify:
3160      {
3161        segment_info.x2=(short int) event.xmotion.x;
3162        segment_info.y2=(short int) event.xmotion.y;
3163      }
3164      default:
3165        break;
3166    }
3167    /*
3168      Check boundary conditions.
3169    */
3170    if (segment_info.x2 < 0)
3171      segment_info.x2=0;
3172    else
3173      if (segment_info.x2 > windows->image.ximage->width)
3174        segment_info.x2=windows->image.ximage->width;
3175    if (segment_info.y2 < 0)
3176      segment_info.y2=0;
3177    else
3178      if (segment_info.y2 > windows->image.ximage->height)
3179        segment_info.y2=windows->image.ximage->height;
3180    distance=(unsigned int)
3181      (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3182       ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3183    /*
3184      Compute chopping geometry.
3185    */
3186    if (direction == HorizontalChopCommand)
3187      {
3188        chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3189        chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3190        chop_info.height=0;
3191        chop_info.y=0;
3192        if (segment_info.x1 > (int) segment_info.x2)
3193          {
3194            chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3195            chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3196          }
3197      }
3198    else
3199      {
3200        chop_info.width=0;
3201        chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3202        chop_info.x=0;
3203        chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3204        if (segment_info.y1 > segment_info.y2)
3205          {
3206            chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3207            chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3208          }
3209      }
3210  } while ((state & ExitState) == 0);
3211  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3212  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3213  if (distance <= 9)
3214    return(MagickTrue);
3215  /*
3216    Image chopping is relative to image configuration.
3217  */
3218  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
3219    exception);
3220  XSetCursorState(display,windows,MagickTrue);
3221  XCheckRefreshWindows(display,windows);
3222  windows->image.window_changes.width=windows->image.ximage->width-
3223    (unsigned int) chop_info.width;
3224  windows->image.window_changes.height=windows->image.ximage->height-
3225    (unsigned int) chop_info.height;
3226  width=(unsigned int) (*image)->columns;
3227  height=(unsigned int) (*image)->rows;
3228  x=0;
3229  y=0;
3230  if (windows->image.crop_geometry != (char *) NULL)
3231    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3232  scale_factor=(MagickRealType) width/windows->image.ximage->width;
3233  chop_info.x+=x;
3234  chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3235  chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3236  scale_factor=(MagickRealType) height/windows->image.ximage->height;
3237  chop_info.y+=y;
3238  chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3239  chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3240  /*
3241    Chop image.
3242  */
3243  chop_image=ChopImage(*image,&chop_info,exception);
3244  XSetCursorState(display,windows,MagickFalse);
3245  if (chop_image == (Image *) NULL)
3246    return(MagickFalse);
3247  *image=DestroyImage(*image);
3248  *image=chop_image;
3249  /*
3250    Update image configuration.
3251  */
3252  XConfigureImageColormap(display,resource_info,windows,*image,exception);
3253  (void) XConfigureImage(display,resource_info,windows,*image,exception);
3254  return(MagickTrue);
3255}
3256
3257/*
3258%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3259%                                                                             %
3260%                                                                             %
3261%                                                                             %
3262+   X C o l o r E d i t I m a g e                                             %
3263%                                                                             %
3264%                                                                             %
3265%                                                                             %
3266%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3267%
3268%  XColorEditImage() allows the user to interactively change the color of one
3269%  pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3270%
3271%  The format of the XColorEditImage method is:
3272%
3273%      MagickBooleanType XColorEditImage(Display *display,
3274%        XResourceInfo *resource_info,XWindows *windows,Image **image,
3275%          ExceptionInfo *exception)
3276%
3277%  A description of each parameter follows:
3278%
3279%    o display: Specifies a connection to an X server;  returned from
3280%      XOpenDisplay.
3281%
3282%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3283%
3284%    o windows: Specifies a pointer to a XWindows structure.
3285%
3286%    o image: the image; returned from ReadImage.
3287%
3288%    o exception: return any errors or warnings in this structure.
3289%
3290*/
3291static MagickBooleanType XColorEditImage(Display *display,
3292  XResourceInfo *resource_info,XWindows *windows,Image **image,
3293  ExceptionInfo *exception)
3294{
3295  static const char
3296    *ColorEditMenu[] =
3297    {
3298      "Method",
3299      "Pixel Color",
3300      "Border Color",
3301      "Fuzz",
3302      "Undo",
3303      "Help",
3304      "Dismiss",
3305      (char *) NULL
3306    };
3307
3308  static const ModeType
3309    ColorEditCommands[] =
3310    {
3311      ColorEditMethodCommand,
3312      ColorEditColorCommand,
3313      ColorEditBorderCommand,
3314      ColorEditFuzzCommand,
3315      ColorEditUndoCommand,
3316      ColorEditHelpCommand,
3317      ColorEditDismissCommand
3318    };
3319
3320  static PaintMethod
3321    method = PointMethod;
3322
3323  static unsigned int
3324    pen_id = 0;
3325
3326  static XColor
3327    border_color = { 0, 0, 0, 0, 0, 0 };
3328
3329  char
3330    command[MaxTextExtent],
3331    text[MaxTextExtent];
3332
3333  Cursor
3334    cursor;
3335
3336  int
3337    entry,
3338    id,
3339    x,
3340    x_offset,
3341    y,
3342    y_offset;
3343
3344  register Quantum
3345    *q;
3346
3347  register ssize_t
3348    i;
3349
3350  unsigned int
3351    height,
3352    width;
3353
3354  size_t
3355    state;
3356
3357  XColor
3358    color;
3359
3360  XEvent
3361    event;
3362
3363  /*
3364    Map Command widget.
3365  */
3366  (void) CloneString(&windows->command.name,"Color Edit");
3367  windows->command.data=4;
3368  (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3369  (void) XMapRaised(display,windows->command.id);
3370  XClientMessage(display,windows->image.id,windows->im_protocols,
3371    windows->im_update_widget,CurrentTime);
3372  /*
3373    Make cursor.
3374  */
3375  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3376    resource_info->background_color,resource_info->foreground_color);
3377  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3378  /*
3379    Track pointer until button 1 is pressed.
3380  */
3381  XQueryPosition(display,windows->image.id,&x,&y);
3382  (void) XSelectInput(display,windows->image.id,
3383    windows->image.attributes.event_mask | PointerMotionMask);
3384  state=DefaultState;
3385  do
3386  {
3387    if (windows->info.mapped != MagickFalse)
3388      {
3389        /*
3390          Display pointer position.
3391        */
3392        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
3393          x+windows->image.x,y+windows->image.y);
3394        XInfoWidget(display,windows,text);
3395      }
3396    /*
3397      Wait for next event.
3398    */
3399    XScreenEvent(display,windows,&event,exception);
3400    if (event.xany.window == windows->command.id)
3401      {
3402        /*
3403          Select a command from the Command widget.
3404        */
3405        id=XCommandWidget(display,windows,ColorEditMenu,&event);
3406        if (id < 0)
3407          {
3408            (void) XCheckDefineCursor(display,windows->image.id,cursor);
3409            continue;
3410          }
3411        switch (ColorEditCommands[id])
3412        {
3413          case ColorEditMethodCommand:
3414          {
3415            char
3416              **methods;
3417
3418            /*
3419              Select a method from the pop-up menu.
3420            */
3421            methods=(char **) GetCommandOptions(MagickMethodOptions);
3422            if (methods == (char **) NULL)
3423              break;
3424            entry=XMenuWidget(display,windows,ColorEditMenu[id],
3425              (const char **) methods,command);
3426            if (entry >= 0)
3427              method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3428                MagickFalse,methods[entry]);
3429            methods=DestroyStringList(methods);
3430            break;
3431          }
3432          case ColorEditColorCommand:
3433          {
3434            const char
3435              *ColorMenu[MaxNumberPens];
3436
3437            int
3438              pen_number;
3439
3440            /*
3441              Initialize menu selections.
3442            */
3443            for (i=0; i < (int) (MaxNumberPens-2); i++)
3444              ColorMenu[i]=resource_info->pen_colors[i];
3445            ColorMenu[MaxNumberPens-2]="Browser...";
3446            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3447            /*
3448              Select a pen color from the pop-up menu.
3449            */
3450            pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3451              (const char **) ColorMenu,command);
3452            if (pen_number < 0)
3453              break;
3454            if (pen_number == (MaxNumberPens-2))
3455              {
3456                static char
3457                  color_name[MaxTextExtent] = "gray";
3458
3459                /*
3460                  Select a pen color from a dialog.
3461                */
3462                resource_info->pen_colors[pen_number]=color_name;
3463                XColorBrowserWidget(display,windows,"Select",color_name);
3464                if (*color_name == '\0')
3465                  break;
3466              }
3467            /*
3468              Set pen color.
3469            */
3470            (void) XParseColor(display,windows->map_info->colormap,
3471              resource_info->pen_colors[pen_number],&color);
3472            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3473              (unsigned int) MaxColors,&color);
3474            windows->pixel_info->pen_colors[pen_number]=color;
3475            pen_id=(unsigned int) pen_number;
3476            break;
3477          }
3478          case ColorEditBorderCommand:
3479          {
3480            const char
3481              *ColorMenu[MaxNumberPens];
3482
3483            int
3484              pen_number;
3485
3486            /*
3487              Initialize menu selections.
3488            */
3489            for (i=0; i < (int) (MaxNumberPens-2); i++)
3490              ColorMenu[i]=resource_info->pen_colors[i];
3491            ColorMenu[MaxNumberPens-2]="Browser...";
3492            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3493            /*
3494              Select a pen color from the pop-up menu.
3495            */
3496            pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3497              (const char **) ColorMenu,command);
3498            if (pen_number < 0)
3499              break;
3500            if (pen_number == (MaxNumberPens-2))
3501              {
3502                static char
3503                  color_name[MaxTextExtent] = "gray";
3504
3505                /*
3506                  Select a pen color from a dialog.
3507                */
3508                resource_info->pen_colors[pen_number]=color_name;
3509                XColorBrowserWidget(display,windows,"Select",color_name);
3510                if (*color_name == '\0')
3511                  break;
3512              }
3513            /*
3514              Set border color.
3515            */
3516            (void) XParseColor(display,windows->map_info->colormap,
3517              resource_info->pen_colors[pen_number],&border_color);
3518            break;
3519          }
3520          case ColorEditFuzzCommand:
3521          {
3522            static char
3523              fuzz[MaxTextExtent];
3524
3525            static const char
3526              *FuzzMenu[] =
3527              {
3528                "0%",
3529                "2%",
3530                "5%",
3531                "10%",
3532                "15%",
3533                "Dialog...",
3534                (char *) NULL,
3535              };
3536
3537            /*
3538              Select a command from the pop-up menu.
3539            */
3540            entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3541              command);
3542            if (entry < 0)
3543              break;
3544            if (entry != 5)
3545              {
3546                (*image)->fuzz=SiPrefixToDouble(FuzzMenu[entry],(double)
3547                  QuantumRange+1.0);
3548                break;
3549              }
3550            (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
3551            (void) XDialogWidget(display,windows,"Ok",
3552              "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3553            if (*fuzz == '\0')
3554              break;
3555            (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
3556            (*image)->fuzz=SiPrefixToDouble(fuzz,(double) QuantumRange+1.0);
3557            break;
3558          }
3559          case ColorEditUndoCommand:
3560          {
3561            (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3562              image,exception);
3563            break;
3564          }
3565          case ColorEditHelpCommand:
3566          default:
3567          {
3568            XTextViewWidget(display,resource_info,windows,MagickFalse,
3569              "Help Viewer - Image Annotation",ImageColorEditHelp);
3570            break;
3571          }
3572          case ColorEditDismissCommand:
3573          {
3574            /*
3575              Prematurely exit.
3576            */
3577            state|=EscapeState;
3578            state|=ExitState;
3579            break;
3580          }
3581        }
3582        (void) XCheckDefineCursor(display,windows->image.id,cursor);
3583        continue;
3584      }
3585    switch (event.type)
3586    {
3587      case ButtonPress:
3588      {
3589        if (event.xbutton.button != Button1)
3590          break;
3591        if ((event.xbutton.window != windows->image.id) &&
3592            (event.xbutton.window != windows->magnify.id))
3593          break;
3594        /*
3595          exit loop.
3596        */
3597        x=event.xbutton.x;
3598        y=event.xbutton.y;
3599        (void) XMagickCommand(display,resource_info,windows,
3600          SaveToUndoBufferCommand,image,exception);
3601        state|=UpdateConfigurationState;
3602        break;
3603      }
3604      case ButtonRelease:
3605      {
3606        if (event.xbutton.button != Button1)
3607          break;
3608        if ((event.xbutton.window != windows->image.id) &&
3609            (event.xbutton.window != windows->magnify.id))
3610          break;
3611        /*
3612          Update colormap information.
3613        */
3614        x=event.xbutton.x;
3615        y=event.xbutton.y;
3616        XConfigureImageColormap(display,resource_info,windows,*image,exception);
3617        (void) XConfigureImage(display,resource_info,windows,*image,exception);
3618        XInfoWidget(display,windows,text);
3619        (void) XCheckDefineCursor(display,windows->image.id,cursor);
3620        state&=(~UpdateConfigurationState);
3621        break;
3622      }
3623      case Expose:
3624        break;
3625      case KeyPress:
3626      {
3627        KeySym
3628          key_symbol;
3629
3630        if (event.xkey.window == windows->magnify.id)
3631          {
3632            Window
3633              window;
3634
3635            window=windows->magnify.id;
3636            while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3637          }
3638        if (event.xkey.window != windows->image.id)
3639          break;
3640        /*
3641          Respond to a user key press.
3642        */
3643        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3644          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3645        switch ((int) key_symbol)
3646        {
3647          case XK_Escape:
3648          case XK_F20:
3649          {
3650            /*
3651              Prematurely exit.
3652            */
3653            state|=ExitState;
3654            break;
3655          }
3656          case XK_F1:
3657          case XK_Help:
3658          {
3659            XTextViewWidget(display,resource_info,windows,MagickFalse,
3660              "Help Viewer - Image Annotation",ImageColorEditHelp);
3661            break;
3662          }
3663          default:
3664          {
3665            (void) XBell(display,0);
3666            break;
3667          }
3668        }
3669        break;
3670      }
3671      case MotionNotify:
3672      {
3673        /*
3674          Map and unmap Info widget as cursor crosses its boundaries.
3675        */
3676        x=event.xmotion.x;
3677        y=event.xmotion.y;
3678        if (windows->info.mapped != MagickFalse)
3679          {
3680            if ((x < (int) (windows->info.x+windows->info.width)) &&
3681                (y < (int) (windows->info.y+windows->info.height)))
3682              (void) XWithdrawWindow(display,windows->info.id,
3683                windows->info.screen);
3684          }
3685        else
3686          if ((x > (int) (windows->info.x+windows->info.width)) ||
3687              (y > (int) (windows->info.y+windows->info.height)))
3688            (void) XMapWindow(display,windows->info.id);
3689        break;
3690      }
3691      default:
3692        break;
3693    }
3694    if (event.xany.window == windows->magnify.id)
3695      {
3696        x=windows->magnify.x-windows->image.x;
3697        y=windows->magnify.y-windows->image.y;
3698      }
3699    x_offset=x;
3700    y_offset=y;
3701    if ((state & UpdateConfigurationState) != 0)
3702      {
3703        CacheView
3704          *image_view;
3705
3706        int
3707          x,
3708          y;
3709
3710        /*
3711          Pixel edit is relative to image configuration.
3712        */
3713        (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3714          MagickTrue);
3715        color=windows->pixel_info->pen_colors[pen_id];
3716        XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3717        width=(unsigned int) (*image)->columns;
3718        height=(unsigned int) (*image)->rows;
3719        x=0;
3720        y=0;
3721        if (windows->image.crop_geometry != (char *) NULL)
3722          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3723            &width,&height);
3724        x_offset=(int)
3725          (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3726        y_offset=(int)
3727          (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3728        if ((x_offset < 0) || (y_offset < 0))
3729          continue;
3730        if ((x_offset >= (int) (*image)->columns) ||
3731            (y_offset >= (int) (*image)->rows))
3732          continue;
3733        image_view=AcquireCacheView(*image);
3734        switch (method)
3735        {
3736          case PointMethod:
3737          default:
3738          {
3739            /*
3740              Update color information using point algorithm.
3741            */
3742            if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3743              return(MagickFalse);
3744            q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3745              (ssize_t) y_offset,1,1,exception);
3746            if (q == (Quantum *) NULL)
3747              break;
3748            SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3749            SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3750            SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3751            (void) SyncCacheViewAuthenticPixels(image_view,exception);
3752            break;
3753          }
3754          case ReplaceMethod:
3755          {
3756            PixelInfo
3757              pixel,
3758              target;
3759
3760            Quantum
3761              virtual_pixel[CompositePixelChannel];
3762
3763            /*
3764              Update color information using replace algorithm.
3765            */
3766            (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
3767              (ssize_t) y_offset,virtual_pixel,exception);
3768            target.red=virtual_pixel[RedPixelChannel];
3769            target.green=virtual_pixel[GreenPixelChannel];
3770            target.blue=virtual_pixel[BluePixelChannel];
3771            target.alpha=virtual_pixel[AlphaPixelChannel];
3772            if ((*image)->storage_class == DirectClass)
3773              {
3774                for (y=0; y < (int) (*image)->rows; y++)
3775                {
3776                  q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3777                    (*image)->columns,1,exception);
3778                  if (q == (Quantum *) NULL)
3779                    break;
3780                  for (x=0; x < (int) (*image)->columns; x++)
3781                  {
3782                    GetPixelInfoPixel(*image,q,&pixel);
3783                    if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
3784                      {
3785                        SetPixelRed(*image,ScaleShortToQuantum(
3786                          color.red),q);
3787                        SetPixelGreen(*image,ScaleShortToQuantum(
3788                          color.green),q);
3789                        SetPixelBlue(*image,ScaleShortToQuantum(
3790                          color.blue),q);
3791                      }
3792                    q+=GetPixelChannels(*image);
3793                  }
3794                  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3795                    break;
3796                }
3797              }
3798            else
3799              {
3800                for (i=0; i < (ssize_t) (*image)->colors; i++)
3801                  if (IsFuzzyEquivalencePixelInfo((*image)->colormap+i,&target))
3802                    {
3803                      (*image)->colormap[i].red=ScaleShortToQuantum(
3804                        color.red);
3805                      (*image)->colormap[i].green=ScaleShortToQuantum(
3806                        color.green);
3807                      (*image)->colormap[i].blue=ScaleShortToQuantum(
3808                        color.blue);
3809                    }
3810                (void) SyncImage(*image,exception);
3811              }
3812            break;
3813          }
3814          case FloodfillMethod:
3815          case FillToBorderMethod:
3816          {
3817            DrawInfo
3818              *draw_info;
3819
3820            PixelInfo
3821              target;
3822
3823            /*
3824              Update color information using floodfill algorithm.
3825            */
3826            (void) GetOneVirtualMagickPixel(*image,
3827              GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
3828              y_offset,&target,exception);
3829            if (method == FillToBorderMethod)
3830              {
3831                target.red=(MagickRealType)
3832                  ScaleShortToQuantum(border_color.red);
3833                target.green=(MagickRealType)
3834                  ScaleShortToQuantum(border_color.green);
3835                target.blue=(MagickRealType)
3836                  ScaleShortToQuantum(border_color.blue);
3837              }
3838            draw_info=CloneDrawInfo(resource_info->image_info,
3839              (DrawInfo *) NULL);
3840            (void) QueryColorCompliance(resource_info->pen_colors[pen_id],
3841              AllCompliance,&draw_info->fill,exception);
3842            (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
3843              x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
3844              MagickFalse : MagickTrue,exception);
3845            draw_info=DestroyDrawInfo(draw_info);
3846            break;
3847          }
3848          case ResetMethod:
3849          {
3850            /*
3851              Update color information using reset algorithm.
3852            */
3853            if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3854              return(MagickFalse);
3855            for (y=0; y < (int) (*image)->rows; y++)
3856            {
3857              q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3858                (*image)->columns,1,exception);
3859              if (q == (Quantum *) NULL)
3860                break;
3861              for (x=0; x < (int) (*image)->columns; x++)
3862              {
3863                SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3864                SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3865                SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3866                q+=GetPixelChannels(*image);
3867              }
3868              if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3869                break;
3870            }
3871            break;
3872          }
3873        }
3874        image_view=DestroyCacheView(image_view);
3875        state&=(~UpdateConfigurationState);
3876      }
3877  } while ((state & ExitState) == 0);
3878  (void) XSelectInput(display,windows->image.id,
3879    windows->image.attributes.event_mask);
3880  XSetCursorState(display,windows,MagickFalse);
3881  (void) XFreeCursor(display,cursor);
3882  return(MagickTrue);
3883}
3884
3885/*
3886%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3887%                                                                             %
3888%                                                                             %
3889%                                                                             %
3890+   X C o m p o s i t e I m a g e                                             %
3891%                                                                             %
3892%                                                                             %
3893%                                                                             %
3894%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3895%
3896%  XCompositeImage() requests an image name from the user, reads the image and
3897%  composites it with the X window image at a location the user chooses with
3898%  the pointer.
3899%
3900%  The format of the XCompositeImage method is:
3901%
3902%      MagickBooleanType XCompositeImage(Display *display,
3903%        XResourceInfo *resource_info,XWindows *windows,Image *image,
3904%        ExceptionInfo *exception)
3905%
3906%  A description of each parameter follows:
3907%
3908%    o display: Specifies a connection to an X server;  returned from
3909%      XOpenDisplay.
3910%
3911%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3912%
3913%    o windows: Specifies a pointer to a XWindows structure.
3914%
3915%    o image: the image; returned from ReadImage.
3916%
3917%    o exception: return any errors or warnings in this structure.
3918%
3919*/
3920static MagickBooleanType XCompositeImage(Display *display,
3921  XResourceInfo *resource_info,XWindows *windows,Image *image,
3922  ExceptionInfo *exception)
3923{
3924  static char
3925    displacement_geometry[MaxTextExtent] = "30x30",
3926    filename[MaxTextExtent] = "\0";
3927
3928  static const char
3929    *CompositeMenu[] =
3930    {
3931      "Operators",
3932      "Dissolve",
3933      "Displace",
3934      "Help",
3935      "Dismiss",
3936      (char *) NULL
3937    };
3938
3939  static CompositeOperator
3940    compose = CopyCompositeOp;
3941
3942  static const ModeType
3943    CompositeCommands[] =
3944    {
3945      CompositeOperatorsCommand,
3946      CompositeDissolveCommand,
3947      CompositeDisplaceCommand,
3948      CompositeHelpCommand,
3949      CompositeDismissCommand
3950    };
3951
3952  char
3953    text[MaxTextExtent];
3954
3955  Cursor
3956    cursor;
3957
3958  Image
3959    *composite_image;
3960
3961  int
3962    entry,
3963    id,
3964    x,
3965    y;
3966
3967  MagickRealType
3968    blend,
3969    scale_factor;
3970
3971  RectangleInfo
3972    highlight_info,
3973    composite_info;
3974
3975  unsigned int
3976    height,
3977    width;
3978
3979  size_t
3980    state;
3981
3982  XEvent
3983    event;
3984
3985  /*
3986    Request image file name from user.
3987  */
3988  XFileBrowserWidget(display,windows,"Composite",filename);
3989  if (*filename == '\0')
3990    return(MagickTrue);
3991  /*
3992    Read image.
3993  */
3994  XSetCursorState(display,windows,MagickTrue);
3995  XCheckRefreshWindows(display,windows);
3996  (void) CopyMagickString(resource_info->image_info->filename,filename,
3997    MaxTextExtent);
3998  composite_image=ReadImage(resource_info->image_info,exception);
3999  CatchException(exception);
4000  XSetCursorState(display,windows,MagickFalse);
4001  if (composite_image == (Image *) NULL)
4002    return(MagickFalse);
4003  /*
4004    Map Command widget.
4005  */
4006  (void) CloneString(&windows->command.name,"Composite");
4007  windows->command.data=1;
4008  (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
4009  (void) XMapRaised(display,windows->command.id);
4010  XClientMessage(display,windows->image.id,windows->im_protocols,
4011    windows->im_update_widget,CurrentTime);
4012  /*
4013    Track pointer until button 1 is pressed.
4014  */
4015  XQueryPosition(display,windows->image.id,&x,&y);
4016  (void) XSelectInput(display,windows->image.id,
4017    windows->image.attributes.event_mask | PointerMotionMask);
4018  composite_info.x=(ssize_t) windows->image.x+x;
4019  composite_info.y=(ssize_t) windows->image.y+y;
4020  composite_info.width=0;
4021  composite_info.height=0;
4022  cursor=XCreateFontCursor(display,XC_ul_angle);
4023  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4024  blend=0.0;
4025  state=DefaultState;
4026  do
4027  {
4028    if (windows->info.mapped != MagickFalse)
4029      {
4030        /*
4031          Display pointer position.
4032        */
4033        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4034          (long) composite_info.x,(long) composite_info.y);
4035        XInfoWidget(display,windows,text);
4036      }
4037    highlight_info=composite_info;
4038    highlight_info.x=composite_info.x-windows->image.x;
4039    highlight_info.y=composite_info.y-windows->image.y;
4040    XHighlightRectangle(display,windows->image.id,
4041      windows->image.highlight_context,&highlight_info);
4042    /*
4043      Wait for next event.
4044    */
4045    XScreenEvent(display,windows,&event,exception);
4046    XHighlightRectangle(display,windows->image.id,
4047      windows->image.highlight_context,&highlight_info);
4048    if (event.xany.window == windows->command.id)
4049      {
4050        /*
4051          Select a command from the Command widget.
4052        */
4053        id=XCommandWidget(display,windows,CompositeMenu,&event);
4054        if (id < 0)
4055          continue;
4056        switch (CompositeCommands[id])
4057        {
4058          case CompositeOperatorsCommand:
4059          {
4060            char
4061              command[MaxTextExtent],
4062              **operators;
4063
4064            /*
4065              Select a command from the pop-up menu.
4066            */
4067            operators=GetCommandOptions(MagickComposeOptions);
4068            if (operators == (char **) NULL)
4069              break;
4070            entry=XMenuWidget(display,windows,CompositeMenu[id],
4071              (const char **) operators,command);
4072            if (entry >= 0)
4073              compose=(CompositeOperator) ParseCommandOption(
4074                MagickComposeOptions,MagickFalse,operators[entry]);
4075            operators=DestroyStringList(operators);
4076            break;
4077          }
4078          case CompositeDissolveCommand:
4079          {
4080            static char
4081              factor[MaxTextExtent] = "20.0";
4082
4083            /*
4084              Dissolve the two images a given percent.
4085            */
4086            (void) XSetFunction(display,windows->image.highlight_context,
4087              GXcopy);
4088            (void) XDialogWidget(display,windows,"Dissolve",
4089              "Enter the blend factor (0.0 - 99.9%):",factor);
4090            (void) XSetFunction(display,windows->image.highlight_context,
4091              GXinvert);
4092            if (*factor == '\0')
4093              break;
4094            blend=InterpretLocaleValue(factor,(char **) NULL);
4095            compose=DissolveCompositeOp;
4096            break;
4097          }
4098          case CompositeDisplaceCommand:
4099          {
4100            /*
4101              Get horizontal and vertical scale displacement geometry.
4102            */
4103            (void) XSetFunction(display,windows->image.highlight_context,
4104              GXcopy);
4105            (void) XDialogWidget(display,windows,"Displace",
4106              "Enter the horizontal and vertical scale:",displacement_geometry);
4107            (void) XSetFunction(display,windows->image.highlight_context,
4108              GXinvert);
4109            if (*displacement_geometry == '\0')
4110              break;
4111            compose=DisplaceCompositeOp;
4112            break;
4113          }
4114          case CompositeHelpCommand:
4115          {
4116            (void) XSetFunction(display,windows->image.highlight_context,
4117              GXcopy);
4118            XTextViewWidget(display,resource_info,windows,MagickFalse,
4119              "Help Viewer - Image Composite",ImageCompositeHelp);
4120            (void) XSetFunction(display,windows->image.highlight_context,
4121              GXinvert);
4122            break;
4123          }
4124          case CompositeDismissCommand:
4125          {
4126            /*
4127              Prematurely exit.
4128            */
4129            state|=EscapeState;
4130            state|=ExitState;
4131            break;
4132          }
4133          default:
4134            break;
4135        }
4136        continue;
4137      }
4138    switch (event.type)
4139    {
4140      case ButtonPress:
4141      {
4142        if (image->debug != MagickFalse)
4143          (void) LogMagickEvent(X11Event,GetMagickModule(),
4144            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4145            event.xbutton.button,event.xbutton.x,event.xbutton.y);
4146        if (event.xbutton.button != Button1)
4147          break;
4148        if (event.xbutton.window != windows->image.id)
4149          break;
4150        /*
4151          Change cursor.
4152        */
4153        composite_info.width=composite_image->columns;
4154        composite_info.height=composite_image->rows;
4155        (void) XCheckDefineCursor(display,windows->image.id,cursor);
4156        composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4157        composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4158        break;
4159      }
4160      case ButtonRelease:
4161      {
4162        if (image->debug != MagickFalse)
4163          (void) LogMagickEvent(X11Event,GetMagickModule(),
4164            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4165            event.xbutton.button,event.xbutton.x,event.xbutton.y);
4166        if (event.xbutton.button != Button1)
4167          break;
4168        if (event.xbutton.window != windows->image.id)
4169          break;
4170        if ((composite_info.width != 0) && (composite_info.height != 0))
4171          {
4172            /*
4173              User has selected the location of the composite image.
4174            */
4175            composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4176            composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4177            state|=ExitState;
4178          }
4179        break;
4180      }
4181      case Expose:
4182        break;
4183      case KeyPress:
4184      {
4185        char
4186          command[MaxTextExtent];
4187
4188        KeySym
4189          key_symbol;
4190
4191        int
4192          length;
4193
4194        if (event.xkey.window != windows->image.id)
4195          break;
4196        /*
4197          Respond to a user key press.
4198        */
4199        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4200          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4201        *(command+length)='\0';
4202        if (image->debug != MagickFalse)
4203          (void) LogMagickEvent(X11Event,GetMagickModule(),
4204            "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4205        switch ((int) key_symbol)
4206        {
4207          case XK_Escape:
4208          case XK_F20:
4209          {
4210            /*
4211              Prematurely exit.
4212            */
4213            composite_image=DestroyImage(composite_image);
4214            state|=EscapeState;
4215            state|=ExitState;
4216            break;
4217          }
4218          case XK_F1:
4219          case XK_Help:
4220          {
4221            (void) XSetFunction(display,windows->image.highlight_context,
4222              GXcopy);
4223            XTextViewWidget(display,resource_info,windows,MagickFalse,
4224              "Help Viewer - Image Composite",ImageCompositeHelp);
4225            (void) XSetFunction(display,windows->image.highlight_context,
4226              GXinvert);
4227            break;
4228          }
4229          default:
4230          {
4231            (void) XBell(display,0);
4232            break;
4233          }
4234        }
4235        break;
4236      }
4237      case MotionNotify:
4238      {
4239        /*
4240          Map and unmap Info widget as text cursor crosses its boundaries.
4241        */
4242        x=event.xmotion.x;
4243        y=event.xmotion.y;
4244        if (windows->info.mapped != MagickFalse)
4245          {
4246            if ((x < (int) (windows->info.x+windows->info.width)) &&
4247                (y < (int) (windows->info.y+windows->info.height)))
4248              (void) XWithdrawWindow(display,windows->info.id,
4249                windows->info.screen);
4250          }
4251        else
4252          if ((x > (int) (windows->info.x+windows->info.width)) ||
4253              (y > (int) (windows->info.y+windows->info.height)))
4254            (void) XMapWindow(display,windows->info.id);
4255        composite_info.x=(ssize_t) windows->image.x+x;
4256        composite_info.y=(ssize_t) windows->image.y+y;
4257        break;
4258      }
4259      default:
4260      {
4261        if (image->debug != MagickFalse)
4262          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4263            event.type);
4264        break;
4265      }
4266    }
4267  } while ((state & ExitState) == 0);
4268  (void) XSelectInput(display,windows->image.id,
4269    windows->image.attributes.event_mask);
4270  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4271  XSetCursorState(display,windows,MagickFalse);
4272  (void) XFreeCursor(display,cursor);
4273  if ((state & EscapeState) != 0)
4274    return(MagickTrue);
4275  /*
4276    Image compositing is relative to image configuration.
4277  */
4278  XSetCursorState(display,windows,MagickTrue);
4279  XCheckRefreshWindows(display,windows);
4280  width=(unsigned int) image->columns;
4281  height=(unsigned int) image->rows;
4282  x=0;
4283  y=0;
4284  if (windows->image.crop_geometry != (char *) NULL)
4285    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4286  scale_factor=(MagickRealType) width/windows->image.ximage->width;
4287  composite_info.x+=x;
4288  composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4289  composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4290  scale_factor=(MagickRealType) height/windows->image.ximage->height;
4291  composite_info.y+=y;
4292  composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4293  composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4294  if ((composite_info.width != composite_image->columns) ||
4295      (composite_info.height != composite_image->rows))
4296    {
4297      Image
4298        *resize_image;
4299
4300      /*
4301        Scale composite image.
4302      */
4303      resize_image=ResizeImage(composite_image,composite_info.width,
4304        composite_info.height,composite_image->filter,composite_image->blur,
4305        exception);
4306      composite_image=DestroyImage(composite_image);
4307      if (resize_image == (Image *) NULL)
4308        {
4309          XSetCursorState(display,windows,MagickFalse);
4310          return(MagickFalse);
4311        }
4312      composite_image=resize_image;
4313    }
4314  if (compose == DisplaceCompositeOp)
4315    (void) SetImageArtifact(composite_image,"compose:args",
4316      displacement_geometry);
4317  if (blend != 0.0)
4318    {
4319      CacheView
4320        *image_view;
4321
4322      int
4323        y;
4324
4325      Quantum
4326        opacity;
4327
4328      register int
4329        x;
4330
4331      register Quantum
4332        *q;
4333
4334      /*
4335        Create mattes for blending.
4336      */
4337      (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,exception);
4338      opacity=(Quantum) (ScaleQuantumToChar((Quantum) QuantumRange)-
4339        ((ssize_t) ScaleQuantumToChar((Quantum) QuantumRange)*blend)/100);
4340      if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
4341        return(MagickFalse);
4342      image->matte=MagickTrue;
4343      image_view=AcquireCacheView(image);
4344      for (y=0; y < (int) image->rows; y++)
4345      {
4346        q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4347          exception);
4348        if (q == (Quantum *) NULL)
4349          break;
4350        for (x=0; x < (int) image->columns; x++)
4351        {
4352          SetPixelAlpha(image,opacity,q);
4353          q+=GetPixelChannels(image);
4354        }
4355        if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4356          break;
4357      }
4358      image_view=DestroyCacheView(image_view);
4359    }
4360  /*
4361    Composite image with X Image window.
4362  */
4363  (void) CompositeImage(image,compose,composite_image,composite_info.x,
4364    composite_info.y,exception);
4365  composite_image=DestroyImage(composite_image);
4366  XSetCursorState(display,windows,MagickFalse);
4367  /*
4368    Update image configuration.
4369  */
4370  XConfigureImageColormap(display,resource_info,windows,image,exception);
4371  (void) XConfigureImage(display,resource_info,windows,image,exception);
4372  return(MagickTrue);
4373}
4374
4375/*
4376%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4377%                                                                             %
4378%                                                                             %
4379%                                                                             %
4380+   X C o n f i g u r e I m a g e                                             %
4381%                                                                             %
4382%                                                                             %
4383%                                                                             %
4384%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4385%
4386%  XConfigureImage() creates a new X image.  It also notifies the window
4387%  manager of the new image size and configures the transient widows.
4388%
4389%  The format of the XConfigureImage method is:
4390%
4391%      MagickBooleanType XConfigureImage(Display *display,
4392%        XResourceInfo *resource_info,XWindows *windows,Image *image,
4393%        ExceptionInfo *exception)
4394%
4395%  A description of each parameter follows:
4396%
4397%    o display: Specifies a connection to an X server; returned from
4398%      XOpenDisplay.
4399%
4400%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4401%
4402%    o windows: Specifies a pointer to a XWindows structure.
4403%
4404%    o image: the image.
4405%
4406%    o exception: return any errors or warnings in this structure.
4407%
4408%    o exception: return any errors or warnings in this structure.
4409%
4410*/
4411static MagickBooleanType XConfigureImage(Display *display,
4412  XResourceInfo *resource_info,XWindows *windows,Image *image,
4413  ExceptionInfo *exception)
4414{
4415  char
4416    geometry[MaxTextExtent];
4417
4418  MagickStatusType
4419    status;
4420
4421  size_t
4422    mask,
4423    height,
4424    width;
4425
4426  ssize_t
4427    x,
4428    y;
4429
4430  XSizeHints
4431    *size_hints;
4432
4433  XWindowChanges
4434    window_changes;
4435
4436  /*
4437    Dismiss if window dimensions are zero.
4438  */
4439  width=(unsigned int) windows->image.window_changes.width;
4440  height=(unsigned int) windows->image.window_changes.height;
4441  if (image->debug != MagickFalse)
4442    (void) LogMagickEvent(X11Event,GetMagickModule(),
4443      "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4444      windows->image.ximage->height,(double) width,(double) height);
4445  if ((width*height) == 0)
4446    return(MagickTrue);
4447  x=0;
4448  y=0;
4449  /*
4450    Resize image to fit Image window dimensions.
4451  */
4452  XSetCursorState(display,windows,MagickTrue);
4453  (void) XFlush(display);
4454  if (((int) width != windows->image.ximage->width) ||
4455      ((int) height != windows->image.ximage->height))
4456    image->taint=MagickTrue;
4457  windows->magnify.x=(int)
4458    width*windows->magnify.x/windows->image.ximage->width;
4459  windows->magnify.y=(int)
4460    height*windows->magnify.y/windows->image.ximage->height;
4461  windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4462  windows->image.y=(int)
4463    (height*windows->image.y/windows->image.ximage->height);
4464  status=XMakeImage(display,resource_info,&windows->image,image,
4465    (unsigned int) width,(unsigned int) height,exception);
4466  if (status == MagickFalse)
4467    XNoticeWidget(display,windows,"Unable to configure X image:",
4468      windows->image.name);
4469  /*
4470    Notify window manager of the new configuration.
4471  */
4472  if (resource_info->image_geometry != (char *) NULL)
4473    (void) FormatLocaleString(geometry,MaxTextExtent,"%s>!",
4474      resource_info->image_geometry);
4475  else
4476    (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
4477      XDisplayWidth(display,windows->image.screen),
4478      XDisplayHeight(display,windows->image.screen));
4479  (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4480  window_changes.width=(int) width;
4481  if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4482    window_changes.width=XDisplayWidth(display,windows->image.screen);
4483  window_changes.height=(int) height;
4484  if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4485    window_changes.height=XDisplayHeight(display,windows->image.screen);
4486  mask=(size_t) (CWWidth | CWHeight);
4487  if (resource_info->backdrop)
4488    {
4489      mask|=CWX | CWY;
4490      window_changes.x=(int)
4491        ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4492      window_changes.y=(int)
4493        ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4494    }
4495  (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4496    (unsigned int) mask,&window_changes);
4497  (void) XClearWindow(display,windows->image.id);
4498  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4499  /*
4500    Update Magnify window configuration.
4501  */
4502  if (windows->magnify.mapped != MagickFalse)
4503    XMakeMagnifyImage(display,windows,exception);
4504  windows->pan.crop_geometry=windows->image.crop_geometry;
4505  XBestIconSize(display,&windows->pan,image);
4506  while (((windows->pan.width << 1) < MaxIconSize) &&
4507         ((windows->pan.height << 1) < MaxIconSize))
4508  {
4509    windows->pan.width<<=1;
4510    windows->pan.height<<=1;
4511  }
4512  if (windows->pan.geometry != (char *) NULL)
4513    (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4514      &windows->pan.width,&windows->pan.height);
4515  window_changes.width=(int) windows->pan.width;
4516  window_changes.height=(int) windows->pan.height;
4517  size_hints=XAllocSizeHints();
4518  if (size_hints != (XSizeHints *) NULL)
4519    {
4520      /*
4521        Set new size hints.
4522      */
4523      size_hints->flags=PSize | PMinSize | PMaxSize;
4524      size_hints->width=window_changes.width;
4525      size_hints->height=window_changes.height;
4526      size_hints->min_width=size_hints->width;
4527      size_hints->min_height=size_hints->height;
4528      size_hints->max_width=size_hints->width;
4529      size_hints->max_height=size_hints->height;
4530      (void) XSetNormalHints(display,windows->pan.id,size_hints);
4531      (void) XFree((void *) size_hints);
4532    }
4533  (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4534    (unsigned int) (CWWidth | CWHeight),&window_changes);
4535  /*
4536    Update icon window configuration.
4537  */
4538  windows->icon.crop_geometry=windows->image.crop_geometry;
4539  XBestIconSize(display,&windows->icon,image);
4540  window_changes.width=(int) windows->icon.width;
4541  window_changes.height=(int) windows->icon.height;
4542  (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4543    (unsigned int) (CWWidth | CWHeight),&window_changes);
4544  XSetCursorState(display,windows,MagickFalse);
4545  return(status != 0 ? MagickTrue : MagickFalse);
4546}
4547
4548/*
4549%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4550%                                                                             %
4551%                                                                             %
4552%                                                                             %
4553+   X C r o p I m a g e                                                       %
4554%                                                                             %
4555%                                                                             %
4556%                                                                             %
4557%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4558%
4559%  XCropImage() allows the user to select a region of the image and crop, copy,
4560%  or cut it.  For copy or cut, the image can subsequently be composited onto
4561%  the image with XPasteImage.
4562%
4563%  The format of the XCropImage method is:
4564%
4565%      MagickBooleanType XCropImage(Display *display,
4566%        XResourceInfo *resource_info,XWindows *windows,Image *image,
4567%        const ClipboardMode mode,ExceptionInfo *exception)
4568%
4569%  A description of each parameter follows:
4570%
4571%    o display: Specifies a connection to an X server; returned from
4572%      XOpenDisplay.
4573%
4574%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4575%
4576%    o windows: Specifies a pointer to a XWindows structure.
4577%
4578%    o image: the image; returned from ReadImage.
4579%
4580%    o mode: This unsigned value specified whether the image should be
4581%      cropped, copied, or cut.
4582%
4583%    o exception: return any errors or warnings in this structure.
4584%
4585*/
4586static MagickBooleanType XCropImage(Display *display,
4587  XResourceInfo *resource_info,XWindows *windows,Image *image,
4588  const ClipboardMode mode,ExceptionInfo *exception)
4589{
4590  static const char
4591    *CropModeMenu[] =
4592    {
4593      "Help",
4594      "Dismiss",
4595      (char *) NULL
4596    },
4597    *RectifyModeMenu[] =
4598    {
4599      "Crop",
4600      "Help",
4601      "Dismiss",
4602      (char *) NULL
4603    };
4604
4605  static const ModeType
4606    CropCommands[] =
4607    {
4608      CropHelpCommand,
4609      CropDismissCommand
4610    },
4611    RectifyCommands[] =
4612    {
4613      RectifyCopyCommand,
4614      RectifyHelpCommand,
4615      RectifyDismissCommand
4616    };
4617
4618  CacheView
4619    *image_view;
4620
4621  char
4622    command[MaxTextExtent],
4623    text[MaxTextExtent];
4624
4625  Cursor
4626    cursor;
4627
4628  int
4629    id,
4630    x,
4631    y;
4632
4633  KeySym
4634    key_symbol;
4635
4636  Image
4637    *crop_image;
4638
4639  MagickRealType
4640    scale_factor;
4641
4642  RectangleInfo
4643    crop_info,
4644    highlight_info;
4645
4646  register Quantum
4647    *q;
4648
4649  unsigned int
4650    height,
4651    width;
4652
4653  size_t
4654    state;
4655
4656  XEvent
4657    event;
4658
4659  /*
4660    Map Command widget.
4661  */
4662  switch (mode)
4663  {
4664    case CopyMode:
4665    {
4666      (void) CloneString(&windows->command.name,"Copy");
4667      break;
4668    }
4669    case CropMode:
4670    {
4671      (void) CloneString(&windows->command.name,"Crop");
4672      break;
4673    }
4674    case CutMode:
4675    {
4676      (void) CloneString(&windows->command.name,"Cut");
4677      break;
4678    }
4679  }
4680  RectifyModeMenu[0]=windows->command.name;
4681  windows->command.data=0;
4682  (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4683  (void) XMapRaised(display,windows->command.id);
4684  XClientMessage(display,windows->image.id,windows->im_protocols,
4685    windows->im_update_widget,CurrentTime);
4686  /*
4687    Track pointer until button 1 is pressed.
4688  */
4689  XQueryPosition(display,windows->image.id,&x,&y);
4690  (void) XSelectInput(display,windows->image.id,
4691    windows->image.attributes.event_mask | PointerMotionMask);
4692  crop_info.x=(ssize_t) windows->image.x+x;
4693  crop_info.y=(ssize_t) windows->image.y+y;
4694  crop_info.width=0;
4695  crop_info.height=0;
4696  cursor=XCreateFontCursor(display,XC_fleur);
4697  state=DefaultState;
4698  do
4699  {
4700    if (windows->info.mapped != MagickFalse)
4701      {
4702        /*
4703          Display pointer position.
4704        */
4705        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4706          (long) crop_info.x,(long) crop_info.y);
4707        XInfoWidget(display,windows,text);
4708      }
4709    /*
4710      Wait for next event.
4711    */
4712    XScreenEvent(display,windows,&event,exception);
4713    if (event.xany.window == windows->command.id)
4714      {
4715        /*
4716          Select a command from the Command widget.
4717        */
4718        id=XCommandWidget(display,windows,CropModeMenu,&event);
4719        if (id < 0)
4720          continue;
4721        switch (CropCommands[id])
4722        {
4723          case CropHelpCommand:
4724          {
4725            switch (mode)
4726            {
4727              case CopyMode:
4728              {
4729                XTextViewWidget(display,resource_info,windows,MagickFalse,
4730                  "Help Viewer - Image Copy",ImageCopyHelp);
4731                break;
4732              }
4733              case CropMode:
4734              {
4735                XTextViewWidget(display,resource_info,windows,MagickFalse,
4736                  "Help Viewer - Image Crop",ImageCropHelp);
4737                break;
4738              }
4739              case CutMode:
4740              {
4741                XTextViewWidget(display,resource_info,windows,MagickFalse,
4742                  "Help Viewer - Image Cut",ImageCutHelp);
4743                break;
4744              }
4745            }
4746            break;
4747          }
4748          case CropDismissCommand:
4749          {
4750            /*
4751              Prematurely exit.
4752            */
4753            state|=EscapeState;
4754            state|=ExitState;
4755            break;
4756          }
4757          default:
4758            break;
4759        }
4760        continue;
4761      }
4762    switch (event.type)
4763    {
4764      case ButtonPress:
4765      {
4766        if (event.xbutton.button != Button1)
4767          break;
4768        if (event.xbutton.window != windows->image.id)
4769          break;
4770        /*
4771          Note first corner of cropping rectangle-- exit loop.
4772        */
4773        (void) XCheckDefineCursor(display,windows->image.id,cursor);
4774        crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4775        crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4776        state|=ExitState;
4777        break;
4778      }
4779      case ButtonRelease:
4780        break;
4781      case Expose:
4782        break;
4783      case KeyPress:
4784      {
4785        if (event.xkey.window != windows->image.id)
4786          break;
4787        /*
4788          Respond to a user key press.
4789        */
4790        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4791          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4792        switch ((int) key_symbol)
4793        {
4794          case XK_Escape:
4795          case XK_F20:
4796          {
4797            /*
4798              Prematurely exit.
4799            */
4800            state|=EscapeState;
4801            state|=ExitState;
4802            break;
4803          }
4804          case XK_F1:
4805          case XK_Help:
4806          {
4807            switch (mode)
4808            {
4809              case CopyMode:
4810              {
4811                XTextViewWidget(display,resource_info,windows,MagickFalse,
4812                  "Help Viewer - Image Copy",ImageCopyHelp);
4813                break;
4814              }
4815              case CropMode:
4816              {
4817                XTextViewWidget(display,resource_info,windows,MagickFalse,
4818                  "Help Viewer - Image Crop",ImageCropHelp);
4819                break;
4820              }
4821              case CutMode:
4822              {
4823                XTextViewWidget(display,resource_info,windows,MagickFalse,
4824                  "Help Viewer - Image Cut",ImageCutHelp);
4825                break;
4826              }
4827            }
4828            break;
4829          }
4830          default:
4831          {
4832            (void) XBell(display,0);
4833            break;
4834          }
4835        }
4836        break;
4837      }
4838      case MotionNotify:
4839      {
4840        if (event.xmotion.window != windows->image.id)
4841          break;
4842        /*
4843          Map and unmap Info widget as text cursor crosses its boundaries.
4844        */
4845        x=event.xmotion.x;
4846        y=event.xmotion.y;
4847        if (windows->info.mapped != MagickFalse)
4848          {
4849            if ((x < (int) (windows->info.x+windows->info.width)) &&
4850                (y < (int) (windows->info.y+windows->info.height)))
4851              (void) XWithdrawWindow(display,windows->info.id,
4852                windows->info.screen);
4853          }
4854        else
4855          if ((x > (int) (windows->info.x+windows->info.width)) ||
4856              (y > (int) (windows->info.y+windows->info.height)))
4857            (void) XMapWindow(display,windows->info.id);
4858        crop_info.x=(ssize_t) windows->image.x+x;
4859        crop_info.y=(ssize_t) windows->image.y+y;
4860        break;
4861      }
4862      default:
4863        break;
4864    }
4865  } while ((state & ExitState) == 0);
4866  (void) XSelectInput(display,windows->image.id,
4867    windows->image.attributes.event_mask);
4868  if ((state & EscapeState) != 0)
4869    {
4870      /*
4871        User want to exit without cropping.
4872      */
4873      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4874      (void) XFreeCursor(display,cursor);
4875      return(MagickTrue);
4876    }
4877  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4878  do
4879  {
4880    /*
4881      Size rectangle as pointer moves until the mouse button is released.
4882    */
4883    x=(int) crop_info.x;
4884    y=(int) crop_info.y;
4885    crop_info.width=0;
4886    crop_info.height=0;
4887    state=DefaultState;
4888    do
4889    {
4890      highlight_info=crop_info;
4891      highlight_info.x=crop_info.x-windows->image.x;
4892      highlight_info.y=crop_info.y-windows->image.y;
4893      if ((highlight_info.width > 3) && (highlight_info.height > 3))
4894        {
4895          /*
4896            Display info and draw cropping rectangle.
4897          */
4898          if (windows->info.mapped == MagickFalse)
4899            (void) XMapWindow(display,windows->info.id);
4900          (void) FormatLocaleString(text,MaxTextExtent,
4901            " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4902            crop_info.height,(double) crop_info.x,(double) crop_info.y);
4903          XInfoWidget(display,windows,text);
4904          XHighlightRectangle(display,windows->image.id,
4905            windows->image.highlight_context,&highlight_info);
4906        }
4907      else
4908        if (windows->info.mapped != MagickFalse)
4909          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4910      /*
4911        Wait for next event.
4912      */
4913      XScreenEvent(display,windows,&event,exception);
4914      if ((highlight_info.width > 3) && (highlight_info.height > 3))
4915        XHighlightRectangle(display,windows->image.id,
4916          windows->image.highlight_context,&highlight_info);
4917      switch (event.type)
4918      {
4919        case ButtonPress:
4920        {
4921          crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4922          crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4923          break;
4924        }
4925        case ButtonRelease:
4926        {
4927          /*
4928            User has committed to cropping rectangle.
4929          */
4930          crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4931          crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4932          XSetCursorState(display,windows,MagickFalse);
4933          state|=ExitState;
4934          windows->command.data=0;
4935          (void) XCommandWidget(display,windows,RectifyModeMenu,
4936            (XEvent *) NULL);
4937          break;
4938        }
4939        case Expose:
4940          break;
4941        case MotionNotify:
4942        {
4943          crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4944          crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4945        }
4946        default:
4947          break;
4948      }
4949      if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4950          ((state & ExitState) != 0))
4951        {
4952          /*
4953            Check boundary conditions.
4954          */
4955          if (crop_info.x < 0)
4956            crop_info.x=0;
4957          else
4958            if (crop_info.x > (ssize_t) windows->image.ximage->width)
4959              crop_info.x=(ssize_t) windows->image.ximage->width;
4960          if ((int) crop_info.x < x)
4961            crop_info.width=(unsigned int) (x-crop_info.x);
4962          else
4963            {
4964              crop_info.width=(unsigned int) (crop_info.x-x);
4965              crop_info.x=(ssize_t) x;
4966            }
4967          if (crop_info.y < 0)
4968            crop_info.y=0;
4969          else
4970            if (crop_info.y > (ssize_t) windows->image.ximage->height)
4971              crop_info.y=(ssize_t) windows->image.ximage->height;
4972          if ((int) crop_info.y < y)
4973            crop_info.height=(unsigned int) (y-crop_info.y);
4974          else
4975            {
4976              crop_info.height=(unsigned int) (crop_info.y-y);
4977              crop_info.y=(ssize_t) y;
4978            }
4979        }
4980    } while ((state & ExitState) == 0);
4981    /*
4982      Wait for user to grab a corner of the rectangle or press return.
4983    */
4984    state=DefaultState;
4985    (void) XMapWindow(display,windows->info.id);
4986    do
4987    {
4988      if (windows->info.mapped != MagickFalse)
4989        {
4990          /*
4991            Display pointer position.
4992          */
4993          (void) FormatLocaleString(text,MaxTextExtent,
4994            " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4995            crop_info.height,(double) crop_info.x,(double) crop_info.y);
4996          XInfoWidget(display,windows,text);
4997        }
4998      highlight_info=crop_info;
4999      highlight_info.x=crop_info.x-windows->image.x;
5000      highlight_info.y=crop_info.y-windows->image.y;
5001      if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
5002        {
5003          state|=EscapeState;
5004          state|=ExitState;
5005          break;
5006        }
5007      XHighlightRectangle(display,windows->image.id,
5008        windows->image.highlight_context,&highlight_info);
5009      XScreenEvent(display,windows,&event,exception);
5010      if (event.xany.window == windows->command.id)
5011        {
5012          /*
5013            Select a command from the Command widget.
5014          */
5015          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5016          id=XCommandWidget(display,windows,RectifyModeMenu,&event);
5017          (void) XSetFunction(display,windows->image.highlight_context,
5018            GXinvert);
5019          XHighlightRectangle(display,windows->image.id,
5020            windows->image.highlight_context,&highlight_info);
5021          if (id >= 0)
5022            switch (RectifyCommands[id])
5023            {
5024              case RectifyCopyCommand:
5025              {
5026                state|=ExitState;
5027                break;
5028              }
5029              case RectifyHelpCommand:
5030              {
5031                (void) XSetFunction(display,windows->image.highlight_context,
5032                  GXcopy);
5033                switch (mode)
5034                {
5035                  case CopyMode:
5036                  {
5037                    XTextViewWidget(display,resource_info,windows,MagickFalse,
5038                      "Help Viewer - Image Copy",ImageCopyHelp);
5039                    break;
5040                  }
5041                  case CropMode:
5042                  {
5043                    XTextViewWidget(display,resource_info,windows,MagickFalse,
5044                      "Help Viewer - Image Crop",ImageCropHelp);
5045                    break;
5046                  }
5047                  case CutMode:
5048                  {
5049                    XTextViewWidget(display,resource_info,windows,MagickFalse,
5050                      "Help Viewer - Image Cut",ImageCutHelp);
5051                    break;
5052                  }
5053                }
5054                (void) XSetFunction(display,windows->image.highlight_context,
5055                  GXinvert);
5056                break;
5057              }
5058              case RectifyDismissCommand:
5059              {
5060                /*
5061                  Prematurely exit.
5062                */
5063                state|=EscapeState;
5064                state|=ExitState;
5065                break;
5066              }
5067              default:
5068                break;
5069            }
5070          continue;
5071        }
5072      XHighlightRectangle(display,windows->image.id,
5073        windows->image.highlight_context,&highlight_info);
5074      switch (event.type)
5075      {
5076        case ButtonPress:
5077        {
5078          if (event.xbutton.button != Button1)
5079            break;
5080          if (event.xbutton.window != windows->image.id)
5081            break;
5082          x=windows->image.x+event.xbutton.x;
5083          y=windows->image.y+event.xbutton.y;
5084          if ((x < (int) (crop_info.x+RoiDelta)) &&
5085              (x > (int) (crop_info.x-RoiDelta)) &&
5086              (y < (int) (crop_info.y+RoiDelta)) &&
5087              (y > (int) (crop_info.y-RoiDelta)))
5088            {
5089              crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5090              crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5091              state|=UpdateConfigurationState;
5092              break;
5093            }
5094          if ((x < (int) (crop_info.x+RoiDelta)) &&
5095              (x > (int) (crop_info.x-RoiDelta)) &&
5096              (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5097              (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5098            {
5099              crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5100              state|=UpdateConfigurationState;
5101              break;
5102            }
5103          if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5104              (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5105              (y < (int) (crop_info.y+RoiDelta)) &&
5106              (y > (int) (crop_info.y-RoiDelta)))
5107            {
5108              crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5109              state|=UpdateConfigurationState;
5110              break;
5111            }
5112          if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5113              (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5114              (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5115              (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5116            {
5117              state|=UpdateConfigurationState;
5118              break;
5119            }
5120        }
5121        case ButtonRelease:
5122        {
5123          if (event.xbutton.window == windows->pan.id)
5124            if ((highlight_info.x != crop_info.x-windows->image.x) ||
5125                (highlight_info.y != crop_info.y-windows->image.y))
5126              XHighlightRectangle(display,windows->image.id,
5127                windows->image.highlight_context,&highlight_info);
5128          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5129            event.xbutton.time);
5130          break;
5131        }
5132        case Expose:
5133        {
5134          if (event.xexpose.window == windows->image.id)
5135            if (event.xexpose.count == 0)
5136              {
5137                event.xexpose.x=(int) highlight_info.x;
5138                event.xexpose.y=(int) highlight_info.y;
5139                event.xexpose.width=(int) highlight_info.width;
5140                event.xexpose.height=(int) highlight_info.height;
5141                XRefreshWindow(display,&windows->image,&event);
5142              }
5143          if (event.xexpose.window == windows->info.id)
5144            if (event.xexpose.count == 0)
5145              XInfoWidget(display,windows,text);
5146          break;
5147        }
5148        case KeyPress:
5149        {
5150          if (event.xkey.window != windows->image.id)
5151            break;
5152          /*
5153            Respond to a user key press.
5154          */
5155          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5156            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5157          switch ((int) key_symbol)
5158          {
5159            case XK_Escape:
5160            case XK_F20:
5161              state|=EscapeState;
5162            case XK_Return:
5163            {
5164              state|=ExitState;
5165              break;
5166            }
5167            case XK_Home:
5168            case XK_KP_Home:
5169            {
5170              crop_info.x=(ssize_t) (windows->image.width/2L-
5171                crop_info.width/2L);
5172              crop_info.y=(ssize_t) (windows->image.height/2L-
5173                crop_info.height/2L);
5174              break;
5175            }
5176            case XK_Left:
5177            case XK_KP_Left:
5178            {
5179              crop_info.x--;
5180              break;
5181            }
5182            case XK_Up:
5183            case XK_KP_Up:
5184            case XK_Next:
5185            {
5186              crop_info.y--;
5187              break;
5188            }
5189            case XK_Right:
5190            case XK_KP_Right:
5191            {
5192              crop_info.x++;
5193              break;
5194            }
5195            case XK_Prior:
5196            case XK_Down:
5197            case XK_KP_Down:
5198            {
5199              crop_info.y++;
5200              break;
5201            }
5202            case XK_F1:
5203            case XK_Help:
5204            {
5205              (void) XSetFunction(display,windows->image.highlight_context,
5206                GXcopy);
5207              switch (mode)
5208              {
5209                case CopyMode:
5210                {
5211                  XTextViewWidget(display,resource_info,windows,MagickFalse,
5212                    "Help Viewer - Image Copy",ImageCopyHelp);
5213                  break;
5214                }
5215                case CropMode:
5216                {
5217                  XTextViewWidget(display,resource_info,windows,MagickFalse,
5218                    "Help Viewer - Image Cropg",ImageCropHelp);
5219                  break;
5220                }
5221                case CutMode:
5222                {
5223                  XTextViewWidget(display,resource_info,windows,MagickFalse,
5224                    "Help Viewer - Image Cutg",ImageCutHelp);
5225                  break;
5226                }
5227              }
5228              (void) XSetFunction(display,windows->image.highlight_context,
5229                GXinvert);
5230              break;
5231            }
5232            default:
5233            {
5234              (void) XBell(display,0);
5235              break;
5236            }
5237          }
5238          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5239            event.xkey.time);
5240          break;
5241        }
5242        case KeyRelease:
5243          break;
5244        case MotionNotify:
5245        {
5246          if (event.xmotion.window != windows->image.id)
5247            break;
5248          /*
5249            Map and unmap Info widget as text cursor crosses its boundaries.
5250          */
5251          x=event.xmotion.x;
5252          y=event.xmotion.y;
5253          if (windows->info.mapped != MagickFalse)
5254            {
5255              if ((x < (int) (windows->info.x+windows->info.width)) &&
5256                  (y < (int) (windows->info.y+windows->info.height)))
5257                (void) XWithdrawWindow(display,windows->info.id,
5258                  windows->info.screen);
5259            }
5260          else
5261            if ((x > (int) (windows->info.x+windows->info.width)) ||
5262                (y > (int) (windows->info.y+windows->info.height)))
5263              (void) XMapWindow(display,windows->info.id);
5264          crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5265          crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5266          break;
5267        }
5268        case SelectionRequest:
5269        {
5270          XSelectionEvent
5271            notify;
5272
5273          XSelectionRequestEvent
5274            *request;
5275
5276          /*
5277            Set primary selection.
5278          */
5279          (void) FormatLocaleString(text,MaxTextExtent,
5280            "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5281            crop_info.height,(double) crop_info.x,(double) crop_info.y);
5282          request=(&(event.xselectionrequest));
5283          (void) XChangeProperty(request->display,request->requestor,
5284            request->property,request->target,8,PropModeReplace,
5285            (unsigned char *) text,(int) strlen(text));
5286          notify.type=SelectionNotify;
5287          notify.display=request->display;
5288          notify.requestor=request->requestor;
5289          notify.selection=request->selection;
5290          notify.target=request->target;
5291          notify.time=request->time;
5292          if (request->property == None)
5293            notify.property=request->target;
5294          else
5295            notify.property=request->property;
5296          (void) XSendEvent(request->display,request->requestor,False,0,
5297            (XEvent *) &notify);
5298        }
5299        default:
5300          break;
5301      }
5302      if ((state & UpdateConfigurationState) != 0)
5303        {
5304          (void) XPutBackEvent(display,&event);
5305          (void) XCheckDefineCursor(display,windows->image.id,cursor);
5306          break;
5307        }
5308    } while ((state & ExitState) == 0);
5309  } while ((state & ExitState) == 0);
5310  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5311  XSetCursorState(display,windows,MagickFalse);
5312  if ((state & EscapeState) != 0)
5313    return(MagickTrue);
5314  if (mode == CropMode)
5315    if (((int) crop_info.width != windows->image.ximage->width) ||
5316        ((int) crop_info.height != windows->image.ximage->height))
5317      {
5318        /*
5319          Reconfigure Image window as defined by cropping rectangle.
5320        */
5321        XSetCropGeometry(display,windows,&crop_info,image);
5322        windows->image.window_changes.width=(int) crop_info.width;
5323        windows->image.window_changes.height=(int) crop_info.height;
5324        (void) XConfigureImage(display,resource_info,windows,image,exception);
5325        return(MagickTrue);
5326      }
5327  /*
5328    Copy image before applying image transforms.
5329  */
5330  XSetCursorState(display,windows,MagickTrue);
5331  XCheckRefreshWindows(display,windows);
5332  width=(unsigned int) image->columns;
5333  height=(unsigned int) image->rows;
5334  x=0;
5335  y=0;
5336  if (windows->image.crop_geometry != (char *) NULL)
5337    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5338  scale_factor=(MagickRealType) width/windows->image.ximage->width;
5339  crop_info.x+=x;
5340  crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5341  crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5342  scale_factor=(MagickRealType) height/windows->image.ximage->height;
5343  crop_info.y+=y;
5344  crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5345  crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5346  crop_image=CropImage(image,&crop_info,exception);
5347  XSetCursorState(display,windows,MagickFalse);
5348  if (crop_image == (Image *) NULL)
5349    return(MagickFalse);
5350  if (resource_info->copy_image != (Image *) NULL)
5351    resource_info->copy_image=DestroyImage(resource_info->copy_image);
5352  resource_info->copy_image=crop_image;
5353  if (mode == CopyMode)
5354    {
5355      (void) XConfigureImage(display,resource_info,windows,image,exception);
5356      return(MagickTrue);
5357    }
5358  /*
5359    Cut image.
5360  */
5361  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
5362    return(MagickFalse);
5363  image->matte=MagickTrue;
5364  image_view=AcquireCacheView(image);
5365  for (y=0; y < (int) crop_info.height; y++)
5366  {
5367    q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5368      crop_info.width,1,exception);
5369    if (q == (Quantum *) NULL)
5370      break;
5371    for (x=0; x < (int) crop_info.width; x++)
5372    {
5373      SetPixelAlpha(image,TransparentAlpha,q);
5374      q+=GetPixelChannels(image);
5375    }
5376    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5377      break;
5378  }
5379  image_view=DestroyCacheView(image_view);
5380  /*
5381    Update image configuration.
5382  */
5383  XConfigureImageColormap(display,resource_info,windows,image,exception);
5384  (void) XConfigureImage(display,resource_info,windows,image,exception);
5385  return(MagickTrue);
5386}
5387
5388/*
5389%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5390%                                                                             %
5391%                                                                             %
5392%                                                                             %
5393+   X D r a w I m a g e                                                       %
5394%                                                                             %
5395%                                                                             %
5396%                                                                             %
5397%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5398%
5399%  XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5400%  the image.
5401%
5402%  The format of the XDrawEditImage method is:
5403%
5404%      MagickBooleanType XDrawEditImage(Display *display,
5405%        XResourceInfo *resource_info,XWindows *windows,Image **image,
5406%        ExceptionInfo *exception)
5407%
5408%  A description of each parameter follows:
5409%
5410%    o display: Specifies a connection to an X server; returned from
5411%      XOpenDisplay.
5412%
5413%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5414%
5415%    o windows: Specifies a pointer to a XWindows structure.
5416%
5417%    o image: the image.
5418%
5419%    o exception: return any errors or warnings in this structure.
5420%
5421*/
5422static MagickBooleanType XDrawEditImage(Display *display,
5423  XResourceInfo *resource_info,XWindows *windows,Image **image,
5424  ExceptionInfo *exception)
5425{
5426  static const char
5427    *DrawMenu[] =
5428    {
5429      "Element",
5430      "Color",
5431      "Stipple",
5432      "Width",
5433      "Undo",
5434      "Help",
5435      "Dismiss",
5436      (char *) NULL
5437    };
5438
5439  static ElementType
5440    element = PointElement;
5441
5442  static const ModeType
5443    DrawCommands[] =
5444    {
5445      DrawElementCommand,
5446      DrawColorCommand,
5447      DrawStippleCommand,
5448      DrawWidthCommand,
5449      DrawUndoCommand,
5450      DrawHelpCommand,
5451      DrawDismissCommand
5452    };
5453
5454  static Pixmap
5455    stipple = (Pixmap) NULL;
5456
5457  static unsigned int
5458    pen_id = 0,
5459    line_width = 1;
5460
5461  char
5462    command[MaxTextExtent],
5463    text[MaxTextExtent];
5464
5465  Cursor
5466    cursor;
5467
5468  int
5469    entry,
5470    id,
5471    number_coordinates,
5472    x,
5473    y;
5474
5475  MagickRealType
5476    degrees;
5477
5478  MagickStatusType
5479    status;
5480
5481  RectangleInfo
5482    rectangle_info;
5483
5484  register int
5485    i;
5486
5487  unsigned int
5488    distance,
5489    height,
5490    max_coordinates,
5491    width;
5492
5493  size_t
5494    state;
5495
5496  Window
5497    root_window;
5498
5499  XDrawInfo
5500    draw_info;
5501
5502  XEvent
5503    event;
5504
5505  XPoint
5506    *coordinate_info;
5507
5508  XSegment
5509    line_info;
5510
5511  /*
5512    Allocate polygon info.
5513  */
5514  max_coordinates=2048;
5515  coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5516    sizeof(*coordinate_info));
5517  if (coordinate_info == (XPoint *) NULL)
5518    {
5519      (void) ThrowMagickException(exception,GetMagickModule(),
5520        ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5521      return(MagickFalse);
5522    }
5523  /*
5524    Map Command widget.
5525  */
5526  (void) CloneString(&windows->command.name,"Draw");
5527  windows->command.data=4;
5528  (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5529  (void) XMapRaised(display,windows->command.id);
5530  XClientMessage(display,windows->image.id,windows->im_protocols,
5531    windows->im_update_widget,CurrentTime);
5532  /*
5533    Wait for first button press.
5534  */
5535  root_window=XRootWindow(display,XDefaultScreen(display));
5536  draw_info.stencil=OpaqueStencil;
5537  status=MagickTrue;
5538  cursor=XCreateFontCursor(display,XC_tcross);
5539  for ( ; ; )
5540  {
5541    XQueryPosition(display,windows->image.id,&x,&y);
5542    (void) XSelectInput(display,windows->image.id,
5543      windows->image.attributes.event_mask | PointerMotionMask);
5544    (void) XCheckDefineCursor(display,windows->image.id,cursor);
5545    state=DefaultState;
5546    do
5547    {
5548      if (windows->info.mapped != MagickFalse)
5549        {
5550          /*
5551            Display pointer position.
5552          */
5553          (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
5554            x+windows->image.x,y+windows->image.y);
5555          XInfoWidget(display,windows,text);
5556        }
5557      /*
5558        Wait for next event.
5559      */
5560      XScreenEvent(display,windows,&event,exception);
5561      if (event.xany.window == windows->command.id)
5562        {
5563          /*
5564            Select a command from the Command widget.
5565          */
5566          id=XCommandWidget(display,windows,DrawMenu,&event);
5567          if (id < 0)
5568            continue;
5569          switch (DrawCommands[id])
5570          {
5571            case DrawElementCommand:
5572            {
5573              static const char
5574                *Elements[] =
5575                {
5576                  "point",
5577                  "line",
5578                  "rectangle",
5579                  "fill rectangle",
5580                  "circle",
5581                  "fill circle",
5582                  "ellipse",
5583                  "fill ellipse",
5584                  "polygon",
5585                  "fill polygon",
5586                  (char *) NULL,
5587                };
5588
5589              /*
5590                Select a command from the pop-up menu.
5591              */
5592              element=(ElementType) (XMenuWidget(display,windows,
5593                DrawMenu[id],Elements,command)+1);
5594              break;
5595            }
5596            case DrawColorCommand:
5597            {
5598              const char
5599                *ColorMenu[MaxNumberPens+1];
5600
5601              int
5602                pen_number;
5603
5604              MagickBooleanType
5605                transparent;
5606
5607              XColor
5608                color;
5609
5610              /*
5611                Initialize menu selections.
5612              */
5613              for (i=0; i < (int) (MaxNumberPens-2); i++)
5614                ColorMenu[i]=resource_info->pen_colors[i];
5615              ColorMenu[MaxNumberPens-2]="transparent";
5616              ColorMenu[MaxNumberPens-1]="Browser...";
5617              ColorMenu[MaxNumberPens]=(char *) NULL;
5618              /*
5619                Select a pen color from the pop-up menu.
5620              */
5621              pen_number=XMenuWidget(display,windows,DrawMenu[id],
5622                (const char **) ColorMenu,command);
5623              if (pen_number < 0)
5624                break;
5625              transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5626                MagickFalse;
5627              if (transparent != MagickFalse)
5628                {
5629                  draw_info.stencil=TransparentStencil;
5630                  break;
5631                }
5632              if (pen_number == (MaxNumberPens-1))
5633                {
5634                  static char
5635                    color_name[MaxTextExtent] = "gray";
5636
5637                  /*
5638                    Select a pen color from a dialog.
5639                  */
5640                  resource_info->pen_colors[pen_number]=color_name;
5641                  XColorBrowserWidget(display,windows,"Select",color_name);
5642                  if (*color_name == '\0')
5643                    break;
5644                }
5645              /*
5646                Set pen color.
5647              */
5648              (void) XParseColor(display,windows->map_info->colormap,
5649                resource_info->pen_colors[pen_number],&color);
5650              XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5651                (unsigned int) MaxColors,&color);
5652              windows->pixel_info->pen_colors[pen_number]=color;
5653              pen_id=(unsigned int) pen_number;
5654              draw_info.stencil=OpaqueStencil;
5655              break;
5656            }
5657            case DrawStippleCommand:
5658            {
5659              Image
5660                *stipple_image;
5661
5662              ImageInfo
5663                *image_info;
5664
5665              int
5666                status;
5667
5668              static char
5669                filename[MaxTextExtent] = "\0";
5670
5671              static const char
5672                *StipplesMenu[] =
5673                {
5674                  "Brick",
5675                  "Diagonal",
5676                  "Scales",
5677                  "Vertical",
5678                  "Wavy",
5679                  "Translucent",
5680                  "Opaque",
5681                  (char *) NULL,
5682                  (char *) NULL,
5683                };
5684
5685              /*
5686                Select a command from the pop-up menu.
5687              */
5688              StipplesMenu[7]="Open...";
5689              entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5690                command);
5691              if (entry < 0)
5692                break;
5693              if (stipple != (Pixmap) NULL)
5694                (void) XFreePixmap(display,stipple);
5695              stipple=(Pixmap) NULL;
5696              if (entry != 7)
5697                {
5698                  switch (entry)
5699                  {
5700                    case 0:
5701                    {
5702                      stipple=XCreateBitmapFromData(display,root_window,
5703                        (char *) BricksBitmap,BricksWidth,BricksHeight);
5704                      break;
5705                    }
5706                    case 1:
5707                    {
5708                      stipple=XCreateBitmapFromData(display,root_window,
5709                        (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5710                      break;
5711                    }
5712                    case 2:
5713                    {
5714                      stipple=XCreateBitmapFromData(display,root_window,
5715                        (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5716                      break;
5717                    }
5718                    case 3:
5719                    {
5720                      stipple=XCreateBitmapFromData(display,root_window,
5721                        (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5722                      break;
5723                    }
5724                    case 4:
5725                    {
5726                      stipple=XCreateBitmapFromData(display,root_window,
5727                        (char *) WavyBitmap,WavyWidth,WavyHeight);
5728                      break;
5729                    }
5730                    case 5:
5731                    {
5732                      stipple=XCreateBitmapFromData(display,root_window,
5733                        (char *) HighlightBitmap,HighlightWidth,
5734                        HighlightHeight);
5735                      break;
5736                    }
5737                    case 6:
5738                    default:
5739                    {
5740                      stipple=XCreateBitmapFromData(display,root_window,
5741                        (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5742                      break;
5743                    }
5744                  }
5745                  break;
5746                }
5747              XFileBrowserWidget(display,windows,"Stipple",filename);
5748              if (*filename == '\0')
5749                break;
5750              /*
5751                Read image.
5752              */
5753              XSetCursorState(display,windows,MagickTrue);
5754              XCheckRefreshWindows(display,windows);
5755              image_info=AcquireImageInfo();
5756              (void) CopyMagickString(image_info->filename,filename,
5757                MaxTextExtent);
5758              stipple_image=ReadImage(image_info,exception);
5759              CatchException(exception);
5760              XSetCursorState(display,windows,MagickFalse);
5761              if (stipple_image == (Image *) NULL)
5762                break;
5763              (void) AcquireUniqueFileResource(filename);
5764              (void) FormatLocaleString(stipple_image->filename,MaxTextExtent,
5765                "xbm:%s",filename);
5766              (void) WriteImage(image_info,stipple_image,exception);
5767              stipple_image=DestroyImage(stipple_image);
5768              image_info=DestroyImageInfo(image_info);
5769              status=XReadBitmapFile(display,root_window,filename,&width,
5770                &height,&stipple,&x,&y);
5771              (void) RelinquishUniqueFileResource(filename);
5772              if ((status != BitmapSuccess) != 0)
5773                XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5774                  filename);
5775              break;
5776            }
5777            case DrawWidthCommand:
5778            {
5779              static char
5780                width[MaxTextExtent] = "0";
5781
5782              static const char
5783                *WidthsMenu[] =
5784                {
5785                  "1",
5786                  "2",
5787                  "4",
5788                  "8",
5789                  "16",
5790                  "Dialog...",
5791                  (char *) NULL,
5792                };
5793
5794              /*
5795                Select a command from the pop-up menu.
5796              */
5797              entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5798                command);
5799              if (entry < 0)
5800                break;
5801              if (entry != 5)
5802                {
5803                  line_width=(unsigned int) StringToUnsignedLong(
5804                    WidthsMenu[entry]);
5805                  break;
5806                }
5807              (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5808                width);
5809              if (*width == '\0')
5810                break;
5811              line_width=(unsigned int) StringToUnsignedLong(width);
5812              break;
5813            }
5814            case DrawUndoCommand:
5815            {
5816              (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5817                image,exception);
5818              break;
5819            }
5820            case DrawHelpCommand:
5821            {
5822              XTextViewWidget(display,resource_info,windows,MagickFalse,
5823                "Help Viewer - Image Rotation",ImageDrawHelp);
5824              (void) XCheckDefineCursor(display,windows->image.id,cursor);
5825              break;
5826            }
5827            case DrawDismissCommand:
5828            {
5829              /*
5830                Prematurely exit.
5831              */
5832              state|=EscapeState;
5833              state|=ExitState;
5834              break;
5835            }
5836            default:
5837              break;
5838          }
5839          (void) XCheckDefineCursor(display,windows->image.id,cursor);
5840          continue;
5841        }
5842      switch (event.type)
5843      {
5844        case ButtonPress:
5845        {
5846          if (event.xbutton.button != Button1)
5847            break;
5848          if (event.xbutton.window != windows->image.id)
5849            break;
5850          /*
5851            exit loop.
5852          */
5853          x=event.xbutton.x;
5854          y=event.xbutton.y;
5855          state|=ExitState;
5856          break;
5857        }
5858        case ButtonRelease:
5859          break;
5860        case Expose:
5861          break;
5862        case KeyPress:
5863        {
5864          KeySym
5865            key_symbol;
5866
5867          if (event.xkey.window != windows->image.id)
5868            break;
5869          /*
5870            Respond to a user key press.
5871          */
5872          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5873            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5874          switch ((int) key_symbol)
5875          {
5876            case XK_Escape:
5877            case XK_F20:
5878            {
5879              /*
5880                Prematurely exit.
5881              */
5882              state|=EscapeState;
5883              state|=ExitState;
5884              break;
5885            }
5886            case XK_F1:
5887            case XK_Help:
5888            {
5889              XTextViewWidget(display,resource_info,windows,MagickFalse,
5890                "Help Viewer - Image Rotation",ImageDrawHelp);
5891              break;
5892            }
5893            default:
5894            {
5895              (void) XBell(display,0);
5896              break;
5897            }
5898          }
5899          break;
5900        }
5901        case MotionNotify:
5902        {
5903          /*
5904            Map and unmap Info widget as text cursor crosses its boundaries.
5905          */
5906          x=event.xmotion.x;
5907          y=event.xmotion.y;
5908          if (windows->info.mapped != MagickFalse)
5909            {
5910              if ((x < (int) (windows->info.x+windows->info.width)) &&
5911                  (y < (int) (windows->info.y+windows->info.height)))
5912                (void) XWithdrawWindow(display,windows->info.id,
5913                  windows->info.screen);
5914            }
5915          else
5916            if ((x > (int) (windows->info.x+windows->info.width)) ||
5917                (y > (int) (windows->info.y+windows->info.height)))
5918              (void) XMapWindow(display,windows->info.id);
5919          break;
5920        }
5921      }
5922    } while ((state & ExitState) == 0);
5923    (void) XSelectInput(display,windows->image.id,
5924      windows->image.attributes.event_mask);
5925    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5926    if ((state & EscapeState) != 0)
5927      break;
5928    /*
5929      Draw element as pointer moves until the button is released.
5930    */
5931    distance=0;
5932    degrees=0.0;
5933    line_info.x1=x;
5934    line_info.y1=y;
5935    line_info.x2=x;
5936    line_info.y2=y;
5937    rectangle_info.x=(ssize_t) x;
5938    rectangle_info.y=(ssize_t) y;
5939    rectangle_info.width=0;
5940    rectangle_info.height=0;
5941    number_coordinates=1;
5942    coordinate_info->x=x;
5943    coordinate_info->y=y;
5944    (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5945    state=DefaultState;
5946    do
5947    {
5948      switch (element)
5949      {
5950        case PointElement:
5951        default:
5952        {
5953          if (number_coordinates > 1)
5954            {
5955              (void) XDrawLines(display,windows->image.id,
5956                windows->image.highlight_context,coordinate_info,
5957                number_coordinates,CoordModeOrigin);
5958              (void) FormatLocaleString(text,MaxTextExtent," %+d%+d",
5959                coordinate_info[number_coordinates-1].x,
5960                coordinate_info[number_coordinates-1].y);
5961              XInfoWidget(display,windows,text);
5962            }
5963          break;
5964        }
5965        case LineElement:
5966        {
5967          if (distance > 9)
5968            {
5969              /*
5970                Display angle of the line.
5971              */
5972              degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5973                line_info.y1),(double) (line_info.x2-line_info.x1)));
5974              (void) FormatLocaleString(text,MaxTextExtent," %g",
5975                (double) degrees);
5976              XInfoWidget(display,windows,text);
5977              XHighlightLine(display,windows->image.id,
5978                windows->image.highlight_context,&line_info);
5979            }
5980          else
5981            if (windows->info.mapped != MagickFalse)
5982              (void) XWithdrawWindow(display,windows->info.id,
5983                windows->info.screen);
5984          break;
5985        }
5986        case RectangleElement:
5987        case FillRectangleElement:
5988        {
5989          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5990            {
5991              /*
5992                Display info and draw drawing rectangle.
5993              */
5994              (void) FormatLocaleString(text,MaxTextExtent,
5995                " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5996                (double) rectangle_info.height,(double) rectangle_info.x,
5997                (double) rectangle_info.y);
5998              XInfoWidget(display,windows,text);
5999              XHighlightRectangle(display,windows->image.id,
6000                windows->image.highlight_context,&rectangle_info);
6001            }
6002          else
6003            if (windows->info.mapped != MagickFalse)
6004              (void) XWithdrawWindow(display,windows->info.id,
6005                windows->info.screen);
6006          break;
6007        }
6008        case CircleElement:
6009        case FillCircleElement:
6010        case EllipseElement:
6011        case FillEllipseElement:
6012        {
6013          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6014            {
6015              /*
6016                Display info and draw drawing rectangle.
6017              */
6018              (void) FormatLocaleString(text,MaxTextExtent,
6019                " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
6020                (double) rectangle_info.height,(double) rectangle_info.x,
6021                (double) rectangle_info.y);
6022              XInfoWidget(display,windows,text);
6023              XHighlightEllipse(display,windows->image.id,
6024                windows->image.highlight_context,&rectangle_info);
6025            }
6026          else
6027            if (windows->info.mapped != MagickFalse)
6028              (void) XWithdrawWindow(display,windows->info.id,
6029                windows->info.screen);
6030          break;
6031        }
6032        case PolygonElement:
6033        case FillPolygonElement:
6034        {
6035          if (number_coordinates > 1)
6036            (void) XDrawLines(display,windows->image.id,
6037              windows->image.highlight_context,coordinate_info,
6038              number_coordinates,CoordModeOrigin);
6039          if (distance > 9)
6040            {
6041              /*
6042                Display angle of the line.
6043              */
6044              degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
6045                line_info.y1),(double) (line_info.x2-line_info.x1)));
6046              (void) FormatLocaleString(text,MaxTextExtent," %g",
6047                (double) degrees);
6048              XInfoWidget(display,windows,text);
6049              XHighlightLine(display,windows->image.id,
6050                windows->image.highlight_context,&line_info);
6051            }
6052          else
6053            if (windows->info.mapped != MagickFalse)
6054              (void) XWithdrawWindow(display,windows->info.id,
6055                windows->info.screen);
6056          break;
6057        }
6058      }
6059      /*
6060        Wait for next event.
6061      */
6062      XScreenEvent(display,windows,&event,exception);
6063      switch (element)
6064      {
6065        case PointElement:
6066        default:
6067        {
6068          if (number_coordinates > 1)
6069            (void) XDrawLines(display,windows->image.id,
6070              windows->image.highlight_context,coordinate_info,
6071              number_coordinates,CoordModeOrigin);
6072          break;
6073        }
6074        case LineElement:
6075        {
6076          if (distance > 9)
6077            XHighlightLine(display,windows->image.id,
6078              windows->image.highlight_context,&line_info);
6079          break;
6080        }
6081        case RectangleElement:
6082        case FillRectangleElement:
6083        {
6084          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6085            XHighlightRectangle(display,windows->image.id,
6086              windows->image.highlight_context,&rectangle_info);
6087          break;
6088        }
6089        case CircleElement:
6090        case FillCircleElement:
6091        case EllipseElement:
6092        case FillEllipseElement:
6093        {
6094          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6095            XHighlightEllipse(display,windows->image.id,
6096              windows->image.highlight_context,&rectangle_info);
6097          break;
6098        }
6099        case PolygonElement:
6100        case FillPolygonElement:
6101        {
6102          if (number_coordinates > 1)
6103            (void) XDrawLines(display,windows->image.id,
6104              windows->image.highlight_context,coordinate_info,
6105              number_coordinates,CoordModeOrigin);
6106          if (distance > 9)
6107            XHighlightLine(display,windows->image.id,
6108              windows->image.highlight_context,&line_info);
6109          break;
6110        }
6111      }
6112      switch (event.type)
6113      {
6114        case ButtonPress:
6115          break;
6116        case ButtonRelease:
6117        {
6118          /*
6119            User has committed to element.
6120          */
6121          line_info.x2=event.xbutton.x;
6122          line_info.y2=event.xbutton.y;
6123          rectangle_info.x=(ssize_t) event.xbutton.x;
6124          rectangle_info.y=(ssize_t) event.xbutton.y;
6125          coordinate_info[number_coordinates].x=event.xbutton.x;
6126          coordinate_info[number_coordinates].y=event.xbutton.y;
6127          if (((element != PolygonElement) &&
6128               (element != FillPolygonElement)) || (distance <= 9))
6129            {
6130              state|=ExitState;
6131              break;
6132            }
6133          number_coordinates++;
6134          if (number_coordinates < (int) max_coordinates)
6135            {
6136              line_info.x1=event.xbutton.x;
6137              line_info.y1=event.xbutton.y;
6138              break;
6139            }
6140          max_coordinates<<=1;
6141          coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6142            max_coordinates,sizeof(*coordinate_info));
6143          if (coordinate_info == (XPoint *) NULL)
6144            (void) ThrowMagickException(exception,GetMagickModule(),
6145              ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6146          break;
6147        }
6148        case Expose:
6149          break;
6150        case MotionNotify:
6151        {
6152          if (event.xmotion.window != windows->image.id)
6153            break;
6154          if (element != PointElement)
6155            {
6156              line_info.x2=event.xmotion.x;
6157              line_info.y2=event.xmotion.y;
6158              rectangle_info.x=(ssize_t) event.xmotion.x;
6159              rectangle_info.y=(ssize_t) event.xmotion.y;
6160              break;
6161            }
6162          coordinate_info[number_coordinates].x=event.xbutton.x;
6163          coordinate_info[number_coordinates].y=event.xbutton.y;
6164          number_coordinates++;
6165          if (number_coordinates < (int) max_coordinates)
6166            break;
6167          max_coordinates<<=1;
6168          coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6169            max_coordinates,sizeof(*coordinate_info));
6170          if (coordinate_info == (XPoint *) NULL)
6171            (void) ThrowMagickException(exception,GetMagickModule(),
6172              ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6173          break;
6174        }
6175        default:
6176          break;
6177      }
6178      /*
6179        Check boundary conditions.
6180      */
6181      if (line_info.x2 < 0)
6182        line_info.x2=0;
6183      else
6184        if (line_info.x2 > (int) windows->image.width)
6185          line_info.x2=(short) windows->image.width;
6186      if (line_info.y2 < 0)
6187        line_info.y2=0;
6188      else
6189        if (line_info.y2 > (int) windows->image.height)
6190          line_info.y2=(short) windows->image.height;
6191      distance=(unsigned int)
6192        (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6193         ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6194      if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6195          ((state & ExitState) != 0))
6196        {
6197          if (rectangle_info.x < 0)
6198            rectangle_info.x=0;
6199          else
6200            if (rectangle_info.x > (ssize_t) windows->image.width)
6201              rectangle_info.x=(ssize_t) windows->image.width;
6202          if ((int) rectangle_info.x < x)
6203            rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6204          else
6205            {
6206              rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6207              rectangle_info.x=(ssize_t) x;
6208            }
6209          if (rectangle_info.y < 0)
6210            rectangle_info.y=0;
6211          else
6212            if (rectangle_info.y > (ssize_t) windows->image.height)
6213              rectangle_info.y=(ssize_t) windows->image.height;
6214          if ((int) rectangle_info.y < y)
6215            rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6216          else
6217            {
6218              rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6219              rectangle_info.y=(ssize_t) y;
6220            }
6221        }
6222    } while ((state & ExitState) == 0);
6223    (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6224    if ((element == PointElement) || (element == PolygonElement) ||
6225        (element == FillPolygonElement))
6226      {
6227        /*
6228          Determine polygon bounding box.
6229        */
6230        rectangle_info.x=(ssize_t) coordinate_info->x;
6231        rectangle_info.y=(ssize_t) coordinate_info->y;
6232        x=coordinate_info->x;
6233        y=coordinate_info->y;
6234        for (i=1; i < number_coordinates; i++)
6235        {
6236          if (coordinate_info[i].x > x)
6237            x=coordinate_info[i].x;
6238          if (coordinate_info[i].y > y)
6239            y=coordinate_info[i].y;
6240          if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6241            rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6242          if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6243            rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6244        }
6245        rectangle_info.width=(size_t) (x-rectangle_info.x);
6246        rectangle_info.height=(size_t) (y-rectangle_info.y);
6247        for (i=0; i < number_coordinates; i++)
6248        {
6249          coordinate_info[i].x-=rectangle_info.x;
6250          coordinate_info[i].y-=rectangle_info.y;
6251        }
6252      }
6253    else
6254      if (distance <= 9)
6255        continue;
6256      else
6257        if ((element == RectangleElement) ||
6258            (element == CircleElement) || (element == EllipseElement))
6259          {
6260            rectangle_info.width--;
6261            rectangle_info.height--;
6262          }
6263    /*
6264      Drawing is relative to image configuration.
6265    */
6266    draw_info.x=(int) rectangle_info.x;
6267    draw_info.y=(int) rectangle_info.y;
6268    (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6269      image,exception);
6270    width=(unsigned int) (*image)->columns;
6271    height=(unsigned int) (*image)->rows;
6272    x=0;
6273    y=0;
6274    if (windows->image.crop_geometry != (char *) NULL)
6275      (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6276    draw_info.x+=windows->image.x-(line_width/2);
6277    if (draw_info.x < 0)
6278      draw_info.x=0;
6279    draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6280    draw_info.y+=windows->image.y-(line_width/2);
6281    if (draw_info.y < 0)
6282      draw_info.y=0;
6283    draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6284    draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6285    if (draw_info.width > (unsigned int) (*image)->columns)
6286      draw_info.width=(unsigned int) (*image)->columns;
6287    draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6288    if (draw_info.height > (unsigned int) (*image)->rows)
6289      draw_info.height=(unsigned int) (*image)->rows;
6290    (void) FormatLocaleString(draw_info.geometry,MaxTextExtent,"%ux%u%+d%+d",
6291      width*draw_info.width/windows->image.ximage->width,
6292      height*draw_info.height/windows->image.ximage->height,
6293      draw_info.x+x,draw_info.y+y);
6294    /*
6295      Initialize drawing attributes.
6296    */
6297    draw_info.degrees=0.0;
6298    draw_info.element=element;
6299    draw_info.stipple=stipple;
6300    draw_info.line_width=line_width;
6301    draw_info.line_info=line_info;
6302    if (line_info.x1 > (int) (line_width/2))
6303      draw_info.line_info.x1=(short) line_width/2;
6304    if (line_info.y1 > (int) (line_width/2))
6305      draw_info.line_info.y1=(short) line_width/2;
6306    draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6307    draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6308    if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6309      {
6310        draw_info.line_info.x2=(-draw_info.line_info.x2);
6311        draw_info.line_info.y2=(-draw_info.line_info.y2);
6312      }
6313    if (draw_info.line_info.x2 < 0)
6314      {
6315        draw_info.line_info.x2=(-draw_info.line_info.x2);
6316        Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6317      }
6318    if (draw_info.line_info.y2 < 0)
6319      {
6320        draw_info.line_info.y2=(-draw_info.line_info.y2);
6321        Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6322      }
6323    draw_info.rectangle_info=rectangle_info;
6324    if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6325      draw_info.rectangle_info.x=(ssize_t) line_width/2;
6326    if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6327      draw_info.rectangle_info.y=(ssize_t) line_width/2;
6328    draw_info.number_coordinates=(unsigned int) number_coordinates;
6329    draw_info.coordinate_info=coordinate_info;
6330    windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6331    /*
6332      Draw element on image.
6333    */
6334    XSetCursorState(display,windows,MagickTrue);
6335    XCheckRefreshWindows(display,windows);
6336    status=XDrawImage(display,windows->pixel_info,&draw_info,*image,exception);
6337    XSetCursorState(display,windows,MagickFalse);
6338    /*
6339      Update image colormap and return to image drawing.
6340    */
6341    XConfigureImageColormap(display,resource_info,windows,*image,exception);
6342    (void) XConfigureImage(display,resource_info,windows,*image,exception);
6343  }
6344  XSetCursorState(display,windows,MagickFalse);
6345  coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6346  return(status != 0 ? MagickTrue : MagickFalse);
6347}
6348
6349/*
6350%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6351%                                                                             %
6352%                                                                             %
6353%                                                                             %
6354+   X D r a w P a n R e c t a n g l e                                         %
6355%                                                                             %
6356%                                                                             %
6357%                                                                             %
6358%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6359%
6360%  XDrawPanRectangle() draws a rectangle in the pan window.  The pan window
6361%  displays a zoom image and the rectangle shows which portion of the image is
6362%  displayed in the Image window.
6363%
6364%  The format of the XDrawPanRectangle method is:
6365%
6366%      XDrawPanRectangle(Display *display,XWindows *windows)
6367%
6368%  A description of each parameter follows:
6369%
6370%    o display: Specifies a connection to an X server;  returned from
6371%      XOpenDisplay.
6372%
6373%    o windows: Specifies a pointer to a XWindows structure.
6374%
6375*/
6376static void XDrawPanRectangle(Display *display,XWindows *windows)
6377{
6378  MagickRealType
6379    scale_factor;
6380
6381  RectangleInfo
6382    highlight_info;
6383
6384  /*
6385    Determine dimensions of the panning rectangle.
6386  */
6387  scale_factor=(MagickRealType) windows->pan.width/windows->image.ximage->width;
6388  highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6389  highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6390  scale_factor=(MagickRealType)
6391    windows->pan.height/windows->image.ximage->height;
6392  highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6393  highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6394  /*
6395    Display the panning rectangle.
6396  */
6397  (void) XClearWindow(display,windows->pan.id);
6398  XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6399    &highlight_info);
6400}
6401
6402/*
6403%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6404%                                                                             %
6405%                                                                             %
6406%                                                                             %
6407+   X I m a g e C a c h e                                                     %
6408%                                                                             %
6409%                                                                             %
6410%                                                                             %
6411%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6412%
6413%  XImageCache() handles the creation, manipulation, and destruction of the
6414%  image cache (undo and redo buffers).
6415%
6416%  The format of the XImageCache method is:
6417%
6418%      void XImageCache(Display *display,XResourceInfo *resource_info,
6419%        XWindows *windows,const CommandType command,Image **image,
6420%        ExceptionInfo *exception)
6421%
6422%  A description of each parameter follows:
6423%
6424%    o display: Specifies a connection to an X server; returned from
6425%      XOpenDisplay.
6426%
6427%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6428%
6429%    o windows: Specifies a pointer to a XWindows structure.
6430%
6431%    o command: Specifies a command to perform.
6432%
6433%    o image: the image;  XImageCache may transform the image and return a new
6434%      image pointer.
6435%
6436%    o exception: return any errors or warnings in this structure.
6437%
6438*/
6439static void XImageCache(Display *display,XResourceInfo *resource_info,
6440  XWindows *windows,const CommandType command,Image **image,
6441  ExceptionInfo *exception)
6442{
6443  Image
6444    *cache_image;
6445
6446  static Image
6447    *redo_image = (Image *) NULL,
6448    *undo_image = (Image *) NULL;
6449
6450  switch (command)
6451  {
6452    case FreeBuffersCommand:
6453    {
6454      /*
6455        Free memory from the undo and redo cache.
6456      */
6457      while (undo_image != (Image *) NULL)
6458      {
6459        cache_image=undo_image;
6460        undo_image=GetPreviousImageInList(undo_image);
6461        cache_image->list=DestroyImage(cache_image->list);
6462        cache_image=DestroyImage(cache_image);
6463      }
6464      undo_image=NewImageList();
6465      if (redo_image != (Image *) NULL)
6466        redo_image=DestroyImage(redo_image);
6467      redo_image=NewImageList();
6468      return;
6469    }
6470    case UndoCommand:
6471    {
6472      char
6473        image_geometry[MaxTextExtent];
6474
6475      /*
6476        Undo the last image transformation.
6477      */
6478      if (undo_image == (Image *) NULL)
6479        {
6480          (void) XBell(display,0);
6481          return;
6482        }
6483      cache_image=undo_image;
6484      undo_image=GetPreviousImageInList(undo_image);
6485      windows->image.window_changes.width=(int) cache_image->columns;
6486      windows->image.window_changes.height=(int) cache_image->rows;
6487      (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
6488        windows->image.ximage->width,windows->image.ximage->height);
6489      (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
6490        exception);
6491      if (windows->image.crop_geometry != (char *) NULL)
6492        windows->image.crop_geometry=(char *) RelinquishMagickMemory(
6493          windows->image.crop_geometry);
6494      windows->image.crop_geometry=cache_image->geometry;
6495      if (redo_image != (Image *) NULL)
6496        redo_image=DestroyImage(redo_image);
6497      redo_image=(*image);
6498      *image=cache_image->list;
6499      cache_image=DestroyImage(cache_image);
6500      if (windows->image.orphan != MagickFalse)
6501        return;
6502      XConfigureImageColormap(display,resource_info,windows,*image,exception);
6503      (void) XConfigureImage(display,resource_info,windows,*image,exception);
6504      return;
6505    }
6506    case CutCommand:
6507    case PasteCommand:
6508    case ApplyCommand:
6509    case HalfSizeCommand:
6510    case OriginalSizeCommand:
6511    case DoubleSizeCommand:
6512    case ResizeCommand:
6513    case TrimCommand:
6514    case CropCommand:
6515    case ChopCommand:
6516    case FlipCommand:
6517    case FlopCommand:
6518    case RotateRightCommand:
6519    case RotateLeftCommand:
6520    case RotateCommand:
6521    case ShearCommand:
6522    case RollCommand:
6523    case NegateCommand:
6524    case ContrastStretchCommand:
6525    case SigmoidalContrastCommand:
6526    case NormalizeCommand:
6527    case EqualizeCommand:
6528    case HueCommand:
6529    case SaturationCommand:
6530    case BrightnessCommand:
6531    case GammaCommand:
6532    case SpiffCommand:
6533    case DullCommand:
6534    case GrayscaleCommand:
6535    case MapCommand:
6536    case QuantizeCommand:
6537    case DespeckleCommand:
6538    case EmbossCommand:
6539    case ReduceNoiseCommand:
6540    case AddNoiseCommand:
6541    case SharpenCommand:
6542    case BlurCommand:
6543    case ThresholdCommand:
6544    case EdgeDetectCommand:
6545    case SpreadCommand:
6546    case ShadeCommand:
6547    case RaiseCommand:
6548    case SegmentCommand:
6549    case SolarizeCommand:
6550    case SepiaToneCommand:
6551    case SwirlCommand:
6552    case ImplodeCommand:
6553    case VignetteCommand:
6554    case WaveCommand:
6555    case OilPaintCommand:
6556    case CharcoalDrawCommand:
6557    case AnnotateCommand:
6558    case AddBorderCommand:
6559    case AddFrameCommand:
6560    case CompositeCommand:
6561    case CommentCommand:
6562    case LaunchCommand:
6563    case RegionofInterestCommand:
6564    case SaveToUndoBufferCommand:
6565    case RedoCommand:
6566    {
6567      Image
6568        *previous_image;
6569
6570      ssize_t
6571        bytes;
6572
6573      bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelInfo));
6574      if (undo_image != (Image *) NULL)
6575        {
6576          /*
6577            Ensure the undo cache has enough memory available.
6578          */
6579          previous_image=undo_image;
6580          while (previous_image != (Image *) NULL)
6581          {
6582            bytes+=previous_image->list->columns*previous_image->list->rows*
6583              sizeof(PixelInfo);
6584            if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6585              {
6586                previous_image=GetPreviousImageInList(previous_image);
6587                continue;
6588              }
6589            bytes-=previous_image->list->columns*previous_image->list->rows*
6590              sizeof(PixelInfo);
6591            if (previous_image == undo_image)
6592              undo_image=NewImageList();
6593            else
6594              previous_image->next->previous=NewImageList();
6595            break;
6596          }
6597          while (previous_image != (Image *) NULL)
6598          {
6599            /*
6600              Delete any excess memory from undo cache.
6601            */
6602            cache_image=previous_image;
6603            previous_image=GetPreviousImageInList(previous_image);
6604            cache_image->list=DestroyImage(cache_image->list);
6605            cache_image=DestroyImage(cache_image);
6606          }
6607        }
6608      if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6609        break;
6610      /*
6611        Save image before transformations are applied.
6612      */
6613      cache_image=AcquireImage((ImageInfo *) NULL,exception);
6614      if (cache_image == (Image *) NULL)
6615        break;
6616      XSetCursorState(display,windows,MagickTrue);
6617      XCheckRefreshWindows(display,windows);
6618      cache_image->list=CloneImage(*image,0,0,MagickTrue,exception);
6619      XSetCursorState(display,windows,MagickFalse);
6620      if (cache_image->list == (Image *) NULL)
6621        {
6622          cache_image=DestroyImage(cache_image);
6623          break;
6624        }
6625      cache_image->columns=(size_t) windows->image.ximage->width;
6626      cache_image->rows=(size_t) windows->image.ximage->height;
6627      cache_image->geometry=windows->image.crop_geometry;
6628      if (windows->image.crop_geometry != (char *) NULL)
6629        {
6630          cache_image->geometry=AcquireString((char *) NULL);
6631          (void) CopyMagickString(cache_image->geometry,
6632            windows->image.crop_geometry,MaxTextExtent);
6633        }
6634      if (undo_image == (Image *) NULL)
6635        {
6636          undo_image=cache_image;
6637          break;
6638        }
6639      undo_image->next=cache_image;
6640      undo_image->next->previous=undo_image;
6641      undo_image=undo_image->next;
6642      break;
6643    }
6644    default:
6645      break;
6646  }
6647  if (command == RedoCommand)
6648    {
6649      /*
6650        Redo the last image transformation.
6651      */
6652      if (redo_image == (Image *) NULL)
6653        {
6654          (void) XBell(display,0);
6655          return;
6656        }
6657      windows->image.window_changes.width=(int) redo_image->columns;
6658      windows->image.window_changes.height=(int) redo_image->rows;
6659      if (windows->image.crop_geometry != (char *) NULL)
6660        windows->image.crop_geometry=(char *)
6661          RelinquishMagickMemory(windows->image.crop_geometry);
6662      windows->image.crop_geometry=redo_image->geometry;
6663      *image=DestroyImage(*image);
6664      *image=redo_image;
6665      redo_image=NewImageList();
6666      if (windows->image.orphan != MagickFalse)
6667        return;
6668      XConfigureImageColormap(display,resource_info,windows,*image,exception);
6669      (void) XConfigureImage(display,resource_info,windows,*image,exception);
6670      return;
6671    }
6672  if (command != InfoCommand)
6673    return;
6674  /*
6675    Display image info.
6676  */
6677  XSetCursorState(display,windows,MagickTrue);
6678  XCheckRefreshWindows(display,windows);
6679  XDisplayImageInfo(display,resource_info,windows,undo_image,*image,exception);
6680  XSetCursorState(display,windows,MagickFalse);
6681}
6682
6683/*
6684%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6685%                                                                             %
6686%                                                                             %
6687%                                                                             %
6688+   X I m a g e W i n d o w C o m m a n d                                     %
6689%                                                                             %
6690%                                                                             %
6691%                                                                             %
6692%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6693%
6694%  XImageWindowCommand() makes a transform to the image or Image window as
6695%  specified by a user menu button or keyboard command.
6696%
6697%  The format of the XImageWindowCommand method is:
6698%
6699%      CommandType XImageWindowCommand(Display *display,
6700%        XResourceInfo *resource_info,XWindows *windows,
6701%        const MagickStatusType state,KeySym key_symbol,Image **image,
6702%        ExceptionInfo *exception)
6703%
6704%  A description of each parameter follows:
6705%
6706%    o nexus:  Method XImageWindowCommand returns an image when the
6707%      user chooses 'Open Image' from the command menu.  Otherwise a null
6708%      image is returned.
6709%
6710%    o display: Specifies a connection to an X server; returned from
6711%      XOpenDisplay.
6712%
6713%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6714%
6715%    o windows: Specifies a pointer to a XWindows structure.
6716%
6717%    o state: key mask.
6718%
6719%    o key_symbol: Specifies a command to perform.
6720%
6721%    o image: the image;  XImageWIndowCommand may transform the image and
6722%      return a new image pointer.
6723%
6724%    o exception: return any errors or warnings in this structure.
6725%
6726*/
6727static CommandType XImageWindowCommand(Display *display,
6728  XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6729  KeySym key_symbol,Image **image,ExceptionInfo *exception)
6730{
6731  static char
6732    delta[MaxTextExtent] = "";
6733
6734  static const char
6735    Digits[] = "01234567890";
6736
6737  static KeySym
6738    last_symbol = XK_0;
6739
6740  if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6741    {
6742      if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6743        {
6744          *delta='\0';
6745          resource_info->quantum=1;
6746        }
6747      last_symbol=key_symbol;
6748      delta[strlen(delta)+1]='\0';
6749      delta[strlen(delta)]=Digits[key_symbol-XK_0];
6750      resource_info->quantum=StringToLong(delta);
6751      return(NullCommand);
6752    }
6753  last_symbol=key_symbol;
6754  if (resource_info->immutable)
6755    {
6756      /*
6757        Virtual image window has a restricted command set.
6758      */
6759      switch (key_symbol)
6760      {
6761        case XK_question:
6762          return(InfoCommand);
6763        case XK_p:
6764        case XK_Print:
6765          return(PrintCommand);
6766        case XK_space:
6767          return(NextCommand);
6768        case XK_q:
6769        case XK_Escape:
6770          return(QuitCommand);
6771        default:
6772          break;
6773      }
6774      return(NullCommand);
6775    }
6776  switch ((int) key_symbol)
6777  {
6778    case XK_o:
6779    {
6780      if ((state & ControlMask) == 0)
6781        break;
6782      return(OpenCommand);
6783    }
6784    case XK_space:
6785      return(NextCommand);
6786    case XK_BackSpace:
6787      return(FormerCommand);
6788    case XK_s:
6789    {
6790      if ((state & Mod1Mask) != 0)
6791        return(SwirlCommand);
6792      if ((state & ControlMask) == 0)
6793        return(ShearCommand);
6794      return(SaveCommand);
6795    }
6796    case XK_p:
6797    case XK_Print:
6798    {
6799      if ((state & Mod1Mask) != 0)
6800        return(OilPaintCommand);
6801      if ((state & Mod4Mask) != 0)
6802        return(ColorCommand);
6803      if ((state & ControlMask) == 0)
6804        return(NullCommand);
6805      return(PrintCommand);
6806    }
6807    case XK_d:
6808    {
6809      if ((state & Mod4Mask) != 0)
6810        return(DrawCommand);
6811      if ((state & ControlMask) == 0)
6812        return(NullCommand);
6813      return(DeleteCommand);
6814    }
6815    case XK_Select:
6816    {
6817      if ((state & ControlMask) == 0)
6818        return(NullCommand);
6819      return(SelectCommand);
6820    }
6821    case XK_n:
6822    {
6823      if ((state & ControlMask) == 0)
6824        return(NullCommand);
6825      return(NewCommand);
6826    }
6827    case XK_q:
6828    case XK_Escape:
6829      return(QuitCommand);
6830    case XK_z:
6831    case XK_Undo:
6832    {
6833      if ((state & ControlMask) == 0)
6834        return(NullCommand);
6835      return(UndoCommand);
6836    }
6837    case XK_r:
6838    case XK_Redo:
6839    {
6840      if ((state & ControlMask) == 0)
6841        return(RollCommand);
6842      return(RedoCommand);
6843    }
6844    case XK_x:
6845    {
6846      if ((state & ControlMask) == 0)
6847        return(NullCommand);
6848      return(CutCommand);
6849    }
6850    case XK_c:
6851    {
6852      if ((state & Mod1Mask) != 0)
6853        return(CharcoalDrawCommand);
6854      if ((state & ControlMask) == 0)
6855        return(CropCommand);
6856      return(CopyCommand);
6857    }
6858    case XK_v:
6859    case XK_Insert:
6860    {
6861      if ((state & Mod4Mask) != 0)
6862        return(CompositeCommand);
6863      if ((state & ControlMask) == 0)
6864        return(FlipCommand);
6865      return(PasteCommand);
6866    }
6867    case XK_less:
6868      return(HalfSizeCommand);
6869    case XK_minus:
6870      return(OriginalSizeCommand);
6871    case XK_greater:
6872      return(DoubleSizeCommand);
6873    case XK_percent:
6874      return(ResizeCommand);
6875    case XK_at:
6876      return(RefreshCommand);
6877    case XK_bracketleft:
6878      return(ChopCommand);
6879    case XK_h:
6880      return(FlopCommand);
6881    case XK_slash:
6882      return(RotateRightCommand);
6883    case XK_backslash:
6884      return(RotateLeftCommand);
6885    case XK_asterisk:
6886      return(RotateCommand);
6887    case XK_t:
6888      return(TrimCommand);
6889    case XK_H:
6890      return(HueCommand);
6891    case XK_S:
6892      return(SaturationCommand);
6893    case XK_L:
6894      return(BrightnessCommand);
6895    case XK_G:
6896      return(GammaCommand);
6897    case XK_C:
6898      return(SpiffCommand);
6899    case XK_Z:
6900      return(DullCommand);
6901    case XK_N:
6902      return(NormalizeCommand);
6903    case XK_equal:
6904      return(EqualizeCommand);
6905    case XK_asciitilde:
6906      return(NegateCommand);
6907    case XK_period:
6908      return(GrayscaleCommand);
6909    case XK_numbersign:
6910      return(QuantizeCommand);
6911    case XK_F2:
6912      return(DespeckleCommand);
6913    case XK_F3:
6914      return(EmbossCommand);
6915    case XK_F4:
6916      return(ReduceNoiseCommand);
6917    case XK_F5:
6918      return(AddNoiseCommand);
6919    case XK_F6:
6920      return(SharpenCommand);
6921    case XK_F7:
6922      return(BlurCommand);
6923    case XK_F8:
6924      return(ThresholdCommand);
6925    case XK_F9:
6926      return(EdgeDetectCommand);
6927    case XK_F10:
6928      return(SpreadCommand);
6929    case XK_F11:
6930      return(ShadeCommand);
6931    case XK_F12:
6932      return(RaiseCommand);
6933    case XK_F13:
6934      return(SegmentCommand);
6935    case XK_i:
6936    {
6937      if ((state & Mod1Mask) == 0)
6938        return(NullCommand);
6939      return(ImplodeCommand);
6940    }
6941    case XK_w:
6942    {
6943      if ((state & Mod1Mask) == 0)
6944        return(NullCommand);
6945      return(WaveCommand);
6946    }
6947    case XK_m:
6948    {
6949      if ((state & Mod4Mask) == 0)
6950        return(NullCommand);
6951      return(MatteCommand);
6952    }
6953    case XK_b:
6954    {
6955      if ((state & Mod4Mask) == 0)
6956        return(NullCommand);
6957      return(AddBorderCommand);
6958    }
6959    case XK_f:
6960    {
6961      if ((state & Mod4Mask) == 0)
6962        return(NullCommand);
6963      return(AddFrameCommand);
6964    }
6965    case XK_exclam:
6966    {
6967      if ((state & Mod4Mask) == 0)
6968        return(NullCommand);
6969      return(CommentCommand);
6970    }
6971    case XK_a:
6972    {
6973      if ((state & Mod1Mask) != 0)
6974        return(ApplyCommand);
6975      if ((state & Mod4Mask) != 0)
6976        return(AnnotateCommand);
6977      if ((state & ControlMask) == 0)
6978        return(NullCommand);
6979      return(RegionofInterestCommand);
6980    }
6981    case XK_question:
6982      return(InfoCommand);
6983    case XK_plus:
6984      return(ZoomCommand);
6985    case XK_P:
6986    {
6987      if ((state & ShiftMask) == 0)
6988        return(NullCommand);
6989      return(ShowPreviewCommand);
6990    }
6991    case XK_Execute:
6992      return(LaunchCommand);
6993    case XK_F1:
6994      return(HelpCommand);
6995    case XK_Find:
6996      return(BrowseDocumentationCommand);
6997    case XK_Menu:
6998    {
6999      (void) XMapRaised(display,windows->command.id);
7000      return(NullCommand);
7001    }
7002    case XK_Next:
7003    case XK_Prior:
7004    case XK_Home:
7005    case XK_KP_Home:
7006    {
7007      XTranslateImage(display,windows,*image,key_symbol);
7008      return(NullCommand);
7009    }
7010    case XK_Up:
7011    case XK_KP_Up:
7012    case XK_Down:
7013    case XK_KP_Down:
7014    case XK_Left:
7015    case XK_KP_Left:
7016    case XK_Right:
7017    case XK_KP_Right:
7018    {
7019      if ((state & Mod1Mask) != 0)
7020        {
7021          RectangleInfo
7022            crop_info;
7023
7024          /*
7025            Trim one pixel from edge of image.
7026          */
7027          crop_info.x=0;
7028          crop_info.y=0;
7029          crop_info.width=(size_t) windows->image.ximage->width;
7030          crop_info.height=(size_t) windows->image.ximage->height;
7031          if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
7032            {
7033              if (resource_info->quantum >= (int) crop_info.height)
7034                resource_info->quantum=(int) crop_info.height-1;
7035              crop_info.height-=resource_info->quantum;
7036            }
7037          if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
7038            {
7039              if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
7040                resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
7041              crop_info.y+=resource_info->quantum;
7042              crop_info.height-=resource_info->quantum;
7043            }
7044          if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
7045            {
7046              if (resource_info->quantum >= (int) crop_info.width)
7047                resource_info->quantum=(int) crop_info.width-1;
7048              crop_info.width-=resource_info->quantum;
7049            }
7050          if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
7051            {
7052              if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
7053                resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
7054              crop_info.x+=resource_info->quantum;
7055              crop_info.width-=resource_info->quantum;
7056            }
7057          if ((int) (windows->image.x+windows->image.width) >
7058              (int) crop_info.width)
7059            windows->image.x=(int) (crop_info.width-windows->image.width);
7060          if ((int) (windows->image.y+windows->image.height) >
7061              (int) crop_info.height)
7062            windows->image.y=(int) (crop_info.height-windows->image.height);
7063          XSetCropGeometry(display,windows,&crop_info,*image);
7064          windows->image.window_changes.width=(int) crop_info.width;
7065          windows->image.window_changes.height=(int) crop_info.height;
7066          (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
7067          (void) XConfigureImage(display,resource_info,windows,*image,
7068            exception);
7069          return(NullCommand);
7070        }
7071      XTranslateImage(display,windows,*image,key_symbol);
7072      return(NullCommand);
7073    }
7074    default:
7075      return(NullCommand);
7076  }
7077  return(NullCommand);
7078}
7079
7080/*
7081%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7082%                                                                             %
7083%                                                                             %
7084%                                                                             %
7085+   X M a g i c k C o m m a n d                                               %
7086%                                                                             %
7087%                                                                             %
7088%                                                                             %
7089%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7090%
7091%  XMagickCommand() makes a transform to the image or Image window as
7092%  specified by a user menu button or keyboard command.
7093%
7094%  The format of the XMagickCommand method is:
7095%
7096%      Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7097%        XWindows *windows,const CommandType command,Image **image,
7098%        ExceptionInfo *exception)
7099%
7100%  A description of each parameter follows:
7101%
7102%    o display: Specifies a connection to an X server; returned from
7103%      XOpenDisplay.
7104%
7105%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7106%
7107%    o windows: Specifies a pointer to a XWindows structure.
7108%
7109%    o command: Specifies a command to perform.
7110%
7111%    o image: the image;  XMagickCommand may transform the image and return a
7112%      new image pointer.
7113%
7114%    o exception: return any errors or warnings in this structure.
7115%
7116*/
7117static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7118  XWindows *windows,const CommandType command,Image **image,
7119  ExceptionInfo *exception)
7120{
7121  char
7122    filename[MaxTextExtent],
7123    geometry[MaxTextExtent],
7124    modulate_factors[MaxTextExtent];
7125
7126  GeometryInfo
7127    geometry_info;
7128
7129  Image
7130    *nexus;
7131
7132  ImageInfo
7133    *image_info;
7134
7135  int
7136    x,
7137    y;
7138
7139  MagickStatusType
7140    flags,
7141    status;
7142
7143  QuantizeInfo
7144    quantize_info;
7145
7146  RectangleInfo
7147    page_geometry;
7148
7149  register int
7150    i;
7151
7152  static char
7153    color[MaxTextExtent] = "gray";
7154
7155  unsigned int
7156    height,
7157    width;
7158
7159  /*
7160    Process user command.
7161  */
7162  XCheckRefreshWindows(display,windows);
7163  XImageCache(display,resource_info,windows,command,image,exception);
7164  nexus=NewImageList();
7165  windows->image.window_changes.width=windows->image.ximage->width;
7166  windows->image.window_changes.height=windows->image.ximage->height;
7167  image_info=CloneImageInfo(resource_info->image_info);
7168  SetGeometryInfo(&geometry_info);
7169  GetQuantizeInfo(&quantize_info);
7170  switch (command)
7171  {
7172    case OpenCommand:
7173    {
7174      /*
7175        Load image.
7176      */
7177      nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7178      break;
7179    }
7180    case NextCommand:
7181    {
7182      /*
7183        Display next image.
7184      */
7185      for (i=0; i < resource_info->quantum; i++)
7186        XClientMessage(display,windows->image.id,windows->im_protocols,
7187          windows->im_next_image,CurrentTime);
7188      break;
7189    }
7190    case FormerCommand:
7191    {
7192      /*
7193        Display former image.
7194      */
7195      for (i=0; i < resource_info->quantum; i++)
7196        XClientMessage(display,windows->image.id,windows->im_protocols,
7197          windows->im_former_image,CurrentTime);
7198      break;
7199    }
7200    case SelectCommand:
7201    {
7202      int
7203        status;
7204
7205      /*
7206        Select image.
7207      */
7208      status=chdir(resource_info->home_directory);
7209      if (status == -1)
7210        (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
7211          "UnableToOpenFile","%s",resource_info->home_directory);
7212      nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7213      break;
7214    }
7215    case SaveCommand:
7216    {
7217      /*
7218        Save image.
7219      */
7220      status=XSaveImage(display,resource_info,windows,*image,exception);
7221      if (status == MagickFalse)
7222        {
7223          char
7224            message[MaxTextExtent];
7225
7226          (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7227            exception->reason != (char *) NULL ? exception->reason : "",
7228            exception->description != (char *) NULL ? exception->description :
7229            "");
7230          XNoticeWidget(display,windows,"Unable to save file:",message);
7231          break;
7232        }
7233      break;
7234    }
7235    case PrintCommand:
7236    {
7237      /*
7238        Print image.
7239      */
7240      status=XPrintImage(display,resource_info,windows,*image,exception);
7241      if (status == MagickFalse)
7242        {
7243          char
7244            message[MaxTextExtent];
7245
7246          (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7247            exception->reason != (char *) NULL ? exception->reason : "",
7248            exception->description != (char *) NULL ? exception->description :
7249            "");
7250          XNoticeWidget(display,windows,"Unable to print file:",message);
7251          break;
7252        }
7253      break;
7254    }
7255    case DeleteCommand:
7256    {
7257      static char
7258        filename[MaxTextExtent] = "\0";
7259
7260      /*
7261        Delete image file.
7262      */
7263      XFileBrowserWidget(display,windows,"Delete",filename);
7264      if (*filename == '\0')
7265        break;
7266      status=remove_utf8(filename) != 0 ? MagickTrue : MagickFalse;
7267      if (status != MagickFalse)
7268        XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7269      break;
7270    }
7271    case NewCommand:
7272    {
7273      int
7274        status;
7275
7276      static char
7277        color[MaxTextExtent] = "gray",
7278        geometry[MaxTextExtent] = "640x480";
7279
7280      static const char
7281        *format = "gradient";
7282
7283      /*
7284        Query user for canvas geometry.
7285      */
7286      status=XDialogWidget(display,windows,"New","Enter image geometry:",
7287        geometry);
7288      if (*geometry == '\0')
7289        break;
7290      if (status == 0)
7291        format="xc";
7292      XColorBrowserWidget(display,windows,"Select",color);
7293      if (*color == '\0')
7294        break;
7295      /*
7296        Create canvas.
7297      */
7298      (void) FormatLocaleString(image_info->filename,MaxTextExtent,
7299        "%s:%s",format,color);
7300      (void) CloneString(&image_info->size,geometry);
7301      nexus=ReadImage(image_info,exception);
7302      CatchException(exception);
7303      XClientMessage(display,windows->image.id,windows->im_protocols,
7304        windows->im_next_image,CurrentTime);
7305      break;
7306    }
7307    case VisualDirectoryCommand:
7308    {
7309      /*
7310        Visual Image directory.
7311      */
7312      nexus=XVisualDirectoryImage(display,resource_info,windows,exception);
7313      break;
7314    }
7315    case QuitCommand:
7316    {
7317      /*
7318        exit program.
7319      */
7320      if (resource_info->confirm_exit == MagickFalse)
7321        XClientMessage(display,windows->image.id,windows->im_protocols,
7322          windows->im_exit,CurrentTime);
7323      else
7324        {
7325          int
7326            status;
7327
7328          /*
7329            Confirm program exit.
7330          */
7331          status=XConfirmWidget(display,windows,"Do you really want to exit",
7332            resource_info->client_name);
7333          if (status > 0)
7334            XClientMessage(display,windows->image.id,windows->im_protocols,
7335              windows->im_exit,CurrentTime);
7336        }
7337      break;
7338    }
7339    case CutCommand:
7340    {
7341      /*
7342        Cut image.
7343      */
7344      (void) XCropImage(display,resource_info,windows,*image,CutMode,exception);
7345      break;
7346    }
7347    case CopyCommand:
7348    {
7349      /*
7350        Copy image.
7351      */
7352      (void) XCropImage(display,resource_info,windows,*image,CopyMode,
7353        exception);
7354      break;
7355    }
7356    case PasteCommand:
7357    {
7358      /*
7359        Paste image.
7360      */
7361      status=XPasteImage(display,resource_info,windows,*image,exception);
7362      if (status == MagickFalse)
7363        {
7364          XNoticeWidget(display,windows,"Unable to paste X image",
7365            (*image)->filename);
7366          break;
7367        }
7368      break;
7369    }
7370    case HalfSizeCommand:
7371    {
7372      /*
7373        Half image size.
7374      */
7375      windows->image.window_changes.width=windows->image.ximage->width/2;
7376      windows->image.window_changes.height=windows->image.ximage->height/2;
7377      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7378      break;
7379    }
7380    case OriginalSizeCommand:
7381    {
7382      /*
7383        Original image size.
7384      */
7385      windows->image.window_changes.width=(int) (*image)->columns;
7386      windows->image.window_changes.height=(int) (*image)->rows;
7387      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7388      break;
7389    }
7390    case DoubleSizeCommand:
7391    {
7392      /*
7393        Double the image size.
7394      */
7395      windows->image.window_changes.width=windows->image.ximage->width << 1;
7396      windows->image.window_changes.height=windows->image.ximage->height << 1;
7397      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7398      break;
7399    }
7400    case ResizeCommand:
7401    {
7402      int
7403        status;
7404
7405      size_t
7406        height,
7407        width;
7408
7409      ssize_t
7410        x,
7411        y;
7412
7413      /*
7414        Resize image.
7415      */
7416      width=(size_t) windows->image.ximage->width;
7417      height=(size_t) windows->image.ximage->height;
7418      x=0;
7419      y=0;
7420      (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g+0+0",
7421        (double) width,(double) height);
7422      status=XDialogWidget(display,windows,"Resize",
7423        "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7424      if (*geometry == '\0')
7425        break;
7426      if (status == 0)
7427        (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7428      (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7429      windows->image.window_changes.width=(int) width;
7430      windows->image.window_changes.height=(int) height;
7431      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7432      break;
7433    }
7434    case ApplyCommand:
7435    {
7436      char
7437        image_geometry[MaxTextExtent];
7438
7439      if ((windows->image.crop_geometry == (char *) NULL) &&
7440          ((int) (*image)->columns == windows->image.ximage->width) &&
7441          ((int) (*image)->rows == windows->image.ximage->height))
7442        break;
7443      /*
7444        Apply size transforms to image.
7445      */
7446      XSetCursorState(display,windows,MagickTrue);
7447      XCheckRefreshWindows(display,windows);
7448      /*
7449        Crop and/or scale displayed image.
7450      */
7451      (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
7452        windows->image.ximage->width,windows->image.ximage->height);
7453      (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
7454        exception);
7455      if (windows->image.crop_geometry != (char *) NULL)
7456        windows->image.crop_geometry=(char *) RelinquishMagickMemory(
7457          windows->image.crop_geometry);
7458      windows->image.x=0;
7459      windows->image.y=0;
7460      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7461      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7462      break;
7463    }
7464    case RefreshCommand:
7465    {
7466      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7467      break;
7468    }
7469    case RestoreCommand:
7470    {
7471      /*
7472        Restore Image window to its original size.
7473      */
7474      if ((windows->image.width == (unsigned int) (*image)->columns) &&
7475          (windows->image.height == (unsigned int) (*image)->rows) &&
7476          (windows->image.crop_geometry == (char *) NULL))
7477        {
7478          (void) XBell(display,0);
7479          break;
7480        }
7481      windows->image.window_changes.width=(int) (*image)->columns;
7482      windows->image.window_changes.height=(int) (*image)->rows;
7483      if (windows->image.crop_geometry != (char *) NULL)
7484        {
7485          windows->image.crop_geometry=(char *)
7486            RelinquishMagickMemory(windows->image.crop_geometry);
7487          windows->image.crop_geometry=(char *) NULL;
7488          windows->image.x=0;
7489          windows->image.y=0;
7490        }
7491      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7492      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7493      break;
7494    }
7495    case CropCommand:
7496    {
7497      /*
7498        Crop image.
7499      */
7500      (void) XCropImage(display,resource_info,windows,*image,CropMode,
7501        exception);
7502      break;
7503    }
7504    case ChopCommand:
7505    {
7506      /*
7507        Chop image.
7508      */
7509      status=XChopImage(display,resource_info,windows,image,exception);
7510      if (status == MagickFalse)
7511        {
7512          XNoticeWidget(display,windows,"Unable to cut X image",
7513            (*image)->filename);
7514          break;
7515        }
7516      break;
7517    }
7518    case FlopCommand:
7519    {
7520      Image
7521        *flop_image;
7522
7523      /*
7524        Flop image scanlines.
7525      */
7526      XSetCursorState(display,windows,MagickTrue);
7527      XCheckRefreshWindows(display,windows);
7528      flop_image=FlopImage(*image,exception);
7529      if (flop_image != (Image *) NULL)
7530        {
7531          *image=DestroyImage(*image);
7532          *image=flop_image;
7533        }
7534      CatchException(exception);
7535      XSetCursorState(display,windows,MagickFalse);
7536      if (windows->image.crop_geometry != (char *) NULL)
7537        {
7538          /*
7539            Flop crop geometry.
7540          */
7541          width=(unsigned int) (*image)->columns;
7542          height=(unsigned int) (*image)->rows;
7543          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7544            &width,&height);
7545          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7546            "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7547        }
7548      if (windows->image.orphan != MagickFalse)
7549        break;
7550      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7551      break;
7552    }
7553    case FlipCommand:
7554    {
7555      Image
7556        *flip_image;
7557
7558      /*
7559        Flip image scanlines.
7560      */
7561      XSetCursorState(display,windows,MagickTrue);
7562      XCheckRefreshWindows(display,windows);
7563      flip_image=FlipImage(*image,exception);
7564      if (flip_image != (Image *) NULL)
7565        {
7566          *image=DestroyImage(*image);
7567          *image=flip_image;
7568        }
7569      CatchException(exception);
7570      XSetCursorState(display,windows,MagickFalse);
7571      if (windows->image.crop_geometry != (char *) NULL)
7572        {
7573          /*
7574            Flip crop geometry.
7575          */
7576          width=(unsigned int) (*image)->columns;
7577          height=(unsigned int) (*image)->rows;
7578          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7579            &width,&height);
7580          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7581            "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7582        }
7583      if (windows->image.orphan != MagickFalse)
7584        break;
7585      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7586      break;
7587    }
7588    case RotateRightCommand:
7589    {
7590      /*
7591        Rotate image 90 degrees clockwise.
7592      */
7593      status=XRotateImage(display,resource_info,windows,90.0,image,exception);
7594      if (status == MagickFalse)
7595        {
7596          XNoticeWidget(display,windows,"Unable to rotate X image",
7597            (*image)->filename);
7598          break;
7599        }
7600      break;
7601    }
7602    case RotateLeftCommand:
7603    {
7604      /*
7605        Rotate image 90 degrees counter-clockwise.
7606      */
7607      status=XRotateImage(display,resource_info,windows,-90.0,image,exception);
7608      if (status == MagickFalse)
7609        {
7610          XNoticeWidget(display,windows,"Unable to rotate X image",
7611            (*image)->filename);
7612          break;
7613        }
7614      break;
7615    }
7616    case RotateCommand:
7617    {
7618      /*
7619        Rotate image.
7620      */
7621      status=XRotateImage(display,resource_info,windows,0.0,image,exception);
7622      if (status == MagickFalse)
7623        {
7624          XNoticeWidget(display,windows,"Unable to rotate X image",
7625            (*image)->filename);
7626          break;
7627        }
7628      break;
7629    }
7630    case ShearCommand:
7631    {
7632      Image
7633        *shear_image;
7634
7635      static char
7636        geometry[MaxTextExtent] = "45.0x45.0";
7637
7638      /*
7639        Query user for shear color and geometry.
7640      */
7641      XColorBrowserWidget(display,windows,"Select",color);
7642      if (*color == '\0')
7643        break;
7644      (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7645        geometry);
7646      if (*geometry == '\0')
7647        break;
7648      /*
7649        Shear image.
7650      */
7651      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7652        exception);
7653      XSetCursorState(display,windows,MagickTrue);
7654      XCheckRefreshWindows(display,windows);
7655      (void) QueryColorCompliance(color,AllCompliance,
7656        &(*image)->background_color,exception);
7657      flags=ParseGeometry(geometry,&geometry_info);
7658      if ((flags & SigmaValue) == 0)
7659        geometry_info.sigma=geometry_info.rho;
7660      shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7661        exception);
7662      if (shear_image != (Image *) NULL)
7663        {
7664          *image=DestroyImage(*image);
7665          *image=shear_image;
7666        }
7667      CatchException(exception);
7668      XSetCursorState(display,windows,MagickFalse);
7669      if (windows->image.orphan != MagickFalse)
7670        break;
7671      windows->image.window_changes.width=(int) (*image)->columns;
7672      windows->image.window_changes.height=(int) (*image)->rows;
7673      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7674      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7675      break;
7676    }
7677    case RollCommand:
7678    {
7679      Image
7680        *roll_image;
7681
7682      static char
7683        geometry[MaxTextExtent] = "+2+2";
7684
7685      /*
7686        Query user for the roll geometry.
7687      */
7688      (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7689        geometry);
7690      if (*geometry == '\0')
7691        break;
7692      /*
7693        Roll image.
7694      */
7695      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7696        exception);
7697      XSetCursorState(display,windows,MagickTrue);
7698      XCheckRefreshWindows(display,windows);
7699      (void) ParsePageGeometry(*image,geometry,&page_geometry,
7700        exception);
7701      roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7702        exception);
7703      if (roll_image != (Image *) NULL)
7704        {
7705          *image=DestroyImage(*image);
7706          *image=roll_image;
7707        }
7708      CatchException(exception);
7709      XSetCursorState(display,windows,MagickFalse);
7710      if (windows->image.orphan != MagickFalse)
7711        break;
7712      windows->image.window_changes.width=(int) (*image)->columns;
7713      windows->image.window_changes.height=(int) (*image)->rows;
7714      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7715      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7716      break;
7717    }
7718    case TrimCommand:
7719    {
7720      static char
7721        fuzz[MaxTextExtent];
7722
7723      /*
7724        Query user for the fuzz factor.
7725      */
7726      (void) FormatLocaleString(fuzz,MaxTextExtent,"%g%%",100.0*
7727        (*image)->fuzz/(QuantumRange+1.0));
7728      (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7729      if (*fuzz == '\0')
7730        break;
7731      (*image)->fuzz=SiPrefixToDouble(fuzz,(double) QuantumRange+1.0);
7732      /*
7733        Trim image.
7734      */
7735      status=XTrimImage(display,resource_info,windows,*image,exception);
7736      if (status == MagickFalse)
7737        {
7738          XNoticeWidget(display,windows,"Unable to trim X image",
7739            (*image)->filename);
7740          break;
7741        }
7742      break;
7743    }
7744    case HueCommand:
7745    {
7746      static char
7747        hue_percent[MaxTextExtent] = "110";
7748
7749      /*
7750        Query user for percent hue change.
7751      */
7752      (void) XDialogWidget(display,windows,"Apply",
7753        "Enter percent change in image hue (0-200):",hue_percent);
7754      if (*hue_percent == '\0')
7755        break;
7756      /*
7757        Vary the image hue.
7758      */
7759      XSetCursorState(display,windows,MagickTrue);
7760      XCheckRefreshWindows(display,windows);
7761      (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7762      (void) ConcatenateMagickString(modulate_factors,hue_percent,
7763        MaxTextExtent);
7764      (void) ModulateImage(*image,modulate_factors,exception);
7765      XSetCursorState(display,windows,MagickFalse);
7766      if (windows->image.orphan != MagickFalse)
7767        break;
7768      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7769      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7770      break;
7771    }
7772    case SaturationCommand:
7773    {
7774      static char
7775        saturation_percent[MaxTextExtent] = "110";
7776
7777      /*
7778        Query user for percent saturation change.
7779      */
7780      (void) XDialogWidget(display,windows,"Apply",
7781        "Enter percent change in color saturation (0-200):",saturation_percent);
7782      if (*saturation_percent == '\0')
7783        break;
7784      /*
7785        Vary color saturation.
7786      */
7787      XSetCursorState(display,windows,MagickTrue);
7788      XCheckRefreshWindows(display,windows);
7789      (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7790      (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7791        MaxTextExtent);
7792      (void) ModulateImage(*image,modulate_factors,exception);
7793      XSetCursorState(display,windows,MagickFalse);
7794      if (windows->image.orphan != MagickFalse)
7795        break;
7796      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7797      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7798      break;
7799    }
7800    case BrightnessCommand:
7801    {
7802      static char
7803        brightness_percent[MaxTextExtent] = "110";
7804
7805      /*
7806        Query user for percent brightness change.
7807      */
7808      (void) XDialogWidget(display,windows,"Apply",
7809        "Enter percent change in color brightness (0-200):",brightness_percent);
7810      if (*brightness_percent == '\0')
7811        break;
7812      /*
7813        Vary the color brightness.
7814      */
7815      XSetCursorState(display,windows,MagickTrue);
7816      XCheckRefreshWindows(display,windows);
7817      (void) CopyMagickString(modulate_factors,brightness_percent,
7818        MaxTextExtent);
7819      (void) ModulateImage(*image,modulate_factors,exception);
7820      XSetCursorState(display,windows,MagickFalse);
7821      if (windows->image.orphan != MagickFalse)
7822        break;
7823      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7824      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7825      break;
7826    }
7827    case GammaCommand:
7828    {
7829      static char
7830        factor[MaxTextExtent] = "1.6";
7831
7832      /*
7833        Query user for gamma value.
7834      */
7835      (void) XDialogWidget(display,windows,"Gamma",
7836        "Enter gamma value (e.g. 1.2):",factor);
7837      if (*factor == '\0')
7838        break;
7839      /*
7840        Gamma correct image.
7841      */
7842      XSetCursorState(display,windows,MagickTrue);
7843      XCheckRefreshWindows(display,windows);
7844      (void) GammaImage(*image,atof(factor),exception);
7845      XSetCursorState(display,windows,MagickFalse);
7846      if (windows->image.orphan != MagickFalse)
7847        break;
7848      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7849      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7850      break;
7851    }
7852    case SpiffCommand:
7853    {
7854      /*
7855        Sharpen the image contrast.
7856      */
7857      XSetCursorState(display,windows,MagickTrue);
7858      XCheckRefreshWindows(display,windows);
7859      (void) ContrastImage(*image,MagickTrue,exception);
7860      XSetCursorState(display,windows,MagickFalse);
7861      if (windows->image.orphan != MagickFalse)
7862        break;
7863      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7864      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7865      break;
7866    }
7867    case DullCommand:
7868    {
7869      /*
7870        Dull the image contrast.
7871      */
7872      XSetCursorState(display,windows,MagickTrue);
7873      XCheckRefreshWindows(display,windows);
7874      (void) ContrastImage(*image,MagickFalse,exception);
7875      XSetCursorState(display,windows,MagickFalse);
7876      if (windows->image.orphan != MagickFalse)
7877        break;
7878      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7879      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7880      break;
7881    }
7882    case ContrastStretchCommand:
7883    {
7884      double
7885        black_point,
7886        white_point;
7887
7888      static char
7889        levels[MaxTextExtent] = "1%";
7890
7891      /*
7892        Query user for gamma value.
7893      */
7894      (void) XDialogWidget(display,windows,"Contrast Stretch",
7895        "Enter black and white points:",levels);
7896      if (*levels == '\0')
7897        break;
7898      /*
7899        Contrast stretch image.
7900      */
7901      XSetCursorState(display,windows,MagickTrue);
7902      XCheckRefreshWindows(display,windows);
7903      flags=ParseGeometry(levels,&geometry_info);
7904      black_point=geometry_info.rho;
7905      white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7906      if ((flags & PercentValue) != 0)
7907        {
7908          black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7909          white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7910        }
7911      white_point=(MagickRealType) (*image)->columns*(*image)->rows-white_point;
7912      (void) ContrastStretchImage(*image,black_point,white_point,
7913        exception);
7914      XSetCursorState(display,windows,MagickFalse);
7915      if (windows->image.orphan != MagickFalse)
7916        break;
7917      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7918      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7919      break;
7920    }
7921    case SigmoidalContrastCommand:
7922    {
7923      GeometryInfo
7924        geometry_info;
7925
7926      MagickStatusType
7927        flags;
7928
7929      static char
7930        levels[MaxTextExtent] = "3x50%";
7931
7932      /*
7933        Query user for gamma value.
7934      */
7935      (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7936        "Enter contrast and midpoint:",levels);
7937      if (*levels == '\0')
7938        break;
7939      /*
7940        Contrast stretch image.
7941      */
7942      XSetCursorState(display,windows,MagickTrue);
7943      XCheckRefreshWindows(display,windows);
7944      flags=ParseGeometry(levels,&geometry_info);
7945      if ((flags & SigmaValue) == 0)
7946        geometry_info.sigma=1.0*QuantumRange/2.0;
7947      if ((flags & PercentValue) != 0)
7948        geometry_info.sigma=1.0*QuantumRange*geometry_info.sigma/100.0;
7949      (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
7950        geometry_info.sigma,exception);
7951      XSetCursorState(display,windows,MagickFalse);
7952      if (windows->image.orphan != MagickFalse)
7953        break;
7954      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7955      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7956      break;
7957    }
7958    case NormalizeCommand:
7959    {
7960      /*
7961        Perform histogram normalization on the image.
7962      */
7963      XSetCursorState(display,windows,MagickTrue);
7964      XCheckRefreshWindows(display,windows);
7965      (void) NormalizeImage(*image,exception);
7966      XSetCursorState(display,windows,MagickFalse);
7967      if (windows->image.orphan != MagickFalse)
7968        break;
7969      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7970      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7971      break;
7972    }
7973    case EqualizeCommand:
7974    {
7975      /*
7976        Perform histogram equalization on the image.
7977      */
7978      XSetCursorState(display,windows,MagickTrue);
7979      XCheckRefreshWindows(display,windows);
7980      (void) EqualizeImage(*image,exception);
7981      XSetCursorState(display,windows,MagickFalse);
7982      if (windows->image.orphan != MagickFalse)
7983        break;
7984      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7985      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7986      break;
7987    }
7988    case NegateCommand:
7989    {
7990      /*
7991        Negate colors in image.
7992      */
7993      XSetCursorState(display,windows,MagickTrue);
7994      XCheckRefreshWindows(display,windows);
7995      (void) NegateImage(*image,MagickFalse,exception);
7996      XSetCursorState(display,windows,MagickFalse);
7997      if (windows->image.orphan != MagickFalse)
7998        break;
7999      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8000      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8001      break;
8002    }
8003    case GrayscaleCommand:
8004    {
8005      /*
8006        Convert image to grayscale.
8007      */
8008      XSetCursorState(display,windows,MagickTrue);
8009      XCheckRefreshWindows(display,windows);
8010      (void) SetImageType(*image,(*image)->matte == MagickFalse ?
8011        GrayscaleType : GrayscaleMatteType,exception);
8012      XSetCursorState(display,windows,MagickFalse);
8013      if (windows->image.orphan != MagickFalse)
8014        break;
8015      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8016      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8017      break;
8018    }
8019    case MapCommand:
8020    {
8021      Image
8022        *affinity_image;
8023
8024      static char
8025        filename[MaxTextExtent] = "\0";
8026
8027      /*
8028        Request image file name from user.
8029      */
8030      XFileBrowserWidget(display,windows,"Map",filename);
8031      if (*filename == '\0')
8032        break;
8033      /*
8034        Map image.
8035      */
8036      XSetCursorState(display,windows,MagickTrue);
8037      XCheckRefreshWindows(display,windows);
8038      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
8039      affinity_image=ReadImage(image_info,exception);
8040      if (affinity_image != (Image *) NULL)
8041        {
8042          (void) RemapImage(&quantize_info,*image,affinity_image,exception);
8043          affinity_image=DestroyImage(affinity_image);
8044        }
8045      CatchException(exception);
8046      XSetCursorState(display,windows,MagickFalse);
8047      if (windows->image.orphan != MagickFalse)
8048        break;
8049      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8050      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8051      break;
8052    }
8053    case QuantizeCommand:
8054    {
8055      int
8056        status;
8057
8058      static char
8059        colors[MaxTextExtent] = "256";
8060
8061      /*
8062        Query user for maximum number of colors.
8063      */
8064      status=XDialogWidget(display,windows,"Quantize",
8065        "Maximum number of colors:",colors);
8066      if (*colors == '\0')
8067        break;
8068      /*
8069        Color reduce the image.
8070      */
8071      XSetCursorState(display,windows,MagickTrue);
8072      XCheckRefreshWindows(display,windows);
8073      quantize_info.number_colors=StringToUnsignedLong(colors);
8074      quantize_info.dither=status != 0 ? MagickTrue : MagickFalse;
8075      (void) QuantizeImage(&quantize_info,*image,exception);
8076      XSetCursorState(display,windows,MagickFalse);
8077      if (windows->image.orphan != MagickFalse)
8078        break;
8079      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8080      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8081      break;
8082    }
8083    case DespeckleCommand:
8084    {
8085      Image
8086        *despeckle_image;
8087
8088      /*
8089        Despeckle image.
8090      */
8091      XSetCursorState(display,windows,MagickTrue);
8092      XCheckRefreshWindows(display,windows);
8093      despeckle_image=DespeckleImage(*image,exception);
8094      if (despeckle_image != (Image *) NULL)
8095        {
8096          *image=DestroyImage(*image);
8097          *image=despeckle_image;
8098        }
8099      CatchException(exception);
8100      XSetCursorState(display,windows,MagickFalse);
8101      if (windows->image.orphan != MagickFalse)
8102        break;
8103      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8104      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8105      break;
8106    }
8107    case EmbossCommand:
8108    {
8109      Image
8110        *emboss_image;
8111
8112      static char
8113        radius[MaxTextExtent] = "0.0x1.0";
8114
8115      /*
8116        Query user for emboss radius.
8117      */
8118      (void) XDialogWidget(display,windows,"Emboss",
8119        "Enter the emboss radius and standard deviation:",radius);
8120      if (*radius == '\0')
8121        break;
8122      /*
8123        Reduce noise in the image.
8124      */
8125      XSetCursorState(display,windows,MagickTrue);
8126      XCheckRefreshWindows(display,windows);
8127      flags=ParseGeometry(radius,&geometry_info);
8128      if ((flags & SigmaValue) == 0)
8129        geometry_info.sigma=1.0;
8130      emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8131        exception);
8132      if (emboss_image != (Image *) NULL)
8133        {
8134          *image=DestroyImage(*image);
8135          *image=emboss_image;
8136        }
8137      CatchException(exception);
8138      XSetCursorState(display,windows,MagickFalse);
8139      if (windows->image.orphan != MagickFalse)
8140        break;
8141      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8142      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8143      break;
8144    }
8145    case ReduceNoiseCommand:
8146    {
8147      Image
8148        *noise_image;
8149
8150      static char
8151        radius[MaxTextExtent] = "0";
8152
8153      /*
8154        Query user for noise radius.
8155      */
8156      (void) XDialogWidget(display,windows,"Reduce Noise",
8157        "Enter the noise radius:",radius);
8158      if (*radius == '\0')
8159        break;
8160      /*
8161        Reduce noise in the image.
8162      */
8163      XSetCursorState(display,windows,MagickTrue);
8164      XCheckRefreshWindows(display,windows);
8165      flags=ParseGeometry(radius,&geometry_info);
8166      noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8167        geometry_info.rho,(size_t) geometry_info.rho,exception);
8168      if (noise_image != (Image *) NULL)
8169        {
8170          *image=DestroyImage(*image);
8171          *image=noise_image;
8172        }
8173      CatchException(exception);
8174      XSetCursorState(display,windows,MagickFalse);
8175      if (windows->image.orphan != MagickFalse)
8176        break;
8177      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8178      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8179      break;
8180    }
8181    case AddNoiseCommand:
8182    {
8183      char
8184        **noises;
8185
8186      Image
8187        *noise_image;
8188
8189      static char
8190        noise_type[MaxTextExtent] = "Gaussian";
8191
8192      /*
8193        Add noise to the image.
8194      */
8195      noises=GetCommandOptions(MagickNoiseOptions);
8196      if (noises == (char **) NULL)
8197        break;
8198      XListBrowserWidget(display,windows,&windows->widget,
8199        (const char **) noises,"Add Noise",
8200        "Select a type of noise to add to your image:",noise_type);
8201      noises=DestroyStringList(noises);
8202      if (*noise_type == '\0')
8203        break;
8204      XSetCursorState(display,windows,MagickTrue);
8205      XCheckRefreshWindows(display,windows);
8206      noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8207        MagickNoiseOptions,MagickFalse,noise_type),1.0,exception);
8208      if (noise_image != (Image *) NULL)
8209        {
8210          *image=DestroyImage(*image);
8211          *image=noise_image;
8212        }
8213      CatchException(exception);
8214      XSetCursorState(display,windows,MagickFalse);
8215      if (windows->image.orphan != MagickFalse)
8216        break;
8217      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8218      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8219      break;
8220    }
8221    case SharpenCommand:
8222    {
8223      Image
8224        *sharp_image;
8225
8226      static char
8227        radius[MaxTextExtent] = "0.0x1.0";
8228
8229      /*
8230        Query user for sharpen radius.
8231      */
8232      (void) XDialogWidget(display,windows,"Sharpen",
8233        "Enter the sharpen radius and standard deviation:",radius);
8234      if (*radius == '\0')
8235        break;
8236      /*
8237        Sharpen image scanlines.
8238      */
8239      XSetCursorState(display,windows,MagickTrue);
8240      XCheckRefreshWindows(display,windows);
8241      flags=ParseGeometry(radius,&geometry_info);
8242      sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8243        geometry_info.xi,exception);
8244      if (sharp_image != (Image *) NULL)
8245        {
8246          *image=DestroyImage(*image);
8247          *image=sharp_image;
8248        }
8249      CatchException(exception);
8250      XSetCursorState(display,windows,MagickFalse);
8251      if (windows->image.orphan != MagickFalse)
8252        break;
8253      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8254      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8255      break;
8256    }
8257    case BlurCommand:
8258    {
8259      Image
8260        *blur_image;
8261
8262      static char
8263        radius[MaxTextExtent] = "0.0x1.0";
8264
8265      /*
8266        Query user for blur radius.
8267      */
8268      (void) XDialogWidget(display,windows,"Blur",
8269        "Enter the blur radius and standard deviation:",radius);
8270      if (*radius == '\0')
8271        break;
8272      /*
8273        Blur an image.
8274      */
8275      XSetCursorState(display,windows,MagickTrue);
8276      XCheckRefreshWindows(display,windows);
8277      flags=ParseGeometry(radius,&geometry_info);
8278      blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8279        geometry_info.xi,exception);
8280      if (blur_image != (Image *) NULL)
8281        {
8282          *image=DestroyImage(*image);
8283          *image=blur_image;
8284        }
8285      CatchException(exception);
8286      XSetCursorState(display,windows,MagickFalse);
8287      if (windows->image.orphan != MagickFalse)
8288        break;
8289      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8290      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8291      break;
8292    }
8293    case ThresholdCommand:
8294    {
8295      double
8296        threshold;
8297
8298      static char
8299        factor[MaxTextExtent] = "128";
8300
8301      /*
8302        Query user for threshold value.
8303      */
8304      (void) XDialogWidget(display,windows,"Threshold",
8305        "Enter threshold value:",factor);
8306      if (*factor == '\0')
8307        break;
8308      /*
8309        Gamma correct image.
8310      */
8311      XSetCursorState(display,windows,MagickTrue);
8312      XCheckRefreshWindows(display,windows);
8313      threshold=SiPrefixToDouble(factor,QuantumRange);
8314      (void) BilevelImage(*image,threshold,exception);
8315      XSetCursorState(display,windows,MagickFalse);
8316      if (windows->image.orphan != MagickFalse)
8317        break;
8318      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8319      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8320      break;
8321    }
8322    case EdgeDetectCommand:
8323    {
8324      Image
8325        *edge_image;
8326
8327      static char
8328        radius[MaxTextExtent] = "0";
8329
8330      /*
8331        Query user for edge factor.
8332      */
8333      (void) XDialogWidget(display,windows,"Detect Edges",
8334        "Enter the edge detect radius:",radius);
8335      if (*radius == '\0')
8336        break;
8337      /*
8338        Detect edge in image.
8339      */
8340      XSetCursorState(display,windows,MagickTrue);
8341      XCheckRefreshWindows(display,windows);
8342      flags=ParseGeometry(radius,&geometry_info);
8343      edge_image=EdgeImage(*image,geometry_info.rho,geometry_info.sigma,
8344        exception);
8345      if (edge_image != (Image *) NULL)
8346        {
8347          *image=DestroyImage(*image);
8348          *image=edge_image;
8349        }
8350      CatchException(exception);
8351      XSetCursorState(display,windows,MagickFalse);
8352      if (windows->image.orphan != MagickFalse)
8353        break;
8354      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8355      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8356      break;
8357    }
8358    case SpreadCommand:
8359    {
8360      Image
8361        *spread_image;
8362
8363      static char
8364        amount[MaxTextExtent] = "2";
8365
8366      /*
8367        Query user for spread amount.
8368      */
8369      (void) XDialogWidget(display,windows,"Spread",
8370        "Enter the displacement amount:",amount);
8371      if (*amount == '\0')
8372        break;
8373      /*
8374        Displace image pixels by a random amount.
8375      */
8376      XSetCursorState(display,windows,MagickTrue);
8377      XCheckRefreshWindows(display,windows);
8378      flags=ParseGeometry(amount,&geometry_info);
8379      spread_image=EdgeImage(*image,geometry_info.rho,geometry_info.sigma,
8380        exception);
8381      if (spread_image != (Image *) NULL)
8382        {
8383          *image=DestroyImage(*image);
8384          *image=spread_image;
8385        }
8386      CatchException(exception);
8387      XSetCursorState(display,windows,MagickFalse);
8388      if (windows->image.orphan != MagickFalse)
8389        break;
8390      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8391      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8392      break;
8393    }
8394    case ShadeCommand:
8395    {
8396      Image
8397        *shade_image;
8398
8399      int
8400        status;
8401
8402      static char
8403        geometry[MaxTextExtent] = "30x30";
8404
8405      /*
8406        Query user for the shade geometry.
8407      */
8408      status=XDialogWidget(display,windows,"Shade",
8409        "Enter the azimuth and elevation of the light source:",geometry);
8410      if (*geometry == '\0')
8411        break;
8412      /*
8413        Shade image pixels.
8414      */
8415      XSetCursorState(display,windows,MagickTrue);
8416      XCheckRefreshWindows(display,windows);
8417      flags=ParseGeometry(geometry,&geometry_info);
8418      if ((flags & SigmaValue) == 0)
8419        geometry_info.sigma=1.0;
8420      shade_image=ShadeImage(*image,status != 0 ? MagickFalse : MagickTrue,
8421        geometry_info.rho,geometry_info.sigma,exception);
8422      if (shade_image != (Image *) NULL)
8423        {
8424          *image=DestroyImage(*image);
8425          *image=shade_image;
8426        }
8427      CatchException(exception);
8428      XSetCursorState(display,windows,MagickFalse);
8429      if (windows->image.orphan != MagickFalse)
8430        break;
8431      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8432      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8433      break;
8434    }
8435    case RaiseCommand:
8436    {
8437      static char
8438        bevel_width[MaxTextExtent] = "10";
8439
8440      /*
8441        Query user for bevel width.
8442      */
8443      (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8444      if (*bevel_width == '\0')
8445        break;
8446      /*
8447        Raise an image.
8448      */
8449      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8450        exception);
8451      XSetCursorState(display,windows,MagickTrue);
8452      XCheckRefreshWindows(display,windows);
8453      (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8454        exception);
8455      (void) RaiseImage(*image,&page_geometry,MagickTrue,exception);
8456      XSetCursorState(display,windows,MagickFalse);
8457      if (windows->image.orphan != MagickFalse)
8458        break;
8459      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8460      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8461      break;
8462    }
8463    case SegmentCommand:
8464    {
8465      static char
8466        threshold[MaxTextExtent] = "1.0x1.5";
8467
8468      /*
8469        Query user for smoothing threshold.
8470      */
8471      (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8472        threshold);
8473      if (*threshold == '\0')
8474        break;
8475      /*
8476        Segment an image.
8477      */
8478      XSetCursorState(display,windows,MagickTrue);
8479      XCheckRefreshWindows(display,windows);
8480      flags=ParseGeometry(threshold,&geometry_info);
8481      if ((flags & SigmaValue) == 0)
8482        geometry_info.sigma=1.0;
8483      (void) SegmentImage(*image,RGBColorspace,MagickFalse,geometry_info.rho,
8484        geometry_info.sigma,exception);
8485      XSetCursorState(display,windows,MagickFalse);
8486      if (windows->image.orphan != MagickFalse)
8487        break;
8488      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8489      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8490      break;
8491    }
8492    case SepiaToneCommand:
8493    {
8494      double
8495        threshold;
8496
8497      Image
8498        *sepia_image;
8499
8500      static char
8501        factor[MaxTextExtent] = "80%";
8502
8503      /*
8504        Query user for sepia-tone factor.
8505      */
8506      (void) XDialogWidget(display,windows,"Sepia Tone",
8507        "Enter the sepia tone factor (0 - 99.9%):",factor);
8508      if (*factor == '\0')
8509        break;
8510      /*
8511        Sepia tone image pixels.
8512      */
8513      XSetCursorState(display,windows,MagickTrue);
8514      XCheckRefreshWindows(display,windows);
8515      threshold=SiPrefixToDouble(factor,QuantumRange);
8516      sepia_image=SepiaToneImage(*image,threshold,exception);
8517      if (sepia_image != (Image *) NULL)
8518        {
8519          *image=DestroyImage(*image);
8520          *image=sepia_image;
8521        }
8522      CatchException(exception);
8523      XSetCursorState(display,windows,MagickFalse);
8524      if (windows->image.orphan != MagickFalse)
8525        break;
8526      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8527      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8528      break;
8529    }
8530    case SolarizeCommand:
8531    {
8532      double
8533        threshold;
8534
8535      static char
8536        factor[MaxTextExtent] = "60%";
8537
8538      /*
8539        Query user for solarize factor.
8540      */
8541      (void) XDialogWidget(display,windows,"Solarize",
8542        "Enter the solarize factor (0 - 99.9%):",factor);
8543      if (*factor == '\0')
8544        break;
8545      /*
8546        Solarize image pixels.
8547      */
8548      XSetCursorState(display,windows,MagickTrue);
8549      XCheckRefreshWindows(display,windows);
8550      threshold=SiPrefixToDouble(factor,QuantumRange);
8551      (void) SolarizeImage(*image,threshold,exception);
8552      XSetCursorState(display,windows,MagickFalse);
8553      if (windows->image.orphan != MagickFalse)
8554        break;
8555      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8556      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8557      break;
8558    }
8559    case SwirlCommand:
8560    {
8561      Image
8562        *swirl_image;
8563
8564      static char
8565        degrees[MaxTextExtent] = "60";
8566
8567      /*
8568        Query user for swirl angle.
8569      */
8570      (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8571        degrees);
8572      if (*degrees == '\0')
8573        break;
8574      /*
8575        Swirl image pixels about the center.
8576      */
8577      XSetCursorState(display,windows,MagickTrue);
8578      XCheckRefreshWindows(display,windows);
8579      flags=ParseGeometry(degrees,&geometry_info);
8580      swirl_image=SwirlImage(*image,geometry_info.rho,(*image)->interpolate,
8581        exception);
8582      if (swirl_image != (Image *) NULL)
8583        {
8584          *image=DestroyImage(*image);
8585          *image=swirl_image;
8586        }
8587      CatchException(exception);
8588      XSetCursorState(display,windows,MagickFalse);
8589      if (windows->image.orphan != MagickFalse)
8590        break;
8591      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8592      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8593      break;
8594    }
8595    case ImplodeCommand:
8596    {
8597      Image
8598        *implode_image;
8599
8600      static char
8601        factor[MaxTextExtent] = "0.3";
8602
8603      /*
8604        Query user for implode factor.
8605      */
8606      (void) XDialogWidget(display,windows,"Implode",
8607        "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8608      if (*factor == '\0')
8609        break;
8610      /*
8611        Implode image pixels about the center.
8612      */
8613      XSetCursorState(display,windows,MagickTrue);
8614      XCheckRefreshWindows(display,windows);
8615      flags=ParseGeometry(factor,&geometry_info);
8616      implode_image=ImplodeImage(*image,geometry_info.rho,(*image)->interpolate,
8617        exception);
8618      if (implode_image != (Image *) NULL)
8619        {
8620          *image=DestroyImage(*image);
8621          *image=implode_image;
8622        }
8623      CatchException(exception);
8624      XSetCursorState(display,windows,MagickFalse);
8625      if (windows->image.orphan != MagickFalse)
8626        break;
8627      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8628      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8629      break;
8630    }
8631    case VignetteCommand:
8632    {
8633      Image
8634        *vignette_image;
8635
8636      static char
8637        geometry[MaxTextExtent] = "0x20";
8638
8639      /*
8640        Query user for the vignette geometry.
8641      */
8642      (void) XDialogWidget(display,windows,"Vignette",
8643        "Enter the radius, sigma, and x and y offsets:",geometry);
8644      if (*geometry == '\0')
8645        break;
8646      /*
8647        Soften the edges of the image in vignette style
8648      */
8649      XSetCursorState(display,windows,MagickTrue);
8650      XCheckRefreshWindows(display,windows);
8651      flags=ParseGeometry(geometry,&geometry_info);
8652      if ((flags & SigmaValue) == 0)
8653        geometry_info.sigma=1.0;
8654      if ((flags & XiValue) == 0)
8655        geometry_info.xi=0.1*(*image)->columns;
8656      if ((flags & PsiValue) == 0)
8657        geometry_info.psi=0.1*(*image)->rows;
8658      vignette_image=VignetteImage(*image,geometry_info.rho,geometry_info.sigma,
8659        (ssize_t) ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-
8660        0.5),exception);
8661      if (vignette_image != (Image *) NULL)
8662        {
8663          *image=DestroyImage(*image);
8664          *image=vignette_image;
8665        }
8666      CatchException(exception);
8667      XSetCursorState(display,windows,MagickFalse);
8668      if (windows->image.orphan != MagickFalse)
8669        break;
8670      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8671      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8672      break;
8673    }
8674    case WaveCommand:
8675    {
8676      Image
8677        *wave_image;
8678
8679      static char
8680        geometry[MaxTextExtent] = "25x150";
8681
8682      /*
8683        Query user for the wave geometry.
8684      */
8685      (void) XDialogWidget(display,windows,"Wave",
8686        "Enter the amplitude and length of the wave:",geometry);
8687      if (*geometry == '\0')
8688        break;
8689      /*
8690        Alter an image along a sine wave.
8691      */
8692      XSetCursorState(display,windows,MagickTrue);
8693      XCheckRefreshWindows(display,windows);
8694      flags=ParseGeometry(geometry,&geometry_info);
8695      if ((flags & SigmaValue) == 0)
8696        geometry_info.sigma=1.0;
8697      wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8698        (*image)->interpolate,exception);
8699      if (wave_image != (Image *) NULL)
8700        {
8701          *image=DestroyImage(*image);
8702          *image=wave_image;
8703        }
8704      CatchException(exception);
8705      XSetCursorState(display,windows,MagickFalse);
8706      if (windows->image.orphan != MagickFalse)
8707        break;
8708      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8709      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8710      break;
8711    }
8712    case OilPaintCommand:
8713    {
8714      Image
8715        *paint_image;
8716
8717      static char
8718        radius[MaxTextExtent] = "0";
8719
8720      /*
8721        Query user for circular neighborhood radius.
8722      */
8723      (void) XDialogWidget(display,windows,"Oil Paint",
8724        "Enter the mask radius:",radius);
8725      if (*radius == '\0')
8726        break;
8727      /*
8728        OilPaint image scanlines.
8729      */
8730      XSetCursorState(display,windows,MagickTrue);
8731      XCheckRefreshWindows(display,windows);
8732      flags=ParseGeometry(radius,&geometry_info);
8733      paint_image=OilPaintImage(*image,geometry_info.rho,geometry_info.sigma,
8734        exception);
8735      if (paint_image != (Image *) NULL)
8736        {
8737          *image=DestroyImage(*image);
8738          *image=paint_image;
8739        }
8740      CatchException(exception);
8741      XSetCursorState(display,windows,MagickFalse);
8742      if (windows->image.orphan != MagickFalse)
8743        break;
8744      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8745      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8746      break;
8747    }
8748    case CharcoalDrawCommand:
8749    {
8750      Image
8751        *charcoal_image;
8752
8753      static char
8754        radius[MaxTextExtent] = "0x1";
8755
8756      /*
8757        Query user for charcoal radius.
8758      */
8759      (void) XDialogWidget(display,windows,"Charcoal Draw",
8760        "Enter the charcoal radius and sigma:",radius);
8761      if (*radius == '\0')
8762        break;
8763      /*
8764        Charcoal the image.
8765      */
8766      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8767        exception);
8768      XSetCursorState(display,windows,MagickTrue);
8769      XCheckRefreshWindows(display,windows);
8770      flags=ParseGeometry(radius,&geometry_info);
8771      if ((flags & SigmaValue) == 0)
8772        geometry_info.sigma=geometry_info.rho;
8773      charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8774        geometry_info.xi,exception);
8775      if (charcoal_image != (Image *) NULL)
8776        {
8777          *image=DestroyImage(*image);
8778          *image=charcoal_image;
8779        }
8780      CatchException(exception);
8781      XSetCursorState(display,windows,MagickFalse);
8782      if (windows->image.orphan != MagickFalse)
8783        break;
8784      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8785      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8786      break;
8787    }
8788    case AnnotateCommand:
8789    {
8790      /*
8791        Annotate the image with text.
8792      */
8793      status=XAnnotateEditImage(display,resource_info,windows,*image,exception);
8794      if (status == MagickFalse)
8795        {
8796          XNoticeWidget(display,windows,"Unable to annotate X image",
8797            (*image)->filename);
8798          break;
8799        }
8800      break;
8801    }
8802    case DrawCommand:
8803    {
8804      /*
8805        Draw image.
8806      */
8807      status=XDrawEditImage(display,resource_info,windows,image,exception);
8808      if (status == MagickFalse)
8809        {
8810          XNoticeWidget(display,windows,"Unable to draw on the X image",
8811            (*image)->filename);
8812          break;
8813        }
8814      break;
8815    }
8816    case ColorCommand:
8817    {
8818      /*
8819        Color edit.
8820      */
8821      status=XColorEditImage(display,resource_info,windows,image,exception);
8822      if (status == MagickFalse)
8823        {
8824          XNoticeWidget(display,windows,"Unable to pixel edit X image",
8825            (*image)->filename);
8826          break;
8827        }
8828      break;
8829    }
8830    case MatteCommand:
8831    {
8832      /*
8833        Matte edit.
8834      */
8835      status=XMatteEditImage(display,resource_info,windows,image,exception);
8836      if (status == MagickFalse)
8837        {
8838          XNoticeWidget(display,windows,"Unable to matte edit X image",
8839            (*image)->filename);
8840          break;
8841        }
8842      break;
8843    }
8844    case CompositeCommand:
8845    {
8846      /*
8847        Composite image.
8848      */
8849      status=XCompositeImage(display,resource_info,windows,*image,
8850        exception);
8851      if (status == MagickFalse)
8852        {
8853          XNoticeWidget(display,windows,"Unable to composite X image",
8854            (*image)->filename);
8855          break;
8856        }
8857      break;
8858    }
8859    case AddBorderCommand:
8860    {
8861      Image
8862        *border_image;
8863
8864      static char
8865        geometry[MaxTextExtent] = "6x6";
8866
8867      /*
8868        Query user for border color and geometry.
8869      */
8870      XColorBrowserWidget(display,windows,"Select",color);
8871      if (*color == '\0')
8872        break;
8873      (void) XDialogWidget(display,windows,"Add Border",
8874        "Enter border geometry:",geometry);
8875      if (*geometry == '\0')
8876        break;
8877      /*
8878        Add a border to the image.
8879      */
8880      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8881        exception);
8882      XSetCursorState(display,windows,MagickTrue);
8883      XCheckRefreshWindows(display,windows);
8884      (void) QueryColorCompliance(color,AllCompliance,&(*image)->border_color,
8885        exception);
8886      (void) ParsePageGeometry(*image,geometry,&page_geometry,
8887        exception);
8888      border_image=BorderImage(*image,&page_geometry,(*image)->compose,
8889        exception);
8890      if (border_image != (Image *) NULL)
8891        {
8892          *image=DestroyImage(*image);
8893          *image=border_image;
8894        }
8895      CatchException(exception);
8896      XSetCursorState(display,windows,MagickFalse);
8897      if (windows->image.orphan != MagickFalse)
8898        break;
8899      windows->image.window_changes.width=(int) (*image)->columns;
8900      windows->image.window_changes.height=(int) (*image)->rows;
8901      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8902      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8903      break;
8904    }
8905    case AddFrameCommand:
8906    {
8907      FrameInfo
8908        frame_info;
8909
8910      Image
8911        *frame_image;
8912
8913      static char
8914        geometry[MaxTextExtent] = "6x6";
8915
8916      /*
8917        Query user for frame color and geometry.
8918      */
8919      XColorBrowserWidget(display,windows,"Select",color);
8920      if (*color == '\0')
8921        break;
8922      (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8923        geometry);
8924      if (*geometry == '\0')
8925        break;
8926      /*
8927        Surround image with an ornamental border.
8928      */
8929      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8930        exception);
8931      XSetCursorState(display,windows,MagickTrue);
8932      XCheckRefreshWindows(display,windows);
8933      (void) QueryColorCompliance(color,AllCompliance,&(*image)->matte_color,
8934        exception);
8935      (void) ParsePageGeometry(*image,geometry,&page_geometry,
8936        exception);
8937      frame_info.width=page_geometry.width;
8938      frame_info.height=page_geometry.height;
8939      frame_info.outer_bevel=page_geometry.x;
8940      frame_info.inner_bevel=page_geometry.y;
8941      frame_info.x=(ssize_t) frame_info.width;
8942      frame_info.y=(ssize_t) frame_info.height;
8943      frame_info.width=(*image)->columns+2*frame_info.width;
8944      frame_info.height=(*image)->rows+2*frame_info.height;
8945      frame_image=FrameImage(*image,&frame_info,(*image)->compose,exception);
8946      if (frame_image != (Image *) NULL)
8947        {
8948          *image=DestroyImage(*image);
8949          *image=frame_image;
8950        }
8951      CatchException(exception);
8952      XSetCursorState(display,windows,MagickFalse);
8953      if (windows->image.orphan != MagickFalse)
8954        break;
8955      windows->image.window_changes.width=(int) (*image)->columns;
8956      windows->image.window_changes.height=(int) (*image)->rows;
8957      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8958      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8959      break;
8960    }
8961    case CommentCommand:
8962    {
8963      const char
8964        *value;
8965
8966      FILE
8967        *file;
8968
8969      int
8970        unique_file;
8971
8972      /*
8973        Edit image comment.
8974      */
8975      unique_file=AcquireUniqueFileResource(image_info->filename);
8976      if (unique_file == -1)
8977        XNoticeWidget(display,windows,"Unable to edit image comment",
8978          image_info->filename);
8979      value=GetImageProperty(*image,"comment",exception);
8980      if (value == (char *) NULL)
8981        unique_file=close(unique_file)-1;
8982      else
8983        {
8984          register const char
8985            *p;
8986
8987          file=fdopen(unique_file,"w");
8988          if (file == (FILE *) NULL)
8989            {
8990              XNoticeWidget(display,windows,"Unable to edit image comment",
8991                image_info->filename);
8992              break;
8993            }
8994          for (p=value; *p != '\0'; p++)
8995            (void) fputc((int) *p,file);
8996          (void) fputc('\n',file);
8997          (void) fclose(file);
8998        }
8999      XSetCursorState(display,windows,MagickTrue);
9000      XCheckRefreshWindows(display,windows);
9001      status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
9002        exception);
9003      if (status == MagickFalse)
9004        XNoticeWidget(display,windows,"Unable to edit image comment",
9005          (char *) NULL);
9006      else
9007        {
9008          char
9009            *comment;
9010
9011          comment=FileToString(image_info->filename,~0UL,exception);
9012          if (comment != (char *) NULL)
9013            {
9014              (void) SetImageProperty(*image,"comment",comment,exception);
9015              (*image)->taint=MagickTrue;
9016            }
9017        }
9018      (void) RelinquishUniqueFileResource(image_info->filename);
9019      XSetCursorState(display,windows,MagickFalse);
9020      break;
9021    }
9022    case LaunchCommand:
9023    {
9024      /*
9025        Launch program.
9026      */
9027      XSetCursorState(display,windows,MagickTrue);
9028      XCheckRefreshWindows(display,windows);
9029      (void) AcquireUniqueFilename(filename);
9030      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"launch:%s",
9031        filename);
9032      status=WriteImage(image_info,*image,exception);
9033      if (status == MagickFalse)
9034        XNoticeWidget(display,windows,"Unable to launch image editor",
9035          (char *) NULL);
9036      else
9037        {
9038          nexus=ReadImage(resource_info->image_info,exception);
9039          CatchException(exception);
9040          XClientMessage(display,windows->image.id,windows->im_protocols,
9041            windows->im_next_image,CurrentTime);
9042        }
9043      (void) RelinquishUniqueFileResource(filename);
9044      XSetCursorState(display,windows,MagickFalse);
9045      break;
9046    }
9047    case RegionofInterestCommand:
9048    {
9049      /*
9050        Apply an image processing technique to a region of interest.
9051      */
9052      (void) XROIImage(display,resource_info,windows,image,exception);
9053      break;
9054    }
9055    case InfoCommand:
9056      break;
9057    case ZoomCommand:
9058    {
9059      /*
9060        Zoom image.
9061      */
9062      if (windows->magnify.mapped != MagickFalse)
9063        (void) XRaiseWindow(display,windows->magnify.id);
9064      else
9065        {
9066          /*
9067            Make magnify image.
9068          */
9069          XSetCursorState(display,windows,MagickTrue);
9070          (void) XMapRaised(display,windows->magnify.id);
9071          XSetCursorState(display,windows,MagickFalse);
9072        }
9073      break;
9074    }
9075    case ShowPreviewCommand:
9076    {
9077      char
9078        **previews;
9079
9080      Image
9081        *preview_image;
9082
9083      static char
9084        preview_type[MaxTextExtent] = "Gamma";
9085
9086      /*
9087        Select preview type from menu.
9088      */
9089      previews=GetCommandOptions(MagickPreviewOptions);
9090      if (previews == (char **) NULL)
9091        break;
9092      XListBrowserWidget(display,windows,&windows->widget,
9093        (const char **) previews,"Preview",
9094        "Select an enhancement, effect, or F/X:",preview_type);
9095      previews=DestroyStringList(previews);
9096      if (*preview_type == '\0')
9097        break;
9098      /*
9099        Show image preview.
9100      */
9101      XSetCursorState(display,windows,MagickTrue);
9102      XCheckRefreshWindows(display,windows);
9103      image_info->preview_type=(PreviewType)
9104        ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
9105      image_info->group=(ssize_t) windows->image.id;
9106      (void) DeleteImageProperty(*image,"label");
9107      (void) SetImageProperty(*image,"label","Preview",exception);
9108      (void) AcquireUniqueFilename(filename);
9109      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"preview:%s",
9110        filename);
9111      status=WriteImage(image_info,*image,exception);
9112      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9113      preview_image=ReadImage(image_info,exception);
9114      (void) RelinquishUniqueFileResource(filename);
9115      if (preview_image == (Image *) NULL)
9116        break;
9117      (void) FormatLocaleString(preview_image->filename,MaxTextExtent,"show:%s",
9118        filename);
9119      status=WriteImage(image_info,preview_image,exception);
9120      preview_image=DestroyImage(preview_image);
9121      if (status == MagickFalse)
9122        XNoticeWidget(display,windows,"Unable to show image preview",
9123          (*image)->filename);
9124      XDelay(display,1500);
9125      XSetCursorState(display,windows,MagickFalse);
9126      break;
9127    }
9128    case ShowHistogramCommand:
9129    {
9130      Image
9131        *histogram_image;
9132
9133      /*
9134        Show image histogram.
9135      */
9136      XSetCursorState(display,windows,MagickTrue);
9137      XCheckRefreshWindows(display,windows);
9138      image_info->group=(ssize_t) windows->image.id;
9139      (void) DeleteImageProperty(*image,"label");
9140      (void) SetImageProperty(*image,"label","Histogram",exception);
9141      (void) AcquireUniqueFilename(filename);
9142      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"histogram:%s",
9143        filename);
9144      status=WriteImage(image_info,*image,exception);
9145      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9146      histogram_image=ReadImage(image_info,exception);
9147      (void) RelinquishUniqueFileResource(filename);
9148      if (histogram_image == (Image *) NULL)
9149        break;
9150      (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
9151        "show:%s",filename);
9152      status=WriteImage(image_info,histogram_image,exception);
9153      histogram_image=DestroyImage(histogram_image);
9154      if (status == MagickFalse)
9155        XNoticeWidget(display,windows,"Unable to show histogram",
9156          (*image)->filename);
9157      XDelay(display,1500);
9158      XSetCursorState(display,windows,MagickFalse);
9159      break;
9160    }
9161    case ShowMatteCommand:
9162    {
9163      Image
9164        *matte_image;
9165
9166      if ((*image)->matte == MagickFalse)
9167        {
9168          XNoticeWidget(display,windows,
9169            "Image does not have any matte information",(*image)->filename);
9170          break;
9171        }
9172      /*
9173        Show image matte.
9174      */
9175      XSetCursorState(display,windows,MagickTrue);
9176      XCheckRefreshWindows(display,windows);
9177      image_info->group=(ssize_t) windows->image.id;
9178      (void) DeleteImageProperty(*image,"label");
9179      (void) SetImageProperty(*image,"label","Matte",exception);
9180      (void) AcquireUniqueFilename(filename);
9181      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"matte:%s",
9182        filename);
9183      status=WriteImage(image_info,*image,exception);
9184      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9185      matte_image=ReadImage(image_info,exception);
9186      (void) RelinquishUniqueFileResource(filename);
9187      if (matte_image == (Image *) NULL)
9188        break;
9189      (void) FormatLocaleString(matte_image->filename,MaxTextExtent,"show:%s",
9190        filename);
9191      status=WriteImage(image_info,matte_image,exception);
9192      matte_image=DestroyImage(matte_image);
9193      if (status == MagickFalse)
9194        XNoticeWidget(display,windows,"Unable to show matte",
9195          (*image)->filename);
9196      XDelay(display,1500);
9197      XSetCursorState(display,windows,MagickFalse);
9198      break;
9199    }
9200    case BackgroundCommand:
9201    {
9202      /*
9203        Background image.
9204      */
9205      status=XBackgroundImage(display,resource_info,windows,image,exception);
9206      if (status == MagickFalse)
9207        break;
9208      nexus=CloneImage(*image,0,0,MagickTrue,exception);
9209      if (nexus != (Image *) NULL)
9210        XClientMessage(display,windows->image.id,windows->im_protocols,
9211          windows->im_next_image,CurrentTime);
9212      break;
9213    }
9214    case SlideShowCommand:
9215    {
9216      static char
9217        delay[MaxTextExtent] = "5";
9218
9219      /*
9220        Display next image after pausing.
9221      */
9222      (void) XDialogWidget(display,windows,"Slide Show",
9223        "Pause how many 1/100ths of a second between images:",delay);
9224      if (*delay == '\0')
9225        break;
9226      resource_info->delay=StringToUnsignedLong(delay);
9227      XClientMessage(display,windows->image.id,windows->im_protocols,
9228        windows->im_next_image,CurrentTime);
9229      break;
9230    }
9231    case PreferencesCommand:
9232    {
9233      /*
9234        Set user preferences.
9235      */
9236      status=XPreferencesWidget(display,resource_info,windows);
9237      if (status == MagickFalse)
9238        break;
9239      nexus=CloneImage(*image,0,0,MagickTrue,exception);
9240      if (nexus != (Image *) NULL)
9241        XClientMessage(display,windows->image.id,windows->im_protocols,
9242          windows->im_next_image,CurrentTime);
9243      break;
9244    }
9245    case HelpCommand:
9246    {
9247      /*
9248        User requested help.
9249      */
9250      XTextViewWidget(display,resource_info,windows,MagickFalse,
9251        "Help Viewer - Display",DisplayHelp);
9252      break;
9253    }
9254    case BrowseDocumentationCommand:
9255    {
9256      Atom
9257        mozilla_atom;
9258
9259      Window
9260        mozilla_window,
9261        root_window;
9262
9263      /*
9264        Browse the ImageMagick documentation.
9265      */
9266      root_window=XRootWindow(display,XDefaultScreen(display));
9267      mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9268      mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9269      if (mozilla_window != (Window) NULL)
9270        {
9271          char
9272            command[MaxTextExtent],
9273            *url;
9274
9275          /*
9276            Display documentation using Netscape remote control.
9277          */
9278          url=GetMagickHomeURL();
9279          (void) FormatLocaleString(command,MaxTextExtent,
9280            "openurl(%s,new-tab)",url);
9281          url=DestroyString(url);
9282          mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9283          (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9284            8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9285          XSetCursorState(display,windows,MagickFalse);
9286          break;
9287        }
9288      XSetCursorState(display,windows,MagickTrue);
9289      XCheckRefreshWindows(display,windows);
9290      status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9291        exception);
9292      if (status == MagickFalse)
9293        XNoticeWidget(display,windows,"Unable to browse documentation",
9294          (char *) NULL);
9295      XDelay(display,1500);
9296      XSetCursorState(display,windows,MagickFalse);
9297      break;
9298    }
9299    case VersionCommand:
9300    {
9301      XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9302        GetMagickCopyright());
9303      break;
9304    }
9305    case SaveToUndoBufferCommand:
9306      break;
9307    default:
9308    {
9309      (void) XBell(display,0);
9310      break;
9311    }
9312  }
9313  image_info=DestroyImageInfo(image_info);
9314  return(nexus);
9315}
9316
9317/*
9318%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9319%                                                                             %
9320%                                                                             %
9321%                                                                             %
9322+   X M a g n i f y I m a g e                                                 %
9323%                                                                             %
9324%                                                                             %
9325%                                                                             %
9326%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9327%
9328%  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9329%  The magnified portion is displayed in a separate window.
9330%
9331%  The format of the XMagnifyImage method is:
9332%
9333%      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9334%        ExceptionInfo *exception)
9335%
9336%  A description of each parameter follows:
9337%
9338%    o display: Specifies a connection to an X server;  returned from
9339%      XOpenDisplay.
9340%
9341%    o windows: Specifies a pointer to a XWindows structure.
9342%
9343%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
9344%      the entire image is refreshed.
9345%
9346%    o exception: return any errors or warnings in this structure.
9347%
9348*/
9349static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9350  ExceptionInfo *exception)
9351{
9352  char
9353    text[MaxTextExtent];
9354
9355  register int
9356    x,
9357    y;
9358
9359  size_t
9360    state;
9361
9362  /*
9363    Update magnified image until the mouse button is released.
9364  */
9365  (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9366  state=DefaultState;
9367  x=event->xbutton.x;
9368  y=event->xbutton.y;
9369  windows->magnify.x=(int) windows->image.x+x;
9370  windows->magnify.y=(int) windows->image.y+y;
9371  do
9372  {
9373    /*
9374      Map and unmap Info widget as text cursor crosses its boundaries.
9375    */
9376    if (windows->info.mapped != MagickFalse)
9377      {
9378        if ((x < (int) (windows->info.x+windows->info.width)) &&
9379            (y < (int) (windows->info.y+windows->info.height)))
9380          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9381      }
9382    else
9383      if ((x > (int) (windows->info.x+windows->info.width)) ||
9384          (y > (int) (windows->info.y+windows->info.height)))
9385        (void) XMapWindow(display,windows->info.id);
9386    if (windows->info.mapped != MagickFalse)
9387      {
9388        /*
9389          Display pointer position.
9390        */
9391        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9392          windows->magnify.x,windows->magnify.y);
9393        XInfoWidget(display,windows,text);
9394      }
9395    /*
9396      Wait for next event.
9397    */
9398    XScreenEvent(display,windows,event,exception);
9399    switch (event->type)
9400    {
9401      case ButtonPress:
9402        break;
9403      case ButtonRelease:
9404      {
9405        /*
9406          User has finished magnifying image.
9407        */
9408        x=event->xbutton.x;
9409        y=event->xbutton.y;
9410        state|=ExitState;
9411        break;
9412      }
9413      case Expose:
9414        break;
9415      case MotionNotify:
9416      {
9417        x=event->xmotion.x;
9418        y=event->xmotion.y;
9419        break;
9420      }
9421      default:
9422        break;
9423    }
9424    /*
9425      Check boundary conditions.
9426    */
9427    if (x < 0)
9428      x=0;
9429    else
9430      if (x >= (int) windows->image.width)
9431        x=(int) windows->image.width-1;
9432    if (y < 0)
9433      y=0;
9434    else
9435     if (y >= (int) windows->image.height)
9436       y=(int) windows->image.height-1;
9437  } while ((state & ExitState) == 0);
9438  /*
9439    Display magnified image.
9440  */
9441  XSetCursorState(display,windows,MagickFalse);
9442}
9443
9444/*
9445%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9446%                                                                             %
9447%                                                                             %
9448%                                                                             %
9449+   X M a g n i f y W i n d o w C o m m a n d                                 %
9450%                                                                             %
9451%                                                                             %
9452%                                                                             %
9453%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9454%
9455%  XMagnifyWindowCommand() moves the image within an Magnify window by one
9456%  pixel as specified by the key symbol.
9457%
9458%  The format of the XMagnifyWindowCommand method is:
9459%
9460%      void XMagnifyWindowCommand(Display *display,XWindows *windows,
9461%        const MagickStatusType state,const KeySym key_symbol,
9462%        ExceptionInfo *exception)
9463%
9464%  A description of each parameter follows:
9465%
9466%    o display: Specifies a connection to an X server; returned from
9467%      XOpenDisplay.
9468%
9469%    o windows: Specifies a pointer to a XWindows structure.
9470%
9471%    o state: key mask.
9472%
9473%    o key_symbol: Specifies a KeySym which indicates which side of the image
9474%      to trim.
9475%
9476%    o exception: return any errors or warnings in this structure.
9477%
9478*/
9479static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9480  const MagickStatusType state,const KeySym key_symbol,ExceptionInfo *exception)
9481{
9482  unsigned int
9483    quantum;
9484
9485  /*
9486    User specified a magnify factor or position.
9487  */
9488  quantum=1;
9489  if ((state & Mod1Mask) != 0)
9490    quantum=10;
9491  switch ((int) key_symbol)
9492  {
9493    case QuitCommand:
9494    {
9495      (void) XWithdrawWindow(display,windows->magnify.id,
9496        windows->magnify.screen);
9497      break;
9498    }
9499    case XK_Home:
9500    case XK_KP_Home:
9501    {
9502      windows->magnify.x=(int) windows->image.width/2;
9503      windows->magnify.y=(int) windows->image.height/2;
9504      break;
9505    }
9506    case XK_Left:
9507    case XK_KP_Left:
9508    {
9509      if (windows->magnify.x > 0)
9510        windows->magnify.x-=quantum;
9511      break;
9512    }
9513    case XK_Up:
9514    case XK_KP_Up:
9515    {
9516      if (windows->magnify.y > 0)
9517        windows->magnify.y-=quantum;
9518      break;
9519    }
9520    case XK_Right:
9521    case XK_KP_Right:
9522    {
9523      if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9524        windows->magnify.x+=quantum;
9525      break;
9526    }
9527    case XK_Down:
9528    case XK_KP_Down:
9529    {
9530      if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9531        windows->magnify.y+=quantum;
9532      break;
9533    }
9534    case XK_0:
9535    case XK_1:
9536    case XK_2:
9537    case XK_3:
9538    case XK_4:
9539    case XK_5:
9540    case XK_6:
9541    case XK_7:
9542    case XK_8:
9543    case XK_9:
9544    {
9545      windows->magnify.data=(key_symbol-XK_0);
9546      break;
9547    }
9548    case XK_KP_0:
9549    case XK_KP_1:
9550    case XK_KP_2:
9551    case XK_KP_3:
9552    case XK_KP_4:
9553    case XK_KP_5:
9554    case XK_KP_6:
9555    case XK_KP_7:
9556    case XK_KP_8:
9557    case XK_KP_9:
9558    {
9559      windows->magnify.data=(key_symbol-XK_KP_0);
9560      break;
9561    }
9562    default:
9563      break;
9564  }
9565  XMakeMagnifyImage(display,windows,exception);
9566}
9567
9568/*
9569%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9570%                                                                             %
9571%                                                                             %
9572%                                                                             %
9573+   X M a k e P a n I m a g e                                                 %
9574%                                                                             %
9575%                                                                             %
9576%                                                                             %
9577%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9578%
9579%  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9580%  icon window.
9581%
9582%  The format of the XMakePanImage method is:
9583%
9584%        void XMakePanImage(Display *display,XResourceInfo *resource_info,
9585%          XWindows *windows,Image *image,ExceptionInfo *exception)
9586%
9587%  A description of each parameter follows:
9588%
9589%    o display: Specifies a connection to an X server;  returned from
9590%      XOpenDisplay.
9591%
9592%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9593%
9594%    o windows: Specifies a pointer to a XWindows structure.
9595%
9596%    o image: the image.
9597%
9598%    o exception: return any errors or warnings in this structure.
9599%
9600*/
9601static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9602  XWindows *windows,Image *image,ExceptionInfo *exception)
9603{
9604  MagickStatusType
9605    status;
9606
9607  /*
9608    Create and display image for panning icon.
9609  */
9610  XSetCursorState(display,windows,MagickTrue);
9611  XCheckRefreshWindows(display,windows);
9612  windows->pan.x=(int) windows->image.x;
9613  windows->pan.y=(int) windows->image.y;
9614  status=XMakeImage(display,resource_info,&windows->pan,image,
9615    windows->pan.width,windows->pan.height,exception);
9616  if (status == MagickFalse)
9617    ThrowXWindowFatalException(ResourceLimitError,
9618     "MemoryAllocationFailed",image->filename);
9619  (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9620    windows->pan.pixmap);
9621  (void) XClearWindow(display,windows->pan.id);
9622  XDrawPanRectangle(display,windows);
9623  XSetCursorState(display,windows,MagickFalse);
9624}
9625
9626/*
9627%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9628%                                                                             %
9629%                                                                             %
9630%                                                                             %
9631+   X M a t t a E d i t I m a g e                                             %
9632%                                                                             %
9633%                                                                             %
9634%                                                                             %
9635%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9636%
9637%  XMatteEditImage() allows the user to interactively change the Matte channel
9638%  of an image.  If the image is PseudoClass it is promoted to DirectClass
9639%  before the matte information is stored.
9640%
9641%  The format of the XMatteEditImage method is:
9642%
9643%      MagickBooleanType XMatteEditImage(Display *display,
9644%        XResourceInfo *resource_info,XWindows *windows,Image **image,
9645%        ExceptionInfo *exception)
9646%
9647%  A description of each parameter follows:
9648%
9649%    o display: Specifies a connection to an X server;  returned from
9650%      XOpenDisplay.
9651%
9652%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9653%
9654%    o windows: Specifies a pointer to a XWindows structure.
9655%
9656%    o image: the image; returned from ReadImage.
9657%
9658%    o exception: return any errors or warnings in this structure.
9659%
9660*/
9661static MagickBooleanType XMatteEditImage(Display *display,
9662  XResourceInfo *resource_info,XWindows *windows,Image **image,
9663  ExceptionInfo *exception)
9664{
9665  static char
9666    matte[MaxTextExtent] = "0";
9667
9668  static const char
9669    *MatteEditMenu[] =
9670    {
9671      "Method",
9672      "Border Color",
9673      "Fuzz",
9674      "Matte Value",
9675      "Undo",
9676      "Help",
9677      "Dismiss",
9678      (char *) NULL
9679    };
9680
9681  static const ModeType
9682    MatteEditCommands[] =
9683    {
9684      MatteEditMethod,
9685      MatteEditBorderCommand,
9686      MatteEditFuzzCommand,
9687      MatteEditValueCommand,
9688      MatteEditUndoCommand,
9689      MatteEditHelpCommand,
9690      MatteEditDismissCommand
9691    };
9692
9693  static PaintMethod
9694    method = PointMethod;
9695
9696  static XColor
9697    border_color = { 0, 0, 0, 0, 0, 0 };
9698
9699  char
9700    command[MaxTextExtent],
9701    text[MaxTextExtent];
9702
9703  Cursor
9704    cursor;
9705
9706  int
9707    entry,
9708    id,
9709    x,
9710    x_offset,
9711    y,
9712    y_offset;
9713
9714  register int
9715    i;
9716
9717  register Quantum
9718    *q;
9719
9720  unsigned int
9721    height,
9722    width;
9723
9724  size_t
9725    state;
9726
9727  XEvent
9728    event;
9729
9730  /*
9731    Map Command widget.
9732  */
9733  (void) CloneString(&windows->command.name,"Matte Edit");
9734  windows->command.data=4;
9735  (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9736  (void) XMapRaised(display,windows->command.id);
9737  XClientMessage(display,windows->image.id,windows->im_protocols,
9738    windows->im_update_widget,CurrentTime);
9739  /*
9740    Make cursor.
9741  */
9742  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9743    resource_info->background_color,resource_info->foreground_color);
9744  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9745  /*
9746    Track pointer until button 1 is pressed.
9747  */
9748  XQueryPosition(display,windows->image.id,&x,&y);
9749  (void) XSelectInput(display,windows->image.id,
9750    windows->image.attributes.event_mask | PointerMotionMask);
9751  state=DefaultState;
9752  do
9753  {
9754    if (windows->info.mapped != MagickFalse)
9755      {
9756        /*
9757          Display pointer position.
9758        */
9759        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9760          x+windows->image.x,y+windows->image.y);
9761        XInfoWidget(display,windows,text);
9762      }
9763    /*
9764      Wait for next event.
9765    */
9766    XScreenEvent(display,windows,&event,exception);
9767    if (event.xany.window == windows->command.id)
9768      {
9769        /*
9770          Select a command from the Command widget.
9771        */
9772        id=XCommandWidget(display,windows,MatteEditMenu,&event);
9773        if (id < 0)
9774          {
9775            (void) XCheckDefineCursor(display,windows->image.id,cursor);
9776            continue;
9777          }
9778        switch (MatteEditCommands[id])
9779        {
9780          case MatteEditMethod:
9781          {
9782            char
9783              **methods;
9784
9785            /*
9786              Select a method from the pop-up menu.
9787            */
9788            methods=GetCommandOptions(MagickMethodOptions);
9789            if (methods == (char **) NULL)
9790              break;
9791            entry=XMenuWidget(display,windows,MatteEditMenu[id],
9792              (const char **) methods,command);
9793            if (entry >= 0)
9794              method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9795                MagickFalse,methods[entry]);
9796            methods=DestroyStringList(methods);
9797            break;
9798          }
9799          case MatteEditBorderCommand:
9800          {
9801            const char
9802              *ColorMenu[MaxNumberPens];
9803
9804            int
9805              pen_number;
9806
9807            /*
9808              Initialize menu selections.
9809            */
9810            for (i=0; i < (int) (MaxNumberPens-2); i++)
9811              ColorMenu[i]=resource_info->pen_colors[i];
9812            ColorMenu[MaxNumberPens-2]="Browser...";
9813            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9814            /*
9815              Select a pen color from the pop-up menu.
9816            */
9817            pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9818              (const char **) ColorMenu,command);
9819            if (pen_number < 0)
9820              break;
9821            if (pen_number == (MaxNumberPens-2))
9822              {
9823                static char
9824                  color_name[MaxTextExtent] = "gray";
9825
9826                /*
9827                  Select a pen color from a dialog.
9828                */
9829                resource_info->pen_colors[pen_number]=color_name;
9830                XColorBrowserWidget(display,windows,"Select",color_name);
9831                if (*color_name == '\0')
9832                  break;
9833              }
9834            /*
9835              Set border color.
9836            */
9837            (void) XParseColor(display,windows->map_info->colormap,
9838              resource_info->pen_colors[pen_number],&border_color);
9839            break;
9840          }
9841          case MatteEditFuzzCommand:
9842          {
9843            static char
9844              fuzz[MaxTextExtent];
9845
9846            static const char
9847              *FuzzMenu[] =
9848              {
9849                "0%",
9850                "2%",
9851                "5%",
9852                "10%",
9853                "15%",
9854                "Dialog...",
9855                (char *) NULL,
9856              };
9857
9858            /*
9859              Select a command from the pop-up menu.
9860            */
9861            entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9862              command);
9863            if (entry < 0)
9864              break;
9865            if (entry != 5)
9866              {
9867                (*image)->fuzz=SiPrefixToDouble(FuzzMenu[entry],(double)
9868                  QuantumRange+1.0);
9869                break;
9870              }
9871            (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9872            (void) XDialogWidget(display,windows,"Ok",
9873              "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9874            if (*fuzz == '\0')
9875              break;
9876            (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
9877            (*image)->fuzz=SiPrefixToDouble(fuzz,(double) QuantumRange+1.0);
9878            break;
9879          }
9880          case MatteEditValueCommand:
9881          {
9882            static char
9883              message[MaxTextExtent];
9884
9885            static const char
9886              *MatteMenu[] =
9887              {
9888                "Opaque",
9889                "Transparent",
9890                "Dialog...",
9891                (char *) NULL,
9892              };
9893
9894            /*
9895              Select a command from the pop-up menu.
9896            */
9897            entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9898              command);
9899            if (entry < 0)
9900              break;
9901            if (entry != 2)
9902              {
9903                (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9904                  OpaqueAlpha);
9905                if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9906                  (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9907                    (Quantum) TransparentAlpha);
9908                break;
9909              }
9910            (void) FormatLocaleString(message,MaxTextExtent,
9911              "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9912              QuantumRange);
9913            (void) XDialogWidget(display,windows,"Matte",message,matte);
9914            if (*matte == '\0')
9915              break;
9916            break;
9917          }
9918          case MatteEditUndoCommand:
9919          {
9920            (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9921              image,exception);
9922            break;
9923          }
9924          case MatteEditHelpCommand:
9925          {
9926            XTextViewWidget(display,resource_info,windows,MagickFalse,
9927              "Help Viewer - Matte Edit",ImageMatteEditHelp);
9928            break;
9929          }
9930          case MatteEditDismissCommand:
9931          {
9932            /*
9933              Prematurely exit.
9934            */
9935            state|=EscapeState;
9936            state|=ExitState;
9937            break;
9938          }
9939          default:
9940            break;
9941        }
9942        (void) XCheckDefineCursor(display,windows->image.id,cursor);
9943        continue;
9944      }
9945    switch (event.type)
9946    {
9947      case ButtonPress:
9948      {
9949        if (event.xbutton.button != Button1)
9950          break;
9951        if ((event.xbutton.window != windows->image.id) &&
9952            (event.xbutton.window != windows->magnify.id))
9953          break;
9954        /*
9955          Update matte data.
9956        */
9957        x=event.xbutton.x;
9958        y=event.xbutton.y;
9959        (void) XMagickCommand(display,resource_info,windows,
9960          SaveToUndoBufferCommand,image,exception);
9961        state|=UpdateConfigurationState;
9962        break;
9963      }
9964      case ButtonRelease:
9965      {
9966        if (event.xbutton.button != Button1)
9967          break;
9968        if ((event.xbutton.window != windows->image.id) &&
9969            (event.xbutton.window != windows->magnify.id))
9970          break;
9971        /*
9972          Update colormap information.
9973        */
9974        x=event.xbutton.x;
9975        y=event.xbutton.y;
9976        XConfigureImageColormap(display,resource_info,windows,*image,exception);
9977        (void) XConfigureImage(display,resource_info,windows,*image,exception);
9978        XInfoWidget(display,windows,text);
9979        (void) XCheckDefineCursor(display,windows->image.id,cursor);
9980        state&=(~UpdateConfigurationState);
9981        break;
9982      }
9983      case Expose:
9984        break;
9985      case KeyPress:
9986      {
9987        char
9988          command[MaxTextExtent];
9989
9990        KeySym
9991          key_symbol;
9992
9993        if (event.xkey.window == windows->magnify.id)
9994          {
9995            Window
9996              window;
9997
9998            window=windows->magnify.id;
9999            while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
10000          }
10001        if (event.xkey.window != windows->image.id)
10002          break;
10003        /*
10004          Respond to a user key press.
10005        */
10006        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
10007          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10008        switch ((int) key_symbol)
10009        {
10010          case XK_Escape:
10011          case XK_F20:
10012          {
10013            /*
10014              Prematurely exit.
10015            */
10016            state|=ExitState;
10017            break;
10018          }
10019          case XK_F1:
10020          case XK_Help:
10021          {
10022            XTextViewWidget(display,resource_info,windows,MagickFalse,
10023              "Help Viewer - Matte Edit",ImageMatteEditHelp);
10024            break;
10025          }
10026          default:
10027          {
10028            (void) XBell(display,0);
10029            break;
10030          }
10031        }
10032        break;
10033      }
10034      case MotionNotify:
10035      {
10036        /*
10037          Map and unmap Info widget as cursor crosses its boundaries.
10038        */
10039        x=event.xmotion.x;
10040        y=event.xmotion.y;
10041        if (windows->info.mapped != MagickFalse)
10042          {
10043            if ((x < (int) (windows->info.x+windows->info.width)) &&
10044                (y < (int) (windows->info.y+windows->info.height)))
10045              (void) XWithdrawWindow(display,windows->info.id,
10046                windows->info.screen);
10047          }
10048        else
10049          if ((x > (int) (windows->info.x+windows->info.width)) ||
10050              (y > (int) (windows->info.y+windows->info.height)))
10051            (void) XMapWindow(display,windows->info.id);
10052        break;
10053      }
10054      default:
10055        break;
10056    }
10057    if (event.xany.window == windows->magnify.id)
10058      {
10059        x=windows->magnify.x-windows->image.x;
10060        y=windows->magnify.y-windows->image.y;
10061      }
10062    x_offset=x;
10063    y_offset=y;
10064    if ((state & UpdateConfigurationState) != 0)
10065      {
10066        CacheView
10067          *image_view;
10068
10069        int
10070          x,
10071          y;
10072
10073        /*
10074          Matte edit is relative to image configuration.
10075        */
10076        (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
10077          MagickTrue);
10078        XPutPixel(windows->image.ximage,x_offset,y_offset,
10079          windows->pixel_info->background_color.pixel);
10080        width=(unsigned int) (*image)->columns;
10081        height=(unsigned int) (*image)->rows;
10082        x=0;
10083        y=0;
10084        if (windows->image.crop_geometry != (char *) NULL)
10085          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
10086            &height);
10087        x_offset=(int) (width*(windows->image.x+x_offset)/
10088          windows->image.ximage->width+x);
10089        y_offset=(int) (height*(windows->image.y+y_offset)/
10090          windows->image.ximage->height+y);
10091        if ((x_offset < 0) || (y_offset < 0))
10092          continue;
10093        if ((x_offset >= (int) (*image)->columns) ||
10094            (y_offset >= (int) (*image)->rows))
10095          continue;
10096        if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10097          return(MagickFalse);
10098        (*image)->matte=MagickTrue;
10099        image_view=AcquireCacheView(*image);
10100        switch (method)
10101        {
10102          case PointMethod:
10103          default:
10104          {
10105            /*
10106              Update matte information using point algorithm.
10107            */
10108            q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10109              (ssize_t) y_offset,1,1,exception);
10110            if (q == (Quantum *) NULL)
10111              break;
10112            SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10113            (void) SyncCacheViewAuthenticPixels(image_view,exception);
10114            break;
10115          }
10116          case ReplaceMethod:
10117          {
10118            PixelInfo
10119              pixel,
10120              target;
10121
10122            Quantum
10123              virtual_pixel[CompositePixelChannel];
10124
10125            /*
10126              Update matte information using replace algorithm.
10127            */
10128            (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
10129              (ssize_t) y_offset,virtual_pixel,exception);
10130            target.red=virtual_pixel[RedPixelChannel];
10131            target.green=virtual_pixel[GreenPixelChannel];
10132            target.blue=virtual_pixel[BluePixelChannel];
10133            target.alpha=virtual_pixel[AlphaPixelChannel];
10134            for (y=0; y < (int) (*image)->rows; y++)
10135            {
10136              q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10137                (*image)->columns,1,exception);
10138              if (q == (Quantum *) NULL)
10139                break;
10140              for (x=0; x < (int) (*image)->columns; x++)
10141              {
10142                GetPixelInfoPixel(*image,q,&pixel);
10143                if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
10144                  SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10145                q+=GetPixelChannels(*image);
10146              }
10147              if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10148                break;
10149            }
10150            break;
10151          }
10152          case FloodfillMethod:
10153          case FillToBorderMethod:
10154          {
10155            ChannelType
10156              channel_mask;
10157
10158            DrawInfo
10159              *draw_info;
10160
10161            PixelInfo
10162              target;
10163
10164            /*
10165              Update matte information using floodfill algorithm.
10166            */
10167            (void) GetOneVirtualMagickPixel(*image,
10168              GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
10169              y_offset,&target,exception);
10170            if (method == FillToBorderMethod)
10171              {
10172                target.red=(MagickRealType) ScaleShortToQuantum(
10173                  border_color.red);
10174                target.green=(MagickRealType) ScaleShortToQuantum(
10175                  border_color.green);
10176                target.blue=(MagickRealType) ScaleShortToQuantum(
10177                  border_color.blue);
10178              }
10179            draw_info=CloneDrawInfo(resource_info->image_info,
10180              (DrawInfo *) NULL);
10181            draw_info->fill.alpha=ClampToQuantum(InterpretLocaleValue(matte,
10182              (char **) NULL));
10183            channel_mask=SetPixelChannelMask(*image,AlphaChannel);
10184            (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10185              x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
10186              MagickFalse : MagickTrue,exception);
10187            (void) SetPixelChannelMap(*image,channel_mask);
10188            draw_info=DestroyDrawInfo(draw_info);
10189            break;
10190          }
10191          case ResetMethod:
10192          {
10193            /*
10194              Update matte information using reset algorithm.
10195            */
10196            if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10197              return(MagickFalse);
10198            for (y=0; y < (int) (*image)->rows; y++)
10199            {
10200              q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10201                (*image)->columns,1,exception);
10202              if (q == (Quantum *) NULL)
10203                break;
10204              for (x=0; x < (int) (*image)->columns; x++)
10205              {
10206                SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10207                q+=GetPixelChannels(*image);
10208              }
10209              if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10210                break;
10211            }
10212            if (StringToLong(matte) == (long) OpaqueAlpha)
10213              (*image)->matte=MagickFalse;
10214            break;
10215          }
10216        }
10217        image_view=DestroyCacheView(image_view);
10218        state&=(~UpdateConfigurationState);
10219      }
10220  } while ((state & ExitState) == 0);
10221  (void) XSelectInput(display,windows->image.id,
10222    windows->image.attributes.event_mask);
10223  XSetCursorState(display,windows,MagickFalse);
10224  (void) XFreeCursor(display,cursor);
10225  return(MagickTrue);
10226}
10227
10228/*
10229%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10230%                                                                             %
10231%                                                                             %
10232%                                                                             %
10233+   X O p e n I m a g e                                                       %
10234%                                                                             %
10235%                                                                             %
10236%                                                                             %
10237%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10238%
10239%  XOpenImage() loads an image from a file.
10240%
10241%  The format of the XOpenImage method is:
10242%
10243%     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10244%       XWindows *windows,const unsigned int command)
10245%
10246%  A description of each parameter follows:
10247%
10248%    o display: Specifies a connection to an X server; returned from
10249%      XOpenDisplay.
10250%
10251%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10252%
10253%    o windows: Specifies a pointer to a XWindows structure.
10254%
10255%    o command: A value other than zero indicates that the file is selected
10256%      from the command line argument list.
10257%
10258*/
10259static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10260  XWindows *windows,const MagickBooleanType command)
10261{
10262  const MagickInfo
10263    *magick_info;
10264
10265  ExceptionInfo
10266    *exception;
10267
10268  Image
10269    *nexus;
10270
10271  ImageInfo
10272    *image_info;
10273
10274  static char
10275    filename[MaxTextExtent] = "\0";
10276
10277  /*
10278    Request file name from user.
10279  */
10280  if (command == MagickFalse)
10281    XFileBrowserWidget(display,windows,"Open",filename);
10282  else
10283    {
10284      char
10285        **filelist,
10286        **files;
10287
10288      int
10289        count,
10290        status;
10291
10292      register int
10293        i,
10294        j;
10295
10296      /*
10297        Select next image from the command line.
10298      */
10299      status=XGetCommand(display,windows->image.id,&files,&count);
10300      if (status == 0)
10301        {
10302          ThrowXWindowFatalException(XServerError,"UnableToGetProperty","...");
10303          return((Image *) NULL);
10304        }
10305      filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10306      if (filelist == (char **) NULL)
10307        {
10308          ThrowXWindowFatalException(ResourceLimitError,
10309            "MemoryAllocationFailed","...");
10310          (void) XFreeStringList(files);
10311          return((Image *) NULL);
10312        }
10313      j=0;
10314      for (i=1; i < count; i++)
10315        if (*files[i] != '-')
10316          filelist[j++]=files[i];
10317      filelist[j]=(char *) NULL;
10318      XListBrowserWidget(display,windows,&windows->widget,
10319        (const char **) filelist,"Load","Select Image to Load:",filename);
10320      filelist=(char **) RelinquishMagickMemory(filelist);
10321      (void) XFreeStringList(files);
10322    }
10323  if (*filename == '\0')
10324    return((Image *) NULL);
10325  image_info=CloneImageInfo(resource_info->image_info);
10326  (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10327    (void *) NULL);
10328  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10329  exception=AcquireExceptionInfo();
10330  (void) SetImageInfo(image_info,0,exception);
10331  if (LocaleCompare(image_info->magick,"X") == 0)
10332    {
10333      char
10334        seconds[MaxTextExtent];
10335
10336      /*
10337        User may want to delay the X server screen grab.
10338      */
10339      (void) CopyMagickString(seconds,"0",MaxTextExtent);
10340      (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10341        seconds);
10342      if (*seconds == '\0')
10343        return((Image *) NULL);
10344      XDelay(display,(size_t) (1000*StringToLong(seconds)));
10345    }
10346  magick_info=GetMagickInfo(image_info->magick,exception);
10347  if ((magick_info != (const MagickInfo *) NULL) &&
10348      (magick_info->raw != MagickFalse))
10349    {
10350      char
10351        geometry[MaxTextExtent];
10352
10353      /*
10354        Request image size from the user.
10355      */
10356      (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10357      if (image_info->size != (char *) NULL)
10358        (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10359      (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10360        geometry);
10361      (void) CloneString(&image_info->size,geometry);
10362    }
10363  /*
10364    Load the image.
10365  */
10366  XSetCursorState(display,windows,MagickTrue);
10367  XCheckRefreshWindows(display,windows);
10368  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10369  nexus=ReadImage(image_info,exception);
10370  CatchException(exception);
10371  XSetCursorState(display,windows,MagickFalse);
10372  if (nexus != (Image *) NULL)
10373    XClientMessage(display,windows->image.id,windows->im_protocols,
10374      windows->im_next_image,CurrentTime);
10375  else
10376    {
10377      char
10378        *text,
10379        **textlist;
10380
10381      /*
10382        Unknown image format.
10383      */
10384      text=FileToString(filename,~0,exception);
10385      if (text == (char *) NULL)
10386        return((Image *) NULL);
10387      textlist=StringToList(text);
10388      if (textlist != (char **) NULL)
10389        {
10390          char
10391            title[MaxTextExtent];
10392
10393          register int
10394            i;
10395
10396          (void) FormatLocaleString(title,MaxTextExtent,
10397            "Unknown format: %s",filename);
10398          XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10399            (const char **) textlist);
10400          for (i=0; textlist[i] != (char *) NULL; i++)
10401            textlist[i]=DestroyString(textlist[i]);
10402          textlist=(char **) RelinquishMagickMemory(textlist);
10403        }
10404      text=DestroyString(text);
10405    }
10406  exception=DestroyExceptionInfo(exception);
10407  image_info=DestroyImageInfo(image_info);
10408  return(nexus);
10409}
10410
10411/*
10412%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10413%                                                                             %
10414%                                                                             %
10415%                                                                             %
10416+   X P a n I m a g e                                                         %
10417%                                                                             %
10418%                                                                             %
10419%                                                                             %
10420%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10421%
10422%  XPanImage() pans the image until the mouse button is released.
10423%
10424%  The format of the XPanImage method is:
10425%
10426%      void XPanImage(Display *display,XWindows *windows,XEvent *event,
10427%        ExceptionInfo *exception)
10428%
10429%  A description of each parameter follows:
10430%
10431%    o display: Specifies a connection to an X server;  returned from
10432%      XOpenDisplay.
10433%
10434%    o windows: Specifies a pointer to a XWindows structure.
10435%
10436%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10437%      the entire image is refreshed.
10438%
10439%    o exception: return any errors or warnings in this structure.
10440%
10441*/
10442static void XPanImage(Display *display,XWindows *windows,XEvent *event,
10443  ExceptionInfo *exception)
10444{
10445  char
10446    text[MaxTextExtent];
10447
10448  Cursor
10449    cursor;
10450
10451  MagickRealType
10452    x_factor,
10453    y_factor;
10454
10455  RectangleInfo
10456    pan_info;
10457
10458  size_t
10459    state;
10460
10461  /*
10462    Define cursor.
10463  */
10464  if ((windows->image.ximage->width > (int) windows->image.width) &&
10465      (windows->image.ximage->height > (int) windows->image.height))
10466    cursor=XCreateFontCursor(display,XC_fleur);
10467  else
10468    if (windows->image.ximage->width > (int) windows->image.width)
10469      cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10470    else
10471      if (windows->image.ximage->height > (int) windows->image.height)
10472        cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10473      else
10474        cursor=XCreateFontCursor(display,XC_arrow);
10475  (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10476  /*
10477    Pan image as pointer moves until the mouse button is released.
10478  */
10479  x_factor=(MagickRealType) windows->image.ximage->width/windows->pan.width;
10480  y_factor=(MagickRealType) windows->image.ximage->height/windows->pan.height;
10481  pan_info.width=windows->pan.width*windows->image.width/
10482    windows->image.ximage->width;
10483  pan_info.height=windows->pan.height*windows->image.height/
10484    windows->image.ximage->height;
10485  pan_info.x=0;
10486  pan_info.y=0;
10487  state=UpdateConfigurationState;
10488  do
10489  {
10490    switch (event->type)
10491    {
10492      case ButtonPress:
10493      {
10494        /*
10495          User choose an initial pan location.
10496        */
10497        pan_info.x=(ssize_t) event->xbutton.x;
10498        pan_info.y=(ssize_t) event->xbutton.y;
10499        state|=UpdateConfigurationState;
10500        break;
10501      }
10502      case ButtonRelease:
10503      {
10504        /*
10505          User has finished panning the image.
10506        */
10507        pan_info.x=(ssize_t) event->xbutton.x;
10508        pan_info.y=(ssize_t) event->xbutton.y;
10509        state|=UpdateConfigurationState | ExitState;
10510        break;
10511      }
10512      case MotionNotify:
10513      {
10514        pan_info.x=(ssize_t) event->xmotion.x;
10515        pan_info.y=(ssize_t) event->xmotion.y;
10516        state|=UpdateConfigurationState;
10517      }
10518      default:
10519        break;
10520    }
10521    if ((state & UpdateConfigurationState) != 0)
10522      {
10523        /*
10524          Check boundary conditions.
10525        */
10526        if (pan_info.x < (ssize_t) (pan_info.width/2))
10527          pan_info.x=0;
10528        else
10529          pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10530        if (pan_info.x < 0)
10531          pan_info.x=0;
10532        else
10533          if ((int) (pan_info.x+windows->image.width) >
10534              windows->image.ximage->width)
10535            pan_info.x=(ssize_t)
10536              (windows->image.ximage->width-windows->image.width);
10537        if (pan_info.y < (ssize_t) (pan_info.height/2))
10538          pan_info.y=0;
10539        else
10540          pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10541        if (pan_info.y < 0)
10542          pan_info.y=0;
10543        else
10544          if ((int) (pan_info.y+windows->image.height) >
10545              windows->image.ximage->height)
10546            pan_info.y=(ssize_t)
10547              (windows->image.ximage->height-windows->image.height);
10548        if ((windows->image.x != (int) pan_info.x) ||
10549            (windows->image.y != (int) pan_info.y))
10550          {
10551            /*
10552              Display image pan offset.
10553            */
10554            windows->image.x=(int) pan_info.x;
10555            windows->image.y=(int) pan_info.y;
10556            (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
10557              windows->image.width,windows->image.height,windows->image.x,
10558              windows->image.y);
10559            XInfoWidget(display,windows,text);
10560            /*
10561              Refresh Image window.
10562            */
10563            XDrawPanRectangle(display,windows);
10564            XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10565          }
10566        state&=(~UpdateConfigurationState);
10567      }
10568    /*
10569      Wait for next event.
10570    */
10571    if ((state & ExitState) == 0)
10572      XScreenEvent(display,windows,event,exception);
10573  } while ((state & ExitState) == 0);
10574  /*
10575    Restore cursor.
10576  */
10577  (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10578  (void) XFreeCursor(display,cursor);
10579  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10580}
10581
10582/*
10583%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10584%                                                                             %
10585%                                                                             %
10586%                                                                             %
10587+   X P a s t e I m a g e                                                     %
10588%                                                                             %
10589%                                                                             %
10590%                                                                             %
10591%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10592%
10593%  XPasteImage() pastes an image previously saved with XCropImage in the X
10594%  window image at a location the user chooses with the pointer.
10595%
10596%  The format of the XPasteImage method is:
10597%
10598%      MagickBooleanType XPasteImage(Display *display,
10599%        XResourceInfo *resource_info,XWindows *windows,Image *image,
10600%        ExceptionInfo *exception)
10601%
10602%  A description of each parameter follows:
10603%
10604%    o display: Specifies a connection to an X server;  returned from
10605%      XOpenDisplay.
10606%
10607%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10608%
10609%    o windows: Specifies a pointer to a XWindows structure.
10610%
10611%    o image: the image; returned from ReadImage.
10612%
10613%    o exception: return any errors or warnings in this structure.
10614%
10615*/
10616static MagickBooleanType XPasteImage(Display *display,
10617  XResourceInfo *resource_info,XWindows *windows,Image *image,
10618  ExceptionInfo *exception)
10619{
10620  static const char
10621    *PasteMenu[] =
10622    {
10623      "Operator",
10624      "Help",
10625      "Dismiss",
10626      (char *) NULL
10627    };
10628
10629  static const ModeType
10630    PasteCommands[] =
10631    {
10632      PasteOperatorsCommand,
10633      PasteHelpCommand,
10634      PasteDismissCommand
10635    };
10636
10637  static CompositeOperator
10638    compose = CopyCompositeOp;
10639
10640  char
10641    text[MaxTextExtent];
10642
10643  Cursor
10644    cursor;
10645
10646  Image
10647    *paste_image;
10648
10649  int
10650    entry,
10651    id,
10652    x,
10653    y;
10654
10655  MagickRealType
10656    scale_factor;
10657
10658  RectangleInfo
10659    highlight_info,
10660    paste_info;
10661
10662  unsigned int
10663    height,
10664    width;
10665
10666  size_t
10667    state;
10668
10669  XEvent
10670    event;
10671
10672  /*
10673    Copy image.
10674  */
10675  if (resource_info->copy_image == (Image *) NULL)
10676    return(MagickFalse);
10677  paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,exception);
10678  /*
10679    Map Command widget.
10680  */
10681  (void) CloneString(&windows->command.name,"Paste");
10682  windows->command.data=1;
10683  (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10684  (void) XMapRaised(display,windows->command.id);
10685  XClientMessage(display,windows->image.id,windows->im_protocols,
10686    windows->im_update_widget,CurrentTime);
10687  /*
10688    Track pointer until button 1 is pressed.
10689  */
10690  XSetCursorState(display,windows,MagickFalse);
10691  XQueryPosition(display,windows->image.id,&x,&y);
10692  (void) XSelectInput(display,windows->image.id,
10693    windows->image.attributes.event_mask | PointerMotionMask);
10694  paste_info.x=(ssize_t) windows->image.x+x;
10695  paste_info.y=(ssize_t) windows->image.y+y;
10696  paste_info.width=0;
10697  paste_info.height=0;
10698  cursor=XCreateFontCursor(display,XC_ul_angle);
10699  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10700  state=DefaultState;
10701  do
10702  {
10703    if (windows->info.mapped != MagickFalse)
10704      {
10705        /*
10706          Display pointer position.
10707        */
10708        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
10709          (long) paste_info.x,(long) paste_info.y);
10710        XInfoWidget(display,windows,text);
10711      }
10712    highlight_info=paste_info;
10713    highlight_info.x=paste_info.x-windows->image.x;
10714    highlight_info.y=paste_info.y-windows->image.y;
10715    XHighlightRectangle(display,windows->image.id,
10716      windows->image.highlight_context,&highlight_info);
10717    /*
10718      Wait for next event.
10719    */
10720    XScreenEvent(display,windows,&event,exception);
10721    XHighlightRectangle(display,windows->image.id,
10722      windows->image.highlight_context,&highlight_info);
10723    if (event.xany.window == windows->command.id)
10724      {
10725        /*
10726          Select a command from the Command widget.
10727        */
10728        id=XCommandWidget(display,windows,PasteMenu,&event);
10729        if (id < 0)
10730          continue;
10731        switch (PasteCommands[id])
10732        {
10733          case PasteOperatorsCommand:
10734          {
10735            char
10736              command[MaxTextExtent],
10737              **operators;
10738
10739            /*
10740              Select a command from the pop-up menu.
10741            */
10742            operators=GetCommandOptions(MagickComposeOptions);
10743            if (operators == (char **) NULL)
10744              break;
10745            entry=XMenuWidget(display,windows,PasteMenu[id],
10746              (const char **) operators,command);
10747            if (entry >= 0)
10748              compose=(CompositeOperator) ParseCommandOption(
10749                MagickComposeOptions,MagickFalse,operators[entry]);
10750            operators=DestroyStringList(operators);
10751            break;
10752          }
10753          case PasteHelpCommand:
10754          {
10755            XTextViewWidget(display,resource_info,windows,MagickFalse,
10756              "Help Viewer - Image Composite",ImagePasteHelp);
10757            break;
10758          }
10759          case PasteDismissCommand:
10760          {
10761            /*
10762              Prematurely exit.
10763            */
10764            state|=EscapeState;
10765            state|=ExitState;
10766            break;
10767          }
10768          default:
10769            break;
10770        }
10771        continue;
10772      }
10773    switch (event.type)
10774    {
10775      case ButtonPress:
10776      {
10777        if (image->debug != MagickFalse)
10778          (void) LogMagickEvent(X11Event,GetMagickModule(),
10779            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10780            event.xbutton.button,event.xbutton.x,event.xbutton.y);
10781        if (event.xbutton.button != Button1)
10782          break;
10783        if (event.xbutton.window != windows->image.id)
10784          break;
10785        /*
10786          Paste rectangle is relative to image configuration.
10787        */
10788        width=(unsigned int) image->columns;
10789        height=(unsigned int) image->rows;
10790        x=0;
10791        y=0;
10792        if (windows->image.crop_geometry != (char *) NULL)
10793          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10794            &width,&height);
10795        scale_factor=(MagickRealType) windows->image.ximage->width/width;
10796        paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10797        scale_factor=(MagickRealType) windows->image.ximage->height/height;
10798        paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10799        (void) XCheckDefineCursor(display,windows->image.id,cursor);
10800        paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10801        paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10802        break;
10803      }
10804      case ButtonRelease:
10805      {
10806        if (image->debug != MagickFalse)
10807          (void) LogMagickEvent(X11Event,GetMagickModule(),
10808            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10809            event.xbutton.button,event.xbutton.x,event.xbutton.y);
10810        if (event.xbutton.button != Button1)
10811          break;
10812        if (event.xbutton.window != windows->image.id)
10813          break;
10814        if ((paste_info.width != 0) && (paste_info.height != 0))
10815          {
10816            /*
10817              User has selected the location of the paste image.
10818            */
10819            paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10820            paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10821            state|=ExitState;
10822          }
10823        break;
10824      }
10825      case Expose:
10826        break;
10827      case KeyPress:
10828      {
10829        char
10830          command[MaxTextExtent];
10831
10832        KeySym
10833          key_symbol;
10834
10835        int
10836          length;
10837
10838        if (event.xkey.window != windows->image.id)
10839          break;
10840        /*
10841          Respond to a user key press.
10842        */
10843        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10844          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10845        *(command+length)='\0';
10846        if (image->debug != MagickFalse)
10847          (void) LogMagickEvent(X11Event,GetMagickModule(),
10848            "Key press: 0x%lx (%s)",(long) key_symbol,command);
10849        switch ((int) key_symbol)
10850        {
10851          case XK_Escape:
10852          case XK_F20:
10853          {
10854            /*
10855              Prematurely exit.
10856            */
10857            paste_image=DestroyImage(paste_image);
10858            state|=EscapeState;
10859            state|=ExitState;
10860            break;
10861          }
10862          case XK_F1:
10863          case XK_Help:
10864          {
10865            (void) XSetFunction(display,windows->image.highlight_context,
10866              GXcopy);
10867            XTextViewWidget(display,resource_info,windows,MagickFalse,
10868              "Help Viewer - Image Composite",ImagePasteHelp);
10869            (void) XSetFunction(display,windows->image.highlight_context,
10870              GXinvert);
10871            break;
10872          }
10873          default:
10874          {
10875            (void) XBell(display,0);
10876            break;
10877          }
10878        }
10879        break;
10880      }
10881      case MotionNotify:
10882      {
10883        /*
10884          Map and unmap Info widget as text cursor crosses its boundaries.
10885        */
10886        x=event.xmotion.x;
10887        y=event.xmotion.y;
10888        if (windows->info.mapped != MagickFalse)
10889          {
10890            if ((x < (int) (windows->info.x+windows->info.width)) &&
10891                (y < (int) (windows->info.y+windows->info.height)))
10892              (void) XWithdrawWindow(display,windows->info.id,
10893                windows->info.screen);
10894          }
10895        else
10896          if ((x > (int) (windows->info.x+windows->info.width)) ||
10897              (y > (int) (windows->info.y+windows->info.height)))
10898            (void) XMapWindow(display,windows->info.id);
10899        paste_info.x=(ssize_t) windows->image.x+x;
10900        paste_info.y=(ssize_t) windows->image.y+y;
10901        break;
10902      }
10903      default:
10904      {
10905        if (image->debug != MagickFalse)
10906          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10907            event.type);
10908        break;
10909      }
10910    }
10911  } while ((state & ExitState) == 0);
10912  (void) XSelectInput(display,windows->image.id,
10913    windows->image.attributes.event_mask);
10914  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10915  XSetCursorState(display,windows,MagickFalse);
10916  (void) XFreeCursor(display,cursor);
10917  if ((state & EscapeState) != 0)
10918    return(MagickTrue);
10919  /*
10920    Image pasting is relative to image configuration.
10921  */
10922  XSetCursorState(display,windows,MagickTrue);
10923  XCheckRefreshWindows(display,windows);
10924  width=(unsigned int) image->columns;
10925  height=(unsigned int) image->rows;
10926  x=0;
10927  y=0;
10928  if (windows->image.crop_geometry != (char *) NULL)
10929    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10930  scale_factor=(MagickRealType) width/windows->image.ximage->width;
10931  paste_info.x+=x;
10932  paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10933  paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10934  scale_factor=(MagickRealType) height/windows->image.ximage->height;
10935  paste_info.y+=y;
10936  paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10937  paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10938  /*
10939    Paste image with X Image window.
10940  */
10941  (void) CompositeImage(image,compose,paste_image,paste_info.x,paste_info.y,
10942    exception);
10943  paste_image=DestroyImage(paste_image);
10944  XSetCursorState(display,windows,MagickFalse);
10945  /*
10946    Update image colormap.
10947  */
10948  XConfigureImageColormap(display,resource_info,windows,image,exception);
10949  (void) XConfigureImage(display,resource_info,windows,image,exception);
10950  return(MagickTrue);
10951}
10952
10953/*
10954%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10955%                                                                             %
10956%                                                                             %
10957%                                                                             %
10958+   X P r i n t I m a g e                                                     %
10959%                                                                             %
10960%                                                                             %
10961%                                                                             %
10962%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10963%
10964%  XPrintImage() prints an image to a Postscript printer.
10965%
10966%  The format of the XPrintImage method is:
10967%
10968%      MagickBooleanType XPrintImage(Display *display,
10969%        XResourceInfo *resource_info,XWindows *windows,Image *image,
10970%        ExceptionInfo *exception)
10971%
10972%  A description of each parameter follows:
10973%
10974%    o display: Specifies a connection to an X server; returned from
10975%      XOpenDisplay.
10976%
10977%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10978%
10979%    o windows: Specifies a pointer to a XWindows structure.
10980%
10981%    o image: the image.
10982%
10983%    o exception: return any errors or warnings in this structure.
10984%
10985*/
10986static MagickBooleanType XPrintImage(Display *display,
10987  XResourceInfo *resource_info,XWindows *windows,Image *image,
10988  ExceptionInfo *exception)
10989{
10990  char
10991    filename[MaxTextExtent],
10992    geometry[MaxTextExtent];
10993
10994  Image
10995    *print_image;
10996
10997  ImageInfo
10998    *image_info;
10999
11000  MagickStatusType
11001    status;
11002
11003  /*
11004    Request Postscript page geometry from user.
11005  */
11006  image_info=CloneImageInfo(resource_info->image_info);
11007  (void) FormatLocaleString(geometry,MaxTextExtent,"Letter");
11008  if (image_info->page != (char *) NULL)
11009    (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
11010  XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
11011    "Select Postscript Page Geometry:",geometry);
11012  if (*geometry == '\0')
11013    return(MagickTrue);
11014  image_info->page=GetPageGeometry(geometry);
11015  /*
11016    Apply image transforms.
11017  */
11018  XSetCursorState(display,windows,MagickTrue);
11019  XCheckRefreshWindows(display,windows);
11020  print_image=CloneImage(image,0,0,MagickTrue,exception);
11021  if (print_image == (Image *) NULL)
11022    return(MagickFalse);
11023  (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
11024    windows->image.ximage->width,windows->image.ximage->height);
11025  (void) TransformImage(&print_image,windows->image.crop_geometry,geometry,
11026    exception);
11027  /*
11028    Print image.
11029  */
11030  (void) AcquireUniqueFilename(filename);
11031  (void) FormatLocaleString(print_image->filename,MaxTextExtent,"print:%s",
11032    filename);
11033  status=WriteImage(image_info,print_image,exception);
11034  (void) RelinquishUniqueFileResource(filename);
11035  print_image=DestroyImage(print_image);
11036  image_info=DestroyImageInfo(image_info);
11037  XSetCursorState(display,windows,MagickFalse);
11038  return(status != 0 ? MagickTrue : MagickFalse);
11039}
11040
11041/*
11042%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11043%                                                                             %
11044%                                                                             %
11045%                                                                             %
11046+   X R O I I m a g e                                                         %
11047%                                                                             %
11048%                                                                             %
11049%                                                                             %
11050%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11051%
11052%  XROIImage() applies an image processing technique to a region of interest.
11053%
11054%  The format of the XROIImage method is:
11055%
11056%      MagickBooleanType XROIImage(Display *display,
11057%        XResourceInfo *resource_info,XWindows *windows,Image **image,
11058%        ExceptionInfo *exception)
11059%
11060%  A description of each parameter follows:
11061%
11062%    o display: Specifies a connection to an X server; returned from
11063%      XOpenDisplay.
11064%
11065%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11066%
11067%    o windows: Specifies a pointer to a XWindows structure.
11068%
11069%    o image: the image; returned from ReadImage.
11070%
11071%    o exception: return any errors or warnings in this structure.
11072%
11073*/
11074static MagickBooleanType XROIImage(Display *display,
11075  XResourceInfo *resource_info,XWindows *windows,Image **image,
11076  ExceptionInfo *exception)
11077{
11078#define ApplyMenus  7
11079
11080  static const char
11081    *ROIMenu[] =
11082    {
11083      "Help",
11084      "Dismiss",
11085      (char *) NULL
11086    },
11087    *ApplyMenu[] =
11088    {
11089      "File",
11090      "Edit",
11091      "Transform",
11092      "Enhance",
11093      "Effects",
11094      "F/X",
11095      "Miscellany",
11096      "Help",
11097      "Dismiss",
11098      (char *) NULL
11099    },
11100    *FileMenu[] =
11101    {
11102      "Save...",
11103      "Print...",
11104      (char *) NULL
11105    },
11106    *EditMenu[] =
11107    {
11108      "Undo",
11109      "Redo",
11110      (char *) NULL
11111    },
11112    *TransformMenu[] =
11113    {
11114      "Flop",
11115      "Flip",
11116      "Rotate Right",
11117      "Rotate Left",
11118      (char *) NULL
11119    },
11120    *EnhanceMenu[] =
11121    {
11122      "Hue...",
11123      "Saturation...",
11124      "Brightness...",
11125      "Gamma...",
11126      "Spiff",
11127      "Dull",
11128      "Contrast Stretch...",
11129      "Sigmoidal Contrast...",
11130      "Normalize",
11131      "Equalize",
11132      "Negate",
11133      "Grayscale",
11134      "Map...",
11135      "Quantize...",
11136      (char *) NULL
11137    },
11138    *EffectsMenu[] =
11139    {
11140      "Despeckle",
11141      "Emboss",
11142      "Reduce Noise",
11143      "Add Noise",
11144      "Sharpen...",
11145      "Blur...",
11146      "Threshold...",
11147      "Edge Detect...",
11148      "Spread...",
11149      "Shade...",
11150      "Raise...",
11151      "Segment...",
11152      (char *) NULL
11153    },
11154    *FXMenu[] =
11155    {
11156      "Solarize...",
11157      "Sepia Tone...",
11158      "Swirl...",
11159      "Implode...",
11160      "Vignette...",
11161      "Wave...",
11162      "Oil Paint...",
11163      "Charcoal Draw...",
11164      (char *) NULL
11165    },
11166    *MiscellanyMenu[] =
11167    {
11168      "Image Info",
11169      "Zoom Image",
11170      "Show Preview...",
11171      "Show Histogram",
11172      "Show Matte",
11173      (char *) NULL
11174    };
11175
11176  static const char
11177    **Menus[ApplyMenus] =
11178    {
11179      FileMenu,
11180      EditMenu,
11181      TransformMenu,
11182      EnhanceMenu,
11183      EffectsMenu,
11184      FXMenu,
11185      MiscellanyMenu
11186    };
11187
11188  static const CommandType
11189    ApplyCommands[] =
11190    {
11191      NullCommand,
11192      NullCommand,
11193      NullCommand,
11194      NullCommand,
11195      NullCommand,
11196      NullCommand,
11197      NullCommand,
11198      HelpCommand,
11199      QuitCommand
11200    },
11201    FileCommands[] =
11202    {
11203      SaveCommand,
11204      PrintCommand
11205    },
11206    EditCommands[] =
11207    {
11208      UndoCommand,
11209      RedoCommand
11210    },
11211    TransformCommands[] =
11212    {
11213      FlopCommand,
11214      FlipCommand,
11215      RotateRightCommand,
11216      RotateLeftCommand
11217    },
11218    EnhanceCommands[] =
11219    {
11220      HueCommand,
11221      SaturationCommand,
11222      BrightnessCommand,
11223      GammaCommand,
11224      SpiffCommand,
11225      DullCommand,
11226      ContrastStretchCommand,
11227      SigmoidalContrastCommand,
11228      NormalizeCommand,
11229      EqualizeCommand,
11230      NegateCommand,
11231      GrayscaleCommand,
11232      MapCommand,
11233      QuantizeCommand
11234    },
11235    EffectsCommands[] =
11236    {
11237      DespeckleCommand,
11238      EmbossCommand,
11239      ReduceNoiseCommand,
11240      AddNoiseCommand,
11241      SharpenCommand,
11242      BlurCommand,
11243      EdgeDetectCommand,
11244      SpreadCommand,
11245      ShadeCommand,
11246      RaiseCommand,
11247      SegmentCommand
11248    },
11249    FXCommands[] =
11250    {
11251      SolarizeCommand,
11252      SepiaToneCommand,
11253      SwirlCommand,
11254      ImplodeCommand,
11255      VignetteCommand,
11256      WaveCommand,
11257      OilPaintCommand,
11258      CharcoalDrawCommand
11259    },
11260    MiscellanyCommands[] =
11261    {
11262      InfoCommand,
11263      ZoomCommand,
11264      ShowPreviewCommand,
11265      ShowHistogramCommand,
11266      ShowMatteCommand
11267    },
11268    ROICommands[] =
11269    {
11270      ROIHelpCommand,
11271      ROIDismissCommand
11272    };
11273
11274  static const CommandType
11275    *Commands[ApplyMenus] =
11276    {
11277      FileCommands,
11278      EditCommands,
11279      TransformCommands,
11280      EnhanceCommands,
11281      EffectsCommands,
11282      FXCommands,
11283      MiscellanyCommands
11284    };
11285
11286  char
11287    command[MaxTextExtent],
11288    text[MaxTextExtent];
11289
11290  CommandType
11291    command_type;
11292
11293  Cursor
11294    cursor;
11295
11296  Image
11297    *roi_image;
11298
11299  int
11300    entry,
11301    id,
11302    x,
11303    y;
11304
11305  MagickRealType
11306    scale_factor;
11307
11308  MagickProgressMonitor
11309    progress_monitor;
11310
11311  RectangleInfo
11312    crop_info,
11313    highlight_info,
11314    roi_info;
11315
11316  unsigned int
11317    height,
11318    width;
11319
11320  size_t
11321    state;
11322
11323  XEvent
11324    event;
11325
11326  /*
11327    Map Command widget.
11328  */
11329  (void) CloneString(&windows->command.name,"ROI");
11330  windows->command.data=0;
11331  (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11332  (void) XMapRaised(display,windows->command.id);
11333  XClientMessage(display,windows->image.id,windows->im_protocols,
11334    windows->im_update_widget,CurrentTime);
11335  /*
11336    Track pointer until button 1 is pressed.
11337  */
11338  XQueryPosition(display,windows->image.id,&x,&y);
11339  (void) XSelectInput(display,windows->image.id,
11340    windows->image.attributes.event_mask | PointerMotionMask);
11341  roi_info.x=(ssize_t) windows->image.x+x;
11342  roi_info.y=(ssize_t) windows->image.y+y;
11343  roi_info.width=0;
11344  roi_info.height=0;
11345  cursor=XCreateFontCursor(display,XC_fleur);
11346  state=DefaultState;
11347  do
11348  {
11349    if (windows->info.mapped != MagickFalse)
11350      {
11351        /*
11352          Display pointer position.
11353        */
11354        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
11355          (long) roi_info.x,(long) roi_info.y);
11356        XInfoWidget(display,windows,text);
11357      }
11358    /*
11359      Wait for next event.
11360    */
11361    XScreenEvent(display,windows,&event,exception);
11362    if (event.xany.window == windows->command.id)
11363      {
11364        /*
11365          Select a command from the Command widget.
11366        */
11367        id=XCommandWidget(display,windows,ROIMenu,&event);
11368        if (id < 0)
11369          continue;
11370        switch (ROICommands[id])
11371        {
11372          case ROIHelpCommand:
11373          {
11374            XTextViewWidget(display,resource_info,windows,MagickFalse,
11375              "Help Viewer - Region of Interest",ImageROIHelp);
11376            break;
11377          }
11378          case ROIDismissCommand:
11379          {
11380            /*
11381              Prematurely exit.
11382            */
11383            state|=EscapeState;
11384            state|=ExitState;
11385            break;
11386          }
11387          default:
11388            break;
11389        }
11390        continue;
11391      }
11392    switch (event.type)
11393    {
11394      case ButtonPress:
11395      {
11396        if (event.xbutton.button != Button1)
11397          break;
11398        if (event.xbutton.window != windows->image.id)
11399          break;
11400        /*
11401          Note first corner of region of interest rectangle-- exit loop.
11402        */
11403        (void) XCheckDefineCursor(display,windows->image.id,cursor);
11404        roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11405        roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11406        state|=ExitState;
11407        break;
11408      }
11409      case ButtonRelease:
11410        break;
11411      case Expose:
11412        break;
11413      case KeyPress:
11414      {
11415        KeySym
11416          key_symbol;
11417
11418        if (event.xkey.window != windows->image.id)
11419          break;
11420        /*
11421          Respond to a user key press.
11422        */
11423        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11424          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11425        switch ((int) key_symbol)
11426        {
11427          case XK_Escape:
11428          case XK_F20:
11429          {
11430            /*
11431              Prematurely exit.
11432            */
11433            state|=EscapeState;
11434            state|=ExitState;
11435            break;
11436          }
11437          case XK_F1:
11438          case XK_Help:
11439          {
11440            XTextViewWidget(display,resource_info,windows,MagickFalse,
11441              "Help Viewer - Region of Interest",ImageROIHelp);
11442            break;
11443          }
11444          default:
11445          {
11446            (void) XBell(display,0);
11447            break;
11448          }
11449        }
11450        break;
11451      }
11452      case MotionNotify:
11453      {
11454        /*
11455          Map and unmap Info widget as text cursor crosses its boundaries.
11456        */
11457        x=event.xmotion.x;
11458        y=event.xmotion.y;
11459        if (windows->info.mapped != MagickFalse)
11460          {
11461            if ((x < (int) (windows->info.x+windows->info.width)) &&
11462                (y < (int) (windows->info.y+windows->info.height)))
11463              (void) XWithdrawWindow(display,windows->info.id,
11464                windows->info.screen);
11465          }
11466        else
11467          if ((x > (int) (windows->info.x+windows->info.width)) ||
11468              (y > (int) (windows->info.y+windows->info.height)))
11469            (void) XMapWindow(display,windows->info.id);
11470        roi_info.x=(ssize_t) windows->image.x+x;
11471        roi_info.y=(ssize_t) windows->image.y+y;
11472        break;
11473      }
11474      default:
11475        break;
11476    }
11477  } while ((state & ExitState) == 0);
11478  (void) XSelectInput(display,windows->image.id,
11479    windows->image.attributes.event_mask);
11480  if ((state & EscapeState) != 0)
11481    {
11482      /*
11483        User want to exit without region of interest.
11484      */
11485      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11486      (void) XFreeCursor(display,cursor);
11487      return(MagickTrue);
11488    }
11489  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11490  do
11491  {
11492    /*
11493      Size rectangle as pointer moves until the mouse button is released.
11494    */
11495    x=(int) roi_info.x;
11496    y=(int) roi_info.y;
11497    roi_info.width=0;
11498    roi_info.height=0;
11499    state=DefaultState;
11500    do
11501    {
11502      highlight_info=roi_info;
11503      highlight_info.x=roi_info.x-windows->image.x;
11504      highlight_info.y=roi_info.y-windows->image.y;
11505      if ((highlight_info.width > 3) && (highlight_info.height > 3))
11506        {
11507          /*
11508            Display info and draw region of interest rectangle.
11509          */
11510          if (windows->info.mapped == MagickFalse)
11511            (void) XMapWindow(display,windows->info.id);
11512          (void) FormatLocaleString(text,MaxTextExtent,
11513            " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11514            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11515          XInfoWidget(display,windows,text);
11516          XHighlightRectangle(display,windows->image.id,
11517            windows->image.highlight_context,&highlight_info);
11518        }
11519      else
11520        if (windows->info.mapped != MagickFalse)
11521          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11522      /*
11523        Wait for next event.
11524      */
11525      XScreenEvent(display,windows,&event,exception);
11526      if ((highlight_info.width > 3) && (highlight_info.height > 3))
11527        XHighlightRectangle(display,windows->image.id,
11528          windows->image.highlight_context,&highlight_info);
11529      switch (event.type)
11530      {
11531        case ButtonPress:
11532        {
11533          roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11534          roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11535          break;
11536        }
11537        case ButtonRelease:
11538        {
11539          /*
11540            User has committed to region of interest rectangle.
11541          */
11542          roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11543          roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11544          XSetCursorState(display,windows,MagickFalse);
11545          state|=ExitState;
11546          if (LocaleCompare(windows->command.name,"Apply") == 0)
11547            break;
11548          (void) CloneString(&windows->command.name,"Apply");
11549          windows->command.data=ApplyMenus;
11550          (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11551          break;
11552        }
11553        case Expose:
11554          break;
11555        case MotionNotify:
11556        {
11557          roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11558          roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11559        }
11560        default:
11561          break;
11562      }
11563      if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11564          ((state & ExitState) != 0))
11565        {
11566          /*
11567            Check boundary conditions.
11568          */
11569          if (roi_info.x < 0)
11570            roi_info.x=0;
11571          else
11572            if (roi_info.x > (ssize_t) windows->image.ximage->width)
11573              roi_info.x=(ssize_t) windows->image.ximage->width;
11574          if ((int) roi_info.x < x)
11575            roi_info.width=(unsigned int) (x-roi_info.x);
11576          else
11577            {
11578              roi_info.width=(unsigned int) (roi_info.x-x);
11579              roi_info.x=(ssize_t) x;
11580            }
11581          if (roi_info.y < 0)
11582            roi_info.y=0;
11583          else
11584            if (roi_info.y > (ssize_t) windows->image.ximage->height)
11585              roi_info.y=(ssize_t) windows->image.ximage->height;
11586          if ((int) roi_info.y < y)
11587            roi_info.height=(unsigned int) (y-roi_info.y);
11588          else
11589            {
11590              roi_info.height=(unsigned int) (roi_info.y-y);
11591              roi_info.y=(ssize_t) y;
11592            }
11593        }
11594    } while ((state & ExitState) == 0);
11595    /*
11596      Wait for user to grab a corner of the rectangle or press return.
11597    */
11598    state=DefaultState;
11599    command_type=NullCommand;
11600    (void) XMapWindow(display,windows->info.id);
11601    do
11602    {
11603      if (windows->info.mapped != MagickFalse)
11604        {
11605          /*
11606            Display pointer position.
11607          */
11608          (void) FormatLocaleString(text,MaxTextExtent,
11609            " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11610            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11611          XInfoWidget(display,windows,text);
11612        }
11613      highlight_info=roi_info;
11614      highlight_info.x=roi_info.x-windows->image.x;
11615      highlight_info.y=roi_info.y-windows->image.y;
11616      if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11617        {
11618          state|=EscapeState;
11619          state|=ExitState;
11620          break;
11621        }
11622      if ((state & UpdateRegionState) != 0)
11623        {
11624          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11625          switch (command_type)
11626          {
11627            case UndoCommand:
11628            case RedoCommand:
11629            {
11630              (void) XMagickCommand(display,resource_info,windows,command_type,
11631                image,exception);
11632              break;
11633            }
11634            default:
11635            {
11636              /*
11637                Region of interest is relative to image configuration.
11638              */
11639              progress_monitor=SetImageProgressMonitor(*image,
11640                (MagickProgressMonitor) NULL,(*image)->client_data);
11641              crop_info=roi_info;
11642              width=(unsigned int) (*image)->columns;
11643              height=(unsigned int) (*image)->rows;
11644              x=0;
11645              y=0;
11646              if (windows->image.crop_geometry != (char *) NULL)
11647                (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11648                  &width,&height);
11649              scale_factor=(MagickRealType) width/windows->image.ximage->width;
11650              crop_info.x+=x;
11651              crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11652              crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11653              scale_factor=(MagickRealType)
11654                height/windows->image.ximage->height;
11655              crop_info.y+=y;
11656              crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11657              crop_info.height=(unsigned int)
11658                (scale_factor*crop_info.height+0.5);
11659              roi_image=CropImage(*image,&crop_info,exception);
11660              (void) SetImageProgressMonitor(*image,progress_monitor,
11661                (*image)->client_data);
11662              if (roi_image == (Image *) NULL)
11663                continue;
11664              /*
11665                Apply image processing technique to the region of interest.
11666              */
11667              windows->image.orphan=MagickTrue;
11668              (void) XMagickCommand(display,resource_info,windows,command_type,
11669                &roi_image,exception);
11670              progress_monitor=SetImageProgressMonitor(*image,
11671                (MagickProgressMonitor) NULL,(*image)->client_data);
11672              (void) XMagickCommand(display,resource_info,windows,
11673                SaveToUndoBufferCommand,image,exception);
11674              windows->image.orphan=MagickFalse;
11675              (void) CompositeImage(*image,CopyCompositeOp,roi_image,
11676                crop_info.x,crop_info.y,exception);
11677              roi_image=DestroyImage(roi_image);
11678              (void) SetImageProgressMonitor(*image,progress_monitor,
11679                (*image)->client_data);
11680              break;
11681            }
11682          }
11683          if (command_type != InfoCommand)
11684            {
11685              XConfigureImageColormap(display,resource_info,windows,*image,
11686                exception);
11687              (void) XConfigureImage(display,resource_info,windows,*image,
11688                exception);
11689            }
11690          XCheckRefreshWindows(display,windows);
11691          XInfoWidget(display,windows,text);
11692          (void) XSetFunction(display,windows->image.highlight_context,
11693            GXinvert);
11694          state&=(~UpdateRegionState);
11695        }
11696      XHighlightRectangle(display,windows->image.id,
11697        windows->image.highlight_context,&highlight_info);
11698      XScreenEvent(display,windows,&event,exception);
11699      if (event.xany.window == windows->command.id)
11700        {
11701          /*
11702            Select a command from the Command widget.
11703          */
11704          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11705          command_type=NullCommand;
11706          id=XCommandWidget(display,windows,ApplyMenu,&event);
11707          if (id >= 0)
11708            {
11709              (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11710              command_type=ApplyCommands[id];
11711              if (id < ApplyMenus)
11712                {
11713                  /*
11714                    Select a command from a pop-up menu.
11715                  */
11716                  entry=XMenuWidget(display,windows,ApplyMenu[id],
11717                    (const char **) Menus[id],command);
11718                  if (entry >= 0)
11719                    {
11720                      (void) CopyMagickString(command,Menus[id][entry],
11721                        MaxTextExtent);
11722                      command_type=Commands[id][entry];
11723                    }
11724                }
11725            }
11726          (void) XSetFunction(display,windows->image.highlight_context,
11727            GXinvert);
11728          XHighlightRectangle(display,windows->image.id,
11729            windows->image.highlight_context,&highlight_info);
11730          if (command_type == HelpCommand)
11731            {
11732              (void) XSetFunction(display,windows->image.highlight_context,
11733                GXcopy);
11734              XTextViewWidget(display,resource_info,windows,MagickFalse,
11735                "Help Viewer - Region of Interest",ImageROIHelp);
11736              (void) XSetFunction(display,windows->image.highlight_context,
11737                GXinvert);
11738              continue;
11739            }
11740          if (command_type == QuitCommand)
11741            {
11742              /*
11743                exit.
11744              */
11745              state|=EscapeState;
11746              state|=ExitState;
11747              continue;
11748            }
11749          if (command_type != NullCommand)
11750            state|=UpdateRegionState;
11751          continue;
11752        }
11753      XHighlightRectangle(display,windows->image.id,
11754        windows->image.highlight_context,&highlight_info);
11755      switch (event.type)
11756      {
11757        case ButtonPress:
11758        {
11759          x=windows->image.x;
11760          y=windows->image.y;
11761          if (event.xbutton.button != Button1)
11762            break;
11763          if (event.xbutton.window != windows->image.id)
11764            break;
11765          x=windows->image.x+event.xbutton.x;
11766          y=windows->image.y+event.xbutton.y;
11767          if ((x < (int) (roi_info.x+RoiDelta)) &&
11768              (x > (int) (roi_info.x-RoiDelta)) &&
11769              (y < (int) (roi_info.y+RoiDelta)) &&
11770              (y > (int) (roi_info.y-RoiDelta)))
11771            {
11772              roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11773              roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11774              state|=UpdateConfigurationState;
11775              break;
11776            }
11777          if ((x < (int) (roi_info.x+RoiDelta)) &&
11778              (x > (int) (roi_info.x-RoiDelta)) &&
11779              (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11780              (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11781            {
11782              roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11783              state|=UpdateConfigurationState;
11784              break;
11785            }
11786          if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11787              (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11788              (y < (int) (roi_info.y+RoiDelta)) &&
11789              (y > (int) (roi_info.y-RoiDelta)))
11790            {
11791              roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11792              state|=UpdateConfigurationState;
11793              break;
11794            }
11795          if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11796              (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11797              (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11798              (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11799            {
11800              state|=UpdateConfigurationState;
11801              break;
11802            }
11803        }
11804        case ButtonRelease:
11805        {
11806          if (event.xbutton.window == windows->pan.id)
11807            if ((highlight_info.x != crop_info.x-windows->image.x) ||
11808                (highlight_info.y != crop_info.y-windows->image.y))
11809              XHighlightRectangle(display,windows->image.id,
11810                windows->image.highlight_context,&highlight_info);
11811          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11812            event.xbutton.time);
11813          break;
11814        }
11815        case Expose:
11816        {
11817          if (event.xexpose.window == windows->image.id)
11818            if (event.xexpose.count == 0)
11819              {
11820                event.xexpose.x=(int) highlight_info.x;
11821                event.xexpose.y=(int) highlight_info.y;
11822                event.xexpose.width=(int) highlight_info.width;
11823                event.xexpose.height=(int) highlight_info.height;
11824                XRefreshWindow(display,&windows->image,&event);
11825              }
11826          if (event.xexpose.window == windows->info.id)
11827            if (event.xexpose.count == 0)
11828              XInfoWidget(display,windows,text);
11829          break;
11830        }
11831        case KeyPress:
11832        {
11833          KeySym
11834            key_symbol;
11835
11836          if (event.xkey.window != windows->image.id)
11837            break;
11838          /*
11839            Respond to a user key press.
11840          */
11841          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11842            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11843          switch ((int) key_symbol)
11844          {
11845            case XK_Shift_L:
11846            case XK_Shift_R:
11847              break;
11848            case XK_Escape:
11849            case XK_F20:
11850              state|=EscapeState;
11851            case XK_Return:
11852            {
11853              state|=ExitState;
11854              break;
11855            }
11856            case XK_Home:
11857            case XK_KP_Home:
11858            {
11859              roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11860              roi_info.y=(ssize_t) (windows->image.height/2L-
11861                roi_info.height/2L);
11862              break;
11863            }
11864            case XK_Left:
11865            case XK_KP_Left:
11866            {
11867              roi_info.x--;
11868              break;
11869            }
11870            case XK_Up:
11871            case XK_KP_Up:
11872            case XK_Next:
11873            {
11874              roi_info.y--;
11875              break;
11876            }
11877            case XK_Right:
11878            case XK_KP_Right:
11879            {
11880              roi_info.x++;
11881              break;
11882            }
11883            case XK_Prior:
11884            case XK_Down:
11885            case XK_KP_Down:
11886            {
11887              roi_info.y++;
11888              break;
11889            }
11890            case XK_F1:
11891            case XK_Help:
11892            {
11893              (void) XSetFunction(display,windows->image.highlight_context,
11894                GXcopy);
11895              XTextViewWidget(display,resource_info,windows,MagickFalse,
11896                "Help Viewer - Region of Interest",ImageROIHelp);
11897              (void) XSetFunction(display,windows->image.highlight_context,
11898                GXinvert);
11899              break;
11900            }
11901            default:
11902            {
11903              command_type=XImageWindowCommand(display,resource_info,windows,
11904                event.xkey.state,key_symbol,image,exception);
11905              if (command_type != NullCommand)
11906                state|=UpdateRegionState;
11907              break;
11908            }
11909          }
11910          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11911            event.xkey.time);
11912          break;
11913        }
11914        case KeyRelease:
11915          break;
11916        case MotionNotify:
11917        {
11918          if (event.xbutton.window != windows->image.id)
11919            break;
11920          /*
11921            Map and unmap Info widget as text cursor crosses its boundaries.
11922          */
11923          x=event.xmotion.x;
11924          y=event.xmotion.y;
11925          if (windows->info.mapped != MagickFalse)
11926            {
11927              if ((x < (int) (windows->info.x+windows->info.width)) &&
11928                  (y < (int) (windows->info.y+windows->info.height)))
11929                (void) XWithdrawWindow(display,windows->info.id,
11930                  windows->info.screen);
11931            }
11932          else
11933            if ((x > (int) (windows->info.x+windows->info.width)) ||
11934                (y > (int) (windows->info.y+windows->info.height)))
11935              (void) XMapWindow(display,windows->info.id);
11936          roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11937          roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11938          break;
11939        }
11940        case SelectionRequest:
11941        {
11942          XSelectionEvent
11943            notify;
11944
11945          XSelectionRequestEvent
11946            *request;
11947
11948          /*
11949            Set primary selection.
11950          */
11951          (void) FormatLocaleString(text,MaxTextExtent,
11952            "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11953            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11954          request=(&(event.xselectionrequest));
11955          (void) XChangeProperty(request->display,request->requestor,
11956            request->property,request->target,8,PropModeReplace,
11957            (unsigned char *) text,(int) strlen(text));
11958          notify.type=SelectionNotify;
11959          notify.display=request->display;
11960          notify.requestor=request->requestor;
11961          notify.selection=request->selection;
11962          notify.target=request->target;
11963          notify.time=request->time;
11964          if (request->property == None)
11965            notify.property=request->target;
11966          else
11967            notify.property=request->property;
11968          (void) XSendEvent(request->display,request->requestor,False,0,
11969            (XEvent *) &notify);
11970        }
11971        default:
11972          break;
11973      }
11974      if ((state & UpdateConfigurationState) != 0)
11975        {
11976          (void) XPutBackEvent(display,&event);
11977          (void) XCheckDefineCursor(display,windows->image.id,cursor);
11978          break;
11979        }
11980    } while ((state & ExitState) == 0);
11981  } while ((state & ExitState) == 0);
11982  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11983  XSetCursorState(display,windows,MagickFalse);
11984  if ((state & EscapeState) != 0)
11985    return(MagickTrue);
11986  return(MagickTrue);
11987}
11988
11989/*
11990%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11991%                                                                             %
11992%                                                                             %
11993%                                                                             %
11994+   X R o t a t e I m a g e                                                   %
11995%                                                                             %
11996%                                                                             %
11997%                                                                             %
11998%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11999%
12000%  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
12001%  rotation angle is computed from the slope of a line drawn by the user.
12002%
12003%  The format of the XRotateImage method is:
12004%
12005%      MagickBooleanType XRotateImage(Display *display,
12006%        XResourceInfo *resource_info,XWindows *windows,double degrees,
12007%        Image **image,ExceptionInfo *exception)
12008%
12009%  A description of each parameter follows:
12010%
12011%    o display: Specifies a connection to an X server; returned from
12012%      XOpenDisplay.
12013%
12014%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12015%
12016%    o windows: Specifies a pointer to a XWindows structure.
12017%
12018%    o degrees: Specifies the number of degrees to rotate the image.
12019%
12020%    o image: the image.
12021%
12022%    o exception: return any errors or warnings in this structure.
12023%
12024*/
12025static MagickBooleanType XRotateImage(Display *display,
12026  XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
12027  ExceptionInfo *exception)
12028{
12029  static const char
12030    *RotateMenu[] =
12031    {
12032      "Pixel Color",
12033      "Direction",
12034      "Help",
12035      "Dismiss",
12036      (char *) NULL
12037    };
12038
12039  static ModeType
12040    direction = HorizontalRotateCommand;
12041
12042  static const ModeType
12043    DirectionCommands[] =
12044    {
12045      HorizontalRotateCommand,
12046      VerticalRotateCommand
12047    },
12048    RotateCommands[] =
12049    {
12050      RotateColorCommand,
12051      RotateDirectionCommand,
12052      RotateHelpCommand,
12053      RotateDismissCommand
12054    };
12055
12056  static unsigned int
12057    pen_id = 0;
12058
12059  char
12060    command[MaxTextExtent],
12061    text[MaxTextExtent];
12062
12063  Image
12064    *rotate_image;
12065
12066  int
12067    id,
12068    x,
12069    y;
12070
12071  MagickRealType
12072    normalized_degrees;
12073
12074  register int
12075    i;
12076
12077  unsigned int
12078    height,
12079    rotations,
12080    width;
12081
12082  if (degrees == 0.0)
12083    {
12084      unsigned int
12085        distance;
12086
12087      size_t
12088        state;
12089
12090      XEvent
12091        event;
12092
12093      XSegment
12094        rotate_info;
12095
12096      /*
12097        Map Command widget.
12098      */
12099      (void) CloneString(&windows->command.name,"Rotate");
12100      windows->command.data=2;
12101      (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12102      (void) XMapRaised(display,windows->command.id);
12103      XClientMessage(display,windows->image.id,windows->im_protocols,
12104        windows->im_update_widget,CurrentTime);
12105      /*
12106        Wait for first button press.
12107      */
12108      (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12109      XQueryPosition(display,windows->image.id,&x,&y);
12110      rotate_info.x1=x;
12111      rotate_info.y1=y;
12112      rotate_info.x2=x;
12113      rotate_info.y2=y;
12114      state=DefaultState;
12115      do
12116      {
12117        XHighlightLine(display,windows->image.id,
12118          windows->image.highlight_context,&rotate_info);
12119        /*
12120          Wait for next event.
12121        */
12122        XScreenEvent(display,windows,&event,exception);
12123        XHighlightLine(display,windows->image.id,
12124          windows->image.highlight_context,&rotate_info);
12125        if (event.xany.window == windows->command.id)
12126          {
12127            /*
12128              Select a command from the Command widget.
12129            */
12130            id=XCommandWidget(display,windows,RotateMenu,&event);
12131            if (id < 0)
12132              continue;
12133            (void) XSetFunction(display,windows->image.highlight_context,
12134              GXcopy);
12135            switch (RotateCommands[id])
12136            {
12137              case RotateColorCommand:
12138              {
12139                const char
12140                  *ColorMenu[MaxNumberPens];
12141
12142                int
12143                  pen_number;
12144
12145                XColor
12146                  color;
12147
12148                /*
12149                  Initialize menu selections.
12150                */
12151                for (i=0; i < (int) (MaxNumberPens-2); i++)
12152                  ColorMenu[i]=resource_info->pen_colors[i];
12153                ColorMenu[MaxNumberPens-2]="Browser...";
12154                ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12155                /*
12156                  Select a pen color from the pop-up menu.
12157                */
12158                pen_number=XMenuWidget(display,windows,RotateMenu[id],
12159                  (const char **) ColorMenu,command);
12160                if (pen_number < 0)
12161                  break;
12162                if (pen_number == (MaxNumberPens-2))
12163                  {
12164                    static char
12165                      color_name[MaxTextExtent] = "gray";
12166
12167                    /*
12168                      Select a pen color from a dialog.
12169                    */
12170                    resource_info->pen_colors[pen_number]=color_name;
12171                    XColorBrowserWidget(display,windows,"Select",color_name);
12172                    if (*color_name == '\0')
12173                      break;
12174                  }
12175                /*
12176                  Set pen color.
12177                */
12178                (void) XParseColor(display,windows->map_info->colormap,
12179                  resource_info->pen_colors[pen_number],&color);
12180                XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12181                  (unsigned int) MaxColors,&color);
12182                windows->pixel_info->pen_colors[pen_number]=color;
12183                pen_id=(unsigned int) pen_number;
12184                break;
12185              }
12186              case RotateDirectionCommand:
12187              {
12188                static const char
12189                  *Directions[] =
12190                  {
12191                    "horizontal",
12192                    "vertical",
12193                    (char *) NULL,
12194                  };
12195
12196                /*
12197                  Select a command from the pop-up menu.
12198                */
12199                id=XMenuWidget(display,windows,RotateMenu[id],
12200                  Directions,command);
12201                if (id >= 0)
12202                  direction=DirectionCommands[id];
12203                break;
12204              }
12205              case RotateHelpCommand:
12206              {
12207                XTextViewWidget(display,resource_info,windows,MagickFalse,
12208                  "Help Viewer - Image Rotation",ImageRotateHelp);
12209                break;
12210              }
12211              case RotateDismissCommand:
12212              {
12213                /*
12214                  Prematurely exit.
12215                */
12216                state|=EscapeState;
12217                state|=ExitState;
12218                break;
12219              }
12220              default:
12221                break;
12222            }
12223            (void) XSetFunction(display,windows->image.highlight_context,
12224              GXinvert);
12225            continue;
12226          }
12227        switch (event.type)
12228        {
12229          case ButtonPress:
12230          {
12231            if (event.xbutton.button != Button1)
12232              break;
12233            if (event.xbutton.window != windows->image.id)
12234              break;
12235            /*
12236              exit loop.
12237            */
12238            (void) XSetFunction(display,windows->image.highlight_context,
12239              GXcopy);
12240            rotate_info.x1=event.xbutton.x;
12241            rotate_info.y1=event.xbutton.y;
12242            state|=ExitState;
12243            break;
12244          }
12245          case ButtonRelease:
12246            break;
12247          case Expose:
12248            break;
12249          case KeyPress:
12250          {
12251            char
12252              command[MaxTextExtent];
12253
12254            KeySym
12255              key_symbol;
12256
12257            if (event.xkey.window != windows->image.id)
12258              break;
12259            /*
12260              Respond to a user key press.
12261            */
12262            (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12263              sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12264            switch ((int) key_symbol)
12265            {
12266              case XK_Escape:
12267              case XK_F20:
12268              {
12269                /*
12270                  Prematurely exit.
12271                */
12272                state|=EscapeState;
12273                state|=ExitState;
12274                break;
12275              }
12276              case XK_F1:
12277              case XK_Help:
12278              {
12279                (void) XSetFunction(display,windows->image.highlight_context,
12280                  GXcopy);
12281                XTextViewWidget(display,resource_info,windows,MagickFalse,
12282                  "Help Viewer - Image Rotation",ImageRotateHelp);
12283                (void) XSetFunction(display,windows->image.highlight_context,
12284                  GXinvert);
12285                break;
12286              }
12287              default:
12288              {
12289                (void) XBell(display,0);
12290                break;
12291              }
12292            }
12293            break;
12294          }
12295          case MotionNotify:
12296          {
12297            rotate_info.x1=event.xmotion.x;
12298            rotate_info.y1=event.xmotion.y;
12299          }
12300        }
12301        rotate_info.x2=rotate_info.x1;
12302        rotate_info.y2=rotate_info.y1;
12303        if (direction == HorizontalRotateCommand)
12304          rotate_info.x2+=32;
12305        else
12306          rotate_info.y2-=32;
12307      } while ((state & ExitState) == 0);
12308      (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12309      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12310      if ((state & EscapeState) != 0)
12311        return(MagickTrue);
12312      /*
12313        Draw line as pointer moves until the mouse button is released.
12314      */
12315      distance=0;
12316      (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12317      state=DefaultState;
12318      do
12319      {
12320        if (distance > 9)
12321          {
12322            /*
12323              Display info and draw rotation line.
12324            */
12325            if (windows->info.mapped == MagickFalse)
12326              (void) XMapWindow(display,windows->info.id);
12327            (void) FormatLocaleString(text,MaxTextExtent," %g",
12328              direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12329            XInfoWidget(display,windows,text);
12330            XHighlightLine(display,windows->image.id,
12331              windows->image.highlight_context,&rotate_info);
12332          }
12333        else
12334          if (windows->info.mapped != MagickFalse)
12335            (void) XWithdrawWindow(display,windows->info.id,
12336              windows->info.screen);
12337        /*
12338          Wait for next event.
12339        */
12340        XScreenEvent(display,windows,&event,exception);
12341        if (distance > 9)
12342          XHighlightLine(display,windows->image.id,
12343            windows->image.highlight_context,&rotate_info);
12344        switch (event.type)
12345        {
12346          case ButtonPress:
12347            break;
12348          case ButtonRelease:
12349          {
12350            /*
12351              User has committed to rotation line.
12352            */
12353            rotate_info.x2=event.xbutton.x;
12354            rotate_info.y2=event.xbutton.y;
12355            state|=ExitState;
12356            break;
12357          }
12358          case Expose:
12359            break;
12360          case MotionNotify:
12361          {
12362            rotate_info.x2=event.xmotion.x;
12363            rotate_info.y2=event.xmotion.y;
12364          }
12365          default:
12366            break;
12367        }
12368        /*
12369          Check boundary conditions.
12370        */
12371        if (rotate_info.x2 < 0)
12372          rotate_info.x2=0;
12373        else
12374          if (rotate_info.x2 > (int) windows->image.width)
12375            rotate_info.x2=(short) windows->image.width;
12376        if (rotate_info.y2 < 0)
12377          rotate_info.y2=0;
12378        else
12379          if (rotate_info.y2 > (int) windows->image.height)
12380            rotate_info.y2=(short) windows->image.height;
12381        /*
12382          Compute rotation angle from the slope of the line.
12383        */
12384        degrees=0.0;
12385        distance=(unsigned int)
12386          ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12387          ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12388        if (distance > 9)
12389          degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12390            rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12391      } while ((state & ExitState) == 0);
12392      (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12393      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12394      if (distance <= 9)
12395        return(MagickTrue);
12396    }
12397  if (direction == VerticalRotateCommand)
12398    degrees-=90.0;
12399  if (degrees == 0.0)
12400    return(MagickTrue);
12401  /*
12402    Rotate image.
12403  */
12404  normalized_degrees=degrees;
12405  while (normalized_degrees < -45.0)
12406    normalized_degrees+=360.0;
12407  for (rotations=0; normalized_degrees > 45.0; rotations++)
12408    normalized_degrees-=90.0;
12409  if (normalized_degrees != 0.0)
12410    (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12411      exception);
12412  XSetCursorState(display,windows,MagickTrue);
12413  XCheckRefreshWindows(display,windows);
12414  (*image)->background_color.red=ScaleShortToQuantum(
12415    windows->pixel_info->pen_colors[pen_id].red);
12416  (*image)->background_color.green=ScaleShortToQuantum(
12417    windows->pixel_info->pen_colors[pen_id].green);
12418  (*image)->background_color.blue=ScaleShortToQuantum(
12419    windows->pixel_info->pen_colors[pen_id].blue);
12420  rotate_image=RotateImage(*image,degrees,exception);
12421  XSetCursorState(display,windows,MagickFalse);
12422  if (rotate_image == (Image *) NULL)
12423    return(MagickFalse);
12424  *image=DestroyImage(*image);
12425  *image=rotate_image;
12426  if (windows->image.crop_geometry != (char *) NULL)
12427    {
12428      /*
12429        Rotate crop geometry.
12430      */
12431      width=(unsigned int) (*image)->columns;
12432      height=(unsigned int) (*image)->rows;
12433      (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12434      switch (rotations % 4)
12435      {
12436        default:
12437        case 0:
12438          break;
12439        case 1:
12440        {
12441          /*
12442            Rotate 90 degrees.
12443          */
12444          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12445            "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12446            (int) height-y,x);
12447          break;
12448        }
12449        case 2:
12450        {
12451          /*
12452            Rotate 180 degrees.
12453          */
12454          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12455            "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12456          break;
12457        }
12458        case 3:
12459        {
12460          /*
12461            Rotate 270 degrees.
12462          */
12463          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12464            "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12465          break;
12466        }
12467      }
12468    }
12469  if (windows->image.orphan != MagickFalse)
12470    return(MagickTrue);
12471  if (normalized_degrees != 0.0)
12472    {
12473      /*
12474        Update image colormap.
12475      */
12476      windows->image.window_changes.width=(int) (*image)->columns;
12477      windows->image.window_changes.height=(int) (*image)->rows;
12478      if (windows->image.crop_geometry != (char *) NULL)
12479        {
12480          /*
12481            Obtain dimensions of image from crop geometry.
12482          */
12483          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12484            &width,&height);
12485          windows->image.window_changes.width=(int) width;
12486          windows->image.window_changes.height=(int) height;
12487        }
12488      XConfigureImageColormap(display,resource_info,windows,*image,exception);
12489    }
12490  else
12491    if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12492      {
12493        windows->image.window_changes.width=windows->image.ximage->height;
12494        windows->image.window_changes.height=windows->image.ximage->width;
12495      }
12496  /*
12497    Update image configuration.
12498  */
12499  (void) XConfigureImage(display,resource_info,windows,*image,exception);
12500  return(MagickTrue);
12501}
12502
12503/*
12504%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12505%                                                                             %
12506%                                                                             %
12507%                                                                             %
12508+   X S a v e I m a g e                                                       %
12509%                                                                             %
12510%                                                                             %
12511%                                                                             %
12512%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12513%
12514%  XSaveImage() saves an image to a file.
12515%
12516%  The format of the XSaveImage method is:
12517%
12518%      MagickBooleanType XSaveImage(Display *display,
12519%        XResourceInfo *resource_info,XWindows *windows,Image *image,
12520%        ExceptionInfo *exception)
12521%
12522%  A description of each parameter follows:
12523%
12524%    o display: Specifies a connection to an X server; returned from
12525%      XOpenDisplay.
12526%
12527%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12528%
12529%    o windows: Specifies a pointer to a XWindows structure.
12530%
12531%    o image: the image.
12532%
12533%    o exception: return any errors or warnings in this structure.
12534%
12535*/
12536static MagickBooleanType XSaveImage(Display *display,
12537  XResourceInfo *resource_info,XWindows *windows,Image *image,
12538  ExceptionInfo *exception)
12539{
12540  char
12541    filename[MaxTextExtent],
12542    geometry[MaxTextExtent];
12543
12544  Image
12545    *save_image;
12546
12547  ImageInfo
12548    *image_info;
12549
12550  MagickStatusType
12551    status;
12552
12553  /*
12554    Request file name from user.
12555  */
12556  if (resource_info->write_filename != (char *) NULL)
12557    (void) CopyMagickString(filename,resource_info->write_filename,
12558      MaxTextExtent);
12559  else
12560    {
12561      char
12562        path[MaxTextExtent];
12563
12564      int
12565        status;
12566
12567      GetPathComponent(image->filename,HeadPath,path);
12568      GetPathComponent(image->filename,TailPath,filename);
12569      if (*path != '\0')
12570        {
12571          status=chdir(path);
12572          if (status == -1)
12573            (void) ThrowMagickException(exception,GetMagickModule(),
12574              FileOpenError,"UnableToOpenFile","%s",path);
12575        }
12576    }
12577  XFileBrowserWidget(display,windows,"Save",filename);
12578  if (*filename == '\0')
12579    return(MagickTrue);
12580  if (IsPathAccessible(filename) != MagickFalse)
12581    {
12582      int
12583        status;
12584
12585      /*
12586        File exists-- seek user's permission before overwriting.
12587      */
12588      status=XConfirmWidget(display,windows,"Overwrite",filename);
12589      if (status <= 0)
12590        return(MagickTrue);
12591    }
12592  image_info=CloneImageInfo(resource_info->image_info);
12593  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12594  (void) SetImageInfo(image_info,1,exception);
12595  if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12596      (LocaleCompare(image_info->magick,"JPG") == 0))
12597    {
12598      char
12599        quality[MaxTextExtent];
12600
12601      int
12602        status;
12603
12604      /*
12605        Request JPEG quality from user.
12606      */
12607      (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
12608        image->quality);
12609      status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12610        quality);
12611      if (*quality == '\0')
12612        return(MagickTrue);
12613      image->quality=StringToUnsignedLong(quality);
12614      image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12615    }
12616  if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12617      (LocaleCompare(image_info->magick,"PDF") == 0) ||
12618      (LocaleCompare(image_info->magick,"PS") == 0) ||
12619      (LocaleCompare(image_info->magick,"PS2") == 0))
12620    {
12621      char
12622        geometry[MaxTextExtent];
12623
12624      /*
12625        Request page geometry from user.
12626      */
12627      (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12628      if (LocaleCompare(image_info->magick,"PDF") == 0)
12629        (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12630      if (image_info->page != (char *) NULL)
12631        (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12632      XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12633        "Select page geometry:",geometry);
12634      if (*geometry != '\0')
12635        image_info->page=GetPageGeometry(geometry);
12636    }
12637  /*
12638    Apply image transforms.
12639  */
12640  XSetCursorState(display,windows,MagickTrue);
12641  XCheckRefreshWindows(display,windows);
12642  save_image=CloneImage(image,0,0,MagickTrue,exception);
12643  if (save_image == (Image *) NULL)
12644    return(MagickFalse);
12645  (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
12646    windows->image.ximage->width,windows->image.ximage->height);
12647  (void) TransformImage(&save_image,windows->image.crop_geometry,geometry,
12648    exception);
12649  /*
12650    Write image.
12651  */
12652  (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12653  status=WriteImage(image_info,save_image,exception);
12654  if (status != MagickFalse)
12655    image->taint=MagickFalse;
12656  save_image=DestroyImage(save_image);
12657  image_info=DestroyImageInfo(image_info);
12658  XSetCursorState(display,windows,MagickFalse);
12659  return(status != 0 ? MagickTrue : MagickFalse);
12660}
12661
12662/*
12663%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12664%                                                                             %
12665%                                                                             %
12666%                                                                             %
12667+   X S c r e e n E v e n t                                                   %
12668%                                                                             %
12669%                                                                             %
12670%                                                                             %
12671%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12672%
12673%  XScreenEvent() handles global events associated with the Pan and Magnify
12674%  windows.
12675%
12676%  The format of the XScreenEvent function is:
12677%
12678%      void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12679%        ExceptionInfo *exception)
12680%
12681%  A description of each parameter follows:
12682%
12683%    o display: Specifies a pointer to the Display structure;  returned from
12684%      XOpenDisplay.
12685%
12686%    o windows: Specifies a pointer to a XWindows structure.
12687%
12688%    o event: Specifies a pointer to a X11 XEvent structure.
12689%
12690%    o exception: return any errors or warnings in this structure.
12691%
12692*/
12693
12694#if defined(__cplusplus) || defined(c_plusplus)
12695extern "C" {
12696#endif
12697
12698static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12699{
12700  register XWindows
12701    *windows;
12702
12703  windows=(XWindows *) data;
12704  if ((event->type == ClientMessage) &&
12705      (event->xclient.window == windows->image.id))
12706    return(MagickFalse);
12707  return(MagickTrue);
12708}
12709
12710#if defined(__cplusplus) || defined(c_plusplus)
12711}
12712#endif
12713
12714static void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12715  ExceptionInfo *exception)
12716{
12717  register int
12718    x,
12719    y;
12720
12721  (void) XIfEvent(display,event,XPredicate,(char *) windows);
12722  if (event->xany.window == windows->command.id)
12723    return;
12724  switch (event->type)
12725  {
12726    case ButtonPress:
12727    case ButtonRelease:
12728    {
12729      if ((event->xbutton.button == Button3) &&
12730          (event->xbutton.state & Mod1Mask))
12731        {
12732          /*
12733            Convert Alt-Button3 to Button2.
12734          */
12735          event->xbutton.button=Button2;
12736          event->xbutton.state&=(~Mod1Mask);
12737        }
12738      if (event->xbutton.window == windows->backdrop.id)
12739        {
12740          (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12741            event->xbutton.time);
12742          break;
12743        }
12744      if (event->xbutton.window == windows->pan.id)
12745        {
12746          XPanImage(display,windows,event,exception);
12747          break;
12748        }
12749      if (event->xbutton.window == windows->image.id)
12750        if (event->xbutton.button == Button2)
12751          {
12752            /*
12753              Update magnified image.
12754            */
12755            x=event->xbutton.x;
12756            y=event->xbutton.y;
12757            if (x < 0)
12758              x=0;
12759            else
12760              if (x >= (int) windows->image.width)
12761                x=(int) (windows->image.width-1);
12762            windows->magnify.x=(int) windows->image.x+x;
12763            if (y < 0)
12764              y=0;
12765            else
12766             if (y >= (int) windows->image.height)
12767               y=(int) (windows->image.height-1);
12768            windows->magnify.y=windows->image.y+y;
12769            if (windows->magnify.mapped == MagickFalse)
12770              (void) XMapRaised(display,windows->magnify.id);
12771            XMakeMagnifyImage(display,windows,exception);
12772            if (event->type == ButtonRelease)
12773              (void) XWithdrawWindow(display,windows->info.id,
12774                windows->info.screen);
12775            break;
12776          }
12777      break;
12778    }
12779    case ClientMessage:
12780    {
12781      /*
12782        If client window delete message, exit.
12783      */
12784      if (event->xclient.message_type != windows->wm_protocols)
12785        break;
12786      if (*event->xclient.data.l != (long) windows->wm_delete_window)
12787        break;
12788      if (event->xclient.window == windows->magnify.id)
12789        {
12790          (void) XWithdrawWindow(display,windows->magnify.id,
12791            windows->magnify.screen);
12792          break;
12793        }
12794      break;
12795    }
12796    case ConfigureNotify:
12797    {
12798      if (event->xconfigure.window == windows->magnify.id)
12799        {
12800          unsigned int
12801            magnify;
12802
12803          /*
12804            Magnify window has a new configuration.
12805          */
12806          windows->magnify.width=(unsigned int) event->xconfigure.width;
12807          windows->magnify.height=(unsigned int) event->xconfigure.height;
12808          if (windows->magnify.mapped == MagickFalse)
12809            break;
12810          magnify=1;
12811          while ((int) magnify <= event->xconfigure.width)
12812            magnify<<=1;
12813          while ((int) magnify <= event->xconfigure.height)
12814            magnify<<=1;
12815          magnify>>=1;
12816          if (((int) magnify != event->xconfigure.width) ||
12817              ((int) magnify != event->xconfigure.height))
12818            {
12819              XWindowChanges
12820                window_changes;
12821
12822              window_changes.width=(int) magnify;
12823              window_changes.height=(int) magnify;
12824              (void) XReconfigureWMWindow(display,windows->magnify.id,
12825                windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12826                &window_changes);
12827              break;
12828            }
12829          XMakeMagnifyImage(display,windows,exception);
12830          break;
12831        }
12832      break;
12833    }
12834    case Expose:
12835    {
12836      if (event->xexpose.window == windows->image.id)
12837        {
12838          XRefreshWindow(display,&windows->image,event);
12839          break;
12840        }
12841      if (event->xexpose.window == windows->pan.id)
12842        if (event->xexpose.count == 0)
12843          {
12844            XDrawPanRectangle(display,windows);
12845            break;
12846          }
12847      if (event->xexpose.window == windows->magnify.id)
12848        if (event->xexpose.count == 0)
12849          {
12850            XMakeMagnifyImage(display,windows,exception);
12851            break;
12852          }
12853      break;
12854    }
12855    case KeyPress:
12856    {
12857      char
12858        command[MaxTextExtent];
12859
12860      KeySym
12861        key_symbol;
12862
12863      if (event->xkey.window != windows->magnify.id)
12864        break;
12865      /*
12866        Respond to a user key press.
12867      */
12868      (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12869        sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12870      XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol,
12871        exception);
12872      break;
12873    }
12874    case MapNotify:
12875    {
12876      if (event->xmap.window == windows->magnify.id)
12877        {
12878          windows->magnify.mapped=MagickTrue;
12879          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12880          break;
12881        }
12882      if (event->xmap.window == windows->info.id)
12883        {
12884          windows->info.mapped=MagickTrue;
12885          break;
12886        }
12887      break;
12888    }
12889    case MotionNotify:
12890    {
12891      while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12892      if (event->xmotion.window == windows->image.id)
12893        if (windows->magnify.mapped != MagickFalse)
12894          {
12895            /*
12896              Update magnified image.
12897            */
12898            x=event->xmotion.x;
12899            y=event->xmotion.y;
12900            if (x < 0)
12901              x=0;
12902            else
12903              if (x >= (int) windows->image.width)
12904                x=(int) (windows->image.width-1);
12905            windows->magnify.x=(int) windows->image.x+x;
12906            if (y < 0)
12907              y=0;
12908            else
12909             if (y >= (int) windows->image.height)
12910               y=(int) (windows->image.height-1);
12911            windows->magnify.y=windows->image.y+y;
12912            XMakeMagnifyImage(display,windows,exception);
12913          }
12914      break;
12915    }
12916    case UnmapNotify:
12917    {
12918      if (event->xunmap.window == windows->magnify.id)
12919        {
12920          windows->magnify.mapped=MagickFalse;
12921          break;
12922        }
12923      if (event->xunmap.window == windows->info.id)
12924        {
12925          windows->info.mapped=MagickFalse;
12926          break;
12927        }
12928      break;
12929    }
12930    default:
12931      break;
12932  }
12933}
12934
12935/*
12936%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12937%                                                                             %
12938%                                                                             %
12939%                                                                             %
12940+   X S e t C r o p G e o m e t r y                                           %
12941%                                                                             %
12942%                                                                             %
12943%                                                                             %
12944%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12945%
12946%  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12947%  and translates it to a cropping geometry relative to the image.
12948%
12949%  The format of the XSetCropGeometry method is:
12950%
12951%      void XSetCropGeometry(Display *display,XWindows *windows,
12952%        RectangleInfo *crop_info,Image *image)
12953%
12954%  A description of each parameter follows:
12955%
12956%    o display: Specifies a connection to an X server; returned from
12957%      XOpenDisplay.
12958%
12959%    o windows: Specifies a pointer to a XWindows structure.
12960%
12961%    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12962%      Image window to crop.
12963%
12964%    o image: the image.
12965%
12966*/
12967static void XSetCropGeometry(Display *display,XWindows *windows,
12968  RectangleInfo *crop_info,Image *image)
12969{
12970  char
12971    text[MaxTextExtent];
12972
12973  int
12974    x,
12975    y;
12976
12977  MagickRealType
12978    scale_factor;
12979
12980  unsigned int
12981    height,
12982    width;
12983
12984  if (windows->info.mapped != MagickFalse)
12985    {
12986      /*
12987        Display info on cropping rectangle.
12988      */
12989      (void) FormatLocaleString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
12990        (double) crop_info->width,(double) crop_info->height,(double)
12991        crop_info->x,(double) crop_info->y);
12992      XInfoWidget(display,windows,text);
12993    }
12994  /*
12995    Cropping geometry is relative to any previous crop geometry.
12996  */
12997  x=0;
12998  y=0;
12999  width=(unsigned int) image->columns;
13000  height=(unsigned int) image->rows;
13001  if (windows->image.crop_geometry != (char *) NULL)
13002    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13003  else
13004    windows->image.crop_geometry=AcquireString((char *) NULL);
13005  /*
13006    Define the crop geometry string from the cropping rectangle.
13007  */
13008  scale_factor=(MagickRealType) width/windows->image.ximage->width;
13009  if (crop_info->x > 0)
13010    x+=(int) (scale_factor*crop_info->x+0.5);
13011  width=(unsigned int) (scale_factor*crop_info->width+0.5);
13012  if (width == 0)
13013    width=1;
13014  scale_factor=(MagickRealType) height/windows->image.ximage->height;
13015  if (crop_info->y > 0)
13016    y+=(int) (scale_factor*crop_info->y+0.5);
13017  height=(unsigned int) (scale_factor*crop_info->height+0.5);
13018  if (height == 0)
13019    height=1;
13020  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
13021    "%ux%u%+d%+d",width,height,x,y);
13022}
13023
13024/*
13025%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13026%                                                                             %
13027%                                                                             %
13028%                                                                             %
13029+   X T i l e I m a g e                                                       %
13030%                                                                             %
13031%                                                                             %
13032%                                                                             %
13033%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13034%
13035%  XTileImage() loads or deletes a selected tile from a visual image directory.
13036%  The load or delete command is chosen from a menu.
13037%
13038%  The format of the XTileImage method is:
13039%
13040%      Image *XTileImage(Display *display,XResourceInfo *resource_info,
13041%        XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13042%
13043%  A description of each parameter follows:
13044%
13045%    o tile_image:  XTileImage reads or deletes the tile image
13046%      and returns it.  A null image is returned if an error occurs.
13047%
13048%    o display: Specifies a connection to an X server;  returned from
13049%      XOpenDisplay.
13050%
13051%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13052%
13053%    o windows: Specifies a pointer to a XWindows structure.
13054%
13055%    o image: the image; returned from ReadImage.
13056%
13057%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
13058%      the entire image is refreshed.
13059%
13060%    o exception: return any errors or warnings in this structure.
13061%
13062*/
13063static Image *XTileImage(Display *display,XResourceInfo *resource_info,
13064  XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13065{
13066  static const char
13067    *VerbMenu[] =
13068    {
13069      "Load",
13070      "Next",
13071      "Former",
13072      "Delete",
13073      "Update",
13074      (char *) NULL,
13075    };
13076
13077  static const ModeType
13078    TileCommands[] =
13079    {
13080      TileLoadCommand,
13081      TileNextCommand,
13082      TileFormerCommand,
13083      TileDeleteCommand,
13084      TileUpdateCommand
13085    };
13086
13087  char
13088    command[MaxTextExtent],
13089    filename[MaxTextExtent];
13090
13091  Image
13092    *tile_image;
13093
13094  int
13095    id,
13096    status,
13097    tile,
13098    x,
13099    y;
13100
13101  MagickRealType
13102    scale_factor;
13103
13104  register char
13105    *p,
13106    *q;
13107
13108  register int
13109    i;
13110
13111  unsigned int
13112    height,
13113    width;
13114
13115  /*
13116    Tile image is relative to montage image configuration.
13117  */
13118  x=0;
13119  y=0;
13120  width=(unsigned int) image->columns;
13121  height=(unsigned int) image->rows;
13122  if (windows->image.crop_geometry != (char *) NULL)
13123    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13124  scale_factor=(MagickRealType) width/windows->image.ximage->width;
13125  event->xbutton.x+=windows->image.x;
13126  event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13127  scale_factor=(MagickRealType) height/windows->image.ximage->height;
13128  event->xbutton.y+=windows->image.y;
13129  event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13130  /*
13131    Determine size and location of each tile in the visual image directory.
13132  */
13133  width=(unsigned int) image->columns;
13134  height=(unsigned int) image->rows;
13135  x=0;
13136  y=0;
13137  (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13138  tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13139    (event->xbutton.x-x)/width;
13140  if (tile < 0)
13141    {
13142      /*
13143        Button press is outside any tile.
13144      */
13145      (void) XBell(display,0);
13146      return((Image *) NULL);
13147    }
13148  /*
13149    Determine file name from the tile directory.
13150  */
13151  p=image->directory;
13152  for (i=tile; (i != 0) && (*p != '\0'); )
13153  {
13154    if (*p == '\n')
13155      i--;
13156    p++;
13157  }
13158  if (*p == '\0')
13159    {
13160      /*
13161        Button press is outside any tile.
13162      */
13163      (void) XBell(display,0);
13164      return((Image *) NULL);
13165    }
13166  /*
13167    Select a command from the pop-up menu.
13168  */
13169  id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13170  if (id < 0)
13171    return((Image *) NULL);
13172  q=p;
13173  while ((*q != '\n') && (*q != '\0'))
13174    q++;
13175  (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13176  /*
13177    Perform command for the selected tile.
13178  */
13179  XSetCursorState(display,windows,MagickTrue);
13180  XCheckRefreshWindows(display,windows);
13181  tile_image=NewImageList();
13182  switch (TileCommands[id])
13183  {
13184    case TileLoadCommand:
13185    {
13186      /*
13187        Load tile image.
13188      */
13189      XCheckRefreshWindows(display,windows);
13190      (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13191        MaxTextExtent);
13192      (void) CopyMagickString(resource_info->image_info->filename,filename,
13193        MaxTextExtent);
13194      tile_image=ReadImage(resource_info->image_info,exception);
13195      CatchException(exception);
13196      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13197      break;
13198    }
13199    case TileNextCommand:
13200    {
13201      /*
13202        Display next image.
13203      */
13204      XClientMessage(display,windows->image.id,windows->im_protocols,
13205        windows->im_next_image,CurrentTime);
13206      break;
13207    }
13208    case TileFormerCommand:
13209    {
13210      /*
13211        Display former image.
13212      */
13213      XClientMessage(display,windows->image.id,windows->im_protocols,
13214        windows->im_former_image,CurrentTime);
13215      break;
13216    }
13217    case TileDeleteCommand:
13218    {
13219      /*
13220        Delete tile image.
13221      */
13222      if (IsPathAccessible(filename) == MagickFalse)
13223        {
13224          XNoticeWidget(display,windows,"Image file does not exist:",filename);
13225          break;
13226        }
13227      status=XConfirmWidget(display,windows,"Really delete tile",filename);
13228      if (status <= 0)
13229        break;
13230      status=remove_utf8(filename) != 0 ? MagickTrue : MagickFalse;
13231      if (status != MagickFalse)
13232        {
13233          XNoticeWidget(display,windows,"Unable to delete image file:",
13234            filename);
13235          break;
13236        }
13237    }
13238    case TileUpdateCommand:
13239    {
13240      int
13241        x_offset,
13242        y_offset;
13243
13244      PixelInfo
13245        pixel;
13246
13247      Quantum
13248        virtual_pixel[CompositePixelChannel];
13249
13250      register int
13251        j;
13252
13253      register Quantum
13254        *s;
13255
13256      /*
13257        Ensure all the images exist.
13258      */
13259      tile=0;
13260      GetPixelInfo(image,&pixel);
13261      for (p=image->directory; *p != '\0'; p++)
13262      {
13263        CacheView
13264          *image_view;
13265
13266        q=p;
13267        while ((*q != '\n') && (*q != '\0'))
13268          q++;
13269        (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13270        p=q;
13271        if (IsPathAccessible(filename) != MagickFalse)
13272          {
13273            tile++;
13274            continue;
13275          }
13276        /*
13277          Overwrite tile with background color.
13278        */
13279        x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13280        y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13281        image_view=AcquireCacheView(image);
13282        (void) GetOneCacheViewVirtualPixel(image_view,0,0,virtual_pixel,
13283          exception);
13284        pixel.red=virtual_pixel[RedPixelChannel];
13285        pixel.green=virtual_pixel[GreenPixelChannel];
13286        pixel.blue=virtual_pixel[BluePixelChannel];
13287        pixel.alpha=virtual_pixel[AlphaPixelChannel];
13288        for (i=0; i < (int) height; i++)
13289        {
13290          s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13291            y_offset+i,width,1,exception);
13292          if (s == (Quantum *) NULL)
13293            break;
13294          for (j=0; j < (int) width; j++)
13295          {
13296            SetPixelPixelInfo(image,&pixel,s);
13297            s+=GetPixelChannels(image);
13298          }
13299          if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13300            break;
13301        }
13302        image_view=DestroyCacheView(image_view);
13303        tile++;
13304      }
13305      windows->image.window_changes.width=(int) image->columns;
13306      windows->image.window_changes.height=(int) image->rows;
13307      XConfigureImageColormap(display,resource_info,windows,image,exception);
13308      (void) XConfigureImage(display,resource_info,windows,image,exception);
13309      break;
13310    }
13311    default:
13312      break;
13313  }
13314  XSetCursorState(display,windows,MagickFalse);
13315  return(tile_image);
13316}
13317
13318/*
13319%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13320%                                                                             %
13321%                                                                             %
13322%                                                                             %
13323+   X T r a n s l a t e I m a g e                                             %
13324%                                                                             %
13325%                                                                             %
13326%                                                                             %
13327%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13328%
13329%  XTranslateImage() translates the image within an Image window by one pixel
13330%  as specified by the key symbol.  If the image has a `montage string the
13331%  translation is respect to the width and height contained within the string.
13332%
13333%  The format of the XTranslateImage method is:
13334%
13335%      void XTranslateImage(Display *display,XWindows *windows,
13336%        Image *image,const KeySym key_symbol)
13337%
13338%  A description of each parameter follows:
13339%
13340%    o display: Specifies a connection to an X server; returned from
13341%      XOpenDisplay.
13342%
13343%    o windows: Specifies a pointer to a XWindows structure.
13344%
13345%    o image: the image.
13346%
13347%    o key_symbol: Specifies a KeySym which indicates which side of the image
13348%      to trim.
13349%
13350*/
13351static void XTranslateImage(Display *display,XWindows *windows,
13352  Image *image,const KeySym key_symbol)
13353{
13354  char
13355    text[MaxTextExtent];
13356
13357  int
13358    x,
13359    y;
13360
13361  unsigned int
13362    x_offset,
13363    y_offset;
13364
13365  /*
13366    User specified a pan position offset.
13367  */
13368  x_offset=windows->image.width;
13369  y_offset=windows->image.height;
13370  if (image->montage != (char *) NULL)
13371    (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13372  switch ((int) key_symbol)
13373  {
13374    case XK_Home:
13375    case XK_KP_Home:
13376    {
13377      windows->image.x=(int) windows->image.width/2;
13378      windows->image.y=(int) windows->image.height/2;
13379      break;
13380    }
13381    case XK_Left:
13382    case XK_KP_Left:
13383    {
13384      windows->image.x-=x_offset;
13385      break;
13386    }
13387    case XK_Next:
13388    case XK_Up:
13389    case XK_KP_Up:
13390    {
13391      windows->image.y-=y_offset;
13392      break;
13393    }
13394    case XK_Right:
13395    case XK_KP_Right:
13396    {
13397      windows->image.x+=x_offset;
13398      break;
13399    }
13400    case XK_Prior:
13401    case XK_Down:
13402    case XK_KP_Down:
13403    {
13404      windows->image.y+=y_offset;
13405      break;
13406    }
13407    default:
13408      return;
13409  }
13410  /*
13411    Check boundary conditions.
13412  */
13413  if (windows->image.x < 0)
13414    windows->image.x=0;
13415  else
13416    if ((int) (windows->image.x+windows->image.width) >
13417        windows->image.ximage->width)
13418      windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13419  if (windows->image.y < 0)
13420    windows->image.y=0;
13421  else
13422    if ((int) (windows->image.y+windows->image.height) >
13423        windows->image.ximage->height)
13424      windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13425  /*
13426    Refresh Image window.
13427  */
13428  (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
13429    windows->image.width,windows->image.height,windows->image.x,
13430    windows->image.y);
13431  XInfoWidget(display,windows,text);
13432  XCheckRefreshWindows(display,windows);
13433  XDrawPanRectangle(display,windows);
13434  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13435  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13436}
13437
13438/*
13439%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13440%                                                                             %
13441%                                                                             %
13442%                                                                             %
13443+   X T r i m I m a g e                                                       %
13444%                                                                             %
13445%                                                                             %
13446%                                                                             %
13447%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13448%
13449%  XTrimImage() trims the edges from the Image window.
13450%
13451%  The format of the XTrimImage method is:
13452%
13453%      MagickBooleanType XTrimImage(Display *display,
13454%        XResourceInfo *resource_info,XWindows *windows,Image *image,
13455%        ExceptionInfo *exception)
13456%
13457%  A description of each parameter follows:
13458%
13459%    o display: Specifies a connection to an X server; returned from
13460%      XOpenDisplay.
13461%
13462%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13463%
13464%    o windows: Specifies a pointer to a XWindows structure.
13465%
13466%    o image: the image.
13467%
13468%    o exception: return any errors or warnings in this structure.
13469%
13470*/
13471static MagickBooleanType XTrimImage(Display *display,
13472  XResourceInfo *resource_info,XWindows *windows,Image *image,
13473  ExceptionInfo *exception)
13474{
13475  RectangleInfo
13476    trim_info;
13477
13478  register int
13479    x,
13480    y;
13481
13482  size_t
13483    background,
13484    pixel;
13485
13486  /*
13487    Trim edges from image.
13488  */
13489  XSetCursorState(display,windows,MagickTrue);
13490  XCheckRefreshWindows(display,windows);
13491  /*
13492    Crop the left edge.
13493  */
13494  background=XGetPixel(windows->image.ximage,0,0);
13495  trim_info.width=(size_t) windows->image.ximage->width;
13496  for (x=0; x < windows->image.ximage->width; x++)
13497  {
13498    for (y=0; y < windows->image.ximage->height; y++)
13499    {
13500      pixel=XGetPixel(windows->image.ximage,x,y);
13501      if (pixel != background)
13502        break;
13503    }
13504    if (y < windows->image.ximage->height)
13505      break;
13506  }
13507  trim_info.x=(ssize_t) x;
13508  if (trim_info.x == (ssize_t) windows->image.ximage->width)
13509    {
13510      XSetCursorState(display,windows,MagickFalse);
13511      return(MagickFalse);
13512    }
13513  /*
13514    Crop the right edge.
13515  */
13516  background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13517  for (x=windows->image.ximage->width-1; x != 0; x--)
13518  {
13519    for (y=0; y < windows->image.ximage->height; y++)
13520    {
13521      pixel=XGetPixel(windows->image.ximage,x,y);
13522      if (pixel != background)
13523        break;
13524    }
13525    if (y < windows->image.ximage->height)
13526      break;
13527  }
13528  trim_info.width=(size_t) (x-trim_info.x+1);
13529  /*
13530    Crop the top edge.
13531  */
13532  background=XGetPixel(windows->image.ximage,0,0);
13533  trim_info.height=(size_t) windows->image.ximage->height;
13534  for (y=0; y < windows->image.ximage->height; y++)
13535  {
13536    for (x=0; x < windows->image.ximage->width; x++)
13537    {
13538      pixel=XGetPixel(windows->image.ximage,x,y);
13539      if (pixel != background)
13540        break;
13541    }
13542    if (x < windows->image.ximage->width)
13543      break;
13544  }
13545  trim_info.y=(ssize_t) y;
13546  /*
13547    Crop the bottom edge.
13548  */
13549  background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13550  for (y=windows->image.ximage->height-1; y != 0; y--)
13551  {
13552    for (x=0; x < windows->image.ximage->width; x++)
13553    {
13554      pixel=XGetPixel(windows->image.ximage,x,y);
13555      if (pixel != background)
13556        break;
13557    }
13558    if (x < windows->image.ximage->width)
13559      break;
13560  }
13561  trim_info.height=(size_t) y-trim_info.y+1;
13562  if (((unsigned int) trim_info.width != windows->image.width) ||
13563      ((unsigned int) trim_info.height != windows->image.height))
13564    {
13565      /*
13566        Reconfigure Image window as defined by the trimming rectangle.
13567      */
13568      XSetCropGeometry(display,windows,&trim_info,image);
13569      windows->image.window_changes.width=(int) trim_info.width;
13570      windows->image.window_changes.height=(int) trim_info.height;
13571      (void) XConfigureImage(display,resource_info,windows,image,exception);
13572    }
13573  XSetCursorState(display,windows,MagickFalse);
13574  return(MagickTrue);
13575}
13576
13577/*
13578%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13579%                                                                             %
13580%                                                                             %
13581%                                                                             %
13582+   X V i s u a l D i r e c t o r y I m a g e                                 %
13583%                                                                             %
13584%                                                                             %
13585%                                                                             %
13586%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13587%
13588%  XVisualDirectoryImage() creates a Visual Image Directory.
13589%
13590%  The format of the XVisualDirectoryImage method is:
13591%
13592%      Image *XVisualDirectoryImage(Display *display,
13593%        XResourceInfo *resource_info,XWindows *windows,
13594%        ExceptionInfo *exception)
13595%
13596%  A description of each parameter follows:
13597%
13598%    o display: Specifies a connection to an X server; returned from
13599%      XOpenDisplay.
13600%
13601%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13602%
13603%    o windows: Specifies a pointer to a XWindows structure.
13604%
13605%    o exception: return any errors or warnings in this structure.
13606%
13607*/
13608static Image *XVisualDirectoryImage(Display *display,
13609  XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
13610{
13611#define TileImageTag  "Scale/Image"
13612#define XClientName  "montage"
13613
13614  char
13615    **filelist;
13616
13617  Image
13618    *images,
13619    *montage_image,
13620    *next_image,
13621    *thumbnail_image;
13622
13623  ImageInfo
13624    *read_info;
13625
13626  int
13627    number_files;
13628
13629  MagickBooleanType
13630    backdrop;
13631
13632  MagickStatusType
13633    status;
13634
13635  MontageInfo
13636    *montage_info;
13637
13638  RectangleInfo
13639    geometry;
13640
13641  register int
13642    i;
13643
13644  static char
13645    filename[MaxTextExtent] = "\0",
13646    filenames[MaxTextExtent] = "*";
13647
13648  XResourceInfo
13649    background_resources;
13650
13651  /*
13652    Request file name from user.
13653  */
13654  XFileBrowserWidget(display,windows,"Directory",filenames);
13655  if (*filenames == '\0')
13656    return((Image *) NULL);
13657  /*
13658    Expand the filenames.
13659  */
13660  filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13661  if (filelist == (char **) NULL)
13662    {
13663      ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13664        filenames);
13665      return((Image *) NULL);
13666    }
13667  number_files=1;
13668  filelist[0]=filenames;
13669  status=ExpandFilenames(&number_files,&filelist);
13670  if ((status == MagickFalse) || (number_files == 0))
13671    {
13672      if (number_files == 0)
13673        ThrowXWindowFatalException(ImageError,"NoImagesWereFound",filenames)
13674      else
13675        ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13676          filenames);
13677      return((Image *) NULL);
13678    }
13679  /*
13680    Set image background resources.
13681  */
13682  background_resources=(*resource_info);
13683  background_resources.window_id=AcquireString("");
13684  (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
13685    "0x%lx",windows->image.id);
13686  background_resources.backdrop=MagickTrue;
13687  /*
13688    Read each image and convert them to a tile.
13689  */
13690  backdrop=(windows->visual_info->klass == TrueColor) ||
13691    (windows->visual_info->klass == DirectColor) ? MagickTrue : MagickFalse;
13692  read_info=CloneImageInfo(resource_info->image_info);
13693  (void) SetImageOption(read_info,"jpeg:size","120x120");
13694  (void) CloneString(&read_info->size,DefaultTileGeometry);
13695  (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13696    (void *) NULL);
13697  images=NewImageList();
13698  XSetCursorState(display,windows,MagickTrue);
13699  XCheckRefreshWindows(display,windows);
13700  for (i=0; i < (int) number_files; i++)
13701  {
13702    (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13703    filelist[i]=DestroyString(filelist[i]);
13704    *read_info->magick='\0';
13705    next_image=ReadImage(read_info,exception);
13706    CatchException(exception);
13707    if (next_image != (Image *) NULL)
13708      {
13709        (void) DeleteImageProperty(next_image,"label");
13710        (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13711          read_info,next_image,DefaultTileLabel,exception),exception);
13712        (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13713          exception);
13714        thumbnail_image=ThumbnailImage(next_image,geometry.width,
13715          geometry.height,exception);
13716        if (thumbnail_image != (Image *) NULL)
13717          {
13718            next_image=DestroyImage(next_image);
13719            next_image=thumbnail_image;
13720          }
13721        if (backdrop)
13722          {
13723            (void) XDisplayBackgroundImage(display,&background_resources,
13724              next_image,exception);
13725            XSetCursorState(display,windows,MagickTrue);
13726          }
13727        AppendImageToList(&images,next_image);
13728        if (images->progress_monitor != (MagickProgressMonitor) NULL)
13729          {
13730            MagickBooleanType
13731              proceed;
13732
13733            proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13734              (MagickSizeType) number_files);
13735            if (proceed == MagickFalse)
13736              break;
13737          }
13738      }
13739  }
13740  filelist=(char **) RelinquishMagickMemory(filelist);
13741  if (images == (Image *) NULL)
13742    {
13743      read_info=DestroyImageInfo(read_info);
13744      XSetCursorState(display,windows,MagickFalse);
13745      ThrowXWindowFatalException(ImageError,"NoImagesWereLoaded",filenames);
13746      return((Image *) NULL);
13747    }
13748  /*
13749    Create the Visual Image Directory.
13750  */
13751  montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13752  montage_info->pointsize=10;
13753  if (resource_info->font != (char *) NULL)
13754    (void) CloneString(&montage_info->font,resource_info->font);
13755  (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13756  montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13757    images),exception);
13758  images=DestroyImageList(images);
13759  montage_info=DestroyMontageInfo(montage_info);
13760  read_info=DestroyImageInfo(read_info);
13761  XSetCursorState(display,windows,MagickFalse);
13762  if (montage_image == (Image *) NULL)
13763    return(montage_image);
13764  XClientMessage(display,windows->image.id,windows->im_protocols,
13765    windows->im_next_image,CurrentTime);
13766  return(montage_image);
13767}
13768
13769/*
13770%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13771%                                                                             %
13772%                                                                             %
13773%                                                                             %
13774%   X D i s p l a y B a c k g r o u n d I m a g e                             %
13775%                                                                             %
13776%                                                                             %
13777%                                                                             %
13778%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13779%
13780%  XDisplayBackgroundImage() displays an image in the background of a window.
13781%
13782%  The format of the XDisplayBackgroundImage method is:
13783%
13784%      MagickBooleanType XDisplayBackgroundImage(Display *display,
13785%        XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13786%
13787%  A description of each parameter follows:
13788%
13789%    o display: Specifies a connection to an X server;  returned from
13790%      XOpenDisplay.
13791%
13792%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13793%
13794%    o image: the image.
13795%
13796%    o exception: return any errors or warnings in this structure.
13797%
13798*/
13799MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13800  XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13801{
13802  char
13803    geometry[MaxTextExtent],
13804    visual_type[MaxTextExtent];
13805
13806  int
13807    height,
13808    status,
13809    width;
13810
13811  RectangleInfo
13812    geometry_info;
13813
13814  static XPixelInfo
13815    pixel;
13816
13817  static XStandardColormap
13818    *map_info;
13819
13820  static XVisualInfo
13821    *visual_info = (XVisualInfo *) NULL;
13822
13823  static XWindowInfo
13824    window_info;
13825
13826  size_t
13827    delay;
13828
13829  Window
13830    root_window;
13831
13832  XGCValues
13833    context_values;
13834
13835  XResourceInfo
13836    resources;
13837
13838  XWindowAttributes
13839    window_attributes;
13840
13841  /*
13842    Determine target window.
13843  */
13844  assert(image != (Image *) NULL);
13845  assert(image->signature == MagickSignature);
13846  if (image->debug != MagickFalse)
13847    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13848  resources=(*resource_info);
13849  window_info.id=(Window) NULL;
13850  root_window=XRootWindow(display,XDefaultScreen(display));
13851  if (LocaleCompare(resources.window_id,"root") == 0)
13852    window_info.id=root_window;
13853  else
13854    {
13855      if (isdigit((unsigned char) *resources.window_id) != 0)
13856        window_info.id=XWindowByID(display,root_window,
13857          (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13858      if (window_info.id == (Window) NULL)
13859        window_info.id=XWindowByName(display,root_window,resources.window_id);
13860    }
13861  if (window_info.id == (Window) NULL)
13862    {
13863      ThrowXWindowFatalException(XServerError,"NoWindowWithSpecifiedIDExists",
13864        resources.window_id);
13865      return(MagickFalse);
13866    }
13867  /*
13868    Determine window visual id.
13869  */
13870  window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13871  window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13872  (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13873  status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13874  if (status != 0)
13875    (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
13876      XVisualIDFromVisual(window_attributes.visual));
13877  if (visual_info == (XVisualInfo *) NULL)
13878    {
13879      /*
13880        Allocate standard colormap.
13881      */
13882      map_info=XAllocStandardColormap();
13883      if (map_info == (XStandardColormap *) NULL)
13884        ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13885          image->filename);
13886      map_info->colormap=(Colormap) NULL;
13887      pixel.pixels=(unsigned long *) NULL;
13888      /*
13889        Initialize visual info.
13890      */
13891      resources.map_type=(char *) NULL;
13892      resources.visual_type=visual_type;
13893      visual_info=XBestVisualInfo(display,map_info,&resources);
13894      if (visual_info == (XVisualInfo *) NULL)
13895        ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13896          resources.visual_type);
13897      /*
13898        Initialize window info.
13899      */
13900      window_info.ximage=(XImage *) NULL;
13901      window_info.matte_image=(XImage *) NULL;
13902      window_info.pixmap=(Pixmap) NULL;
13903      window_info.matte_pixmap=(Pixmap) NULL;
13904    }
13905  /*
13906    Free previous root colors.
13907  */
13908  if (window_info.id == root_window)
13909    (void) XDestroyWindowColors(display,root_window);
13910  /*
13911    Initialize Standard Colormap.
13912  */
13913  resources.colormap=SharedColormap;
13914  XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel,
13915    exception);
13916  /*
13917    Graphic context superclass.
13918  */
13919  context_values.background=pixel.background_color.pixel;
13920  context_values.foreground=pixel.foreground_color.pixel;
13921  pixel.annotate_context=XCreateGC(display,window_info.id,
13922    (size_t) (GCBackground | GCForeground),&context_values);
13923  if (pixel.annotate_context == (GC) NULL)
13924    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13925      image->filename);
13926  /*
13927    Initialize Image window attributes.
13928  */
13929  window_info.name=AcquireString("\0");
13930  window_info.icon_name=AcquireString("\0");
13931  XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13932    &resources,&window_info);
13933  /*
13934    Create the X image.
13935  */
13936  window_info.width=(unsigned int) image->columns;
13937  window_info.height=(unsigned int) image->rows;
13938  if ((image->columns != window_info.width) ||
13939      (image->rows != window_info.height))
13940    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13941      image->filename);
13942  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
13943    window_attributes.width,window_attributes.height);
13944  geometry_info.width=window_info.width;
13945  geometry_info.height=window_info.height;
13946  geometry_info.x=(ssize_t) window_info.x;
13947  geometry_info.y=(ssize_t) window_info.y;
13948  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13949    &geometry_info.width,&geometry_info.height);
13950  window_info.width=(unsigned int) geometry_info.width;
13951  window_info.height=(unsigned int) geometry_info.height;
13952  window_info.x=(int) geometry_info.x;
13953  window_info.y=(int) geometry_info.y;
13954  status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13955    window_info.height,exception);
13956  if (status == MagickFalse)
13957    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13958      image->filename);
13959  window_info.x=0;
13960  window_info.y=0;
13961  if (image->debug != MagickFalse)
13962    {
13963      (void) LogMagickEvent(X11Event,GetMagickModule(),
13964        "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13965        (double) image->columns,(double) image->rows);
13966      if (image->colors != 0)
13967        (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13968          image->colors);
13969      (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13970    }
13971  /*
13972    Adjust image dimensions as specified by backdrop or geometry options.
13973  */
13974  width=(int) window_info.width;
13975  height=(int) window_info.height;
13976  if (resources.backdrop != MagickFalse)
13977    {
13978      /*
13979        Center image on window.
13980      */
13981      window_info.x=(window_attributes.width/2)-
13982        (window_info.ximage->width/2);
13983      window_info.y=(window_attributes.height/2)-
13984        (window_info.ximage->height/2);
13985      width=window_attributes.width;
13986      height=window_attributes.height;
13987    }
13988  if ((resources.image_geometry != (char *) NULL) &&
13989      (*resources.image_geometry != '\0'))
13990    {
13991      char
13992        default_geometry[MaxTextExtent];
13993
13994      int
13995        flags,
13996        gravity;
13997
13998      XSizeHints
13999        *size_hints;
14000
14001      /*
14002        User specified geometry.
14003      */
14004      size_hints=XAllocSizeHints();
14005      if (size_hints == (XSizeHints *) NULL)
14006        ThrowXWindowFatalException(ResourceLimitFatalError,
14007          "MemoryAllocationFailed",image->filename);
14008      size_hints->flags=0L;
14009      (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
14010        width,height);
14011      flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
14012        default_geometry,window_info.border_width,size_hints,&window_info.x,
14013        &window_info.y,&width,&height,&gravity);
14014      if (flags & (XValue | YValue))
14015        {
14016          width=window_attributes.width;
14017          height=window_attributes.height;
14018        }
14019      (void) XFree((void *) size_hints);
14020    }
14021  /*
14022    Create the X pixmap.
14023  */
14024  window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
14025    (unsigned int) height,window_info.depth);
14026  if (window_info.pixmap == (Pixmap) NULL)
14027    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
14028      image->filename);
14029  /*
14030    Display pixmap on the window.
14031  */
14032  if (((unsigned int) width > window_info.width) ||
14033      ((unsigned int) height > window_info.height))
14034    (void) XFillRectangle(display,window_info.pixmap,
14035      window_info.annotate_context,0,0,(unsigned int) width,
14036      (unsigned int) height);
14037  (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
14038    window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
14039    window_info.width,(unsigned int) window_info.height);
14040  (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
14041  (void) XClearWindow(display,window_info.id);
14042  delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
14043  XDelay(display,delay == 0UL ? 10UL : delay);
14044  (void) XSync(display,MagickFalse);
14045  return(window_info.id == root_window ? MagickTrue : MagickFalse);
14046}
14047
14048/*
14049%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14050%                                                                             %
14051%                                                                             %
14052%                                                                             %
14053+   X D i s p l a y I m a g e                                                 %
14054%                                                                             %
14055%                                                                             %
14056%                                                                             %
14057%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14058%
14059%  XDisplayImage() displays an image via X11.  A new image is created and
14060%  returned if the user interactively transforms the displayed image.
14061%
14062%  The format of the XDisplayImage method is:
14063%
14064%      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14065%        char **argv,int argc,Image **image,size_t *state,
14066%        ExceptionInfo *exception)
14067%
14068%  A description of each parameter follows:
14069%
14070%    o nexus:  Method XDisplayImage returns an image when the
14071%      user chooses 'Open Image' from the command menu or picks a tile
14072%      from the image directory.  Otherwise a null image is returned.
14073%
14074%    o display: Specifies a connection to an X server;  returned from
14075%      XOpenDisplay.
14076%
14077%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14078%
14079%    o argv: Specifies the application's argument list.
14080%
14081%    o argc: Specifies the number of arguments.
14082%
14083%    o image: Specifies an address to an address of an Image structure;
14084%
14085%    o exception: return any errors or warnings in this structure.
14086%
14087*/
14088MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14089  char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
14090{
14091#define MagnifySize  256  /* must be a power of 2 */
14092#define MagickMenus  10
14093#define MagickTitle  "Commands"
14094
14095  static const char
14096    *CommandMenu[] =
14097    {
14098      "File",
14099      "Edit",
14100      "View",
14101      "Transform",
14102      "Enhance",
14103      "Effects",
14104      "F/X",
14105      "Image Edit",
14106      "Miscellany",
14107      "Help",
14108      (char *) NULL
14109    },
14110    *FileMenu[] =
14111    {
14112      "Open...",
14113      "Next",
14114      "Former",
14115      "Select...",
14116      "Save...",
14117      "Print...",
14118      "Delete...",
14119      "New...",
14120      "Visual Directory...",
14121      "Quit",
14122      (char *) NULL
14123    },
14124    *EditMenu[] =
14125    {
14126      "Undo",
14127      "Redo",
14128      "Cut",
14129      "Copy",
14130      "Paste",
14131      (char *) NULL
14132    },
14133    *ViewMenu[] =
14134    {
14135      "Half Size",
14136      "Original Size",
14137      "Double Size",
14138      "Resize...",
14139      "Apply",
14140      "Refresh",
14141      "Restore",
14142      (char *) NULL
14143    },
14144    *TransformMenu[] =
14145    {
14146      "Crop",
14147      "Chop",
14148      "Flop",
14149      "Flip",
14150      "Rotate Right",
14151      "Rotate Left",
14152      "Rotate...",
14153      "Shear...",
14154      "Roll...",
14155      "Trim Edges",
14156      (char *) NULL
14157    },
14158    *EnhanceMenu[] =
14159    {
14160      "Hue...",
14161      "Saturation...",
14162      "Brightness...",
14163      "Gamma...",
14164      "Spiff",
14165      "Dull",
14166      "Contrast Stretch...",
14167      "Sigmoidal Contrast...",
14168      "Normalize",
14169      "Equalize",
14170      "Negate",
14171      "Grayscale",
14172      "Map...",
14173      "Quantize...",
14174      (char *) NULL
14175    },
14176    *EffectsMenu[] =
14177    {
14178      "Despeckle",
14179      "Emboss",
14180      "Reduce Noise",
14181      "Add Noise...",
14182      "Sharpen...",
14183      "Blur...",
14184      "Threshold...",
14185      "Edge Detect...",
14186      "Spread...",
14187      "Shade...",
14188      "Raise...",
14189      "Segment...",
14190      (char *) NULL
14191    },
14192    *FXMenu[] =
14193    {
14194      "Solarize...",
14195      "Sepia Tone...",
14196      "Swirl...",
14197      "Implode...",
14198      "Vignette...",
14199      "Wave...",
14200      "Oil Paint...",
14201      "Charcoal Draw...",
14202      (char *) NULL
14203    },
14204    *ImageEditMenu[] =
14205    {
14206      "Annotate...",
14207      "Draw...",
14208      "Color...",
14209      "Matte...",
14210      "Composite...",
14211      "Add Border...",
14212      "Add Frame...",
14213      "Comment...",
14214      "Launch...",
14215      "Region of Interest...",
14216      (char *) NULL
14217    },
14218    *MiscellanyMenu[] =
14219    {
14220      "Image Info",
14221      "Zoom Image",
14222      "Show Preview...",
14223      "Show Histogram",
14224      "Show Matte",
14225      "Background...",
14226      "Slide Show...",
14227      "Preferences...",
14228      (char *) NULL
14229    },
14230    *HelpMenu[] =
14231    {
14232      "Overview",
14233      "Browse Documentation",
14234      "About Display",
14235      (char *) NULL
14236    },
14237    *ShortCutsMenu[] =
14238    {
14239      "Next",
14240      "Former",
14241      "Open...",
14242      "Save...",
14243      "Print...",
14244      "Undo",
14245      "Restore",
14246      "Image Info",
14247      "Quit",
14248      (char *) NULL
14249    },
14250    *VirtualMenu[] =
14251    {
14252      "Image Info",
14253      "Print",
14254      "Next",
14255      "Quit",
14256      (char *) NULL
14257    };
14258
14259  static const char
14260    **Menus[MagickMenus] =
14261    {
14262      FileMenu,
14263      EditMenu,
14264      ViewMenu,
14265      TransformMenu,
14266      EnhanceMenu,
14267      EffectsMenu,
14268      FXMenu,
14269      ImageEditMenu,
14270      MiscellanyMenu,
14271      HelpMenu
14272    };
14273
14274  static CommandType
14275    CommandMenus[] =
14276    {
14277      NullCommand,
14278      NullCommand,
14279      NullCommand,
14280      NullCommand,
14281      NullCommand,
14282      NullCommand,
14283      NullCommand,
14284      NullCommand,
14285      NullCommand,
14286      NullCommand,
14287    },
14288    FileCommands[] =
14289    {
14290      OpenCommand,
14291      NextCommand,
14292      FormerCommand,
14293      SelectCommand,
14294      SaveCommand,
14295      PrintCommand,
14296      DeleteCommand,
14297      NewCommand,
14298      VisualDirectoryCommand,
14299      QuitCommand
14300    },
14301    EditCommands[] =
14302    {
14303      UndoCommand,
14304      RedoCommand,
14305      CutCommand,
14306      CopyCommand,
14307      PasteCommand
14308    },
14309    ViewCommands[] =
14310    {
14311      HalfSizeCommand,
14312      OriginalSizeCommand,
14313      DoubleSizeCommand,
14314      ResizeCommand,
14315      ApplyCommand,
14316      RefreshCommand,
14317      RestoreCommand
14318    },
14319    TransformCommands[] =
14320    {
14321      CropCommand,
14322      ChopCommand,
14323      FlopCommand,
14324      FlipCommand,
14325      RotateRightCommand,
14326      RotateLeftCommand,
14327      RotateCommand,
14328      ShearCommand,
14329      RollCommand,
14330      TrimCommand
14331    },
14332    EnhanceCommands[] =
14333    {
14334      HueCommand,
14335      SaturationCommand,
14336      BrightnessCommand,
14337      GammaCommand,
14338      SpiffCommand,
14339      DullCommand,
14340      ContrastStretchCommand,
14341      SigmoidalContrastCommand,
14342      NormalizeCommand,
14343      EqualizeCommand,
14344      NegateCommand,
14345      GrayscaleCommand,
14346      MapCommand,
14347      QuantizeCommand
14348    },
14349    EffectsCommands[] =
14350    {
14351      DespeckleCommand,
14352      EmbossCommand,
14353      ReduceNoiseCommand,
14354      AddNoiseCommand,
14355      SharpenCommand,
14356      BlurCommand,
14357      ThresholdCommand,
14358      EdgeDetectCommand,
14359      SpreadCommand,
14360      ShadeCommand,
14361      RaiseCommand,
14362      SegmentCommand
14363    },
14364    FXCommands[] =
14365    {
14366      SolarizeCommand,
14367      SepiaToneCommand,
14368      SwirlCommand,
14369      ImplodeCommand,
14370      VignetteCommand,
14371      WaveCommand,
14372      OilPaintCommand,
14373      CharcoalDrawCommand
14374    },
14375    ImageEditCommands[] =
14376    {
14377      AnnotateCommand,
14378      DrawCommand,
14379      ColorCommand,
14380      MatteCommand,
14381      CompositeCommand,
14382      AddBorderCommand,
14383      AddFrameCommand,
14384      CommentCommand,
14385      LaunchCommand,
14386      RegionofInterestCommand
14387    },
14388    MiscellanyCommands[] =
14389    {
14390      InfoCommand,
14391      ZoomCommand,
14392      ShowPreviewCommand,
14393      ShowHistogramCommand,
14394      ShowMatteCommand,
14395      BackgroundCommand,
14396      SlideShowCommand,
14397      PreferencesCommand
14398    },
14399    HelpCommands[] =
14400    {
14401      HelpCommand,
14402      BrowseDocumentationCommand,
14403      VersionCommand
14404    },
14405    ShortCutsCommands[] =
14406    {
14407      NextCommand,
14408      FormerCommand,
14409      OpenCommand,
14410      SaveCommand,
14411      PrintCommand,
14412      UndoCommand,
14413      RestoreCommand,
14414      InfoCommand,
14415      QuitCommand
14416    },
14417    VirtualCommands[] =
14418    {
14419      InfoCommand,
14420      PrintCommand,
14421      NextCommand,
14422      QuitCommand
14423    };
14424
14425  static CommandType
14426    *Commands[MagickMenus] =
14427    {
14428      FileCommands,
14429      EditCommands,
14430      ViewCommands,
14431      TransformCommands,
14432      EnhanceCommands,
14433      EffectsCommands,
14434      FXCommands,
14435      ImageEditCommands,
14436      MiscellanyCommands,
14437      HelpCommands
14438    };
14439
14440  char
14441    command[MaxTextExtent],
14442    *directory,
14443    geometry[MaxTextExtent],
14444    resource_name[MaxTextExtent];
14445
14446  CommandType
14447    command_type;
14448
14449  Image
14450    *display_image,
14451    *nexus;
14452
14453  int
14454    entry,
14455    id;
14456
14457  KeySym
14458    key_symbol;
14459
14460  MagickStatusType
14461    context_mask,
14462    status;
14463
14464  RectangleInfo
14465    geometry_info;
14466
14467  register int
14468    i;
14469
14470  static char
14471    working_directory[MaxTextExtent];
14472
14473  static XPoint
14474    vid_info;
14475
14476  static XWindowInfo
14477    *magick_windows[MaxXWindows];
14478
14479  static unsigned int
14480    number_windows;
14481
14482  struct stat
14483    attributes;
14484
14485  time_t
14486    timer,
14487    timestamp,
14488    update_time;
14489
14490  unsigned int
14491    height,
14492    width;
14493
14494  size_t
14495    delay;
14496
14497  WarningHandler
14498    warning_handler;
14499
14500  Window
14501    root_window;
14502
14503  XClassHint
14504    *class_hints;
14505
14506  XEvent
14507    event;
14508
14509  XFontStruct
14510    *font_info;
14511
14512  XGCValues
14513    context_values;
14514
14515  XPixelInfo
14516    *icon_pixel,
14517    *pixel;
14518
14519  XResourceInfo
14520    *icon_resources;
14521
14522  XStandardColormap
14523    *icon_map,
14524    *map_info;
14525
14526  XVisualInfo
14527    *icon_visual,
14528    *visual_info;
14529
14530  XWindowChanges
14531    window_changes;
14532
14533  XWindows
14534    *windows;
14535
14536  XWMHints
14537    *manager_hints;
14538
14539  assert(image != (Image **) NULL);
14540  assert((*image)->signature == MagickSignature);
14541  if ((*image)->debug != MagickFalse)
14542    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14543  display_image=(*image);
14544  warning_handler=(WarningHandler) NULL;
14545  windows=XSetWindows((XWindows *) ~0);
14546  if (windows != (XWindows *) NULL)
14547    {
14548      int
14549        status;
14550
14551      status=chdir(working_directory);
14552      if (status == -1)
14553        (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14554          "UnableToOpenFile","%s",working_directory);
14555      warning_handler=resource_info->display_warnings ?
14556        SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14557      warning_handler=resource_info->display_warnings ?
14558        SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14559    }
14560  else
14561    {
14562      /*
14563        Allocate windows structure.
14564      */
14565      resource_info->colors=display_image->colors;
14566      windows=XSetWindows(XInitializeWindows(display,resource_info));
14567      if (windows == (XWindows *) NULL)
14568        ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14569          (*image)->filename);
14570      /*
14571        Initialize window id's.
14572      */
14573      number_windows=0;
14574      magick_windows[number_windows++]=(&windows->icon);
14575      magick_windows[number_windows++]=(&windows->backdrop);
14576      magick_windows[number_windows++]=(&windows->image);
14577      magick_windows[number_windows++]=(&windows->info);
14578      magick_windows[number_windows++]=(&windows->command);
14579      magick_windows[number_windows++]=(&windows->widget);
14580      magick_windows[number_windows++]=(&windows->popup);
14581      magick_windows[number_windows++]=(&windows->magnify);
14582      magick_windows[number_windows++]=(&windows->pan);
14583      for (i=0; i < (int) number_windows; i++)
14584        magick_windows[i]->id=(Window) NULL;
14585      vid_info.x=0;
14586      vid_info.y=0;
14587    }
14588  /*
14589    Initialize font info.
14590  */
14591  if (windows->font_info != (XFontStruct *) NULL)
14592    (void) XFreeFont(display,windows->font_info);
14593  windows->font_info=XBestFont(display,resource_info,MagickFalse);
14594  if (windows->font_info == (XFontStruct *) NULL)
14595    ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14596      resource_info->font);
14597  /*
14598    Initialize Standard Colormap.
14599  */
14600  map_info=windows->map_info;
14601  icon_map=windows->icon_map;
14602  visual_info=windows->visual_info;
14603  icon_visual=windows->icon_visual;
14604  pixel=windows->pixel_info;
14605  icon_pixel=windows->icon_pixel;
14606  font_info=windows->font_info;
14607  icon_resources=windows->icon_resources;
14608  class_hints=windows->class_hints;
14609  manager_hints=windows->manager_hints;
14610  root_window=XRootWindow(display,visual_info->screen);
14611  nexus=NewImageList();
14612  if (display_image->debug != MagickFalse)
14613    {
14614      (void) LogMagickEvent(X11Event,GetMagickModule(),
14615        "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14616        (double) display_image->scene,(double) display_image->columns,
14617        (double) display_image->rows);
14618      if (display_image->colors != 0)
14619        (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14620          display_image->colors);
14621      (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14622        display_image->magick);
14623    }
14624  XMakeStandardColormap(display,visual_info,resource_info,display_image,
14625    map_info,pixel,exception);
14626  display_image->taint=MagickFalse;
14627  /*
14628    Initialize graphic context.
14629  */
14630  windows->context.id=(Window) NULL;
14631  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14632    resource_info,&windows->context);
14633  (void) CloneString(&class_hints->res_name,resource_info->client_name);
14634  (void) CloneString(&class_hints->res_class,resource_info->client_name);
14635  class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14636  manager_hints->flags=InputHint | StateHint;
14637  manager_hints->input=MagickFalse;
14638  manager_hints->initial_state=WithdrawnState;
14639  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14640    &windows->context);
14641  if (display_image->debug != MagickFalse)
14642    (void) LogMagickEvent(X11Event,GetMagickModule(),
14643      "Window id: 0x%lx (context)",windows->context.id);
14644  context_values.background=pixel->background_color.pixel;
14645  context_values.font=font_info->fid;
14646  context_values.foreground=pixel->foreground_color.pixel;
14647  context_values.graphics_exposures=MagickFalse;
14648  context_mask=(MagickStatusType)
14649    (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14650  if (pixel->annotate_context != (GC) NULL)
14651    (void) XFreeGC(display,pixel->annotate_context);
14652  pixel->annotate_context=XCreateGC(display,windows->context.id,
14653    context_mask,&context_values);
14654  if (pixel->annotate_context == (GC) NULL)
14655    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14656      display_image->filename);
14657  context_values.background=pixel->depth_color.pixel;
14658  if (pixel->widget_context != (GC) NULL)
14659    (void) XFreeGC(display,pixel->widget_context);
14660  pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14661    &context_values);
14662  if (pixel->widget_context == (GC) NULL)
14663    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14664      display_image->filename);
14665  context_values.background=pixel->foreground_color.pixel;
14666  context_values.foreground=pixel->background_color.pixel;
14667  context_values.plane_mask=context_values.background ^
14668    context_values.foreground;
14669  if (pixel->highlight_context != (GC) NULL)
14670    (void) XFreeGC(display,pixel->highlight_context);
14671  pixel->highlight_context=XCreateGC(display,windows->context.id,
14672    (size_t) (context_mask | GCPlaneMask),&context_values);
14673  if (pixel->highlight_context == (GC) NULL)
14674    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14675      display_image->filename);
14676  (void) XDestroyWindow(display,windows->context.id);
14677  /*
14678    Initialize icon window.
14679  */
14680  XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14681    icon_resources,&windows->icon);
14682  windows->icon.geometry=resource_info->icon_geometry;
14683  XBestIconSize(display,&windows->icon,display_image);
14684  windows->icon.attributes.colormap=XDefaultColormap(display,
14685    icon_visual->screen);
14686  windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14687  manager_hints->flags=InputHint | StateHint;
14688  manager_hints->input=MagickFalse;
14689  manager_hints->initial_state=IconicState;
14690  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14691    &windows->icon);
14692  if (display_image->debug != MagickFalse)
14693    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14694      windows->icon.id);
14695  /*
14696    Initialize graphic context for icon window.
14697  */
14698  if (icon_pixel->annotate_context != (GC) NULL)
14699    (void) XFreeGC(display,icon_pixel->annotate_context);
14700  context_values.background=icon_pixel->background_color.pixel;
14701  context_values.foreground=icon_pixel->foreground_color.pixel;
14702  icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14703    (size_t) (GCBackground | GCForeground),&context_values);
14704  if (icon_pixel->annotate_context == (GC) NULL)
14705    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14706      display_image->filename);
14707  windows->icon.annotate_context=icon_pixel->annotate_context;
14708  /*
14709    Initialize Image window.
14710  */
14711  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14712    &windows->image);
14713  windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14714  if (resource_info->use_shared_memory == MagickFalse)
14715    windows->image.shared_memory=MagickFalse;
14716  if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14717    {
14718      char
14719        *title;
14720
14721      title=InterpretImageProperties(resource_info->image_info,display_image,
14722        resource_info->title,exception);
14723      (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
14724      (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
14725      title=DestroyString(title);
14726    }
14727  else
14728    {
14729      char
14730        filename[MaxTextExtent];
14731
14732      /*
14733        Window name is the base of the filename.
14734      */
14735      GetPathComponent(display_image->magick_filename,TailPath,filename);
14736      if (display_image->scene == 0)
14737        (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14738          "%s: %s",MagickPackageName,filename);
14739      else
14740        (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14741          "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14742          (double) display_image->scene,(double) GetImageListLength(
14743          display_image));
14744      (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
14745    }
14746  if (resource_info->immutable)
14747    windows->image.immutable=MagickTrue;
14748  windows->image.use_pixmap=resource_info->use_pixmap;
14749  windows->image.geometry=resource_info->image_geometry;
14750  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14751    XDisplayWidth(display,visual_info->screen),
14752    XDisplayHeight(display,visual_info->screen));
14753  geometry_info.width=display_image->columns;
14754  geometry_info.height=display_image->rows;
14755  geometry_info.x=0;
14756  geometry_info.y=0;
14757  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14758    &geometry_info.width,&geometry_info.height);
14759  windows->image.width=(unsigned int) geometry_info.width;
14760  windows->image.height=(unsigned int) geometry_info.height;
14761  windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14762    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14763    KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14764    PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14765  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14766    resource_info,&windows->backdrop);
14767  if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14768    {
14769      /*
14770        Initialize backdrop window.
14771      */
14772      windows->backdrop.x=0;
14773      windows->backdrop.y=0;
14774      (void) CloneString(&windows->backdrop.name,"Backdrop");
14775      windows->backdrop.flags=(size_t) (USSize | USPosition);
14776      windows->backdrop.width=(unsigned int)
14777        XDisplayWidth(display,visual_info->screen);
14778      windows->backdrop.height=(unsigned int)
14779        XDisplayHeight(display,visual_info->screen);
14780      windows->backdrop.border_width=0;
14781      windows->backdrop.immutable=MagickTrue;
14782      windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14783        ButtonReleaseMask;
14784      windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14785        StructureNotifyMask;
14786      manager_hints->flags=IconWindowHint | InputHint | StateHint;
14787      manager_hints->icon_window=windows->icon.id;
14788      manager_hints->input=MagickTrue;
14789      manager_hints->initial_state=resource_info->iconic ? IconicState :
14790        NormalState;
14791      XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14792        &windows->backdrop);
14793      if (display_image->debug != MagickFalse)
14794        (void) LogMagickEvent(X11Event,GetMagickModule(),
14795          "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14796      (void) XMapWindow(display,windows->backdrop.id);
14797      (void) XClearWindow(display,windows->backdrop.id);
14798      if (windows->image.id != (Window) NULL)
14799        {
14800          (void) XDestroyWindow(display,windows->image.id);
14801          windows->image.id=(Window) NULL;
14802        }
14803      /*
14804        Position image in the center the backdrop.
14805      */
14806      windows->image.flags|=USPosition;
14807      windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14808        (windows->image.width/2);
14809      windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14810        (windows->image.height/2);
14811    }
14812  manager_hints->flags=IconWindowHint | InputHint | StateHint;
14813  manager_hints->icon_window=windows->icon.id;
14814  manager_hints->input=MagickTrue;
14815  manager_hints->initial_state=resource_info->iconic ? IconicState :
14816    NormalState;
14817  if (windows->group_leader.id != (Window) NULL)
14818    {
14819      /*
14820        Follow the leader.
14821      */
14822      manager_hints->flags|=WindowGroupHint;
14823      manager_hints->window_group=windows->group_leader.id;
14824      (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14825      if (display_image->debug != MagickFalse)
14826        (void) LogMagickEvent(X11Event,GetMagickModule(),
14827          "Window id: 0x%lx (group leader)",windows->group_leader.id);
14828    }
14829  XMakeWindow(display,
14830    (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14831    argv,argc,class_hints,manager_hints,&windows->image);
14832  (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14833    XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14834  if (windows->group_leader.id != (Window) NULL)
14835    (void) XSetTransientForHint(display,windows->image.id,
14836      windows->group_leader.id);
14837  if (display_image->debug != MagickFalse)
14838    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14839      windows->image.id);
14840  /*
14841    Initialize Info widget.
14842  */
14843  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14844    &windows->info);
14845  (void) CloneString(&windows->info.name,"Info");
14846  (void) CloneString(&windows->info.icon_name,"Info");
14847  windows->info.border_width=1;
14848  windows->info.x=2;
14849  windows->info.y=2;
14850  windows->info.flags|=PPosition;
14851  windows->info.attributes.win_gravity=UnmapGravity;
14852  windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14853    StructureNotifyMask;
14854  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14855  manager_hints->input=MagickFalse;
14856  manager_hints->initial_state=NormalState;
14857  manager_hints->window_group=windows->image.id;
14858  XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14859    &windows->info);
14860  windows->info.highlight_stipple=XCreateBitmapFromData(display,
14861    windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14862  windows->info.shadow_stipple=XCreateBitmapFromData(display,
14863    windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14864  (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14865  if (windows->image.mapped != MagickFalse)
14866    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14867  if (display_image->debug != MagickFalse)
14868    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14869      windows->info.id);
14870  /*
14871    Initialize Command widget.
14872  */
14873  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14874    resource_info,&windows->command);
14875  windows->command.data=MagickMenus;
14876  (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14877  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
14878    resource_info->client_name);
14879  windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14880    resource_name,"geometry",(char *) NULL);
14881  (void) CloneString(&windows->command.name,MagickTitle);
14882  windows->command.border_width=0;
14883  windows->command.flags|=PPosition;
14884  windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14885    ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14886    OwnerGrabButtonMask | StructureNotifyMask;
14887  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14888  manager_hints->input=MagickTrue;
14889  manager_hints->initial_state=NormalState;
14890  manager_hints->window_group=windows->image.id;
14891  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14892    &windows->command);
14893  windows->command.highlight_stipple=XCreateBitmapFromData(display,
14894    windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14895    HighlightHeight);
14896  windows->command.shadow_stipple=XCreateBitmapFromData(display,
14897    windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14898  (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14899  if (windows->command.mapped != MagickFalse)
14900    (void) XMapRaised(display,windows->command.id);
14901  if (display_image->debug != MagickFalse)
14902    (void) LogMagickEvent(X11Event,GetMagickModule(),
14903      "Window id: 0x%lx (command)",windows->command.id);
14904  /*
14905    Initialize Widget window.
14906  */
14907  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14908    resource_info,&windows->widget);
14909  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
14910    resource_info->client_name);
14911  windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14912    resource_name,"geometry",(char *) NULL);
14913  windows->widget.border_width=0;
14914  windows->widget.flags|=PPosition;
14915  windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14916    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14917    KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14918    StructureNotifyMask;
14919  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14920  manager_hints->input=MagickTrue;
14921  manager_hints->initial_state=NormalState;
14922  manager_hints->window_group=windows->image.id;
14923  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14924    &windows->widget);
14925  windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14926    windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14927  windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14928    windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14929  (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14930  if (display_image->debug != MagickFalse)
14931    (void) LogMagickEvent(X11Event,GetMagickModule(),
14932      "Window id: 0x%lx (widget)",windows->widget.id);
14933  /*
14934    Initialize popup window.
14935  */
14936  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14937    resource_info,&windows->popup);
14938  windows->popup.border_width=0;
14939  windows->popup.flags|=PPosition;
14940  windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14941    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14942    KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14943  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14944  manager_hints->input=MagickTrue;
14945  manager_hints->initial_state=NormalState;
14946  manager_hints->window_group=windows->image.id;
14947  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14948    &windows->popup);
14949  windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14950    windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14951  windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14952    windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14953  (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14954  if (display_image->debug != MagickFalse)
14955    (void) LogMagickEvent(X11Event,GetMagickModule(),
14956      "Window id: 0x%lx (pop up)",windows->popup.id);
14957  /*
14958    Initialize Magnify window and cursor.
14959  */
14960  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14961    resource_info,&windows->magnify);
14962  if (resource_info->use_shared_memory == MagickFalse)
14963    windows->magnify.shared_memory=MagickFalse;
14964  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
14965    resource_info->client_name);
14966  windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14967    resource_name,"geometry",(char *) NULL);
14968  (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14969    resource_info->magnify);
14970  if (windows->magnify.cursor != (Cursor) NULL)
14971    (void) XFreeCursor(display,windows->magnify.cursor);
14972  windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14973    map_info->colormap,resource_info->background_color,
14974    resource_info->foreground_color);
14975  if (windows->magnify.cursor == (Cursor) NULL)
14976    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14977      display_image->filename);
14978  windows->magnify.width=MagnifySize;
14979  windows->magnify.height=MagnifySize;
14980  windows->magnify.flags|=PPosition;
14981  windows->magnify.min_width=MagnifySize;
14982  windows->magnify.min_height=MagnifySize;
14983  windows->magnify.width_inc=MagnifySize;
14984  windows->magnify.height_inc=MagnifySize;
14985  windows->magnify.data=resource_info->magnify;
14986  windows->magnify.attributes.cursor=windows->magnify.cursor;
14987  windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14988    ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14989    StructureNotifyMask;
14990  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14991  manager_hints->input=MagickTrue;
14992  manager_hints->initial_state=NormalState;
14993  manager_hints->window_group=windows->image.id;
14994  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14995    &windows->magnify);
14996  if (display_image->debug != MagickFalse)
14997    (void) LogMagickEvent(X11Event,GetMagickModule(),
14998      "Window id: 0x%lx (magnify)",windows->magnify.id);
14999  (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
15000  /*
15001    Initialize panning window.
15002  */
15003  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
15004    resource_info,&windows->pan);
15005  (void) CloneString(&windows->pan.name,"Pan Icon");
15006  windows->pan.width=windows->icon.width;
15007  windows->pan.height=windows->icon.height;
15008  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
15009    resource_info->client_name);
15010  windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
15011    resource_name,"geometry",(char *) NULL);
15012  (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
15013    &windows->pan.width,&windows->pan.height);
15014  windows->pan.flags|=PPosition;
15015  windows->pan.immutable=MagickTrue;
15016  windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
15017    ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
15018    StructureNotifyMask;
15019  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
15020  manager_hints->input=MagickFalse;
15021  manager_hints->initial_state=NormalState;
15022  manager_hints->window_group=windows->image.id;
15023  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
15024    &windows->pan);
15025  if (display_image->debug != MagickFalse)
15026    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
15027      windows->pan.id);
15028  (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
15029  if (windows->info.mapped != MagickFalse)
15030    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15031  if ((windows->image.mapped == MagickFalse) ||
15032      (windows->backdrop.id != (Window) NULL))
15033    (void) XMapWindow(display,windows->image.id);
15034  /*
15035    Set our progress monitor and warning handlers.
15036  */
15037  if (warning_handler == (WarningHandler) NULL)
15038    {
15039      warning_handler=resource_info->display_warnings ?
15040        SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
15041      warning_handler=resource_info->display_warnings ?
15042        SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
15043    }
15044  /*
15045    Initialize Image and Magnify X images.
15046  */
15047  windows->image.x=0;
15048  windows->image.y=0;
15049  windows->magnify.shape=MagickFalse;
15050  width=(unsigned int) display_image->columns;
15051  height=(unsigned int) display_image->rows;
15052  if ((display_image->columns != width) || (display_image->rows != height))
15053    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15054      display_image->filename);
15055  status=XMakeImage(display,resource_info,&windows->image,display_image,
15056    width,height,exception);
15057  if (status == MagickFalse)
15058    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15059      display_image->filename);
15060  status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
15061    windows->magnify.width,windows->magnify.height,exception);
15062  if (status == MagickFalse)
15063    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15064      display_image->filename);
15065  if (windows->magnify.mapped != MagickFalse)
15066    (void) XMapRaised(display,windows->magnify.id);
15067  if (windows->pan.mapped != MagickFalse)
15068    (void) XMapRaised(display,windows->pan.id);
15069  windows->image.window_changes.width=(int) display_image->columns;
15070  windows->image.window_changes.height=(int) display_image->rows;
15071  (void) XConfigureImage(display,resource_info,windows,display_image,exception);
15072  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15073  (void) XSync(display,MagickFalse);
15074  /*
15075    Respond to events.
15076  */
15077  delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
15078  timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15079  update_time=0;
15080  if (resource_info->update != MagickFalse)
15081    {
15082      MagickBooleanType
15083        status;
15084
15085      /*
15086        Determine when file data was last modified.
15087      */
15088      status=GetPathAttributes(display_image->filename,&attributes);
15089      if (status != MagickFalse)
15090        update_time=attributes.st_mtime;
15091    }
15092  *state&=(~FormerImageState);
15093  *state&=(~MontageImageState);
15094  *state&=(~NextImageState);
15095  do
15096  {
15097    /*
15098      Handle a window event.
15099    */
15100    if (windows->image.mapped != MagickFalse)
15101      if ((display_image->delay != 0) || (resource_info->update != 0))
15102        {
15103          if (timer < time((time_t *) NULL))
15104            {
15105              if (resource_info->update == MagickFalse)
15106                *state|=NextImageState | ExitState;
15107              else
15108                {
15109                  MagickBooleanType
15110                    status;
15111
15112                  /*
15113                    Determine if image file was modified.
15114                  */
15115                  status=GetPathAttributes(display_image->filename,&attributes);
15116                  if (status != MagickFalse)
15117                    if (update_time != attributes.st_mtime)
15118                      {
15119                        /*
15120                          Redisplay image.
15121                        */
15122                        (void) FormatLocaleString(
15123                          resource_info->image_info->filename,MaxTextExtent,
15124                          "%s:%s",display_image->magick,
15125                          display_image->filename);
15126                        nexus=ReadImage(resource_info->image_info,exception);
15127                        if (nexus != (Image *) NULL)
15128                          {
15129                            nexus=DestroyImage(nexus);
15130                            *state|=NextImageState | ExitState;
15131                          }
15132                      }
15133                  delay=display_image->delay/MagickMax(
15134                    display_image->ticks_per_second,1L);
15135                  timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15136                }
15137            }
15138          if (XEventsQueued(display,QueuedAfterFlush) == 0)
15139            {
15140              /*
15141                Do not block if delay > 0.
15142              */
15143              XDelay(display,SuspendTime << 2);
15144              continue;
15145            }
15146        }
15147    timestamp=time((time_t *) NULL);
15148    (void) XNextEvent(display,&event);
15149    if (windows->image.stasis == MagickFalse)
15150      windows->image.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15151        MagickTrue : MagickFalse;
15152    if (windows->magnify.stasis == MagickFalse)
15153      windows->magnify.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15154        MagickTrue : MagickFalse;
15155    if (event.xany.window == windows->command.id)
15156      {
15157        /*
15158          Select a command from the Command widget.
15159        */
15160        id=XCommandWidget(display,windows,CommandMenu,&event);
15161        if (id < 0)
15162          continue;
15163        (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15164        command_type=CommandMenus[id];
15165        if (id < MagickMenus)
15166          {
15167            /*
15168              Select a command from a pop-up menu.
15169            */
15170            entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15171              command);
15172            if (entry < 0)
15173              continue;
15174            (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15175            command_type=Commands[id][entry];
15176          }
15177        if (command_type != NullCommand)
15178          nexus=XMagickCommand(display,resource_info,windows,command_type,
15179            &display_image,exception);
15180        continue;
15181      }
15182    switch (event.type)
15183    {
15184      case ButtonPress:
15185      {
15186        if (display_image->debug != MagickFalse)
15187          (void) LogMagickEvent(X11Event,GetMagickModule(),
15188            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15189            event.xbutton.button,event.xbutton.x,event.xbutton.y);
15190        if ((event.xbutton.button == Button3) &&
15191            (event.xbutton.state & Mod1Mask))
15192          {
15193            /*
15194              Convert Alt-Button3 to Button2.
15195            */
15196            event.xbutton.button=Button2;
15197            event.xbutton.state&=(~Mod1Mask);
15198          }
15199        if (event.xbutton.window == windows->backdrop.id)
15200          {
15201            (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15202              event.xbutton.time);
15203            break;
15204          }
15205        if (event.xbutton.window == windows->image.id)
15206          {
15207            switch (event.xbutton.button)
15208            {
15209              case Button1:
15210              {
15211                if (resource_info->immutable)
15212                  {
15213                    /*
15214                      Select a command from the Virtual menu.
15215                    */
15216                    entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15217                      command);
15218                    if (entry >= 0)
15219                      nexus=XMagickCommand(display,resource_info,windows,
15220                        VirtualCommands[entry],&display_image,exception);
15221                    break;
15222                  }
15223                /*
15224                  Map/unmap Command widget.
15225                */
15226                if (windows->command.mapped != MagickFalse)
15227                  (void) XWithdrawWindow(display,windows->command.id,
15228                    windows->command.screen);
15229                else
15230                  {
15231                    (void) XCommandWidget(display,windows,CommandMenu,
15232                      (XEvent *) NULL);
15233                    (void) XMapRaised(display,windows->command.id);
15234                  }
15235                break;
15236              }
15237              case Button2:
15238              {
15239                /*
15240                  User pressed the image magnify button.
15241                */
15242                (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15243                  &display_image,exception);
15244                XMagnifyImage(display,windows,&event,exception);
15245                break;
15246              }
15247              case Button3:
15248              {
15249                if (resource_info->immutable)
15250                  {
15251                    /*
15252                      Select a command from the Virtual menu.
15253                    */
15254                    entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15255                      command);
15256                    if (entry >= 0)
15257                      nexus=XMagickCommand(display,resource_info,windows,
15258                        VirtualCommands[entry],&display_image,exception);
15259                    break;
15260                  }
15261                if (display_image->montage != (char *) NULL)
15262                  {
15263                    /*
15264                      Open or delete a tile from a visual image directory.
15265                    */
15266                    nexus=XTileImage(display,resource_info,windows,
15267                      display_image,&event,exception);
15268                    if (nexus != (Image *) NULL)
15269                      *state|=MontageImageState | NextImageState | ExitState;
15270                    vid_info.x=(short int) windows->image.x;
15271                    vid_info.y=(short int) windows->image.y;
15272                    break;
15273                  }
15274                /*
15275                  Select a command from the Short Cuts menu.
15276                */
15277                entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15278                  command);
15279                if (entry >= 0)
15280                  nexus=XMagickCommand(display,resource_info,windows,
15281                    ShortCutsCommands[entry],&display_image,exception);
15282                break;
15283              }
15284              case Button4:
15285              {
15286                /*
15287                  Wheel up.
15288                */
15289                XTranslateImage(display,windows,*image,XK_Up);
15290                break;
15291              }
15292              case Button5:
15293              {
15294                /*
15295                  Wheel down.
15296                */
15297                XTranslateImage(display,windows,*image,XK_Down);
15298                break;
15299              }
15300              default:
15301                break;
15302            }
15303            break;
15304          }
15305        if (event.xbutton.window == windows->magnify.id)
15306          {
15307            int
15308              factor;
15309
15310            static const char
15311              *MagnifyMenu[] =
15312              {
15313                "2",
15314                "4",
15315                "5",
15316                "6",
15317                "7",
15318                "8",
15319                "9",
15320                "3",
15321                (char *) NULL,
15322              };
15323
15324            static KeySym
15325              MagnifyCommands[] =
15326              {
15327                XK_2,
15328                XK_4,
15329                XK_5,
15330                XK_6,
15331                XK_7,
15332                XK_8,
15333                XK_9,
15334                XK_3
15335              };
15336
15337            /*
15338              Select a magnify factor from the pop-up menu.
15339            */
15340            factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15341            if (factor >= 0)
15342              XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor],
15343                exception);
15344            break;
15345          }
15346        if (event.xbutton.window == windows->pan.id)
15347          {
15348            switch (event.xbutton.button)
15349            {
15350              case Button4:
15351              {
15352                /*
15353                  Wheel up.
15354                */
15355                XTranslateImage(display,windows,*image,XK_Up);
15356                break;
15357              }
15358              case Button5:
15359              {
15360                /*
15361                  Wheel down.
15362                */
15363                XTranslateImage(display,windows,*image,XK_Down);
15364                break;
15365              }
15366              default:
15367              {
15368                XPanImage(display,windows,&event,exception);
15369                break;
15370              }
15371            }
15372            break;
15373          }
15374        delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15375          1L);
15376        timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15377        break;
15378      }
15379      case ButtonRelease:
15380      {
15381        if (display_image->debug != MagickFalse)
15382          (void) LogMagickEvent(X11Event,GetMagickModule(),
15383            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15384            event.xbutton.button,event.xbutton.x,event.xbutton.y);
15385        break;
15386      }
15387      case ClientMessage:
15388      {
15389        if (display_image->debug != MagickFalse)
15390          (void) LogMagickEvent(X11Event,GetMagickModule(),
15391            "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15392            event.xclient.message_type,event.xclient.format,(unsigned long)
15393            event.xclient.data.l[0]);
15394        if (event.xclient.message_type == windows->im_protocols)
15395          {
15396            if (*event.xclient.data.l == (long) windows->im_update_widget)
15397              {
15398                (void) CloneString(&windows->command.name,MagickTitle);
15399                windows->command.data=MagickMenus;
15400                (void) XCommandWidget(display,windows,CommandMenu,
15401                  (XEvent *) NULL);
15402                break;
15403              }
15404            if (*event.xclient.data.l == (long) windows->im_update_colormap)
15405              {
15406                /*
15407                  Update graphic context and window colormap.
15408                */
15409                for (i=0; i < (int) number_windows; i++)
15410                {
15411                  if (magick_windows[i]->id == windows->icon.id)
15412                    continue;
15413                  context_values.background=pixel->background_color.pixel;
15414                  context_values.foreground=pixel->foreground_color.pixel;
15415                  (void) XChangeGC(display,magick_windows[i]->annotate_context,
15416                    context_mask,&context_values);
15417                  (void) XChangeGC(display,magick_windows[i]->widget_context,
15418                    context_mask,&context_values);
15419                  context_values.background=pixel->foreground_color.pixel;
15420                  context_values.foreground=pixel->background_color.pixel;
15421                  context_values.plane_mask=context_values.background ^
15422                    context_values.foreground;
15423                  (void) XChangeGC(display,magick_windows[i]->highlight_context,
15424                    (size_t) (context_mask | GCPlaneMask),
15425                    &context_values);
15426                  magick_windows[i]->attributes.background_pixel=
15427                    pixel->background_color.pixel;
15428                  magick_windows[i]->attributes.border_pixel=
15429                    pixel->border_color.pixel;
15430                  magick_windows[i]->attributes.colormap=map_info->colormap;
15431                  (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15432                    (unsigned long) magick_windows[i]->mask,
15433                    &magick_windows[i]->attributes);
15434                }
15435                if (windows->pan.mapped != MagickFalse)
15436                  {
15437                    (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15438                      windows->pan.pixmap);
15439                    (void) XClearWindow(display,windows->pan.id);
15440                    XDrawPanRectangle(display,windows);
15441                  }
15442                if (windows->backdrop.id != (Window) NULL)
15443                  (void) XInstallColormap(display,map_info->colormap);
15444                break;
15445              }
15446            if (*event.xclient.data.l == (long) windows->im_former_image)
15447              {
15448                *state|=FormerImageState | ExitState;
15449                break;
15450              }
15451            if (*event.xclient.data.l == (long) windows->im_next_image)
15452              {
15453                *state|=NextImageState | ExitState;
15454                break;
15455              }
15456            if (*event.xclient.data.l == (long) windows->im_retain_colors)
15457              {
15458                *state|=RetainColorsState;
15459                break;
15460              }
15461            if (*event.xclient.data.l == (long) windows->im_exit)
15462              {
15463                *state|=ExitState;
15464                break;
15465              }
15466            break;
15467          }
15468        if (event.xclient.message_type == windows->dnd_protocols)
15469          {
15470            Atom
15471              selection,
15472              type;
15473
15474            int
15475              format,
15476              status;
15477
15478            unsigned char
15479              *data;
15480
15481            unsigned long
15482              after,
15483              length;
15484
15485            /*
15486              Display image named by the Drag-and-Drop selection.
15487            */
15488            if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15489              break;
15490            selection=XInternAtom(display,"DndSelection",MagickFalse);
15491            status=XGetWindowProperty(display,root_window,selection,0L,(long)
15492              MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15493              &length,&after,&data);
15494            if ((status != Success) || (length == 0))
15495              break;
15496            if (*event.xclient.data.l == 2)
15497              {
15498                /*
15499                  Offix DND.
15500                */
15501                (void) CopyMagickString(resource_info->image_info->filename,
15502                  (char *) data,MaxTextExtent);
15503              }
15504            else
15505              {
15506                /*
15507                  XDND.
15508                */
15509                if (strncmp((char *) data, "file:", 5) != 0)
15510                  {
15511                    (void) XFree((void *) data);
15512                    break;
15513                  }
15514                (void) CopyMagickString(resource_info->image_info->filename,
15515                  ((char *) data)+5,MaxTextExtent);
15516              }
15517            nexus=ReadImage(resource_info->image_info,exception);
15518            CatchException(exception);
15519            if (nexus != (Image *) NULL)
15520              *state|=NextImageState | ExitState;
15521            (void) XFree((void *) data);
15522            break;
15523          }
15524        /*
15525          If client window delete message, exit.
15526        */
15527        if (event.xclient.message_type != windows->wm_protocols)
15528          break;
15529        if (*event.xclient.data.l != (long) windows->wm_delete_window)
15530          break;
15531        (void) XWithdrawWindow(display,event.xclient.window,
15532          visual_info->screen);
15533        if (event.xclient.window == windows->image.id)
15534          {
15535            *state|=ExitState;
15536            break;
15537          }
15538        if (event.xclient.window == windows->pan.id)
15539          {
15540            /*
15541              Restore original image size when pan window is deleted.
15542            */
15543            windows->image.window_changes.width=windows->image.ximage->width;
15544            windows->image.window_changes.height=windows->image.ximage->height;
15545            (void) XConfigureImage(display,resource_info,windows,
15546              display_image,exception);
15547          }
15548        break;
15549      }
15550      case ConfigureNotify:
15551      {
15552        if (display_image->debug != MagickFalse)
15553          (void) LogMagickEvent(X11Event,GetMagickModule(),
15554            "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15555            event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15556            event.xconfigure.y,event.xconfigure.send_event);
15557        if (event.xconfigure.window == windows->image.id)
15558          {
15559            /*
15560              Image window has a new configuration.
15561            */
15562            if (event.xconfigure.send_event != 0)
15563              {
15564                XWindowChanges
15565                  window_changes;
15566
15567                /*
15568                  Position the transient windows relative of the Image window.
15569                */
15570                if (windows->command.geometry == (char *) NULL)
15571                  if (windows->command.mapped == MagickFalse)
15572                    {
15573                      windows->command.x=event.xconfigure.x-
15574                        windows->command.width-25;
15575                      windows->command.y=event.xconfigure.y;
15576                      XConstrainWindowPosition(display,&windows->command);
15577                      window_changes.x=windows->command.x;
15578                      window_changes.y=windows->command.y;
15579                      (void) XReconfigureWMWindow(display,windows->command.id,
15580                        windows->command.screen,(unsigned int) (CWX | CWY),
15581                        &window_changes);
15582                    }
15583                if (windows->widget.geometry == (char *) NULL)
15584                  if (windows->widget.mapped == MagickFalse)
15585                    {
15586                      windows->widget.x=event.xconfigure.x+
15587                        event.xconfigure.width/10;
15588                      windows->widget.y=event.xconfigure.y+
15589                        event.xconfigure.height/10;
15590                      XConstrainWindowPosition(display,&windows->widget);
15591                      window_changes.x=windows->widget.x;
15592                      window_changes.y=windows->widget.y;
15593                      (void) XReconfigureWMWindow(display,windows->widget.id,
15594                        windows->widget.screen,(unsigned int) (CWX | CWY),
15595                        &window_changes);
15596                    }
15597                if (windows->magnify.geometry == (char *) NULL)
15598                  if (windows->magnify.mapped == MagickFalse)
15599                    {
15600                      windows->magnify.x=event.xconfigure.x+
15601                        event.xconfigure.width+25;
15602                      windows->magnify.y=event.xconfigure.y;
15603                      XConstrainWindowPosition(display,&windows->magnify);
15604                      window_changes.x=windows->magnify.x;
15605                      window_changes.y=windows->magnify.y;
15606                      (void) XReconfigureWMWindow(display,windows->magnify.id,
15607                        windows->magnify.screen,(unsigned int) (CWX | CWY),
15608                        &window_changes);
15609                    }
15610                if (windows->pan.geometry == (char *) NULL)
15611                  if (windows->pan.mapped == MagickFalse)
15612                    {
15613                      windows->pan.x=event.xconfigure.x+
15614                        event.xconfigure.width+25;
15615                      windows->pan.y=event.xconfigure.y+
15616                        windows->magnify.height+50;
15617                      XConstrainWindowPosition(display,&windows->pan);
15618                      window_changes.x=windows->pan.x;
15619                      window_changes.y=windows->pan.y;
15620                      (void) XReconfigureWMWindow(display,windows->pan.id,
15621                        windows->pan.screen,(unsigned int) (CWX | CWY),
15622                        &window_changes);
15623                    }
15624              }
15625            if ((event.xconfigure.width == (int) windows->image.width) &&
15626                (event.xconfigure.height == (int) windows->image.height))
15627              break;
15628            windows->image.width=(unsigned int) event.xconfigure.width;
15629            windows->image.height=(unsigned int) event.xconfigure.height;
15630            windows->image.x=0;
15631            windows->image.y=0;
15632            if (display_image->montage != (char *) NULL)
15633              {
15634                windows->image.x=vid_info.x;
15635                windows->image.y=vid_info.y;
15636              }
15637            if ((windows->image.mapped != MagickFalse) &&
15638                (windows->image.stasis != MagickFalse))
15639              {
15640                /*
15641                  Update image window configuration.
15642                */
15643                windows->image.window_changes.width=event.xconfigure.width;
15644                windows->image.window_changes.height=event.xconfigure.height;
15645                (void) XConfigureImage(display,resource_info,windows,
15646                  display_image,exception);
15647              }
15648            /*
15649              Update pan window configuration.
15650            */
15651            if ((event.xconfigure.width < windows->image.ximage->width) ||
15652                (event.xconfigure.height < windows->image.ximage->height))
15653              {
15654                (void) XMapRaised(display,windows->pan.id);
15655                XDrawPanRectangle(display,windows);
15656              }
15657            else
15658              if (windows->pan.mapped != MagickFalse)
15659                (void) XWithdrawWindow(display,windows->pan.id,
15660                  windows->pan.screen);
15661            break;
15662          }
15663        if (event.xconfigure.window == windows->magnify.id)
15664          {
15665            unsigned int
15666              magnify;
15667
15668            /*
15669              Magnify window has a new configuration.
15670            */
15671            windows->magnify.width=(unsigned int) event.xconfigure.width;
15672            windows->magnify.height=(unsigned int) event.xconfigure.height;
15673            if (windows->magnify.mapped == MagickFalse)
15674              break;
15675            magnify=1;
15676            while ((int) magnify <= event.xconfigure.width)
15677              magnify<<=1;
15678            while ((int) magnify <= event.xconfigure.height)
15679              magnify<<=1;
15680            magnify>>=1;
15681            if (((int) magnify != event.xconfigure.width) ||
15682                ((int) magnify != event.xconfigure.height))
15683              {
15684                window_changes.width=(int) magnify;
15685                window_changes.height=(int) magnify;
15686                (void) XReconfigureWMWindow(display,windows->magnify.id,
15687                  windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15688                  &window_changes);
15689                break;
15690              }
15691            if ((windows->magnify.mapped != MagickFalse) &&
15692                (windows->magnify.stasis != MagickFalse))
15693              {
15694                status=XMakeImage(display,resource_info,&windows->magnify,
15695                  display_image,windows->magnify.width,windows->magnify.height,
15696                  exception);
15697                XMakeMagnifyImage(display,windows,exception);
15698              }
15699            break;
15700          }
15701        if ((windows->magnify.mapped != MagickFalse) &&
15702            (event.xconfigure.window == windows->pan.id))
15703          {
15704            /*
15705              Pan icon window has a new configuration.
15706            */
15707            if (event.xconfigure.send_event != 0)
15708              {
15709                windows->pan.x=event.xconfigure.x;
15710                windows->pan.y=event.xconfigure.y;
15711              }
15712            windows->pan.width=(unsigned int) event.xconfigure.width;
15713            windows->pan.height=(unsigned int) event.xconfigure.height;
15714            break;
15715          }
15716        if (event.xconfigure.window == windows->icon.id)
15717          {
15718            /*
15719              Icon window has a new configuration.
15720            */
15721            windows->icon.width=(unsigned int) event.xconfigure.width;
15722            windows->icon.height=(unsigned int) event.xconfigure.height;
15723            break;
15724          }
15725        break;
15726      }
15727      case DestroyNotify:
15728      {
15729        /*
15730          Group leader has exited.
15731        */
15732        if (display_image->debug != MagickFalse)
15733          (void) LogMagickEvent(X11Event,GetMagickModule(),
15734            "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15735        if (event.xdestroywindow.window == windows->group_leader.id)
15736          {
15737            *state|=ExitState;
15738            break;
15739          }
15740        break;
15741      }
15742      case EnterNotify:
15743      {
15744        /*
15745          Selectively install colormap.
15746        */
15747        if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15748          if (event.xcrossing.mode != NotifyUngrab)
15749            XInstallColormap(display,map_info->colormap);
15750        break;
15751      }
15752      case Expose:
15753      {
15754        if (display_image->debug != MagickFalse)
15755          (void) LogMagickEvent(X11Event,GetMagickModule(),
15756            "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15757            event.xexpose.width,event.xexpose.height,event.xexpose.x,
15758            event.xexpose.y);
15759        /*
15760          Refresh windows that are now exposed.
15761        */
15762        if ((event.xexpose.window == windows->image.id) &&
15763            (windows->image.mapped != MagickFalse))
15764          {
15765            XRefreshWindow(display,&windows->image,&event);
15766            delay=display_image->delay/MagickMax(
15767              display_image->ticks_per_second,1L);
15768            timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15769            break;
15770          }
15771        if ((event.xexpose.window == windows->magnify.id) &&
15772            (windows->magnify.mapped != MagickFalse))
15773          {
15774            XMakeMagnifyImage(display,windows,exception);
15775            break;
15776          }
15777        if (event.xexpose.window == windows->pan.id)
15778          {
15779            XDrawPanRectangle(display,windows);
15780            break;
15781          }
15782        if (event.xexpose.window == windows->icon.id)
15783          {
15784            XRefreshWindow(display,&windows->icon,&event);
15785            break;
15786          }
15787        break;
15788      }
15789      case KeyPress:
15790      {
15791        int
15792          length;
15793
15794        /*
15795          Respond to a user key press.
15796        */
15797        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15798          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15799        *(command+length)='\0';
15800        if (display_image->debug != MagickFalse)
15801          (void) LogMagickEvent(X11Event,GetMagickModule(),
15802            "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15803            key_symbol,command);
15804        if (event.xkey.window == windows->image.id)
15805          {
15806            command_type=XImageWindowCommand(display,resource_info,windows,
15807              event.xkey.state,key_symbol,&display_image,exception);
15808            if (command_type != NullCommand)
15809              nexus=XMagickCommand(display,resource_info,windows,command_type,
15810                &display_image,exception);
15811          }
15812        if (event.xkey.window == windows->magnify.id)
15813          XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol,
15814            exception);
15815        if (event.xkey.window == windows->pan.id)
15816          {
15817            if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15818              (void) XWithdrawWindow(display,windows->pan.id,
15819                windows->pan.screen);
15820            else
15821              if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15822                XTextViewWidget(display,resource_info,windows,MagickFalse,
15823                  "Help Viewer - Image Pan",ImagePanHelp);
15824              else
15825                XTranslateImage(display,windows,*image,key_symbol);
15826          }
15827        delay=display_image->delay/MagickMax(
15828          display_image->ticks_per_second,1L);
15829        timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15830        break;
15831      }
15832      case KeyRelease:
15833      {
15834        /*
15835          Respond to a user key release.
15836        */
15837        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15838          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15839        if (display_image->debug != MagickFalse)
15840          (void) LogMagickEvent(X11Event,GetMagickModule(),
15841            "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15842        break;
15843      }
15844      case LeaveNotify:
15845      {
15846        /*
15847          Selectively uninstall colormap.
15848        */
15849        if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15850          if (event.xcrossing.mode != NotifyUngrab)
15851            XUninstallColormap(display,map_info->colormap);
15852        break;
15853      }
15854      case MapNotify:
15855      {
15856        if (display_image->debug != MagickFalse)
15857          (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15858            event.xmap.window);
15859        if (event.xmap.window == windows->backdrop.id)
15860          {
15861            (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15862              CurrentTime);
15863            windows->backdrop.mapped=MagickTrue;
15864            break;
15865          }
15866        if (event.xmap.window == windows->image.id)
15867          {
15868            if (windows->backdrop.id != (Window) NULL)
15869              (void) XInstallColormap(display,map_info->colormap);
15870            if (LocaleCompare(display_image->magick,"LOGO") == 0)
15871              {
15872                if (LocaleCompare(display_image->filename,"LOGO") == 0)
15873                  nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15874              }
15875            if (((int) windows->image.width < windows->image.ximage->width) ||
15876                ((int) windows->image.height < windows->image.ximage->height))
15877              (void) XMapRaised(display,windows->pan.id);
15878            windows->image.mapped=MagickTrue;
15879            break;
15880          }
15881        if (event.xmap.window == windows->magnify.id)
15882          {
15883            XMakeMagnifyImage(display,windows,exception);
15884            windows->magnify.mapped=MagickTrue;
15885            (void) XWithdrawWindow(display,windows->info.id,
15886              windows->info.screen);
15887            break;
15888          }
15889        if (event.xmap.window == windows->pan.id)
15890          {
15891            XMakePanImage(display,resource_info,windows,display_image,
15892              exception);
15893            windows->pan.mapped=MagickTrue;
15894            break;
15895          }
15896        if (event.xmap.window == windows->info.id)
15897          {
15898            windows->info.mapped=MagickTrue;
15899            break;
15900          }
15901        if (event.xmap.window == windows->icon.id)
15902          {
15903            MagickBooleanType
15904              taint;
15905
15906            /*
15907              Create an icon image.
15908            */
15909            taint=display_image->taint;
15910            XMakeStandardColormap(display,icon_visual,icon_resources,
15911              display_image,icon_map,icon_pixel,exception);
15912            (void) XMakeImage(display,icon_resources,&windows->icon,
15913              display_image,windows->icon.width,windows->icon.height,
15914              exception);
15915            display_image->taint=taint;
15916            (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15917              windows->icon.pixmap);
15918            (void) XClearWindow(display,windows->icon.id);
15919            (void) XWithdrawWindow(display,windows->info.id,
15920              windows->info.screen);
15921            windows->icon.mapped=MagickTrue;
15922            break;
15923          }
15924        if (event.xmap.window == windows->command.id)
15925          {
15926            windows->command.mapped=MagickTrue;
15927            break;
15928          }
15929        if (event.xmap.window == windows->popup.id)
15930          {
15931            windows->popup.mapped=MagickTrue;
15932            break;
15933          }
15934        if (event.xmap.window == windows->widget.id)
15935          {
15936            windows->widget.mapped=MagickTrue;
15937            break;
15938          }
15939        break;
15940      }
15941      case MappingNotify:
15942      {
15943        (void) XRefreshKeyboardMapping(&event.xmapping);
15944        break;
15945      }
15946      case NoExpose:
15947        break;
15948      case PropertyNotify:
15949      {
15950        Atom
15951          type;
15952
15953        int
15954          format,
15955          status;
15956
15957        unsigned char
15958          *data;
15959
15960        unsigned long
15961          after,
15962          length;
15963
15964        if (display_image->debug != MagickFalse)
15965          (void) LogMagickEvent(X11Event,GetMagickModule(),
15966            "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15967            event.xproperty.atom,event.xproperty.state);
15968        if (event.xproperty.atom != windows->im_remote_command)
15969          break;
15970        /*
15971          Display image named by the remote command protocol.
15972        */
15973        status=XGetWindowProperty(display,event.xproperty.window,
15974          event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15975          AnyPropertyType,&type,&format,&length,&after,&data);
15976        if ((status != Success) || (length == 0))
15977          break;
15978        if (LocaleCompare((char *) data,"-quit") == 0)
15979          {
15980            XClientMessage(display,windows->image.id,windows->im_protocols,
15981              windows->im_exit,CurrentTime);
15982            (void) XFree((void *) data);
15983            break;
15984          }
15985        (void) CopyMagickString(resource_info->image_info->filename,
15986          (char *) data,MaxTextExtent);
15987        (void) XFree((void *) data);
15988        nexus=ReadImage(resource_info->image_info,exception);
15989        CatchException(exception);
15990        if (nexus != (Image *) NULL)
15991          *state|=NextImageState | ExitState;
15992        break;
15993      }
15994      case ReparentNotify:
15995      {
15996        if (display_image->debug != MagickFalse)
15997          (void) LogMagickEvent(X11Event,GetMagickModule(),
15998            "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15999            event.xreparent.window);
16000        break;
16001      }
16002      case UnmapNotify:
16003      {
16004        if (display_image->debug != MagickFalse)
16005          (void) LogMagickEvent(X11Event,GetMagickModule(),
16006            "Unmap Notify: 0x%lx",event.xunmap.window);
16007        if (event.xunmap.window == windows->backdrop.id)
16008          {
16009            windows->backdrop.mapped=MagickFalse;
16010            break;
16011          }
16012        if (event.xunmap.window == windows->image.id)
16013          {
16014            windows->image.mapped=MagickFalse;
16015            break;
16016          }
16017        if (event.xunmap.window == windows->magnify.id)
16018          {
16019            windows->magnify.mapped=MagickFalse;
16020            break;
16021          }
16022        if (event.xunmap.window == windows->pan.id)
16023          {
16024            windows->pan.mapped=MagickFalse;
16025            break;
16026          }
16027        if (event.xunmap.window == windows->info.id)
16028          {
16029            windows->info.mapped=MagickFalse;
16030            break;
16031          }
16032        if (event.xunmap.window == windows->icon.id)
16033          {
16034            if (map_info->colormap == icon_map->colormap)
16035              XConfigureImageColormap(display,resource_info,windows,
16036                display_image,exception);
16037            (void) XFreeStandardColormap(display,icon_visual,icon_map,
16038              icon_pixel);
16039            windows->icon.mapped=MagickFalse;
16040            break;
16041          }
16042        if (event.xunmap.window == windows->command.id)
16043          {
16044            windows->command.mapped=MagickFalse;
16045            break;
16046          }
16047        if (event.xunmap.window == windows->popup.id)
16048          {
16049            if (windows->backdrop.id != (Window) NULL)
16050              (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16051                CurrentTime);
16052            windows->popup.mapped=MagickFalse;
16053            break;
16054          }
16055        if (event.xunmap.window == windows->widget.id)
16056          {
16057            if (windows->backdrop.id != (Window) NULL)
16058              (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16059                CurrentTime);
16060            windows->widget.mapped=MagickFalse;
16061            break;
16062          }
16063        break;
16064      }
16065      default:
16066      {
16067        if (display_image->debug != MagickFalse)
16068          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
16069            event.type);
16070        break;
16071      }
16072    }
16073  } while (!(*state & ExitState));
16074  if ((*state & ExitState) == 0)
16075    (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
16076      &display_image,exception);
16077  else
16078    if (resource_info->confirm_edit != MagickFalse)
16079      {
16080        /*
16081          Query user if image has changed.
16082        */
16083        if ((resource_info->immutable == MagickFalse) &&
16084            (display_image->taint != MagickFalse))
16085          {
16086            int
16087              status;
16088
16089            status=XConfirmWidget(display,windows,"Your image changed.",
16090              "Do you want to save it");
16091            if (status == 0)
16092              *state&=(~ExitState);
16093            else
16094              if (status > 0)
16095                (void) XMagickCommand(display,resource_info,windows,SaveCommand,
16096                  &display_image,exception);
16097          }
16098      }
16099  if ((windows->visual_info->klass == GrayScale) ||
16100      (windows->visual_info->klass == PseudoColor) ||
16101      (windows->visual_info->klass == DirectColor))
16102    {
16103      /*
16104        Withdraw pan and Magnify window.
16105      */
16106      if (windows->info.mapped != MagickFalse)
16107        (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16108      if (windows->magnify.mapped != MagickFalse)
16109        (void) XWithdrawWindow(display,windows->magnify.id,
16110          windows->magnify.screen);
16111      if (windows->command.mapped != MagickFalse)
16112        (void) XWithdrawWindow(display,windows->command.id,
16113          windows->command.screen);
16114    }
16115  if (windows->pan.mapped != MagickFalse)
16116    (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16117  if (resource_info->backdrop == MagickFalse)
16118    if (windows->backdrop.mapped)
16119      {
16120        (void) XWithdrawWindow(display,windows->backdrop.id,
16121          windows->backdrop.screen);
16122        (void) XDestroyWindow(display,windows->backdrop.id);
16123        windows->backdrop.id=(Window) NULL;
16124        (void) XWithdrawWindow(display,windows->image.id,
16125          windows->image.screen);
16126        (void) XDestroyWindow(display,windows->image.id);
16127        windows->image.id=(Window) NULL;
16128      }
16129  XSetCursorState(display,windows,MagickTrue);
16130  XCheckRefreshWindows(display,windows);
16131  if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16132    *state&=(~ExitState);
16133  if (*state & ExitState)
16134    {
16135      /*
16136        Free Standard Colormap.
16137      */
16138      (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16139      if (resource_info->map_type == (char *) NULL)
16140        (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16141      /*
16142        Free X resources.
16143      */
16144      if (resource_info->copy_image != (Image *) NULL)
16145        {
16146          resource_info->copy_image=DestroyImage(resource_info->copy_image);
16147          resource_info->copy_image=NewImageList();
16148        }
16149      DestroyXResources();
16150    }
16151  (void) XSync(display,MagickFalse);
16152  /*
16153    Restore our progress monitor and warning handlers.
16154  */
16155  (void) SetErrorHandler(warning_handler);
16156  (void) SetWarningHandler(warning_handler);
16157  /*
16158    Change to home directory.
16159  */
16160  directory=getcwd(working_directory,MaxTextExtent);
16161  (void) directory;
16162  {
16163    int
16164      status;
16165
16166    status=chdir(resource_info->home_directory);
16167    if (status == -1)
16168      (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
16169        "UnableToOpenFile","%s",resource_info->home_directory);
16170  }
16171  *image=display_image;
16172  return(nexus);
16173}
16174#else
16175
16176/*
16177%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16178%                                                                             %
16179%                                                                             %
16180%                                                                             %
16181+   D i s p l a y I m a g e s                                                 %
16182%                                                                             %
16183%                                                                             %
16184%                                                                             %
16185%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16186%
16187%  DisplayImages() displays an image sequence to any X window screen.  It
16188%  returns a value other than 0 if successful.  Check the exception member
16189%  of image to determine the reason for any failure.
16190%
16191%  The format of the DisplayImages method is:
16192%
16193%      MagickBooleanType DisplayImages(const ImageInfo *image_info,
16194%        Image *images,ExceptionInfo *exception)
16195%
16196%  A description of each parameter follows:
16197%
16198%    o image_info: the image info.
16199%
16200%    o image: the image.
16201%
16202%    o exception: return any errors or warnings in this structure.
16203%
16204*/
16205MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16206  Image *image,ExceptionInfo *exception)
16207{
16208  assert(image_info != (const ImageInfo *) NULL);
16209  assert(image_info->signature == MagickSignature);
16210  assert(image != (Image *) NULL);
16211  assert(image->signature == MagickSignature);
16212  if (image->debug != MagickFalse)
16213    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16214  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16215    "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image->filename);
16216  return(MagickFalse);
16217}
16218
16219/*
16220%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16221%                                                                             %
16222%                                                                             %
16223%                                                                             %
16224+   R e m o t e D i s p l a y C o m m a n d                                   %
16225%                                                                             %
16226%                                                                             %
16227%                                                                             %
16228%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16229%
16230%  RemoteDisplayCommand() encourages a remote display program to display the
16231%  specified image filename.
16232%
16233%  The format of the RemoteDisplayCommand method is:
16234%
16235%      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16236%        const char *window,const char *filename,ExceptionInfo *exception)
16237%
16238%  A description of each parameter follows:
16239%
16240%    o image_info: the image info.
16241%
16242%    o window: Specifies the name or id of an X window.
16243%
16244%    o filename: the name of the image filename to display.
16245%
16246%    o exception: return any errors or warnings in this structure.
16247%
16248*/
16249MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16250  const char *window,const char *filename,ExceptionInfo *exception)
16251{
16252  assert(image_info != (const ImageInfo *) NULL);
16253  assert(image_info->signature == MagickSignature);
16254  assert(filename != (char *) NULL);
16255  (void) window;
16256  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16257  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16258    "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image_info->filename);
16259  return(MagickFalse);
16260}
16261#endif
16262