display.c revision ffc870d4c379b91b928adad29dfd8e22ec40adba
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%                                  Cristy                                     %
17%                                July 1992                                    %
18%                                                                             %
19%                                                                             %
20%  Copyright 1999-2015 ImageMagick Studio LLC, a non-profit organization      %
21%  dedicated to making software imaging solutions freely available.           %
22%                                                                             %
23%  You may not use this file except in compliance with the License.  You may  %
24%  obtain a copy of the License at                                            %
25%                                                                             %
26%    http://www.imagemagick.org/script/license.php                            %
27%                                                                             %
28%  Unless required by applicable law or agreed to in writing, software        %
29%  distributed under the License is distributed on an "AS IS" BASIS,          %
30%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31%  See the License for the specific language governing permissions and        %
32%  limitations under the License.                                             %
33%                                                                             %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37*/
38
39/*
40  Include declarations.
41*/
42#include "MagickCore/studio.h"
43#include "MagickCore/artifact.h"
44#include "MagickCore/attribute.h"
45#include "MagickCore/blob.h"
46#include "MagickCore/cache.h"
47#include "MagickCore/cache-private.h"
48#include "MagickCore/channel.h"
49#include "MagickCore/client.h"
50#include "MagickCore/color.h"
51#include "MagickCore/colorspace.h"
52#include "MagickCore/composite.h"
53#include "MagickCore/constitute.h"
54#include "MagickCore/decorate.h"
55#include "MagickCore/delegate.h"
56#include "MagickCore/display.h"
57#include "MagickCore/display-private.h"
58#include "MagickCore/distort.h"
59#include "MagickCore/draw.h"
60#include "MagickCore/effect.h"
61#include "MagickCore/enhance.h"
62#include "MagickCore/exception.h"
63#include "MagickCore/exception-private.h"
64#include "MagickCore/fx.h"
65#include "MagickCore/geometry.h"
66#include "MagickCore/image.h"
67#include "MagickCore/image-private.h"
68#include "MagickCore/list.h"
69#include "MagickCore/log.h"
70#include "MagickCore/magick.h"
71#include "MagickCore/memory_.h"
72#include "MagickCore/monitor.h"
73#include "MagickCore/monitor-private.h"
74#include "MagickCore/montage.h"
75#include "MagickCore/nt-base-private.h"
76#include "MagickCore/option.h"
77#include "MagickCore/paint.h"
78#include "MagickCore/pixel.h"
79#include "MagickCore/pixel-accessor.h"
80#include "MagickCore/PreRvIcccm.h"
81#include "MagickCore/property.h"
82#include "MagickCore/quantum.h"
83#include "MagickCore/quantum-private.h"
84#include "MagickCore/resize.h"
85#include "MagickCore/resource_.h"
86#include "MagickCore/shear.h"
87#include "MagickCore/segment.h"
88#include "MagickCore/statistic.h"
89#include "MagickCore/string_.h"
90#include "MagickCore/string-private.h"
91#include "MagickCore/transform.h"
92#include "MagickCore/threshold.h"
93#include "MagickCore/utility.h"
94#include "MagickCore/utility-private.h"
95#include "MagickCore/version.h"
96#include "MagickCore/widget.h"
97#include "MagickCore/widget-private.h"
98#include "MagickCore/xwindow.h"
99#include "MagickCore/xwindow-private.h"
100
101#if defined(MAGICKCORE_X11_DELEGATE)
102/*
103  Define declarations.
104*/
105#define MaxColors  MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
106
107/*
108  Constant declarations.
109*/
110static const unsigned char
111  HighlightBitmap[8] =
112  {
113    0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
114  },
115  OpaqueBitmap[8] =
116  {
117    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
118  },
119  ShadowBitmap[8] =
120  {
121    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
122  };
123
124static const char
125  *PageSizes[] =
126  {
127    "Letter",
128    "Tabloid",
129    "Ledger",
130    "Legal",
131    "Statement",
132    "Executive",
133    "A3",
134    "A4",
135    "A5",
136    "B4",
137    "B5",
138    "Folio",
139    "Quarto",
140    "10x14",
141    (char *) NULL
142  };
143
144/*
145  Help widget declarations.
146*/
147static const char
148  *ImageAnnotateHelp[] =
149  {
150    "In annotate mode, the Command widget has these options:",
151    "",
152    "    Font Name",
153    "      fixed",
154    "      variable",
155    "      5x8",
156    "      6x10",
157    "      7x13bold",
158    "      8x13bold",
159    "      9x15bold",
160    "      10x20",
161    "      12x24",
162    "      Browser...",
163    "    Font Color",
164    "      black",
165    "      blue",
166    "      cyan",
167    "      green",
168    "      gray",
169    "      red",
170    "      magenta",
171    "      yellow",
172    "      white",
173    "      transparent",
174    "      Browser...",
175    "    Font Color",
176    "      black",
177    "      blue",
178    "      cyan",
179    "      green",
180    "      gray",
181    "      red",
182    "      magenta",
183    "      yellow",
184    "      white",
185    "      transparent",
186    "      Browser...",
187    "    Rotate Text",
188    "      -90",
189    "      -45",
190    "      -30",
191    "      0",
192    "      30",
193    "      45",
194    "      90",
195    "      180",
196    "      Dialog...",
197    "    Help",
198    "    Dismiss",
199    "",
200    "Choose a font name from the Font Name sub-menu.  Additional",
201    "font names can be specified with the font browser.  You can",
202    "change the menu names by setting the X resources font1",
203    "through font9.",
204    "",
205    "Choose a font color from the Font Color sub-menu.",
206    "Additional font colors can be specified with the color",
207    "browser.  You can change the menu colors by setting the X",
208    "resources pen1 through pen9.",
209    "",
210    "If you select the color browser and press Grab, you can",
211    "choose the font color by moving the pointer to the desired",
212    "color on the screen and press any button.",
213    "",
214    "If you choose to rotate the text, choose Rotate Text from the",
215    "menu and select an angle.  Typically you will only want to",
216    "rotate one line of text at a time.  Depending on the angle you",
217    "choose, subsequent lines may end up overwriting each other.",
218    "",
219    "Choosing a font and its color is optional.  The default font",
220    "is fixed and the default color is black.  However, you must",
221    "choose a location to begin entering text and press button 1.",
222    "An underscore character will appear at the location of the",
223    "pointer.  The cursor changes to a pencil to indicate you are",
224    "in text mode.  To exit immediately, press Dismiss.",
225    "",
226    "In text mode, any key presses will display the character at",
227    "the location of the underscore and advance the underscore",
228    "cursor.  Enter your text and once completed press Apply to",
229    "finish your image annotation.  To correct errors press BACK",
230    "SPACE.  To delete an entire line of text, press DELETE.  Any",
231    "text that exceeds the boundaries of the image window is",
232    "automagically continued onto the next line.",
233    "",
234    "The actual color you request for the font is saved in the",
235    "image.  However, the color that appears in your image window",
236    "may be different.  For example, on a monochrome screen the",
237    "text will appear black or white even if you choose the color",
238    "red as the font color.  However, the image saved to a file",
239    "with -write is written with red lettering.  To assure the",
240    "correct color text in the final image, any PseudoClass image",
241    "is promoted to DirectClass (see miff(5)).  To force a",
242    "PseudoClass image to remain PseudoClass, use -colors.",
243    (char *) NULL,
244  },
245  *ImageChopHelp[] =
246  {
247    "In chop mode, the Command widget has these options:",
248    "",
249    "    Direction",
250    "      horizontal",
251    "      vertical",
252    "    Help",
253    "    Dismiss",
254    "",
255    "If the you choose the horizontal direction (this the",
256    "default), the area of the image between the two horizontal",
257    "endpoints of the chop line is removed.  Otherwise, the area",
258    "of the image between the two vertical endpoints of the chop",
259    "line is removed.",
260    "",
261    "Select a location within the image window to begin your chop,",
262    "press and hold any button.  Next, move the pointer to",
263    "another location in the image.  As you move a line will",
264    "connect the initial location and the pointer.  When you",
265    "release the button, the area within the image to chop is",
266    "determined by which direction you choose from the Command",
267    "widget.",
268    "",
269    "To cancel the image chopping, move the pointer back to the",
270    "starting point of the line and release the button.",
271    (char *) NULL,
272  },
273  *ImageColorEditHelp[] =
274  {
275    "In color edit mode, the Command widget has these options:",
276    "",
277    "    Method",
278    "      point",
279    "      replace",
280    "      floodfill",
281    "      filltoborder",
282    "      reset",
283    "    Pixel Color",
284    "      black",
285    "      blue",
286    "      cyan",
287    "      green",
288    "      gray",
289    "      red",
290    "      magenta",
291    "      yellow",
292    "      white",
293    "      Browser...",
294    "    Border Color",
295    "      black",
296    "      blue",
297    "      cyan",
298    "      green",
299    "      gray",
300    "      red",
301    "      magenta",
302    "      yellow",
303    "      white",
304    "      Browser...",
305    "    Fuzz",
306    "      0%",
307    "      2%",
308    "      5%",
309    "      10%",
310    "      15%",
311    "      Dialog...",
312    "    Undo",
313    "    Help",
314    "    Dismiss",
315    "",
316    "Choose a color editing method from the Method sub-menu",
317    "of the Command widget.  The point method recolors any pixel",
318    "selected with the pointer until the button is released.  The",
319    "replace method recolors any pixel that matches the color of",
320    "the pixel you select with a button press.  Floodfill recolors",
321    "any pixel that matches the color of the pixel you select with",
322    "a button press and is a neighbor.  Whereas filltoborder recolors",
323    "any neighbor pixel that is not the border color.  Finally reset",
324    "changes the entire image to the designated color.",
325    "",
326    "Next, choose a pixel color from the Pixel Color sub-menu.",
327    "Additional pixel colors can be specified with the color",
328    "browser.  You can change the menu colors by setting the X",
329    "resources pen1 through pen9.",
330    "",
331    "Now press button 1 to select a pixel within the image window",
332    "to change its color.  Additional pixels may be recolored as",
333    "prescribed by the method you choose.",
334    "",
335    "If the Magnify widget is mapped, it can be helpful in positioning",
336    "your pointer within the image (refer to button 2).",
337    "",
338    "The actual color you request for the pixels is saved in the",
339    "image.  However, the color that appears in your image window",
340    "may be different.  For example, on a monochrome screen the",
341    "pixel will appear black or white even if you choose the",
342    "color red as the pixel color.  However, the image saved to a",
343    "file with -write is written with red pixels.  To assure the",
344    "correct color text in the final image, any PseudoClass image",
345    "is promoted to DirectClass (see miff(5)).  To force a",
346    "PseudoClass image to remain PseudoClass, use -colors.",
347    (char *) NULL,
348  },
349  *ImageCompositeHelp[] =
350  {
351    "First a widget window is displayed requesting you to enter an",
352    "image name. Press Composite, Grab or type a file name.",
353    "Press Cancel if you choose not to create a composite image.",
354    "When you choose Grab, move the pointer to the desired window",
355    "and press any button.",
356    "",
357    "If the Composite image does not have any matte information,",
358    "you are informed and the file browser is displayed again.",
359    "Enter the name of a mask image.  The image is typically",
360    "grayscale and the same size as the composite image.  If the",
361    "image is not grayscale, it is converted to grayscale and the",
362    "resulting intensities are used as matte information.",
363    "",
364    "A small window appears showing the location of the cursor in",
365    "the image window. You are now in composite mode.  To exit",
366    "immediately, press Dismiss.  In composite mode, the Command",
367    "widget has these options:",
368    "",
369    "    Operators",
370    "      Over",
371    "      In",
372    "      Out",
373    "      Atop",
374    "      Xor",
375    "      Plus",
376    "      Minus",
377    "      Add",
378    "      Subtract",
379    "      Difference",
380    "      Multiply",
381    "      Bumpmap",
382    "      Copy",
383    "      CopyRed",
384    "      CopyGreen",
385    "      CopyBlue",
386    "      CopyOpacity",
387    "      Clear",
388    "    Dissolve",
389    "    Displace",
390    "    Help",
391    "    Dismiss",
392    "",
393    "Choose a composite operation from the Operators sub-menu of",
394    "the Command widget.  How each operator behaves is described",
395    "below.  Image window is the image currently displayed on",
396    "your X server and image is the image obtained with the File",
397    "Browser widget.",
398    "",
399    "Over     The result is the union of the two image shapes,",
400    "         with image obscuring image window in the region of",
401    "         overlap.",
402    "",
403    "In       The result is simply image cut by the shape of",
404    "         image window.  None of the image data of image",
405    "         window is in the result.",
406    "",
407    "Out      The resulting image is image with the shape of",
408    "         image window cut out.",
409    "",
410    "Atop     The result is the same shape as image image window,",
411    "         with image obscuring image window where the image",
412    "         shapes overlap.  Note this differs from over",
413    "         because the portion of image outside image window's",
414    "         shape does not appear in the result.",
415    "",
416    "Xor      The result is the image data from both image and",
417    "         image window that is outside the overlap region.",
418    "         The overlap region is blank.",
419    "",
420    "Plus     The result is just the sum of the image data.",
421    "         Output values are cropped to QuantumRange (no overflow).",
422    "",
423    "Minus    The result of image - image window, with underflow",
424    "         cropped to zero.",
425    "",
426    "Add      The result of image + image window, with overflow",
427    "         wrapping around (mod 256).",
428    "",
429    "Subtract The result of image - image window, with underflow",
430    "         wrapping around (mod 256).  The add and subtract",
431    "         operators can be used to perform reversible",
432    "         transformations.",
433    "",
434    "Difference",
435    "         The result of abs(image - image window).  This",
436    "         useful for comparing two very similar images.",
437    "",
438    "Multiply",
439    "         The result of image * image window.  This",
440    "         useful for the creation of drop-shadows.",
441    "",
442    "Bumpmap  The result of surface normals from image * image",
443    "         window.",
444    "",
445    "Copy     The resulting image is image window replaced with",
446    "         image.  Here the matte information is ignored.",
447    "",
448    "CopyRed  The red layer of the image window is replace with",
449    "         the red layer of the image.  The other layers are",
450    "         untouched.",
451    "",
452    "CopyGreen",
453    "         The green layer of the image window is replace with",
454    "         the green layer of the image.  The other layers are",
455    "         untouched.",
456    "",
457    "CopyBlue The blue layer of the image window is replace with",
458    "         the blue layer of the image.  The other layers are",
459    "         untouched.",
460    "",
461    "CopyOpacity",
462    "         The matte layer of the image window is replace with",
463    "         the matte layer of the image.  The other layers are",
464    "         untouched.",
465    "",
466    "The image compositor requires a matte, or alpha channel in",
467    "the image for some operations.  This extra channel usually",
468    "defines a mask which represents a sort of a cookie-cutter",
469    "for the image.  This the case when matte is opaque (full",
470    "coverage) for pixels inside the shape, zero outside, and",
471    "between 0 and QuantumRange on the boundary.  If image does not",
472    "have a matte channel, it is initialized with 0 for any pixel",
473    "matching in color to pixel location (0,0), otherwise QuantumRange.",
474    "",
475    "If you choose Dissolve, the composite operator becomes Over.  The",
476    "image matte channel percent transparency is initialized to factor.",
477    "The image window is initialized to (100-factor). Where factor is the",
478    "value you specify in the Dialog widget.",
479    "",
480    "Displace shifts the image pixels as defined by a displacement",
481    "map.  With this option, image is used as a displacement map.",
482    "Black, within the displacement map, is a maximum positive",
483    "displacement.  White is a maximum negative displacement and",
484    "middle gray is neutral.  The displacement is scaled to determine",
485    "the pixel shift.  By default, the displacement applies in both the",
486    "horizontal and vertical directions.  However, if you specify a mask,",
487    "image is the horizontal X displacement and mask the vertical Y",
488    "displacement.",
489    "",
490    "Note that matte information for image window is not retained",
491    "for colormapped X server visuals (e.g. StaticColor,",
492    "StaticColor, GrayScale, PseudoColor).  Correct compositing",
493    "behavior may require a TrueColor or DirectColor visual or a",
494    "Standard Colormap.",
495    "",
496    "Choosing a composite operator is optional.  The default",
497    "operator is replace.  However, you must choose a location to",
498    "composite your image and press button 1.  Press and hold the",
499    "button before releasing and an outline of the image will",
500    "appear to help you identify your location.",
501    "",
502    "The actual colors of the composite image is saved.  However,",
503    "the color that appears in image window may be different.",
504    "For example, on a monochrome screen image window will appear",
505    "black or white even though your composited image may have",
506    "many colors.  If the image is saved to a file it is written",
507    "with the correct colors.  To assure the correct colors are",
508    "saved in the final image, any PseudoClass image is promoted",
509    "to DirectClass (see miff(5)).  To force a PseudoClass image",
510    "to remain PseudoClass, use -colors.",
511    (char *) NULL,
512  },
513  *ImageCutHelp[] =
514  {
515    "In cut mode, the Command widget has these options:",
516    "",
517    "    Help",
518    "    Dismiss",
519    "",
520    "To define a cut region, press button 1 and drag.  The",
521    "cut region is defined by a highlighted rectangle that",
522    "expands or contracts as it follows the pointer.  Once you",
523    "are satisfied with the cut region, release the button.",
524    "You are now in rectify mode.  In rectify mode, the Command",
525    "widget has these options:",
526    "",
527    "    Cut",
528    "    Help",
529    "    Dismiss",
530    "",
531    "You can make adjustments by moving the pointer to one of the",
532    "cut rectangle corners, pressing a button, and dragging.",
533    "Finally, press Cut to commit your copy region.  To",
534    "exit without cutting the image, press Dismiss.",
535    (char *) NULL,
536  },
537  *ImageCopyHelp[] =
538  {
539    "In copy mode, the Command widget has these options:",
540    "",
541    "    Help",
542    "    Dismiss",
543    "",
544    "To define a copy region, press button 1 and drag.  The",
545    "copy region is defined by a highlighted rectangle that",
546    "expands or contracts as it follows the pointer.  Once you",
547    "are satisfied with the copy region, release the button.",
548    "You are now in rectify mode.  In rectify mode, the Command",
549    "widget has these options:",
550    "",
551    "    Copy",
552    "    Help",
553    "    Dismiss",
554    "",
555    "You can make adjustments by moving the pointer to one of the",
556    "copy rectangle corners, pressing a button, and dragging.",
557    "Finally, press Copy to commit your copy region.  To",
558    "exit without copying the image, press Dismiss.",
559    (char *) NULL,
560  },
561  *ImageCropHelp[] =
562  {
563    "In crop mode, the Command widget has these options:",
564    "",
565    "    Help",
566    "    Dismiss",
567    "",
568    "To define a cropping region, press button 1 and drag.  The",
569    "cropping region is defined by a highlighted rectangle that",
570    "expands or contracts as it follows the pointer.  Once you",
571    "are satisfied with the cropping region, release the button.",
572    "You are now in rectify mode.  In rectify mode, the Command",
573    "widget has these options:",
574    "",
575    "    Crop",
576    "    Help",
577    "    Dismiss",
578    "",
579    "You can make adjustments by moving the pointer to one of the",
580    "cropping rectangle corners, pressing a button, and dragging.",
581    "Finally, press Crop to commit your cropping region.  To",
582    "exit without cropping the image, press Dismiss.",
583    (char *) NULL,
584  },
585  *ImageDrawHelp[] =
586  {
587    "The cursor changes to a crosshair to indicate you are in",
588    "draw mode.  To exit immediately, press Dismiss.  In draw mode,",
589    "the Command widget has these options:",
590    "",
591    "    Element",
592    "      point",
593    "      line",
594    "      rectangle",
595    "      fill rectangle",
596    "      circle",
597    "      fill circle",
598    "      ellipse",
599    "      fill ellipse",
600    "      polygon",
601    "      fill polygon",
602    "    Color",
603    "      black",
604    "      blue",
605    "      cyan",
606    "      green",
607    "      gray",
608    "      red",
609    "      magenta",
610    "      yellow",
611    "      white",
612    "      transparent",
613    "      Browser...",
614    "    Stipple",
615    "      Brick",
616    "      Diagonal",
617    "      Scales",
618    "      Vertical",
619    "      Wavy",
620    "      Translucent",
621    "      Opaque",
622    "      Open...",
623    "    Width",
624    "      1",
625    "      2",
626    "      4",
627    "      8",
628    "      16",
629    "      Dialog...",
630    "    Undo",
631    "    Help",
632    "    Dismiss",
633    "",
634    "Choose a drawing primitive from the Element sub-menu.",
635    "",
636    "Choose a color from the Color sub-menu.  Additional",
637    "colors can be specified with the color browser.",
638    "",
639    "If you choose the color browser and press Grab, you can",
640    "select the color by moving the pointer to the desired",
641    "color on the screen and press any button.  The transparent",
642    "color updates the image matte channel and is useful for",
643    "image compositing.",
644    "",
645    "Choose a stipple, if appropriate, from the Stipple sub-menu.",
646    "Additional stipples can be specified with the file browser.",
647    "Stipples obtained from the file browser must be on disk in the",
648    "X11 bitmap format.",
649    "",
650    "Choose a width, if appropriate, from the Width sub-menu.  To",
651    "choose a specific width select the Dialog widget.",
652    "",
653    "Choose a point in the Image window and press button 1 and",
654    "hold.  Next, move the pointer to another location in the",
655    "image.  As you move, a line connects the initial location and",
656    "the pointer.  When you release the button, the image is",
657    "updated with the primitive you just drew.  For polygons, the",
658    "image is updated when you press and release the button without",
659    "moving the pointer.",
660    "",
661    "To cancel image drawing, move the pointer back to the",
662    "starting point of the line and release the button.",
663    (char *) NULL,
664  },
665  *DisplayHelp[] =
666  {
667    "BUTTONS",
668    "  The effects of each button press is described below.  Three",
669    "  buttons are required.  If you have a two button mouse,",
670    "  button 1 and 3 are returned.  Press ALT and button 3 to",
671    "  simulate button 2.",
672    "",
673    "  1    Press this button to map or unmap the Command widget.",
674    "",
675    "  2    Press and drag to define a region of the image to",
676    "       magnify.",
677    "",
678    "  3    Press and drag to choose from a select set of commands.",
679    "       This button behaves differently if the image being",
680    "       displayed is a visual image directory.  Here, choose a",
681    "       particular tile of the directory and press this button and",
682    "       drag to select a command from a pop-up menu.  Choose from",
683    "       these menu items:",
684    "",
685    "           Open",
686    "           Next",
687    "           Former",
688    "           Delete",
689    "           Update",
690    "",
691    "       If you choose Open, the image represented by the tile is",
692    "       displayed.  To return to the visual image directory, choose",
693    "       Next from the Command widget.  Next and Former moves to the",
694    "       next or former image respectively.  Choose Delete to delete",
695    "       a particular image tile.  Finally, choose Update to",
696    "       synchronize all the image tiles with their respective",
697    "       images.",
698    "",
699    "COMMAND WIDGET",
700    "  The Command widget lists a number of sub-menus and commands.",
701    "  They are",
702    "",
703    "      File",
704    "        Open...",
705    "        Next",
706    "        Former",
707    "        Select...",
708    "        Save...",
709    "        Print...",
710    "        Delete...",
711    "        New...",
712    "        Visual Directory...",
713    "        Quit",
714    "      Edit",
715    "        Undo",
716    "        Redo",
717    "        Cut",
718    "        Copy",
719    "        Paste",
720    "      View",
721    "        Half Size",
722    "        Original Size",
723    "        Double Size",
724    "        Resize...",
725    "        Apply",
726    "        Refresh",
727    "        Restore",
728    "      Transform",
729    "        Crop",
730    "        Chop",
731    "        Flop",
732    "        Flip",
733    "        Rotate Right",
734    "        Rotate Left",
735    "        Rotate...",
736    "        Shear...",
737    "        Roll...",
738    "        Trim Edges",
739    "      Enhance",
740    "        Brightness...",
741    "        Saturation...",
742    "        Hue...",
743    "        Gamma...",
744    "        Sharpen...",
745    "        Dull",
746    "        Contrast Stretch...",
747    "        Sigmoidal Contrast...",
748    "        Normalize",
749    "        Equalize",
750    "        Negate",
751    "        Grayscale",
752    "        Map...",
753    "        Quantize...",
754    "      Effects",
755    "        Despeckle",
756    "        Emboss",
757    "        Reduce Noise",
758    "        Add Noise",
759    "        Sharpen...",
760    "        Blur...",
761    "        Threshold...",
762    "        Edge Detect...",
763    "        Spread...",
764    "        Shade...",
765    "        Painting...",
766    "        Segment...",
767    "      F/X",
768    "        Solarize...",
769    "        Sepia Tone...",
770    "        Swirl...",
771    "        Implode...",
772    "        Vignette...",
773    "        Wave...",
774    "        Oil Painting...",
775    "        Charcoal Drawing...",
776    "      Image Edit",
777    "        Annotate...",
778    "        Draw...",
779    "        Color...",
780    "        Matte...",
781    "        Composite...",
782    "        Add Border...",
783    "        Add Frame...",
784    "        Comment...",
785    "        Launch...",
786    "        Region of Interest...",
787    "      Miscellany",
788    "        Image Info",
789    "        Zoom Image",
790    "        Show Preview...",
791    "        Show Histogram",
792    "        Show Matte",
793    "        Background...",
794    "        Slide Show",
795    "        Preferences...",
796    "      Help",
797    "        Overview",
798    "        Browse Documentation",
799    "        About Display",
800    "",
801    "  Menu items with a indented triangle have a sub-menu.  They",
802    "  are represented above as the indented items.  To access a",
803    "  sub-menu item, move the pointer to the appropriate menu and",
804    "  press a button and drag.  When you find the desired sub-menu",
805    "  item, release the button and the command is executed.  Move",
806    "  the pointer away from the sub-menu if you decide not to",
807    "  execute a particular command.",
808    "",
809    "KEYBOARD ACCELERATORS",
810    "  Accelerators are one or two key presses that effect a",
811    "  particular command.  The keyboard accelerators that",
812    "  display(1) understands is:",
813    "",
814    "  Ctl+O     Press to open an image from a file.",
815    "",
816    "  space     Press to display the next image.",
817    "",
818    "            If the image is a multi-paged document such as a Postscript",
819    "            document, you can skip ahead several pages by preceding",
820    "            this command with a number.  For example to display the",
821    "            third page beyond the current page, press 3<space>.",
822    "",
823    "  backspace Press to display the former image.",
824    "",
825    "            If the image is a multi-paged document such as a Postscript",
826    "            document, you can skip behind several pages by preceding",
827    "            this command with a number.  For example to display the",
828    "            third page preceding the current page, press 3<backspace>.",
829    "",
830    "  Ctl+S     Press to write the image to a file.",
831    "",
832    "  Ctl+P     Press to print the image to a Postscript printer.",
833    "",
834    "  Ctl+D     Press to delete an image file.",
835    "",
836    "  Ctl+N     Press to create a blank canvas.",
837    "",
838    "  Ctl+Q     Press to discard all images and exit program.",
839    "",
840    "  Ctl+Z     Press to undo last image transformation.",
841    "",
842    "  Ctl+R     Press to redo last image transformation.",
843    "",
844    "  Ctl+X     Press to cut a region of the image.",
845    "",
846    "  Ctl+C     Press to copy a region of the image.",
847    "",
848    "  Ctl+V     Press to paste a region to the image.",
849    "",
850    "  <         Press to half the image size.",
851    "",
852    "  -         Press to return to the original image size.",
853    "",
854    "  >         Press to double the image size.",
855    "",
856    "  %         Press to resize the image to a width and height you",
857    "            specify.",
858    "",
859    "Cmd-A       Press to make any image transformations permanent."
860    "",
861    "            By default, any image size transformations are applied",
862    "            to the original image to create the image displayed on",
863    "            the X server.  However, the transformations are not",
864    "            permanent (i.e. the original image does not change",
865    "            size only the X image does).  For example, if you",
866    "            press > the X image will appear to double in size,",
867    "            but the original image will in fact remain the same size.",
868    "            To force the original image to double in size, press >",
869    "            followed by Cmd-A.",
870    "",
871    "  @         Press to refresh the image window.",
872    "",
873    "  C         Press to cut out a rectangular region of the image.",
874    "",
875    "  [         Press to chop the image.",
876    "",
877    "  H         Press to flop image in the horizontal direction.",
878    "",
879    "  V         Press to flip image in the vertical direction.",
880    "",
881    "  /         Press to rotate the image 90 degrees clockwise.",
882    "",
883    " \\         Press to rotate the image 90 degrees counter-clockwise.",
884    "",
885    "  *         Press to rotate the image the number of degrees you",
886    "            specify.",
887    "",
888    "  S         Press to shear the image the number of degrees you",
889    "            specify.",
890    "",
891    "  R         Press to roll the image.",
892    "",
893    "  T         Press to trim the image edges.",
894    "",
895    "  Shft-H    Press to vary the image hue.",
896    "",
897    "  Shft-S    Press to vary the color saturation.",
898    "",
899    "  Shft-L    Press to vary the color brightness.",
900    "",
901    "  Shft-G    Press to gamma correct the image.",
902    "",
903    "  Shft-C    Press to sharpen the image contrast.",
904    "",
905    "  Shft-Z    Press to dull the image contrast.",
906    "",
907    "  =         Press to perform histogram equalization on the image.",
908    "",
909    "  Shft-N    Press to perform histogram normalization on the image.",
910    "",
911    "  Shft-~    Press to negate the colors of the image.",
912    "",
913    "  .         Press to convert the image colors to gray.",
914    "",
915    "  Shft-#    Press to set the maximum number of unique colors in the",
916    "            image.",
917    "",
918    "  F2        Press to reduce the speckles in an image.",
919    "",
920    "  F3        Press to eliminate peak noise from an image.",
921    "",
922    "  F4        Press to add noise to an image.",
923    "",
924    "  F5        Press to sharpen an image.",
925    "",
926    "  F6        Press to delete an image file.",
927    "",
928    "  F7        Press to threshold the image.",
929    "",
930    "  F8        Press to detect edges within an image.",
931    "",
932    "  F9        Press to emboss an image.",
933    "",
934    "  F10       Press to displace pixels by a random amount.",
935    "",
936    "  F11       Press to negate all pixels above the threshold level.",
937    "",
938    "  F12       Press to shade the image using a distant light source.",
939    "",
940    "  F13       Press to lighten or darken image edges to create a 3-D effect.",
941    "",
942    "  F14       Press to segment the image by color.",
943    "",
944    "  Meta-S    Press to swirl image pixels about the center.",
945    "",
946    "  Meta-I    Press to implode image pixels about the center.",
947    "",
948    "  Meta-W    Press to alter an image along a sine wave.",
949    "",
950    "  Meta-P    Press to simulate an oil painting.",
951    "",
952    "  Meta-C    Press to simulate a charcoal drawing.",
953    "",
954    "  Alt-A     Press to annotate the image with text.",
955    "",
956    "  Alt-D     Press to draw on an image.",
957    "",
958    "  Alt-P     Press to edit an image pixel color.",
959    "",
960    "  Alt-M     Press to edit the image matte information.",
961    "",
962    "  Alt-V     Press to composite the image with another.",
963    "",
964    "  Alt-B     Press to add a border to the image.",
965    "",
966    "  Alt-F     Press to add an ornamental border to the image.",
967    "",
968    "  Alt-Shft-!",
969    "            Press to add an image comment.",
970    "",
971    "  Ctl-A     Press to apply image processing techniques to a region",
972    "            of interest.",
973    "",
974    "  Shft-?    Press to display information about the image.",
975    "",
976    "  Shft-+    Press to map the zoom image window.",
977    "",
978    "  Shft-P    Press to preview an image enhancement, effect, or f/x.",
979    "",
980    "  F1        Press to display helpful information about display(1).",
981    "",
982    "  Find      Press to browse documentation about ImageMagick.",
983    "",
984    "  1-9       Press to change the level of magnification.",
985    "",
986    "  Use the arrow keys to move the image one pixel up, down,",
987    "  left, or right within the magnify window.  Be sure to first",
988    "  map the magnify window by pressing button 2.",
989    "",
990    "  Press ALT and one of the arrow keys to trim off one pixel",
991    "  from any side of the image.",
992    (char *) NULL,
993  },
994  *ImageMatteEditHelp[] =
995  {
996    "Matte information within an image is useful for some",
997    "operations such as image compositing (See IMAGE",
998    "COMPOSITING).  This extra channel usually defines a mask",
999    "which represents a sort of a cookie-cutter for the image.",
1000    "This the case when matte is opaque (full coverage) for",
1001    "pixels inside the shape, zero outside, and between 0 and",
1002    "QuantumRange on the boundary.",
1003    "",
1004    "A small window appears showing the location of the cursor in",
1005    "the image window. You are now in matte edit mode.  To exit",
1006    "immediately, press Dismiss.  In matte edit mode, the Command",
1007    "widget has these options:",
1008    "",
1009    "    Method",
1010    "      point",
1011    "      replace",
1012    "      floodfill",
1013    "      filltoborder",
1014    "      reset",
1015    "    Border Color",
1016    "      black",
1017    "      blue",
1018    "      cyan",
1019    "      green",
1020    "      gray",
1021    "      red",
1022    "      magenta",
1023    "      yellow",
1024    "      white",
1025    "      Browser...",
1026    "    Fuzz",
1027    "      0%",
1028    "      2%",
1029    "      5%",
1030    "      10%",
1031    "      15%",
1032    "      Dialog...",
1033    "    Matte",
1034    "      Opaque",
1035    "      Transparent",
1036    "      Dialog...",
1037    "    Undo",
1038    "    Help",
1039    "    Dismiss",
1040    "",
1041    "Choose a matte editing method from the Method sub-menu of",
1042    "the Command widget.  The point method changes the matte value",
1043    "of any pixel selected with the pointer until the button is",
1044    "is released.  The replace method changes the matte value of",
1045    "any pixel that matches the color of the pixel you select with",
1046    "a button press.  Floodfill changes the matte value of any pixel",
1047    "that matches the color of the pixel you select with a button",
1048    "press and is a neighbor.  Whereas filltoborder changes the matte",
1049    "value any neighbor pixel that is not the border color.  Finally",
1050    "reset changes the entire image to the designated matte value.",
1051    "",
1052    "Choose Matte Value and pick Opaque or Transarent.  For other values",
1053    "select the Dialog entry.  Here a dialog appears requesting a matte",
1054    "value.  The value you select is assigned as the opacity value of the",
1055    "selected pixel or pixels.",
1056    "",
1057    "Now, press any button to select a pixel within the image",
1058    "window to change its matte value.",
1059    "",
1060    "If the Magnify widget is mapped, it can be helpful in positioning",
1061    "your pointer within the image (refer to button 2).",
1062    "",
1063    "Matte information is only valid in a DirectClass image.",
1064    "Therefore, any PseudoClass image is promoted to DirectClass",
1065    "(see miff(5)).  Note that matte information for PseudoClass",
1066    "is not retained for colormapped X server visuals (e.g.",
1067    "StaticColor, StaticColor, GrayScale, PseudoColor) unless you",
1068    "immediately save your image to a file (refer to Write).",
1069    "Correct matte editing behavior may require a TrueColor or",
1070    "DirectColor visual or a Standard Colormap.",
1071    (char *) NULL,
1072  },
1073  *ImagePanHelp[] =
1074  {
1075    "When an image exceeds the width or height of the X server",
1076    "screen, display maps a small panning icon.  The rectangle",
1077    "within the panning icon shows the area that is currently",
1078    "displayed in the image window.  To pan about the image,",
1079    "press any button and drag the pointer within the panning",
1080    "icon.  The pan rectangle moves with the pointer and the",
1081    "image window is updated to reflect the location of the",
1082    "rectangle within the panning icon.  When you have selected",
1083    "the area of the image you wish to view, release the button.",
1084    "",
1085    "Use the arrow keys to pan the image one pixel up, down,",
1086    "left, or right within the image window.",
1087    "",
1088    "The panning icon is withdrawn if the image becomes smaller",
1089    "than the dimensions of the X server screen.",
1090    (char *) NULL,
1091  },
1092  *ImagePasteHelp[] =
1093  {
1094    "A small window appears showing the location of the cursor in",
1095    "the image window. You are now in paste mode.  To exit",
1096    "immediately, press Dismiss.  In paste mode, the Command",
1097    "widget has these options:",
1098    "",
1099    "    Operators",
1100    "      over",
1101    "      in",
1102    "      out",
1103    "      atop",
1104    "      xor",
1105    "      plus",
1106    "      minus",
1107    "      add",
1108    "      subtract",
1109    "      difference",
1110    "      replace",
1111    "    Help",
1112    "    Dismiss",
1113    "",
1114    "Choose a composite operation from the Operators sub-menu of",
1115    "the Command widget.  How each operator behaves is described",
1116    "below.  Image window is the image currently displayed on",
1117    "your X server and image is the image obtained with the File",
1118    "Browser widget.",
1119    "",
1120    "Over     The result is the union of the two image shapes,",
1121    "         with image obscuring image window in the region of",
1122    "         overlap.",
1123    "",
1124    "In       The result is simply image cut by the shape of",
1125    "         image window.  None of the image data of image",
1126    "         window is in the result.",
1127    "",
1128    "Out      The resulting image is image with the shape of",
1129    "         image window cut out.",
1130    "",
1131    "Atop     The result is the same shape as image image window,",
1132    "         with image obscuring image window where the image",
1133    "         shapes overlap.  Note this differs from over",
1134    "         because the portion of image outside image window's",
1135    "         shape does not appear in the result.",
1136    "",
1137    "Xor      The result is the image data from both image and",
1138    "         image window that is outside the overlap region.",
1139    "         The overlap region is blank.",
1140    "",
1141    "Plus     The result is just the sum of the image data.",
1142    "         Output values are cropped to QuantumRange (no overflow).",
1143    "         This operation is independent of the matte",
1144    "         channels.",
1145    "",
1146    "Minus    The result of image - image window, with underflow",
1147    "         cropped to zero.",
1148    "",
1149    "Add      The result of image + image window, with overflow",
1150    "         wrapping around (mod 256).",
1151    "",
1152    "Subtract The result of image - image window, with underflow",
1153    "         wrapping around (mod 256).  The add and subtract",
1154    "         operators can be used to perform reversible",
1155    "         transformations.",
1156    "",
1157    "Difference",
1158    "         The result of abs(image - image window).  This",
1159    "         useful for comparing two very similar images.",
1160    "",
1161    "Copy     The resulting image is image window replaced with",
1162    "         image.  Here the matte information is ignored.",
1163    "",
1164    "CopyRed  The red layer of the image window is replace with",
1165    "         the red layer of the image.  The other layers are",
1166    "         untouched.",
1167    "",
1168    "CopyGreen",
1169    "         The green layer of the image window is replace with",
1170    "         the green layer of the image.  The other layers are",
1171    "         untouched.",
1172    "",
1173    "CopyBlue The blue layer of the image window is replace with",
1174    "         the blue layer of the image.  The other layers are",
1175    "         untouched.",
1176    "",
1177    "CopyOpacity",
1178    "         The matte layer of the image window is replace with",
1179    "         the matte layer of the image.  The other layers are",
1180    "         untouched.",
1181    "",
1182    "The image compositor requires a matte, or alpha channel in",
1183    "the image for some operations.  This extra channel usually",
1184    "defines a mask which represents a sort of a cookie-cutter",
1185    "for the image.  This the case when matte is opaque (full",
1186    "coverage) for pixels inside the shape, zero outside, and",
1187    "between 0 and QuantumRange on the boundary.  If image does not",
1188    "have a matte channel, it is initialized with 0 for any pixel",
1189    "matching in color to pixel location (0,0), otherwise QuantumRange.",
1190    "",
1191    "Note that matte information for image window is not retained",
1192    "for colormapped X server visuals (e.g. StaticColor,",
1193    "StaticColor, GrayScale, PseudoColor).  Correct compositing",
1194    "behavior may require a TrueColor or DirectColor visual or a",
1195    "Standard Colormap.",
1196    "",
1197    "Choosing a composite operator is optional.  The default",
1198    "operator is replace.  However, you must choose a location to",
1199    "paste your image and press button 1.  Press and hold the",
1200    "button before releasing and an outline of the image will",
1201    "appear to help you identify your location.",
1202    "",
1203    "The actual colors of the pasted image is saved.  However,",
1204    "the color that appears in image window may be different.",
1205    "For example, on a monochrome screen image window will appear",
1206    "black or white even though your pasted image may have",
1207    "many colors.  If the image is saved to a file it is written",
1208    "with the correct colors.  To assure the correct colors are",
1209    "saved in the final image, any PseudoClass image is promoted",
1210    "to DirectClass (see miff(5)).  To force a PseudoClass image",
1211    "to remain PseudoClass, use -colors.",
1212    (char *) NULL,
1213  },
1214  *ImageROIHelp[] =
1215  {
1216    "In region of interest mode, the Command widget has these",
1217    "options:",
1218    "",
1219    "    Help",
1220    "    Dismiss",
1221    "",
1222    "To define a region of interest, press button 1 and drag.",
1223    "The region of interest is defined by a highlighted rectangle",
1224    "that expands or contracts as it follows the pointer.  Once",
1225    "you are satisfied with the region of interest, release the",
1226    "button.  You are now in apply mode.  In apply mode the",
1227    "Command widget has these options:",
1228    "",
1229    "      File",
1230    "        Save...",
1231    "        Print...",
1232    "      Edit",
1233    "        Undo",
1234    "        Redo",
1235    "      Transform",
1236    "        Flop",
1237    "        Flip",
1238    "        Rotate Right",
1239    "        Rotate Left",
1240    "      Enhance",
1241    "        Hue...",
1242    "        Saturation...",
1243    "        Brightness...",
1244    "        Gamma...",
1245    "        Spiff",
1246    "        Dull",
1247    "        Contrast Stretch",
1248    "        Sigmoidal Contrast...",
1249    "        Normalize",
1250    "        Equalize",
1251    "        Negate",
1252    "        Grayscale",
1253    "        Map...",
1254    "        Quantize...",
1255    "      Effects",
1256    "        Despeckle",
1257    "        Emboss",
1258    "        Reduce Noise",
1259    "        Sharpen...",
1260    "        Blur...",
1261    "        Threshold...",
1262    "        Edge Detect...",
1263    "        Spread...",
1264    "        Shade...",
1265    "        Raise...",
1266    "        Segment...",
1267    "      F/X",
1268    "        Solarize...",
1269    "        Sepia Tone...",
1270    "        Swirl...",
1271    "        Implode...",
1272    "        Vignette...",
1273    "        Wave...",
1274    "        Oil Painting...",
1275    "        Charcoal Drawing...",
1276    "      Miscellany",
1277    "        Image Info",
1278    "        Zoom Image",
1279    "        Show Preview...",
1280    "        Show Histogram",
1281    "        Show Matte",
1282    "      Help",
1283    "      Dismiss",
1284    "",
1285    "You can make adjustments to the region of interest by moving",
1286    "the pointer to one of the rectangle corners, pressing a",
1287    "button, and dragging.  Finally, choose an image processing",
1288    "technique from the Command widget.  You can choose more than",
1289    "one image processing technique to apply to an area.",
1290    "Alternatively, you can move the region of interest before",
1291    "applying another image processing technique.  To exit, press",
1292    "Dismiss.",
1293    (char *) NULL,
1294  },
1295  *ImageRotateHelp[] =
1296  {
1297    "In rotate mode, the Command widget has these options:",
1298    "",
1299    "    Pixel Color",
1300    "      black",
1301    "      blue",
1302    "      cyan",
1303    "      green",
1304    "      gray",
1305    "      red",
1306    "      magenta",
1307    "      yellow",
1308    "      white",
1309    "      Browser...",
1310    "    Direction",
1311    "      horizontal",
1312    "      vertical",
1313    "    Help",
1314    "    Dismiss",
1315    "",
1316    "Choose a background color from the Pixel Color sub-menu.",
1317    "Additional background colors can be specified with the color",
1318    "browser.  You can change the menu colors by setting the X",
1319    "resources pen1 through pen9.",
1320    "",
1321    "If you choose the color browser and press Grab, you can",
1322    "select the background color by moving the pointer to the",
1323    "desired color on the screen and press any button.",
1324    "",
1325    "Choose a point in the image window and press this button and",
1326    "hold.  Next, move the pointer to another location in the",
1327    "image.  As you move a line connects the initial location and",
1328    "the pointer.  When you release the button, the degree of",
1329    "image rotation is determined by the slope of the line you",
1330    "just drew.  The slope is relative to the direction you",
1331    "choose from the Direction sub-menu of the Command widget.",
1332    "",
1333    "To cancel the image rotation, move the pointer back to the",
1334    "starting point of the line and release the button.",
1335    (char *) NULL,
1336  };
1337
1338/*
1339  Enumeration declarations.
1340*/
1341typedef enum
1342{
1343  CopyMode,
1344  CropMode,
1345  CutMode
1346} ClipboardMode;
1347
1348typedef enum
1349{
1350  OpenCommand,
1351  NextCommand,
1352  FormerCommand,
1353  SelectCommand,
1354  SaveCommand,
1355  PrintCommand,
1356  DeleteCommand,
1357  NewCommand,
1358  VisualDirectoryCommand,
1359  QuitCommand,
1360  UndoCommand,
1361  RedoCommand,
1362  CutCommand,
1363  CopyCommand,
1364  PasteCommand,
1365  HalfSizeCommand,
1366  OriginalSizeCommand,
1367  DoubleSizeCommand,
1368  ResizeCommand,
1369  ApplyCommand,
1370  RefreshCommand,
1371  RestoreCommand,
1372  CropCommand,
1373  ChopCommand,
1374  FlopCommand,
1375  FlipCommand,
1376  RotateRightCommand,
1377  RotateLeftCommand,
1378  RotateCommand,
1379  ShearCommand,
1380  RollCommand,
1381  TrimCommand,
1382  HueCommand,
1383  SaturationCommand,
1384  BrightnessCommand,
1385  GammaCommand,
1386  SpiffCommand,
1387  DullCommand,
1388  ContrastStretchCommand,
1389  SigmoidalContrastCommand,
1390  NormalizeCommand,
1391  EqualizeCommand,
1392  NegateCommand,
1393  GrayscaleCommand,
1394  MapCommand,
1395  QuantizeCommand,
1396  DespeckleCommand,
1397  EmbossCommand,
1398  ReduceNoiseCommand,
1399  AddNoiseCommand,
1400  SharpenCommand,
1401  BlurCommand,
1402  ThresholdCommand,
1403  EdgeDetectCommand,
1404  SpreadCommand,
1405  ShadeCommand,
1406  RaiseCommand,
1407  SegmentCommand,
1408  SolarizeCommand,
1409  SepiaToneCommand,
1410  SwirlCommand,
1411  ImplodeCommand,
1412  VignetteCommand,
1413  WaveCommand,
1414  OilPaintCommand,
1415  CharcoalDrawCommand,
1416  AnnotateCommand,
1417  DrawCommand,
1418  ColorCommand,
1419  MatteCommand,
1420  CompositeCommand,
1421  AddBorderCommand,
1422  AddFrameCommand,
1423  CommentCommand,
1424  LaunchCommand,
1425  RegionofInterestCommand,
1426  ROIHelpCommand,
1427  ROIDismissCommand,
1428  InfoCommand,
1429  ZoomCommand,
1430  ShowPreviewCommand,
1431  ShowHistogramCommand,
1432  ShowMatteCommand,
1433  BackgroundCommand,
1434  SlideShowCommand,
1435  PreferencesCommand,
1436  HelpCommand,
1437  BrowseDocumentationCommand,
1438  VersionCommand,
1439  SaveToUndoBufferCommand,
1440  FreeBuffersCommand,
1441  NullCommand
1442} CommandType;
1443
1444typedef enum
1445{
1446  AnnotateNameCommand,
1447  AnnotateFontColorCommand,
1448  AnnotateBackgroundColorCommand,
1449  AnnotateRotateCommand,
1450  AnnotateHelpCommand,
1451  AnnotateDismissCommand,
1452  TextHelpCommand,
1453  TextApplyCommand,
1454  ChopDirectionCommand,
1455  ChopHelpCommand,
1456  ChopDismissCommand,
1457  HorizontalChopCommand,
1458  VerticalChopCommand,
1459  ColorEditMethodCommand,
1460  ColorEditColorCommand,
1461  ColorEditBorderCommand,
1462  ColorEditFuzzCommand,
1463  ColorEditUndoCommand,
1464  ColorEditHelpCommand,
1465  ColorEditDismissCommand,
1466  CompositeOperatorsCommand,
1467  CompositeDissolveCommand,
1468  CompositeDisplaceCommand,
1469  CompositeHelpCommand,
1470  CompositeDismissCommand,
1471  CropHelpCommand,
1472  CropDismissCommand,
1473  RectifyCopyCommand,
1474  RectifyHelpCommand,
1475  RectifyDismissCommand,
1476  DrawElementCommand,
1477  DrawColorCommand,
1478  DrawStippleCommand,
1479  DrawWidthCommand,
1480  DrawUndoCommand,
1481  DrawHelpCommand,
1482  DrawDismissCommand,
1483  MatteEditMethod,
1484  MatteEditBorderCommand,
1485  MatteEditFuzzCommand,
1486  MatteEditValueCommand,
1487  MatteEditUndoCommand,
1488  MatteEditHelpCommand,
1489  MatteEditDismissCommand,
1490  PasteOperatorsCommand,
1491  PasteHelpCommand,
1492  PasteDismissCommand,
1493  RotateColorCommand,
1494  RotateDirectionCommand,
1495  RotateCropCommand,
1496  RotateSharpenCommand,
1497  RotateHelpCommand,
1498  RotateDismissCommand,
1499  HorizontalRotateCommand,
1500  VerticalRotateCommand,
1501  TileLoadCommand,
1502  TileNextCommand,
1503  TileFormerCommand,
1504  TileDeleteCommand,
1505  TileUpdateCommand
1506} ModeType;
1507
1508/*
1509  Stipples.
1510*/
1511#define BricksWidth  20
1512#define BricksHeight  20
1513#define DiagonalWidth  16
1514#define DiagonalHeight  16
1515#define HighlightWidth  8
1516#define HighlightHeight  8
1517#define OpaqueWidth  8
1518#define OpaqueHeight  8
1519#define ScalesWidth  16
1520#define ScalesHeight  16
1521#define ShadowWidth  8
1522#define ShadowHeight  8
1523#define VerticalWidth  16
1524#define VerticalHeight  16
1525#define WavyWidth  16
1526#define WavyHeight  16
1527
1528/*
1529  Constant declaration.
1530*/
1531static const int
1532  RoiDelta = 8;
1533
1534static const unsigned char
1535  BricksBitmap[] =
1536  {
1537    0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1538    0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1539    0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1540    0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1541    0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1542  },
1543  DiagonalBitmap[] =
1544  {
1545    0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1546    0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1547    0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1548  },
1549  ScalesBitmap[] =
1550  {
1551    0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1552    0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1553    0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1554  },
1555  VerticalBitmap[] =
1556  {
1557    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1558    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1559    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1560  },
1561  WavyBitmap[] =
1562  {
1563    0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1564    0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1565    0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1566  };
1567
1568/*
1569  Function prototypes.
1570*/
1571static CommandType
1572  XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1573    const MagickStatusType,KeySym,Image **,ExceptionInfo *);
1574
1575static Image
1576  *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
1577    Image **,ExceptionInfo *),
1578  *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1579  *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *,
1580    ExceptionInfo *),
1581  *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *,
1582    ExceptionInfo *);
1583
1584static MagickBooleanType
1585  XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *,
1586    ExceptionInfo *),
1587  XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **,
1588    ExceptionInfo *),
1589  XChopImage(Display *,XResourceInfo *,XWindows *,Image **,
1590    ExceptionInfo *),
1591  XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode,
1592    ExceptionInfo *),
1593  XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1594    ExceptionInfo *),
1595  XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *,
1596    ExceptionInfo *),
1597  XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1598  XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1599    ExceptionInfo *),
1600  XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1601    ExceptionInfo *),
1602  XPasteImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1603  XPrintImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1604  XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **,
1605    ExceptionInfo *),
1606  XROIImage(Display *,XResourceInfo *,XWindows *,Image **,ExceptionInfo *),
1607  XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1608  XTrimImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
1609
1610static void
1611  XDrawPanRectangle(Display *,XWindows *),
1612  XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **,
1613    ExceptionInfo *),
1614  XMagnifyImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1615  XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1616  XPanImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1617  XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1618    const KeySym,ExceptionInfo *),
1619  XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1620  XScreenEvent(Display *,XWindows *,XEvent *,ExceptionInfo *),
1621  XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1622
1623/*
1624%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1625%                                                                             %
1626%                                                                             %
1627%                                                                             %
1628%   D i s p l a y I m a g e s                                                 %
1629%                                                                             %
1630%                                                                             %
1631%                                                                             %
1632%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1633%
1634%  DisplayImages() displays an image sequence to any X window screen.  It
1635%  returns a value other than 0 if successful.  Check the exception member
1636%  of image to determine the reason for any failure.
1637%
1638%  The format of the DisplayImages method is:
1639%
1640%      MagickBooleanType DisplayImages(const ImageInfo *image_info,
1641%        Image *images,ExceptionInfo *exception)
1642%
1643%  A description of each parameter follows:
1644%
1645%    o image_info: the image info.
1646%
1647%    o image: the image.
1648%
1649%    o exception: return any errors or warnings in this structure.
1650%
1651*/
1652MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1653  Image *images,ExceptionInfo *exception)
1654{
1655  char
1656    *argv[1];
1657
1658  Display
1659    *display;
1660
1661  Image
1662    *image;
1663
1664  register ssize_t
1665    i;
1666
1667  size_t
1668    state;
1669
1670  XrmDatabase
1671    resource_database;
1672
1673  XResourceInfo
1674    resource_info;
1675
1676  assert(image_info != (const ImageInfo *) NULL);
1677  assert(image_info->signature == MagickCoreSignature);
1678  assert(images != (Image *) NULL);
1679  assert(images->signature == MagickCoreSignature);
1680  if (IfMagickTrue(images->debug) )
1681    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1682  display=XOpenDisplay(image_info->server_name);
1683  if (display == (Display *) NULL)
1684    {
1685      (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1686        "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1687      return(MagickFalse);
1688    }
1689  if (exception->severity != UndefinedException)
1690    CatchException(exception);
1691  (void) XSetErrorHandler(XError);
1692  resource_database=XGetResourceDatabase(display,GetClientName());
1693  (void) ResetMagickMemory(&resource_info,0,sizeof(resource_info));
1694  XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1695  if (image_info->page != (char *) NULL)
1696    resource_info.image_geometry=AcquireString(image_info->page);
1697  resource_info.immutable=MagickTrue;
1698  argv[0]=AcquireString(GetClientName());
1699  state=DefaultState;
1700  for (i=0; (state & ExitState) == 0; i++)
1701  {
1702    if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1703      break;
1704    image=GetImageFromList(images,i % GetImageListLength(images));
1705    (void) XDisplayImage(display,&resource_info,argv,1,&image,&state,exception);
1706  }
1707  (void) SetErrorHandler((ErrorHandler) NULL);
1708  (void) SetWarningHandler((WarningHandler) NULL);
1709  argv[0]=DestroyString(argv[0]);
1710  (void) XCloseDisplay(display);
1711  XDestroyResourceInfo(&resource_info);
1712  if (exception->severity != UndefinedException)
1713    return(MagickFalse);
1714  return(MagickTrue);
1715}
1716
1717/*
1718%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1719%                                                                             %
1720%                                                                             %
1721%                                                                             %
1722%   R e m o t e D i s p l a y C o m m a n d                                   %
1723%                                                                             %
1724%                                                                             %
1725%                                                                             %
1726%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1727%
1728%  RemoteDisplayCommand() encourages a remote display program to display the
1729%  specified image filename.
1730%
1731%  The format of the RemoteDisplayCommand method is:
1732%
1733%      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1734%        const char *window,const char *filename,ExceptionInfo *exception)
1735%
1736%  A description of each parameter follows:
1737%
1738%    o image_info: the image info.
1739%
1740%    o window: Specifies the name or id of an X window.
1741%
1742%    o filename: the name of the image filename to display.
1743%
1744%    o exception: return any errors or warnings in this structure.
1745%
1746*/
1747MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1748  const char *window,const char *filename,ExceptionInfo *exception)
1749{
1750  Display
1751    *display;
1752
1753  MagickStatusType
1754    status;
1755
1756  assert(image_info != (const ImageInfo *) NULL);
1757  assert(image_info->signature == MagickCoreSignature);
1758  assert(filename != (char *) NULL);
1759  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1760  display=XOpenDisplay(image_info->server_name);
1761  if (display == (Display *) NULL)
1762    {
1763      (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1764        "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1765      return(MagickFalse);
1766    }
1767  (void) XSetErrorHandler(XError);
1768  status=XRemoteCommand(display,window,filename);
1769  (void) XCloseDisplay(display);
1770  return(IsMagickTrue(status));
1771}
1772
1773/*
1774%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1775%                                                                             %
1776%                                                                             %
1777%                                                                             %
1778+   X A n n o t a t e E d i t I m a g e                                       %
1779%                                                                             %
1780%                                                                             %
1781%                                                                             %
1782%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1783%
1784%  XAnnotateEditImage() annotates the image with text.
1785%
1786%  The format of the XAnnotateEditImage method is:
1787%
1788%      MagickBooleanType XAnnotateEditImage(Display *display,
1789%        XResourceInfo *resource_info,XWindows *windows,Image *image,
1790%        ExceptionInfo *exception)
1791%
1792%  A description of each parameter follows:
1793%
1794%    o display: Specifies a connection to an X server;  returned from
1795%      XOpenDisplay.
1796%
1797%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1798%
1799%    o windows: Specifies a pointer to a XWindows structure.
1800%
1801%    o image: the image; returned from ReadImage.
1802%
1803*/
1804
1805static MagickBooleanType XAnnotateEditImage(Display *display,
1806  XResourceInfo *resource_info,XWindows *windows,Image *image,
1807  ExceptionInfo *exception)
1808{
1809  static const char
1810    *AnnotateMenu[] =
1811    {
1812      "Font Name",
1813      "Font Color",
1814      "Box Color",
1815      "Rotate Text",
1816      "Help",
1817      "Dismiss",
1818      (char *) NULL
1819    },
1820    *TextMenu[] =
1821    {
1822      "Help",
1823      "Apply",
1824      (char *) NULL
1825    };
1826
1827  static const ModeType
1828    AnnotateCommands[] =
1829    {
1830      AnnotateNameCommand,
1831      AnnotateFontColorCommand,
1832      AnnotateBackgroundColorCommand,
1833      AnnotateRotateCommand,
1834      AnnotateHelpCommand,
1835      AnnotateDismissCommand
1836    },
1837    TextCommands[] =
1838    {
1839      TextHelpCommand,
1840      TextApplyCommand
1841    };
1842
1843  static MagickBooleanType
1844    transparent_box = MagickTrue,
1845    transparent_pen = MagickFalse;
1846
1847  static double
1848    degrees = 0.0;
1849
1850  static unsigned int
1851    box_id = MaxNumberPens-2,
1852    font_id = 0,
1853    pen_id = 0;
1854
1855  char
1856    command[MagickPathExtent],
1857    text[MagickPathExtent];
1858
1859  const char
1860    *ColorMenu[MaxNumberPens+1];
1861
1862  Cursor
1863    cursor;
1864
1865  GC
1866    annotate_context;
1867
1868  int
1869    id,
1870    pen_number,
1871    status,
1872    x,
1873    y;
1874
1875  KeySym
1876    key_symbol;
1877
1878  register char
1879    *p;
1880
1881  register ssize_t
1882    i;
1883
1884  unsigned int
1885    height,
1886    width;
1887
1888  size_t
1889    state;
1890
1891  XAnnotateInfo
1892    *annotate_info,
1893    *previous_info;
1894
1895  XColor
1896    color;
1897
1898  XFontStruct
1899    *font_info;
1900
1901  XEvent
1902    event,
1903    text_event;
1904
1905  /*
1906    Map Command widget.
1907  */
1908  (void) CloneString(&windows->command.name,"Annotate");
1909  windows->command.data=4;
1910  (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1911  (void) XMapRaised(display,windows->command.id);
1912  XClientMessage(display,windows->image.id,windows->im_protocols,
1913    windows->im_update_widget,CurrentTime);
1914  /*
1915    Track pointer until button 1 is pressed.
1916  */
1917  XQueryPosition(display,windows->image.id,&x,&y);
1918  (void) XSelectInput(display,windows->image.id,
1919    windows->image.attributes.event_mask | PointerMotionMask);
1920  cursor=XCreateFontCursor(display,XC_left_side);
1921  (void) XCheckDefineCursor(display,windows->image.id,cursor);
1922  state=DefaultState;
1923  do
1924  {
1925    if (IfMagickTrue(windows->info.mapped) )
1926      {
1927        /*
1928          Display pointer position.
1929        */
1930        (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
1931          x+windows->image.x,y+windows->image.y);
1932        XInfoWidget(display,windows,text);
1933      }
1934    /*
1935      Wait for next event.
1936    */
1937    XScreenEvent(display,windows,&event,exception);
1938    if (event.xany.window == windows->command.id)
1939      {
1940        /*
1941          Select a command from the Command widget.
1942        */
1943        id=XCommandWidget(display,windows,AnnotateMenu,&event);
1944        (void) XCheckDefineCursor(display,windows->image.id,cursor);
1945        if (id < 0)
1946          continue;
1947        switch (AnnotateCommands[id])
1948        {
1949          case AnnotateNameCommand:
1950          {
1951            const char
1952              *FontMenu[MaxNumberFonts];
1953
1954            int
1955              font_number;
1956
1957            /*
1958              Initialize menu selections.
1959            */
1960            for (i=0; i < MaxNumberFonts; i++)
1961              FontMenu[i]=resource_info->font_name[i];
1962            FontMenu[MaxNumberFonts-2]="Browser...";
1963            FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1964            /*
1965              Select a font name from the pop-up menu.
1966            */
1967            font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1968              (const char **) FontMenu,command);
1969            if (font_number < 0)
1970              break;
1971            if (font_number == (MaxNumberFonts-2))
1972              {
1973                static char
1974                  font_name[MagickPathExtent] = "fixed";
1975
1976                /*
1977                  Select a font name from a browser.
1978                */
1979                resource_info->font_name[font_number]=font_name;
1980                XFontBrowserWidget(display,windows,"Select",font_name);
1981                if (*font_name == '\0')
1982                  break;
1983              }
1984            /*
1985              Initialize font info.
1986            */
1987            font_info=XLoadQueryFont(display,resource_info->font_name[
1988              font_number]);
1989            if (font_info == (XFontStruct *) NULL)
1990              {
1991                XNoticeWidget(display,windows,"Unable to load font:",
1992                  resource_info->font_name[font_number]);
1993                break;
1994              }
1995            font_id=(unsigned int) font_number;
1996            (void) XFreeFont(display,font_info);
1997            break;
1998          }
1999          case AnnotateFontColorCommand:
2000          {
2001            /*
2002              Initialize menu selections.
2003            */
2004            for (i=0; i < (int) (MaxNumberPens-2); i++)
2005              ColorMenu[i]=resource_info->pen_colors[i];
2006            ColorMenu[MaxNumberPens-2]="transparent";
2007            ColorMenu[MaxNumberPens-1]="Browser...";
2008            ColorMenu[MaxNumberPens]=(const char *) NULL;
2009            /*
2010              Select a pen color from the pop-up menu.
2011            */
2012            pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2013              (const char **) ColorMenu,command);
2014            if (pen_number < 0)
2015              break;
2016            transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
2017              MagickFalse;
2018            if (IfMagickTrue(transparent_pen) )
2019              break;
2020            if (pen_number == (MaxNumberPens-1))
2021              {
2022                static char
2023                  color_name[MagickPathExtent] = "gray";
2024
2025                /*
2026                  Select a pen color from a dialog.
2027                */
2028                resource_info->pen_colors[pen_number]=color_name;
2029                XColorBrowserWidget(display,windows,"Select",color_name);
2030                if (*color_name == '\0')
2031                  break;
2032              }
2033            /*
2034              Set pen color.
2035            */
2036            (void) XParseColor(display,windows->map_info->colormap,
2037              resource_info->pen_colors[pen_number],&color);
2038            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2039              (unsigned int) MaxColors,&color);
2040            windows->pixel_info->pen_colors[pen_number]=color;
2041            pen_id=(unsigned int) pen_number;
2042            break;
2043          }
2044          case AnnotateBackgroundColorCommand:
2045          {
2046            /*
2047              Initialize menu selections.
2048            */
2049            for (i=0; i < (int) (MaxNumberPens-2); i++)
2050              ColorMenu[i]=resource_info->pen_colors[i];
2051            ColorMenu[MaxNumberPens-2]="transparent";
2052            ColorMenu[MaxNumberPens-1]="Browser...";
2053            ColorMenu[MaxNumberPens]=(const char *) NULL;
2054            /*
2055              Select a pen color from the pop-up menu.
2056            */
2057            pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2058              (const char **) ColorMenu,command);
2059            if (pen_number < 0)
2060              break;
2061            transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2062              MagickFalse;
2063            if (IfMagickTrue(transparent_box) )
2064              break;
2065            if (pen_number == (MaxNumberPens-1))
2066              {
2067                static char
2068                  color_name[MagickPathExtent] = "gray";
2069
2070                /*
2071                  Select a pen color from a dialog.
2072                */
2073                resource_info->pen_colors[pen_number]=color_name;
2074                XColorBrowserWidget(display,windows,"Select",color_name);
2075                if (*color_name == '\0')
2076                  break;
2077              }
2078            /*
2079              Set pen color.
2080            */
2081            (void) XParseColor(display,windows->map_info->colormap,
2082              resource_info->pen_colors[pen_number],&color);
2083            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2084              (unsigned int) MaxColors,&color);
2085            windows->pixel_info->pen_colors[pen_number]=color;
2086            box_id=(unsigned int) pen_number;
2087            break;
2088          }
2089          case AnnotateRotateCommand:
2090          {
2091            int
2092              entry;
2093
2094            static char
2095              angle[MagickPathExtent] = "30.0";
2096
2097            static const char
2098              *RotateMenu[] =
2099              {
2100                "-90",
2101                "-45",
2102                "-30",
2103                "0",
2104                "30",
2105                "45",
2106                "90",
2107                "180",
2108                "Dialog...",
2109                (char *) NULL,
2110              };
2111
2112            /*
2113              Select a command from the pop-up menu.
2114            */
2115            entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2116              command);
2117            if (entry < 0)
2118              break;
2119            if (entry != 8)
2120              {
2121                degrees=StringToDouble(RotateMenu[entry],(char **) NULL);
2122                break;
2123              }
2124            (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2125              angle);
2126            if (*angle == '\0')
2127              break;
2128            degrees=StringToDouble(angle,(char **) NULL);
2129            break;
2130          }
2131          case AnnotateHelpCommand:
2132          {
2133            XTextViewWidget(display,resource_info,windows,MagickFalse,
2134              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2135            break;
2136          }
2137          case AnnotateDismissCommand:
2138          {
2139            /*
2140              Prematurely exit.
2141            */
2142            state|=EscapeState;
2143            state|=ExitState;
2144            break;
2145          }
2146          default:
2147            break;
2148        }
2149        continue;
2150      }
2151    switch (event.type)
2152    {
2153      case ButtonPress:
2154      {
2155        if (event.xbutton.button != Button1)
2156          break;
2157        if (event.xbutton.window != windows->image.id)
2158          break;
2159        /*
2160          Change to text entering mode.
2161        */
2162        x=event.xbutton.x;
2163        y=event.xbutton.y;
2164        state|=ExitState;
2165        break;
2166      }
2167      case ButtonRelease:
2168        break;
2169      case Expose:
2170        break;
2171      case KeyPress:
2172      {
2173        if (event.xkey.window != windows->image.id)
2174          break;
2175        /*
2176          Respond to a user key press.
2177        */
2178        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2179          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2180        switch ((int) key_symbol)
2181        {
2182          case XK_Escape:
2183          case XK_F20:
2184          {
2185            /*
2186              Prematurely exit.
2187            */
2188            state|=EscapeState;
2189            state|=ExitState;
2190            break;
2191          }
2192          case XK_F1:
2193          case XK_Help:
2194          {
2195            XTextViewWidget(display,resource_info,windows,MagickFalse,
2196              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2197            break;
2198          }
2199          default:
2200          {
2201            (void) XBell(display,0);
2202            break;
2203          }
2204        }
2205        break;
2206      }
2207      case MotionNotify:
2208      {
2209        /*
2210          Map and unmap Info widget as cursor crosses its boundaries.
2211        */
2212        x=event.xmotion.x;
2213        y=event.xmotion.y;
2214        if (IfMagickTrue(windows->info.mapped) )
2215          {
2216            if ((x < (int) (windows->info.x+windows->info.width)) &&
2217                (y < (int) (windows->info.y+windows->info.height)))
2218              (void) XWithdrawWindow(display,windows->info.id,
2219                windows->info.screen);
2220          }
2221        else
2222          if ((x > (int) (windows->info.x+windows->info.width)) ||
2223              (y > (int) (windows->info.y+windows->info.height)))
2224            (void) XMapWindow(display,windows->info.id);
2225        break;
2226      }
2227      default:
2228        break;
2229    }
2230  } while ((state & ExitState) == 0);
2231  (void) XSelectInput(display,windows->image.id,
2232    windows->image.attributes.event_mask);
2233  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2234  if ((state & EscapeState) != 0)
2235    return(MagickTrue);
2236  /*
2237    Set font info and check boundary conditions.
2238  */
2239  font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2240  if (font_info == (XFontStruct *) NULL)
2241    {
2242      XNoticeWidget(display,windows,"Unable to load font:",
2243        resource_info->font_name[font_id]);
2244      font_info=windows->font_info;
2245    }
2246  if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2247    x=(int) windows->image.width-font_info->max_bounds.width;
2248  if (y < (int) (font_info->ascent+font_info->descent))
2249    y=(int) font_info->ascent+font_info->descent;
2250  if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2251      ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2252    return(MagickFalse);
2253  /*
2254    Initialize annotate structure.
2255  */
2256  annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2257  if (annotate_info == (XAnnotateInfo *) NULL)
2258    return(MagickFalse);
2259  XGetAnnotateInfo(annotate_info);
2260  annotate_info->x=x;
2261  annotate_info->y=y;
2262  if (IfMagickFalse(transparent_box) && IfMagickFalse(transparent_pen))
2263    annotate_info->stencil=OpaqueStencil;
2264  else
2265    if (IfMagickFalse(transparent_box) )
2266      annotate_info->stencil=BackgroundStencil;
2267    else
2268      annotate_info->stencil=ForegroundStencil;
2269  annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2270  annotate_info->degrees=degrees;
2271  annotate_info->font_info=font_info;
2272  annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2273    windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
2274    sizeof(*annotate_info->text));
2275  if (annotate_info->text == (char *) NULL)
2276    return(MagickFalse);
2277  /*
2278    Create cursor and set graphic context.
2279  */
2280  cursor=XCreateFontCursor(display,XC_pencil);
2281  (void) XCheckDefineCursor(display,windows->image.id,cursor);
2282  annotate_context=windows->image.annotate_context;
2283  (void) XSetFont(display,annotate_context,font_info->fid);
2284  (void) XSetBackground(display,annotate_context,
2285    windows->pixel_info->pen_colors[box_id].pixel);
2286  (void) XSetForeground(display,annotate_context,
2287    windows->pixel_info->pen_colors[pen_id].pixel);
2288  /*
2289    Begin annotating the image with text.
2290  */
2291  (void) CloneString(&windows->command.name,"Text");
2292  windows->command.data=0;
2293  (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2294  state=DefaultState;
2295  (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2296  text_event.xexpose.width=(int) font_info->max_bounds.width;
2297  text_event.xexpose.height=font_info->max_bounds.ascent+
2298    font_info->max_bounds.descent;
2299  p=annotate_info->text;
2300  do
2301  {
2302    /*
2303      Display text cursor.
2304    */
2305    *p='\0';
2306    (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2307    /*
2308      Wait for next event.
2309    */
2310    XScreenEvent(display,windows,&event,exception);
2311    if (event.xany.window == windows->command.id)
2312      {
2313        /*
2314          Select a command from the Command widget.
2315        */
2316        (void) XSetBackground(display,annotate_context,
2317          windows->pixel_info->background_color.pixel);
2318        (void) XSetForeground(display,annotate_context,
2319          windows->pixel_info->foreground_color.pixel);
2320        id=XCommandWidget(display,windows,AnnotateMenu,&event);
2321        (void) XSetBackground(display,annotate_context,
2322          windows->pixel_info->pen_colors[box_id].pixel);
2323        (void) XSetForeground(display,annotate_context,
2324          windows->pixel_info->pen_colors[pen_id].pixel);
2325        if (id < 0)
2326          continue;
2327        switch (TextCommands[id])
2328        {
2329          case TextHelpCommand:
2330          {
2331            XTextViewWidget(display,resource_info,windows,MagickFalse,
2332              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2333            (void) XCheckDefineCursor(display,windows->image.id,cursor);
2334            break;
2335          }
2336          case TextApplyCommand:
2337          {
2338            /*
2339              Finished annotating.
2340            */
2341            annotate_info->width=(unsigned int) XTextWidth(font_info,
2342              annotate_info->text,(int) strlen(annotate_info->text));
2343            XRefreshWindow(display,&windows->image,&text_event);
2344            state|=ExitState;
2345            break;
2346          }
2347          default:
2348            break;
2349        }
2350        continue;
2351      }
2352    /*
2353      Erase text cursor.
2354    */
2355    text_event.xexpose.x=x;
2356    text_event.xexpose.y=y-font_info->max_bounds.ascent;
2357    (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2358      (unsigned int) text_event.xexpose.width,(unsigned int)
2359      text_event.xexpose.height,MagickFalse);
2360    XRefreshWindow(display,&windows->image,&text_event);
2361    switch (event.type)
2362    {
2363      case ButtonPress:
2364      {
2365        if (event.xbutton.window != windows->image.id)
2366          break;
2367        if (event.xbutton.button == Button2)
2368          {
2369            /*
2370              Request primary selection.
2371            */
2372            (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2373              windows->image.id,CurrentTime);
2374            break;
2375          }
2376        break;
2377      }
2378      case Expose:
2379      {
2380        if (event.xexpose.count == 0)
2381          {
2382            XAnnotateInfo
2383              *text_info;
2384
2385            /*
2386              Refresh Image window.
2387            */
2388            XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2389            text_info=annotate_info;
2390            while (text_info != (XAnnotateInfo *) NULL)
2391            {
2392              if (annotate_info->stencil == ForegroundStencil)
2393                (void) XDrawString(display,windows->image.id,annotate_context,
2394                  text_info->x,text_info->y,text_info->text,
2395                  (int) strlen(text_info->text));
2396              else
2397                (void) XDrawImageString(display,windows->image.id,
2398                  annotate_context,text_info->x,text_info->y,text_info->text,
2399                  (int) strlen(text_info->text));
2400              text_info=text_info->previous;
2401            }
2402            (void) XDrawString(display,windows->image.id,annotate_context,
2403              x,y,"_",1);
2404          }
2405        break;
2406      }
2407      case KeyPress:
2408      {
2409        int
2410          length;
2411
2412        if (event.xkey.window != windows->image.id)
2413          break;
2414        /*
2415          Respond to a user key press.
2416        */
2417        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2418          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2419        *(command+length)='\0';
2420        if (((event.xkey.state & ControlMask) != 0) ||
2421            ((event.xkey.state & Mod1Mask) != 0))
2422          state|=ModifierState;
2423        if ((state & ModifierState) != 0)
2424          switch ((int) key_symbol)
2425          {
2426            case XK_u:
2427            case XK_U:
2428            {
2429              key_symbol=DeleteCommand;
2430              break;
2431            }
2432            default:
2433              break;
2434          }
2435        switch ((int) key_symbol)
2436        {
2437          case XK_BackSpace:
2438          {
2439            /*
2440              Erase one character.
2441            */
2442            if (p == annotate_info->text)
2443              {
2444                if (annotate_info->previous == (XAnnotateInfo *) NULL)
2445                  break;
2446                else
2447                  {
2448                    /*
2449                      Go to end of the previous line of text.
2450                    */
2451                    annotate_info=annotate_info->previous;
2452                    p=annotate_info->text;
2453                    x=annotate_info->x+annotate_info->width;
2454                    y=annotate_info->y;
2455                    if (annotate_info->width != 0)
2456                      p+=strlen(annotate_info->text);
2457                    break;
2458                  }
2459              }
2460            p--;
2461            x-=XTextWidth(font_info,p,1);
2462            text_event.xexpose.x=x;
2463            text_event.xexpose.y=y-font_info->max_bounds.ascent;
2464            XRefreshWindow(display,&windows->image,&text_event);
2465            break;
2466          }
2467          case XK_bracketleft:
2468          {
2469            key_symbol=XK_Escape;
2470            break;
2471          }
2472          case DeleteCommand:
2473          {
2474            /*
2475              Erase the entire line of text.
2476            */
2477            while (p != annotate_info->text)
2478            {
2479              p--;
2480              x-=XTextWidth(font_info,p,1);
2481              text_event.xexpose.x=x;
2482              XRefreshWindow(display,&windows->image,&text_event);
2483            }
2484            break;
2485          }
2486          case XK_Escape:
2487          case XK_F20:
2488          {
2489            /*
2490              Finished annotating.
2491            */
2492            annotate_info->width=(unsigned int) XTextWidth(font_info,
2493              annotate_info->text,(int) strlen(annotate_info->text));
2494            XRefreshWindow(display,&windows->image,&text_event);
2495            state|=ExitState;
2496            break;
2497          }
2498          default:
2499          {
2500            /*
2501              Draw a single character on the Image window.
2502            */
2503            if ((state & ModifierState) != 0)
2504              break;
2505            if (*command == '\0')
2506              break;
2507            *p=(*command);
2508            if (annotate_info->stencil == ForegroundStencil)
2509              (void) XDrawString(display,windows->image.id,annotate_context,
2510                x,y,p,1);
2511            else
2512              (void) XDrawImageString(display,windows->image.id,
2513                annotate_context,x,y,p,1);
2514            x+=XTextWidth(font_info,p,1);
2515            p++;
2516            if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2517              break;
2518          }
2519          case XK_Return:
2520          case XK_KP_Enter:
2521          {
2522            /*
2523              Advance to the next line of text.
2524            */
2525            *p='\0';
2526            annotate_info->width=(unsigned int) XTextWidth(font_info,
2527              annotate_info->text,(int) strlen(annotate_info->text));
2528            if (annotate_info->next != (XAnnotateInfo *) NULL)
2529              {
2530                /*
2531                  Line of text already exists.
2532                */
2533                annotate_info=annotate_info->next;
2534                x=annotate_info->x;
2535                y=annotate_info->y;
2536                p=annotate_info->text;
2537                break;
2538              }
2539            annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2540              sizeof(*annotate_info->next));
2541            if (annotate_info->next == (XAnnotateInfo *) NULL)
2542              return(MagickFalse);
2543            *annotate_info->next=(*annotate_info);
2544            annotate_info->next->previous=annotate_info;
2545            annotate_info=annotate_info->next;
2546            annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2547              windows->image.width/MagickMax((ssize_t)
2548              font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2549            if (annotate_info->text == (char *) NULL)
2550              return(MagickFalse);
2551            annotate_info->y+=annotate_info->height;
2552            if (annotate_info->y > (int) windows->image.height)
2553              annotate_info->y=(int) annotate_info->height;
2554            annotate_info->next=(XAnnotateInfo *) NULL;
2555            x=annotate_info->x;
2556            y=annotate_info->y;
2557            p=annotate_info->text;
2558            break;
2559          }
2560        }
2561        break;
2562      }
2563      case KeyRelease:
2564      {
2565        /*
2566          Respond to a user key release.
2567        */
2568        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2569          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2570        state&=(~ModifierState);
2571        break;
2572      }
2573      case SelectionNotify:
2574      {
2575        Atom
2576          type;
2577
2578        int
2579          format;
2580
2581        unsigned char
2582          *data;
2583
2584        unsigned long
2585          after,
2586          length;
2587
2588        /*
2589          Obtain response from primary selection.
2590        */
2591        if (event.xselection.property == (Atom) None)
2592          break;
2593        status=XGetWindowProperty(display,event.xselection.requestor,
2594          event.xselection.property,0L,(long) MagickPathExtent,True,XA_STRING,
2595          &type,&format,&length,&after,&data);
2596        if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2597            (length == 0))
2598          break;
2599        /*
2600          Annotate Image window with primary selection.
2601        */
2602        for (i=0; i < (ssize_t) length; i++)
2603        {
2604          if ((char) data[i] != '\n')
2605            {
2606              /*
2607                Draw a single character on the Image window.
2608              */
2609              *p=(char) data[i];
2610              (void) XDrawString(display,windows->image.id,annotate_context,
2611                x,y,p,1);
2612              x+=XTextWidth(font_info,p,1);
2613              p++;
2614              if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2615                continue;
2616            }
2617          /*
2618            Advance to the next line of text.
2619          */
2620          *p='\0';
2621          annotate_info->width=(unsigned int) XTextWidth(font_info,
2622            annotate_info->text,(int) strlen(annotate_info->text));
2623          if (annotate_info->next != (XAnnotateInfo *) NULL)
2624            {
2625              /*
2626                Line of text already exists.
2627              */
2628              annotate_info=annotate_info->next;
2629              x=annotate_info->x;
2630              y=annotate_info->y;
2631              p=annotate_info->text;
2632              continue;
2633            }
2634          annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2635            sizeof(*annotate_info->next));
2636          if (annotate_info->next == (XAnnotateInfo *) NULL)
2637            return(MagickFalse);
2638          *annotate_info->next=(*annotate_info);
2639          annotate_info->next->previous=annotate_info;
2640          annotate_info=annotate_info->next;
2641          annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2642            windows->image.width/MagickMax((ssize_t)
2643            font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2644          if (annotate_info->text == (char *) NULL)
2645            return(MagickFalse);
2646          annotate_info->y+=annotate_info->height;
2647          if (annotate_info->y > (int) windows->image.height)
2648            annotate_info->y=(int) annotate_info->height;
2649          annotate_info->next=(XAnnotateInfo *) NULL;
2650          x=annotate_info->x;
2651          y=annotate_info->y;
2652          p=annotate_info->text;
2653        }
2654        (void) XFree((void *) data);
2655        break;
2656      }
2657      default:
2658        break;
2659    }
2660  } while ((state & ExitState) == 0);
2661  (void) XFreeCursor(display,cursor);
2662  /*
2663    Annotation is relative to image configuration.
2664  */
2665  width=(unsigned int) image->columns;
2666  height=(unsigned int) image->rows;
2667  x=0;
2668  y=0;
2669  if (windows->image.crop_geometry != (char *) NULL)
2670    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2671  /*
2672    Initialize annotated image.
2673  */
2674  XSetCursorState(display,windows,MagickTrue);
2675  XCheckRefreshWindows(display,windows);
2676  while (annotate_info != (XAnnotateInfo *) NULL)
2677  {
2678    if (annotate_info->width == 0)
2679      {
2680        /*
2681          No text on this line--  go to the next line of text.
2682        */
2683        previous_info=annotate_info->previous;
2684        annotate_info->text=(char *)
2685          RelinquishMagickMemory(annotate_info->text);
2686        annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2687        annotate_info=previous_info;
2688        continue;
2689      }
2690    /*
2691      Determine pixel index for box and pen color.
2692    */
2693    windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2694    if (windows->pixel_info->colors != 0)
2695      for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2696        if (windows->pixel_info->pixels[i] ==
2697            windows->pixel_info->pen_colors[box_id].pixel)
2698          {
2699            windows->pixel_info->box_index=(unsigned short) i;
2700            break;
2701          }
2702    windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_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[pen_id].pixel)
2707          {
2708            windows->pixel_info->pen_index=(unsigned short) i;
2709            break;
2710          }
2711    /*
2712      Define the annotate geometry string.
2713    */
2714    annotate_info->x=(int)
2715      width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2716    annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2717      windows->image.y)/windows->image.ximage->height;
2718    (void) FormatLocaleString(annotate_info->geometry,MagickPathExtent,
2719      "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2720      height*annotate_info->height/windows->image.ximage->height,
2721      annotate_info->x+x,annotate_info->y+y);
2722    /*
2723      Annotate image with text.
2724    */
2725    status=XAnnotateImage(display,windows->pixel_info,annotate_info,image,
2726      exception);
2727    if (status == 0)
2728      return(MagickFalse);
2729    /*
2730      Free up memory.
2731    */
2732    previous_info=annotate_info->previous;
2733    annotate_info->text=DestroyString(annotate_info->text);
2734    annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2735    annotate_info=previous_info;
2736  }
2737  (void) XSetForeground(display,annotate_context,
2738    windows->pixel_info->foreground_color.pixel);
2739  (void) XSetBackground(display,annotate_context,
2740    windows->pixel_info->background_color.pixel);
2741  (void) XSetFont(display,annotate_context,windows->font_info->fid);
2742  XSetCursorState(display,windows,MagickFalse);
2743  (void) XFreeFont(display,font_info);
2744  /*
2745    Update image configuration.
2746  */
2747  XConfigureImageColormap(display,resource_info,windows,image,exception);
2748  (void) XConfigureImage(display,resource_info,windows,image,exception);
2749  return(MagickTrue);
2750}
2751
2752/*
2753%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2754%                                                                             %
2755%                                                                             %
2756%                                                                             %
2757+   X B a c k g r o u n d I m a g e                                           %
2758%                                                                             %
2759%                                                                             %
2760%                                                                             %
2761%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2762%
2763%  XBackgroundImage() displays the image in the background of a window.
2764%
2765%  The format of the XBackgroundImage method is:
2766%
2767%      MagickBooleanType XBackgroundImage(Display *display,
2768%        XResourceInfo *resource_info,XWindows *windows,Image **image,
2769%        ExceptionInfo *exception)
2770%
2771%  A description of each parameter follows:
2772%
2773%    o display: Specifies a connection to an X server; returned from
2774%      XOpenDisplay.
2775%
2776%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2777%
2778%    o windows: Specifies a pointer to a XWindows structure.
2779%
2780%    o image: the image.
2781%
2782%    o exception: return any errors or warnings in this structure.
2783%
2784*/
2785static MagickBooleanType XBackgroundImage(Display *display,
2786  XResourceInfo *resource_info,XWindows *windows,Image **image,
2787  ExceptionInfo *exception)
2788{
2789#define BackgroundImageTag  "Background/Image"
2790
2791  int
2792    status;
2793
2794  static char
2795    window_id[MagickPathExtent] = "root";
2796
2797  XResourceInfo
2798    background_resources;
2799
2800  /*
2801    Put image in background.
2802  */
2803  status=XDialogWidget(display,windows,"Background",
2804    "Enter window id (id 0x00 selects window with pointer):",window_id);
2805  if (*window_id == '\0')
2806    return(MagickFalse);
2807  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
2808    exception);
2809  XInfoWidget(display,windows,BackgroundImageTag);
2810  XSetCursorState(display,windows,MagickTrue);
2811  XCheckRefreshWindows(display,windows);
2812  background_resources=(*resource_info);
2813  background_resources.window_id=window_id;
2814  background_resources.backdrop=IsMagickTrue(status);
2815  status=XDisplayBackgroundImage(display,&background_resources,*image,
2816    exception);
2817  if (IfMagickTrue(status))
2818    XClientMessage(display,windows->image.id,windows->im_protocols,
2819      windows->im_retain_colors,CurrentTime);
2820  XSetCursorState(display,windows,MagickFalse);
2821  (void) XMagickCommand(display,resource_info,windows,UndoCommand,image,
2822    exception);
2823  return(MagickTrue);
2824}
2825
2826/*
2827%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2828%                                                                             %
2829%                                                                             %
2830%                                                                             %
2831+   X C h o p I m a g e                                                       %
2832%                                                                             %
2833%                                                                             %
2834%                                                                             %
2835%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2836%
2837%  XChopImage() chops the X image.
2838%
2839%  The format of the XChopImage method is:
2840%
2841%    MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2842%      XWindows *windows,Image **image,ExceptionInfo *exception)
2843%
2844%  A description of each parameter follows:
2845%
2846%    o display: Specifies a connection to an X server; returned from
2847%      XOpenDisplay.
2848%
2849%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2850%
2851%    o windows: Specifies a pointer to a XWindows structure.
2852%
2853%    o image: the image.
2854%
2855%    o exception: return any errors or warnings in this structure.
2856%
2857*/
2858static MagickBooleanType XChopImage(Display *display,
2859  XResourceInfo *resource_info,XWindows *windows,Image **image,
2860  ExceptionInfo *exception)
2861{
2862  static const char
2863    *ChopMenu[] =
2864    {
2865      "Direction",
2866      "Help",
2867      "Dismiss",
2868      (char *) NULL
2869    };
2870
2871  static ModeType
2872    direction = HorizontalChopCommand;
2873
2874  static const ModeType
2875    ChopCommands[] =
2876    {
2877      ChopDirectionCommand,
2878      ChopHelpCommand,
2879      ChopDismissCommand
2880    },
2881    DirectionCommands[] =
2882    {
2883      HorizontalChopCommand,
2884      VerticalChopCommand
2885    };
2886
2887  char
2888    text[MagickPathExtent];
2889
2890  Image
2891    *chop_image;
2892
2893  int
2894    id,
2895    x,
2896    y;
2897
2898  double
2899    scale_factor;
2900
2901  RectangleInfo
2902    chop_info;
2903
2904  unsigned int
2905    distance,
2906    height,
2907    width;
2908
2909  size_t
2910    state;
2911
2912  XEvent
2913    event;
2914
2915  XSegment
2916    segment_info;
2917
2918  /*
2919    Map Command widget.
2920  */
2921  (void) CloneString(&windows->command.name,"Chop");
2922  windows->command.data=1;
2923  (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2924  (void) XMapRaised(display,windows->command.id);
2925  XClientMessage(display,windows->image.id,windows->im_protocols,
2926    windows->im_update_widget,CurrentTime);
2927  /*
2928    Track pointer until button 1 is pressed.
2929  */
2930  XQueryPosition(display,windows->image.id,&x,&y);
2931  (void) XSelectInput(display,windows->image.id,
2932    windows->image.attributes.event_mask | PointerMotionMask);
2933  state=DefaultState;
2934  (void) ResetMagickMemory(&segment_info,0,sizeof(segment_info));
2935  do
2936  {
2937    if (IfMagickTrue(windows->info.mapped) )
2938      {
2939        /*
2940          Display pointer position.
2941        */
2942        (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
2943          x+windows->image.x,y+windows->image.y);
2944        XInfoWidget(display,windows,text);
2945      }
2946    /*
2947      Wait for next event.
2948    */
2949    XScreenEvent(display,windows,&event,exception);
2950    if (event.xany.window == windows->command.id)
2951      {
2952        /*
2953          Select a command from the Command widget.
2954        */
2955        id=XCommandWidget(display,windows,ChopMenu,&event);
2956        if (id < 0)
2957          continue;
2958        switch (ChopCommands[id])
2959        {
2960          case ChopDirectionCommand:
2961          {
2962            char
2963              command[MagickPathExtent];
2964
2965            static const char
2966              *Directions[] =
2967              {
2968                "horizontal",
2969                "vertical",
2970                (char *) NULL,
2971              };
2972
2973            /*
2974              Select a command from the pop-up menu.
2975            */
2976            id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2977            if (id >= 0)
2978              direction=DirectionCommands[id];
2979            break;
2980          }
2981          case ChopHelpCommand:
2982          {
2983            XTextViewWidget(display,resource_info,windows,MagickFalse,
2984              "Help Viewer - Image Chop",ImageChopHelp);
2985            break;
2986          }
2987          case ChopDismissCommand:
2988          {
2989            /*
2990              Prematurely exit.
2991            */
2992            state|=EscapeState;
2993            state|=ExitState;
2994            break;
2995          }
2996          default:
2997            break;
2998        }
2999        continue;
3000      }
3001    switch (event.type)
3002    {
3003      case ButtonPress:
3004      {
3005        if (event.xbutton.button != Button1)
3006          break;
3007        if (event.xbutton.window != windows->image.id)
3008          break;
3009        /*
3010          User has committed to start point of chopping line.
3011        */
3012        segment_info.x1=(short int) event.xbutton.x;
3013        segment_info.x2=(short int) event.xbutton.x;
3014        segment_info.y1=(short int) event.xbutton.y;
3015        segment_info.y2=(short int) event.xbutton.y;
3016        state|=ExitState;
3017        break;
3018      }
3019      case ButtonRelease:
3020        break;
3021      case Expose:
3022        break;
3023      case KeyPress:
3024      {
3025        char
3026          command[MagickPathExtent];
3027
3028        KeySym
3029          key_symbol;
3030
3031        if (event.xkey.window != windows->image.id)
3032          break;
3033        /*
3034          Respond to a user key press.
3035        */
3036        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3037          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3038        switch ((int) key_symbol)
3039        {
3040          case XK_Escape:
3041          case XK_F20:
3042          {
3043            /*
3044              Prematurely exit.
3045            */
3046            state|=EscapeState;
3047            state|=ExitState;
3048            break;
3049          }
3050          case XK_F1:
3051          case XK_Help:
3052          {
3053            (void) XSetFunction(display,windows->image.highlight_context,
3054              GXcopy);
3055            XTextViewWidget(display,resource_info,windows,MagickFalse,
3056              "Help Viewer - Image Chop",ImageChopHelp);
3057            (void) XSetFunction(display,windows->image.highlight_context,
3058              GXinvert);
3059            break;
3060          }
3061          default:
3062          {
3063            (void) XBell(display,0);
3064            break;
3065          }
3066        }
3067        break;
3068      }
3069      case MotionNotify:
3070      {
3071        /*
3072          Map and unmap Info widget as text cursor crosses its boundaries.
3073        */
3074        x=event.xmotion.x;
3075        y=event.xmotion.y;
3076        if (IfMagickTrue(windows->info.mapped) )
3077          {
3078            if ((x < (int) (windows->info.x+windows->info.width)) &&
3079                (y < (int) (windows->info.y+windows->info.height)))
3080              (void) XWithdrawWindow(display,windows->info.id,
3081                windows->info.screen);
3082          }
3083        else
3084          if ((x > (int) (windows->info.x+windows->info.width)) ||
3085              (y > (int) (windows->info.y+windows->info.height)))
3086            (void) XMapWindow(display,windows->info.id);
3087      }
3088    }
3089  } while ((state & ExitState) == 0);
3090  (void) XSelectInput(display,windows->image.id,
3091    windows->image.attributes.event_mask);
3092  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3093  if ((state & EscapeState) != 0)
3094    return(MagickTrue);
3095  /*
3096    Draw line as pointer moves until the mouse button is released.
3097  */
3098  chop_info.width=0;
3099  chop_info.height=0;
3100  chop_info.x=0;
3101  chop_info.y=0;
3102  distance=0;
3103  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3104  state=DefaultState;
3105  do
3106  {
3107    if (distance > 9)
3108      {
3109        /*
3110          Display info and draw chopping line.
3111        */
3112        if (IfMagickFalse(windows->info.mapped) )
3113          (void) XMapWindow(display,windows->info.id);
3114        (void) FormatLocaleString(text,MagickPathExtent,
3115          " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3116          chop_info.height,(double) chop_info.x,(double) chop_info.y);
3117        XInfoWidget(display,windows,text);
3118        XHighlightLine(display,windows->image.id,
3119          windows->image.highlight_context,&segment_info);
3120      }
3121    else
3122      if (IfMagickTrue(windows->info.mapped) )
3123        (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3124    /*
3125      Wait for next event.
3126    */
3127    XScreenEvent(display,windows,&event,exception);
3128    if (distance > 9)
3129      XHighlightLine(display,windows->image.id,
3130        windows->image.highlight_context,&segment_info);
3131    switch (event.type)
3132    {
3133      case ButtonPress:
3134      {
3135        segment_info.x2=(short int) event.xmotion.x;
3136        segment_info.y2=(short int) event.xmotion.y;
3137        break;
3138      }
3139      case ButtonRelease:
3140      {
3141        /*
3142          User has committed to chopping line.
3143        */
3144        segment_info.x2=(short int) event.xbutton.x;
3145        segment_info.y2=(short int) event.xbutton.y;
3146        state|=ExitState;
3147        break;
3148      }
3149      case Expose:
3150        break;
3151      case MotionNotify:
3152      {
3153        segment_info.x2=(short int) event.xmotion.x;
3154        segment_info.y2=(short int) event.xmotion.y;
3155      }
3156      default:
3157        break;
3158    }
3159    /*
3160      Check boundary conditions.
3161    */
3162    if (segment_info.x2 < 0)
3163      segment_info.x2=0;
3164    else
3165      if (segment_info.x2 > windows->image.ximage->width)
3166        segment_info.x2=windows->image.ximage->width;
3167    if (segment_info.y2 < 0)
3168      segment_info.y2=0;
3169    else
3170      if (segment_info.y2 > windows->image.ximage->height)
3171        segment_info.y2=windows->image.ximage->height;
3172    distance=(unsigned int)
3173      (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3174       ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3175    /*
3176      Compute chopping geometry.
3177    */
3178    if (direction == HorizontalChopCommand)
3179      {
3180        chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3181        chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3182        chop_info.height=0;
3183        chop_info.y=0;
3184        if (segment_info.x1 > (int) segment_info.x2)
3185          {
3186            chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3187            chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3188          }
3189      }
3190    else
3191      {
3192        chop_info.width=0;
3193        chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3194        chop_info.x=0;
3195        chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3196        if (segment_info.y1 > segment_info.y2)
3197          {
3198            chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3199            chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3200          }
3201      }
3202  } while ((state & ExitState) == 0);
3203  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3204  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3205  if (distance <= 9)
3206    return(MagickTrue);
3207  /*
3208    Image chopping is relative to image configuration.
3209  */
3210  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
3211    exception);
3212  XSetCursorState(display,windows,MagickTrue);
3213  XCheckRefreshWindows(display,windows);
3214  windows->image.window_changes.width=windows->image.ximage->width-
3215    (unsigned int) chop_info.width;
3216  windows->image.window_changes.height=windows->image.ximage->height-
3217    (unsigned int) chop_info.height;
3218  width=(unsigned int) (*image)->columns;
3219  height=(unsigned int) (*image)->rows;
3220  x=0;
3221  y=0;
3222  if (windows->image.crop_geometry != (char *) NULL)
3223    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3224  scale_factor=(double) width/windows->image.ximage->width;
3225  chop_info.x+=x;
3226  chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3227  chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3228  scale_factor=(double) height/windows->image.ximage->height;
3229  chop_info.y+=y;
3230  chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3231  chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3232  /*
3233    Chop image.
3234  */
3235  chop_image=ChopImage(*image,&chop_info,exception);
3236  XSetCursorState(display,windows,MagickFalse);
3237  if (chop_image == (Image *) NULL)
3238    return(MagickFalse);
3239  *image=DestroyImage(*image);
3240  *image=chop_image;
3241  /*
3242    Update image configuration.
3243  */
3244  XConfigureImageColormap(display,resource_info,windows,*image,exception);
3245  (void) XConfigureImage(display,resource_info,windows,*image,exception);
3246  return(MagickTrue);
3247}
3248
3249/*
3250%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3251%                                                                             %
3252%                                                                             %
3253%                                                                             %
3254+   X C o l o r E d i t I m a g e                                             %
3255%                                                                             %
3256%                                                                             %
3257%                                                                             %
3258%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3259%
3260%  XColorEditImage() allows the user to interactively change the color of one
3261%  pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3262%
3263%  The format of the XColorEditImage method is:
3264%
3265%      MagickBooleanType XColorEditImage(Display *display,
3266%        XResourceInfo *resource_info,XWindows *windows,Image **image,
3267%          ExceptionInfo *exception)
3268%
3269%  A description of each parameter follows:
3270%
3271%    o display: Specifies a connection to an X server;  returned from
3272%      XOpenDisplay.
3273%
3274%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3275%
3276%    o windows: Specifies a pointer to a XWindows structure.
3277%
3278%    o image: the image; returned from ReadImage.
3279%
3280%    o exception: return any errors or warnings in this structure.
3281%
3282*/
3283static MagickBooleanType XColorEditImage(Display *display,
3284  XResourceInfo *resource_info,XWindows *windows,Image **image,
3285  ExceptionInfo *exception)
3286{
3287  static const char
3288    *ColorEditMenu[] =
3289    {
3290      "Method",
3291      "Pixel Color",
3292      "Border Color",
3293      "Fuzz",
3294      "Undo",
3295      "Help",
3296      "Dismiss",
3297      (char *) NULL
3298    };
3299
3300  static const ModeType
3301    ColorEditCommands[] =
3302    {
3303      ColorEditMethodCommand,
3304      ColorEditColorCommand,
3305      ColorEditBorderCommand,
3306      ColorEditFuzzCommand,
3307      ColorEditUndoCommand,
3308      ColorEditHelpCommand,
3309      ColorEditDismissCommand
3310    };
3311
3312  static PaintMethod
3313    method = PointMethod;
3314
3315  static unsigned int
3316    pen_id = 0;
3317
3318  static XColor
3319    border_color = { 0, 0, 0, 0, 0, 0 };
3320
3321  char
3322    command[MagickPathExtent],
3323    text[MagickPathExtent];
3324
3325  Cursor
3326    cursor;
3327
3328  int
3329    entry,
3330    id,
3331    x,
3332    x_offset,
3333    y,
3334    y_offset;
3335
3336  register Quantum
3337    *q;
3338
3339  register ssize_t
3340    i;
3341
3342  unsigned int
3343    height,
3344    width;
3345
3346  size_t
3347    state;
3348
3349  XColor
3350    color;
3351
3352  XEvent
3353    event;
3354
3355  /*
3356    Map Command widget.
3357  */
3358  (void) CloneString(&windows->command.name,"Color Edit");
3359  windows->command.data=4;
3360  (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3361  (void) XMapRaised(display,windows->command.id);
3362  XClientMessage(display,windows->image.id,windows->im_protocols,
3363    windows->im_update_widget,CurrentTime);
3364  /*
3365    Make cursor.
3366  */
3367  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3368    resource_info->background_color,resource_info->foreground_color);
3369  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3370  /*
3371    Track pointer until button 1 is pressed.
3372  */
3373  XQueryPosition(display,windows->image.id,&x,&y);
3374  (void) XSelectInput(display,windows->image.id,
3375    windows->image.attributes.event_mask | PointerMotionMask);
3376  state=DefaultState;
3377  do
3378  {
3379    if (IfMagickTrue(windows->info.mapped) )
3380      {
3381        /*
3382          Display pointer position.
3383        */
3384        (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
3385          x+windows->image.x,y+windows->image.y);
3386        XInfoWidget(display,windows,text);
3387      }
3388    /*
3389      Wait for next event.
3390    */
3391    XScreenEvent(display,windows,&event,exception);
3392    if (event.xany.window == windows->command.id)
3393      {
3394        /*
3395          Select a command from the Command widget.
3396        */
3397        id=XCommandWidget(display,windows,ColorEditMenu,&event);
3398        if (id < 0)
3399          {
3400            (void) XCheckDefineCursor(display,windows->image.id,cursor);
3401            continue;
3402          }
3403        switch (ColorEditCommands[id])
3404        {
3405          case ColorEditMethodCommand:
3406          {
3407            char
3408              **methods;
3409
3410            /*
3411              Select a method from the pop-up menu.
3412            */
3413            methods=(char **) GetCommandOptions(MagickMethodOptions);
3414            if (methods == (char **) NULL)
3415              break;
3416            entry=XMenuWidget(display,windows,ColorEditMenu[id],
3417              (const char **) methods,command);
3418            if (entry >= 0)
3419              method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3420                MagickFalse,methods[entry]);
3421            methods=DestroyStringList(methods);
3422            break;
3423          }
3424          case ColorEditColorCommand:
3425          {
3426            const char
3427              *ColorMenu[MaxNumberPens];
3428
3429            int
3430              pen_number;
3431
3432            /*
3433              Initialize menu selections.
3434            */
3435            for (i=0; i < (int) (MaxNumberPens-2); i++)
3436              ColorMenu[i]=resource_info->pen_colors[i];
3437            ColorMenu[MaxNumberPens-2]="Browser...";
3438            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3439            /*
3440              Select a pen color from the pop-up menu.
3441            */
3442            pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3443              (const char **) ColorMenu,command);
3444            if (pen_number < 0)
3445              break;
3446            if (pen_number == (MaxNumberPens-2))
3447              {
3448                static char
3449                  color_name[MagickPathExtent] = "gray";
3450
3451                /*
3452                  Select a pen color from a dialog.
3453                */
3454                resource_info->pen_colors[pen_number]=color_name;
3455                XColorBrowserWidget(display,windows,"Select",color_name);
3456                if (*color_name == '\0')
3457                  break;
3458              }
3459            /*
3460              Set pen color.
3461            */
3462            (void) XParseColor(display,windows->map_info->colormap,
3463              resource_info->pen_colors[pen_number],&color);
3464            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3465              (unsigned int) MaxColors,&color);
3466            windows->pixel_info->pen_colors[pen_number]=color;
3467            pen_id=(unsigned int) pen_number;
3468            break;
3469          }
3470          case ColorEditBorderCommand:
3471          {
3472            const char
3473              *ColorMenu[MaxNumberPens];
3474
3475            int
3476              pen_number;
3477
3478            /*
3479              Initialize menu selections.
3480            */
3481            for (i=0; i < (int) (MaxNumberPens-2); i++)
3482              ColorMenu[i]=resource_info->pen_colors[i];
3483            ColorMenu[MaxNumberPens-2]="Browser...";
3484            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3485            /*
3486              Select a pen color from the pop-up menu.
3487            */
3488            pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3489              (const char **) ColorMenu,command);
3490            if (pen_number < 0)
3491              break;
3492            if (pen_number == (MaxNumberPens-2))
3493              {
3494                static char
3495                  color_name[MagickPathExtent] = "gray";
3496
3497                /*
3498                  Select a pen color from a dialog.
3499                */
3500                resource_info->pen_colors[pen_number]=color_name;
3501                XColorBrowserWidget(display,windows,"Select",color_name);
3502                if (*color_name == '\0')
3503                  break;
3504              }
3505            /*
3506              Set border color.
3507            */
3508            (void) XParseColor(display,windows->map_info->colormap,
3509              resource_info->pen_colors[pen_number],&border_color);
3510            break;
3511          }
3512          case ColorEditFuzzCommand:
3513          {
3514            static char
3515              fuzz[MagickPathExtent];
3516
3517            static const char
3518              *FuzzMenu[] =
3519              {
3520                "0%",
3521                "2%",
3522                "5%",
3523                "10%",
3524                "15%",
3525                "Dialog...",
3526                (char *) NULL,
3527              };
3528
3529            /*
3530              Select a command from the pop-up menu.
3531            */
3532            entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3533              command);
3534            if (entry < 0)
3535              break;
3536            if (entry != 5)
3537              {
3538                (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
3539                  QuantumRange+1.0);
3540                break;
3541              }
3542            (void) (void) CopyMagickString(fuzz,"20%",MagickPathExtent);
3543            (void) XDialogWidget(display,windows,"Ok",
3544              "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3545            if (*fuzz == '\0')
3546              break;
3547            (void) ConcatenateMagickString(fuzz,"%",MagickPathExtent);
3548            (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
3549              1.0);
3550            break;
3551          }
3552          case ColorEditUndoCommand:
3553          {
3554            (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3555              image,exception);
3556            break;
3557          }
3558          case ColorEditHelpCommand:
3559          default:
3560          {
3561            XTextViewWidget(display,resource_info,windows,MagickFalse,
3562              "Help Viewer - Image Annotation",ImageColorEditHelp);
3563            break;
3564          }
3565          case ColorEditDismissCommand:
3566          {
3567            /*
3568              Prematurely exit.
3569            */
3570            state|=EscapeState;
3571            state|=ExitState;
3572            break;
3573          }
3574        }
3575        (void) XCheckDefineCursor(display,windows->image.id,cursor);
3576        continue;
3577      }
3578    switch (event.type)
3579    {
3580      case ButtonPress:
3581      {
3582        if (event.xbutton.button != Button1)
3583          break;
3584        if ((event.xbutton.window != windows->image.id) &&
3585            (event.xbutton.window != windows->magnify.id))
3586          break;
3587        /*
3588          exit loop.
3589        */
3590        x=event.xbutton.x;
3591        y=event.xbutton.y;
3592        (void) XMagickCommand(display,resource_info,windows,
3593          SaveToUndoBufferCommand,image,exception);
3594        state|=UpdateConfigurationState;
3595        break;
3596      }
3597      case ButtonRelease:
3598      {
3599        if (event.xbutton.button != Button1)
3600          break;
3601        if ((event.xbutton.window != windows->image.id) &&
3602            (event.xbutton.window != windows->magnify.id))
3603          break;
3604        /*
3605          Update colormap information.
3606        */
3607        x=event.xbutton.x;
3608        y=event.xbutton.y;
3609        XConfigureImageColormap(display,resource_info,windows,*image,exception);
3610        (void) XConfigureImage(display,resource_info,windows,*image,exception);
3611        XInfoWidget(display,windows,text);
3612        (void) XCheckDefineCursor(display,windows->image.id,cursor);
3613        state&=(~UpdateConfigurationState);
3614        break;
3615      }
3616      case Expose:
3617        break;
3618      case KeyPress:
3619      {
3620        KeySym
3621          key_symbol;
3622
3623        if (event.xkey.window == windows->magnify.id)
3624          {
3625            Window
3626              window;
3627
3628            window=windows->magnify.id;
3629            while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3630          }
3631        if (event.xkey.window != windows->image.id)
3632          break;
3633        /*
3634          Respond to a user key press.
3635        */
3636        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3637          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3638        switch ((int) key_symbol)
3639        {
3640          case XK_Escape:
3641          case XK_F20:
3642          {
3643            /*
3644              Prematurely exit.
3645            */
3646            state|=ExitState;
3647            break;
3648          }
3649          case XK_F1:
3650          case XK_Help:
3651          {
3652            XTextViewWidget(display,resource_info,windows,MagickFalse,
3653              "Help Viewer - Image Annotation",ImageColorEditHelp);
3654            break;
3655          }
3656          default:
3657          {
3658            (void) XBell(display,0);
3659            break;
3660          }
3661        }
3662        break;
3663      }
3664      case MotionNotify:
3665      {
3666        /*
3667          Map and unmap Info widget as cursor crosses its boundaries.
3668        */
3669        x=event.xmotion.x;
3670        y=event.xmotion.y;
3671        if (IfMagickTrue(windows->info.mapped) )
3672          {
3673            if ((x < (int) (windows->info.x+windows->info.width)) &&
3674                (y < (int) (windows->info.y+windows->info.height)))
3675              (void) XWithdrawWindow(display,windows->info.id,
3676                windows->info.screen);
3677          }
3678        else
3679          if ((x > (int) (windows->info.x+windows->info.width)) ||
3680              (y > (int) (windows->info.y+windows->info.height)))
3681            (void) XMapWindow(display,windows->info.id);
3682        break;
3683      }
3684      default:
3685        break;
3686    }
3687    if (event.xany.window == windows->magnify.id)
3688      {
3689        x=windows->magnify.x-windows->image.x;
3690        y=windows->magnify.y-windows->image.y;
3691      }
3692    x_offset=x;
3693    y_offset=y;
3694    if ((state & UpdateConfigurationState) != 0)
3695      {
3696        CacheView
3697          *image_view;
3698
3699        int
3700          x,
3701          y;
3702
3703        /*
3704          Pixel edit is relative to image configuration.
3705        */
3706        (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3707          MagickTrue);
3708        color=windows->pixel_info->pen_colors[pen_id];
3709        XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3710        width=(unsigned int) (*image)->columns;
3711        height=(unsigned int) (*image)->rows;
3712        x=0;
3713        y=0;
3714        if (windows->image.crop_geometry != (char *) NULL)
3715          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3716            &width,&height);
3717        x_offset=(int)
3718          (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3719        y_offset=(int)
3720          (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3721        if ((x_offset < 0) || (y_offset < 0))
3722          continue;
3723        if ((x_offset >= (int) (*image)->columns) ||
3724            (y_offset >= (int) (*image)->rows))
3725          continue;
3726        image_view=AcquireAuthenticCacheView(*image,exception);
3727        switch (method)
3728        {
3729          case PointMethod:
3730          default:
3731          {
3732            /*
3733              Update color information using point algorithm.
3734            */
3735            if (IfMagickFalse(SetImageStorageClass(*image,DirectClass,exception)) )
3736              return(MagickFalse);
3737            q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3738              (ssize_t) y_offset,1,1,exception);
3739            if (q == (Quantum *) NULL)
3740              break;
3741            SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3742            SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3743            SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3744            (void) SyncCacheViewAuthenticPixels(image_view,exception);
3745            break;
3746          }
3747          case ReplaceMethod:
3748          {
3749            PixelInfo
3750              pixel,
3751              target;
3752
3753            /*
3754              Update color information using replace algorithm.
3755            */
3756            (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
3757              x_offset,(ssize_t) y_offset,&target,exception);
3758            if ((*image)->storage_class == DirectClass)
3759              {
3760                for (y=0; y < (int) (*image)->rows; y++)
3761                {
3762                  q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3763                    (*image)->columns,1,exception);
3764                  if (q == (Quantum *) NULL)
3765                    break;
3766                  for (x=0; x < (int) (*image)->columns; x++)
3767                  {
3768                    GetPixelInfoPixel(*image,q,&pixel);
3769                    if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
3770                      {
3771                        SetPixelRed(*image,ScaleShortToQuantum(
3772                          color.red),q);
3773                        SetPixelGreen(*image,ScaleShortToQuantum(
3774                          color.green),q);
3775                        SetPixelBlue(*image,ScaleShortToQuantum(
3776                          color.blue),q);
3777                      }
3778                    q+=GetPixelChannels(*image);
3779                  }
3780                  if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
3781                    break;
3782                }
3783              }
3784            else
3785              {
3786                for (i=0; i < (ssize_t) (*image)->colors; i++)
3787                  if (IsFuzzyEquivalencePixelInfo((*image)->colormap+i,&target))
3788                    {
3789                      (*image)->colormap[i].red=(double) ScaleShortToQuantum(
3790                        color.red);
3791                      (*image)->colormap[i].green=(double) ScaleShortToQuantum(
3792                        color.green);
3793                      (*image)->colormap[i].blue=(double) ScaleShortToQuantum(
3794                        color.blue);
3795                    }
3796                (void) SyncImage(*image,exception);
3797              }
3798            break;
3799          }
3800          case FloodfillMethod:
3801          case FillToBorderMethod:
3802          {
3803            DrawInfo
3804              *draw_info;
3805
3806            PixelInfo
3807              target;
3808
3809            /*
3810              Update color information using floodfill algorithm.
3811            */
3812            (void) GetOneVirtualPixelInfo(*image,
3813              GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
3814              y_offset,&target,exception);
3815            if (method == FillToBorderMethod)
3816              {
3817                target.red=(double)
3818                  ScaleShortToQuantum(border_color.red);
3819                target.green=(double)
3820                  ScaleShortToQuantum(border_color.green);
3821                target.blue=(double)
3822                  ScaleShortToQuantum(border_color.blue);
3823              }
3824            draw_info=CloneDrawInfo(resource_info->image_info,
3825              (DrawInfo *) NULL);
3826            (void) QueryColorCompliance(resource_info->pen_colors[pen_id],
3827              AllCompliance,&draw_info->fill,exception);
3828            (void) FloodfillPaintImage(*image,draw_info,&target,
3829              (ssize_t)x_offset,(ssize_t)y_offset,
3830              IsMagickFalse(method == FloodfillMethod),exception);
3831            draw_info=DestroyDrawInfo(draw_info);
3832            break;
3833          }
3834          case ResetMethod:
3835          {
3836            /*
3837              Update color information using reset algorithm.
3838            */
3839            if (IfMagickFalse(SetImageStorageClass(*image,DirectClass,exception)) )
3840              return(MagickFalse);
3841            for (y=0; y < (int) (*image)->rows; y++)
3842            {
3843              q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3844                (*image)->columns,1,exception);
3845              if (q == (Quantum *) NULL)
3846                break;
3847              for (x=0; x < (int) (*image)->columns; x++)
3848              {
3849                SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3850                SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3851                SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3852                q+=GetPixelChannels(*image);
3853              }
3854              if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
3855                break;
3856            }
3857            break;
3858          }
3859        }
3860        image_view=DestroyCacheView(image_view);
3861        state&=(~UpdateConfigurationState);
3862      }
3863  } while ((state & ExitState) == 0);
3864  (void) XSelectInput(display,windows->image.id,
3865    windows->image.attributes.event_mask);
3866  XSetCursorState(display,windows,MagickFalse);
3867  (void) XFreeCursor(display,cursor);
3868  return(MagickTrue);
3869}
3870
3871/*
3872%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3873%                                                                             %
3874%                                                                             %
3875%                                                                             %
3876+   X C o m p o s i t e I m a g e                                             %
3877%                                                                             %
3878%                                                                             %
3879%                                                                             %
3880%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3881%
3882%  XCompositeImage() requests an image name from the user, reads the image and
3883%  composites it with the X window image at a location the user chooses with
3884%  the pointer.
3885%
3886%  The format of the XCompositeImage method is:
3887%
3888%      MagickBooleanType XCompositeImage(Display *display,
3889%        XResourceInfo *resource_info,XWindows *windows,Image *image,
3890%        ExceptionInfo *exception)
3891%
3892%  A description of each parameter follows:
3893%
3894%    o display: Specifies a connection to an X server;  returned from
3895%      XOpenDisplay.
3896%
3897%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3898%
3899%    o windows: Specifies a pointer to a XWindows structure.
3900%
3901%    o image: the image; returned from ReadImage.
3902%
3903%    o exception: return any errors or warnings in this structure.
3904%
3905*/
3906static MagickBooleanType XCompositeImage(Display *display,
3907  XResourceInfo *resource_info,XWindows *windows,Image *image,
3908  ExceptionInfo *exception)
3909{
3910  static char
3911    displacement_geometry[MagickPathExtent] = "30x30",
3912    filename[MagickPathExtent] = "\0";
3913
3914  static const char
3915    *CompositeMenu[] =
3916    {
3917      "Operators",
3918      "Dissolve",
3919      "Displace",
3920      "Help",
3921      "Dismiss",
3922      (char *) NULL
3923    };
3924
3925  static CompositeOperator
3926    compose = CopyCompositeOp;
3927
3928  static const ModeType
3929    CompositeCommands[] =
3930    {
3931      CompositeOperatorsCommand,
3932      CompositeDissolveCommand,
3933      CompositeDisplaceCommand,
3934      CompositeHelpCommand,
3935      CompositeDismissCommand
3936    };
3937
3938  char
3939    text[MagickPathExtent];
3940
3941  Cursor
3942    cursor;
3943
3944  Image
3945    *composite_image;
3946
3947  int
3948    entry,
3949    id,
3950    x,
3951    y;
3952
3953  double
3954    blend,
3955    scale_factor;
3956
3957  RectangleInfo
3958    highlight_info,
3959    composite_info;
3960
3961  unsigned int
3962    height,
3963    width;
3964
3965  size_t
3966    state;
3967
3968  XEvent
3969    event;
3970
3971  /*
3972    Request image file name from user.
3973  */
3974  XFileBrowserWidget(display,windows,"Composite",filename);
3975  if (*filename == '\0')
3976    return(MagickTrue);
3977  /*
3978    Read image.
3979  */
3980  XSetCursorState(display,windows,MagickTrue);
3981  XCheckRefreshWindows(display,windows);
3982  (void) CopyMagickString(resource_info->image_info->filename,filename,
3983    MagickPathExtent);
3984  composite_image=ReadImage(resource_info->image_info,exception);
3985  CatchException(exception);
3986  XSetCursorState(display,windows,MagickFalse);
3987  if (composite_image == (Image *) NULL)
3988    return(MagickFalse);
3989  /*
3990    Map Command widget.
3991  */
3992  (void) CloneString(&windows->command.name,"Composite");
3993  windows->command.data=1;
3994  (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
3995  (void) XMapRaised(display,windows->command.id);
3996  XClientMessage(display,windows->image.id,windows->im_protocols,
3997    windows->im_update_widget,CurrentTime);
3998  /*
3999    Track pointer until button 1 is pressed.
4000  */
4001  XQueryPosition(display,windows->image.id,&x,&y);
4002  (void) XSelectInput(display,windows->image.id,
4003    windows->image.attributes.event_mask | PointerMotionMask);
4004  composite_info.x=(ssize_t) windows->image.x+x;
4005  composite_info.y=(ssize_t) windows->image.y+y;
4006  composite_info.width=0;
4007  composite_info.height=0;
4008  cursor=XCreateFontCursor(display,XC_ul_angle);
4009  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4010  blend=0.0;
4011  state=DefaultState;
4012  do
4013  {
4014    if (IfMagickTrue(windows->info.mapped) )
4015      {
4016        /*
4017          Display pointer position.
4018        */
4019        (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
4020          (long) composite_info.x,(long) composite_info.y);
4021        XInfoWidget(display,windows,text);
4022      }
4023    highlight_info=composite_info;
4024    highlight_info.x=composite_info.x-windows->image.x;
4025    highlight_info.y=composite_info.y-windows->image.y;
4026    XHighlightRectangle(display,windows->image.id,
4027      windows->image.highlight_context,&highlight_info);
4028    /*
4029      Wait for next event.
4030    */
4031    XScreenEvent(display,windows,&event,exception);
4032    XHighlightRectangle(display,windows->image.id,
4033      windows->image.highlight_context,&highlight_info);
4034    if (event.xany.window == windows->command.id)
4035      {
4036        /*
4037          Select a command from the Command widget.
4038        */
4039        id=XCommandWidget(display,windows,CompositeMenu,&event);
4040        if (id < 0)
4041          continue;
4042        switch (CompositeCommands[id])
4043        {
4044          case CompositeOperatorsCommand:
4045          {
4046            char
4047              command[MagickPathExtent],
4048              **operators;
4049
4050            /*
4051              Select a command from the pop-up menu.
4052            */
4053            operators=GetCommandOptions(MagickComposeOptions);
4054            if (operators == (char **) NULL)
4055              break;
4056            entry=XMenuWidget(display,windows,CompositeMenu[id],
4057              (const char **) operators,command);
4058            if (entry >= 0)
4059              compose=(CompositeOperator) ParseCommandOption(
4060                MagickComposeOptions,MagickFalse,operators[entry]);
4061            operators=DestroyStringList(operators);
4062            break;
4063          }
4064          case CompositeDissolveCommand:
4065          {
4066            static char
4067              factor[MagickPathExtent] = "20.0";
4068
4069            /*
4070              Dissolve the two images a given percent.
4071            */
4072            (void) XSetFunction(display,windows->image.highlight_context,
4073              GXcopy);
4074            (void) XDialogWidget(display,windows,"Dissolve",
4075              "Enter the blend factor (0.0 - 99.9%):",factor);
4076            (void) XSetFunction(display,windows->image.highlight_context,
4077              GXinvert);
4078            if (*factor == '\0')
4079              break;
4080            blend=StringToDouble(factor,(char **) NULL);
4081            compose=DissolveCompositeOp;
4082            break;
4083          }
4084          case CompositeDisplaceCommand:
4085          {
4086            /*
4087              Get horizontal and vertical scale displacement geometry.
4088            */
4089            (void) XSetFunction(display,windows->image.highlight_context,
4090              GXcopy);
4091            (void) XDialogWidget(display,windows,"Displace",
4092              "Enter the horizontal and vertical scale:",displacement_geometry);
4093            (void) XSetFunction(display,windows->image.highlight_context,
4094              GXinvert);
4095            if (*displacement_geometry == '\0')
4096              break;
4097            compose=DisplaceCompositeOp;
4098            break;
4099          }
4100          case CompositeHelpCommand:
4101          {
4102            (void) XSetFunction(display,windows->image.highlight_context,
4103              GXcopy);
4104            XTextViewWidget(display,resource_info,windows,MagickFalse,
4105              "Help Viewer - Image Composite",ImageCompositeHelp);
4106            (void) XSetFunction(display,windows->image.highlight_context,
4107              GXinvert);
4108            break;
4109          }
4110          case CompositeDismissCommand:
4111          {
4112            /*
4113              Prematurely exit.
4114            */
4115            state|=EscapeState;
4116            state|=ExitState;
4117            break;
4118          }
4119          default:
4120            break;
4121        }
4122        continue;
4123      }
4124    switch (event.type)
4125    {
4126      case ButtonPress:
4127      {
4128        if (IfMagickTrue(image->debug) )
4129          (void) LogMagickEvent(X11Event,GetMagickModule(),
4130            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4131            event.xbutton.button,event.xbutton.x,event.xbutton.y);
4132        if (event.xbutton.button != Button1)
4133          break;
4134        if (event.xbutton.window != windows->image.id)
4135          break;
4136        /*
4137          Change cursor.
4138        */
4139        composite_info.width=composite_image->columns;
4140        composite_info.height=composite_image->rows;
4141        (void) XCheckDefineCursor(display,windows->image.id,cursor);
4142        composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4143        composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4144        break;
4145      }
4146      case ButtonRelease:
4147      {
4148        if (IfMagickTrue(image->debug) )
4149          (void) LogMagickEvent(X11Event,GetMagickModule(),
4150            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4151            event.xbutton.button,event.xbutton.x,event.xbutton.y);
4152        if (event.xbutton.button != Button1)
4153          break;
4154        if (event.xbutton.window != windows->image.id)
4155          break;
4156        if ((composite_info.width != 0) && (composite_info.height != 0))
4157          {
4158            /*
4159              User has selected the location of the composite image.
4160            */
4161            composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4162            composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4163            state|=ExitState;
4164          }
4165        break;
4166      }
4167      case Expose:
4168        break;
4169      case KeyPress:
4170      {
4171        char
4172          command[MagickPathExtent];
4173
4174        KeySym
4175          key_symbol;
4176
4177        int
4178          length;
4179
4180        if (event.xkey.window != windows->image.id)
4181          break;
4182        /*
4183          Respond to a user key press.
4184        */
4185        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4186          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4187        *(command+length)='\0';
4188        if (IfMagickTrue(image->debug) )
4189          (void) LogMagickEvent(X11Event,GetMagickModule(),
4190            "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4191        switch ((int) key_symbol)
4192        {
4193          case XK_Escape:
4194          case XK_F20:
4195          {
4196            /*
4197              Prematurely exit.
4198            */
4199            composite_image=DestroyImage(composite_image);
4200            state|=EscapeState;
4201            state|=ExitState;
4202            break;
4203          }
4204          case XK_F1:
4205          case XK_Help:
4206          {
4207            (void) XSetFunction(display,windows->image.highlight_context,
4208              GXcopy);
4209            XTextViewWidget(display,resource_info,windows,MagickFalse,
4210              "Help Viewer - Image Composite",ImageCompositeHelp);
4211            (void) XSetFunction(display,windows->image.highlight_context,
4212              GXinvert);
4213            break;
4214          }
4215          default:
4216          {
4217            (void) XBell(display,0);
4218            break;
4219          }
4220        }
4221        break;
4222      }
4223      case MotionNotify:
4224      {
4225        /*
4226          Map and unmap Info widget as text cursor crosses its boundaries.
4227        */
4228        x=event.xmotion.x;
4229        y=event.xmotion.y;
4230        if (IfMagickTrue(windows->info.mapped) )
4231          {
4232            if ((x < (int) (windows->info.x+windows->info.width)) &&
4233                (y < (int) (windows->info.y+windows->info.height)))
4234              (void) XWithdrawWindow(display,windows->info.id,
4235                windows->info.screen);
4236          }
4237        else
4238          if ((x > (int) (windows->info.x+windows->info.width)) ||
4239              (y > (int) (windows->info.y+windows->info.height)))
4240            (void) XMapWindow(display,windows->info.id);
4241        composite_info.x=(ssize_t) windows->image.x+x;
4242        composite_info.y=(ssize_t) windows->image.y+y;
4243        break;
4244      }
4245      default:
4246      {
4247        if (IfMagickTrue(image->debug) )
4248          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4249            event.type);
4250        break;
4251      }
4252    }
4253  } while ((state & ExitState) == 0);
4254  (void) XSelectInput(display,windows->image.id,
4255    windows->image.attributes.event_mask);
4256  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4257  XSetCursorState(display,windows,MagickFalse);
4258  (void) XFreeCursor(display,cursor);
4259  if ((state & EscapeState) != 0)
4260    return(MagickTrue);
4261  /*
4262    Image compositing is relative to image configuration.
4263  */
4264  XSetCursorState(display,windows,MagickTrue);
4265  XCheckRefreshWindows(display,windows);
4266  width=(unsigned int) image->columns;
4267  height=(unsigned int) image->rows;
4268  x=0;
4269  y=0;
4270  if (windows->image.crop_geometry != (char *) NULL)
4271    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4272  scale_factor=(double) width/windows->image.ximage->width;
4273  composite_info.x+=x;
4274  composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4275  composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4276  scale_factor=(double) height/windows->image.ximage->height;
4277  composite_info.y+=y;
4278  composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4279  composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4280  if ((composite_info.width != composite_image->columns) ||
4281      (composite_info.height != composite_image->rows))
4282    {
4283      Image
4284        *resize_image;
4285
4286      /*
4287        Scale composite image.
4288      */
4289      resize_image=ResizeImage(composite_image,composite_info.width,
4290        composite_info.height,composite_image->filter,exception);
4291      composite_image=DestroyImage(composite_image);
4292      if (resize_image == (Image *) NULL)
4293        {
4294          XSetCursorState(display,windows,MagickFalse);
4295          return(MagickFalse);
4296        }
4297      composite_image=resize_image;
4298    }
4299  if (compose == DisplaceCompositeOp)
4300    (void) SetImageArtifact(composite_image,"compose:args",
4301      displacement_geometry);
4302  if (blend != 0.0)
4303    {
4304      CacheView
4305        *image_view;
4306
4307      int
4308        y;
4309
4310      Quantum
4311        opacity;
4312
4313      register int
4314        x;
4315
4316      register Quantum
4317        *q;
4318
4319      /*
4320        Create mattes for blending.
4321      */
4322      (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,exception);
4323      opacity=(Quantum) (ScaleQuantumToChar(QuantumRange)-
4324        ((ssize_t) ScaleQuantumToChar(QuantumRange)*blend)/100);
4325      if (IfMagickFalse(SetImageStorageClass(image,DirectClass,exception)) )
4326        return(MagickFalse);
4327      image->alpha_trait=BlendPixelTrait;
4328      image_view=AcquireAuthenticCacheView(image,exception);
4329      for (y=0; y < (int) image->rows; y++)
4330      {
4331        q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4332          exception);
4333        if (q == (Quantum *) NULL)
4334          break;
4335        for (x=0; x < (int) image->columns; x++)
4336        {
4337          SetPixelAlpha(image,opacity,q);
4338          q+=GetPixelChannels(image);
4339        }
4340        if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
4341          break;
4342      }
4343      image_view=DestroyCacheView(image_view);
4344    }
4345  /*
4346    Composite image with X Image window.
4347  */
4348  (void) CompositeImage(image,composite_image,compose,MagickTrue,
4349    composite_info.x,composite_info.y,exception);
4350  composite_image=DestroyImage(composite_image);
4351  XSetCursorState(display,windows,MagickFalse);
4352  /*
4353    Update image configuration.
4354  */
4355  XConfigureImageColormap(display,resource_info,windows,image,exception);
4356  (void) XConfigureImage(display,resource_info,windows,image,exception);
4357  return(MagickTrue);
4358}
4359
4360/*
4361%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4362%                                                                             %
4363%                                                                             %
4364%                                                                             %
4365+   X C o n f i g u r e I m a g e                                             %
4366%                                                                             %
4367%                                                                             %
4368%                                                                             %
4369%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4370%
4371%  XConfigureImage() creates a new X image.  It also notifies the window
4372%  manager of the new image size and configures the transient widows.
4373%
4374%  The format of the XConfigureImage method is:
4375%
4376%      MagickBooleanType XConfigureImage(Display *display,
4377%        XResourceInfo *resource_info,XWindows *windows,Image *image,
4378%        ExceptionInfo *exception)
4379%
4380%  A description of each parameter follows:
4381%
4382%    o display: Specifies a connection to an X server; returned from
4383%      XOpenDisplay.
4384%
4385%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4386%
4387%    o windows: Specifies a pointer to a XWindows structure.
4388%
4389%    o image: the image.
4390%
4391%    o exception: return any errors or warnings in this structure.
4392%
4393%    o exception: return any errors or warnings in this structure.
4394%
4395*/
4396static MagickBooleanType XConfigureImage(Display *display,
4397  XResourceInfo *resource_info,XWindows *windows,Image *image,
4398  ExceptionInfo *exception)
4399{
4400  char
4401    geometry[MagickPathExtent];
4402
4403  MagickStatusType
4404    status;
4405
4406  size_t
4407    mask,
4408    height,
4409    width;
4410
4411  ssize_t
4412    x,
4413    y;
4414
4415  XSizeHints
4416    *size_hints;
4417
4418  XWindowChanges
4419    window_changes;
4420
4421  /*
4422    Dismiss if window dimensions are zero.
4423  */
4424  width=(unsigned int) windows->image.window_changes.width;
4425  height=(unsigned int) windows->image.window_changes.height;
4426  if (IfMagickTrue(image->debug) )
4427    (void) LogMagickEvent(X11Event,GetMagickModule(),
4428      "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4429      windows->image.ximage->height,(double) width,(double) height);
4430  if ((width*height) == 0)
4431    return(MagickTrue);
4432  x=0;
4433  y=0;
4434  /*
4435    Resize image to fit Image window dimensions.
4436  */
4437  XSetCursorState(display,windows,MagickTrue);
4438  (void) XFlush(display);
4439  if (((int) width != windows->image.ximage->width) ||
4440      ((int) height != windows->image.ximage->height))
4441    image->taint=MagickTrue;
4442  windows->magnify.x=(int)
4443    width*windows->magnify.x/windows->image.ximage->width;
4444  windows->magnify.y=(int)
4445    height*windows->magnify.y/windows->image.ximage->height;
4446  windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4447  windows->image.y=(int)
4448    (height*windows->image.y/windows->image.ximage->height);
4449  status=XMakeImage(display,resource_info,&windows->image,image,
4450    (unsigned int) width,(unsigned int) height,exception);
4451  if (IfMagickFalse(status) )
4452    XNoticeWidget(display,windows,"Unable to configure X image:",
4453      windows->image.name);
4454  /*
4455    Notify window manager of the new configuration.
4456  */
4457  if (resource_info->image_geometry != (char *) NULL)
4458    (void) FormatLocaleString(geometry,MagickPathExtent,"%s>!",
4459      resource_info->image_geometry);
4460  else
4461    (void) FormatLocaleString(geometry,MagickPathExtent,"%ux%u+0+0>!",
4462      XDisplayWidth(display,windows->image.screen),
4463      XDisplayHeight(display,windows->image.screen));
4464  (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4465  window_changes.width=(int) width;
4466  if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4467    window_changes.width=XDisplayWidth(display,windows->image.screen);
4468  window_changes.height=(int) height;
4469  if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4470    window_changes.height=XDisplayHeight(display,windows->image.screen);
4471  mask=(size_t) (CWWidth | CWHeight);
4472  if (resource_info->backdrop)
4473    {
4474      mask|=CWX | CWY;
4475      window_changes.x=(int)
4476        ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4477      window_changes.y=(int)
4478        ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4479    }
4480  (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4481    (unsigned int) mask,&window_changes);
4482  (void) XClearWindow(display,windows->image.id);
4483  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4484  /*
4485    Update Magnify window configuration.
4486  */
4487  if (IfMagickTrue(windows->magnify.mapped) )
4488    XMakeMagnifyImage(display,windows,exception);
4489  windows->pan.crop_geometry=windows->image.crop_geometry;
4490  XBestIconSize(display,&windows->pan,image);
4491  while (((windows->pan.width << 1) < MaxIconSize) &&
4492         ((windows->pan.height << 1) < MaxIconSize))
4493  {
4494    windows->pan.width<<=1;
4495    windows->pan.height<<=1;
4496  }
4497  if (windows->pan.geometry != (char *) NULL)
4498    (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4499      &windows->pan.width,&windows->pan.height);
4500  window_changes.width=(int) windows->pan.width;
4501  window_changes.height=(int) windows->pan.height;
4502  size_hints=XAllocSizeHints();
4503  if (size_hints != (XSizeHints *) NULL)
4504    {
4505      /*
4506        Set new size hints.
4507      */
4508      size_hints->flags=PSize | PMinSize | PMaxSize;
4509      size_hints->width=window_changes.width;
4510      size_hints->height=window_changes.height;
4511      size_hints->min_width=size_hints->width;
4512      size_hints->min_height=size_hints->height;
4513      size_hints->max_width=size_hints->width;
4514      size_hints->max_height=size_hints->height;
4515      (void) XSetNormalHints(display,windows->pan.id,size_hints);
4516      (void) XFree((void *) size_hints);
4517    }
4518  (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4519    (unsigned int) (CWWidth | CWHeight),&window_changes);
4520  /*
4521    Update icon window configuration.
4522  */
4523  windows->icon.crop_geometry=windows->image.crop_geometry;
4524  XBestIconSize(display,&windows->icon,image);
4525  window_changes.width=(int) windows->icon.width;
4526  window_changes.height=(int) windows->icon.height;
4527  (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4528    (unsigned int) (CWWidth | CWHeight),&window_changes);
4529  XSetCursorState(display,windows,MagickFalse);
4530  return(IsMagickTrue(status));
4531}
4532
4533/*
4534%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4535%                                                                             %
4536%                                                                             %
4537%                                                                             %
4538+   X C r o p I m a g e                                                       %
4539%                                                                             %
4540%                                                                             %
4541%                                                                             %
4542%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4543%
4544%  XCropImage() allows the user to select a region of the image and crop, copy,
4545%  or cut it.  For copy or cut, the image can subsequently be composited onto
4546%  the image with XPasteImage.
4547%
4548%  The format of the XCropImage method is:
4549%
4550%      MagickBooleanType XCropImage(Display *display,
4551%        XResourceInfo *resource_info,XWindows *windows,Image *image,
4552%        const ClipboardMode mode,ExceptionInfo *exception)
4553%
4554%  A description of each parameter follows:
4555%
4556%    o display: Specifies a connection to an X server; returned from
4557%      XOpenDisplay.
4558%
4559%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4560%
4561%    o windows: Specifies a pointer to a XWindows structure.
4562%
4563%    o image: the image; returned from ReadImage.
4564%
4565%    o mode: This unsigned value specified whether the image should be
4566%      cropped, copied, or cut.
4567%
4568%    o exception: return any errors or warnings in this structure.
4569%
4570*/
4571static MagickBooleanType XCropImage(Display *display,
4572  XResourceInfo *resource_info,XWindows *windows,Image *image,
4573  const ClipboardMode mode,ExceptionInfo *exception)
4574{
4575  static const char
4576    *CropModeMenu[] =
4577    {
4578      "Help",
4579      "Dismiss",
4580      (char *) NULL
4581    },
4582    *RectifyModeMenu[] =
4583    {
4584      "Crop",
4585      "Help",
4586      "Dismiss",
4587      (char *) NULL
4588    };
4589
4590  static const ModeType
4591    CropCommands[] =
4592    {
4593      CropHelpCommand,
4594      CropDismissCommand
4595    },
4596    RectifyCommands[] =
4597    {
4598      RectifyCopyCommand,
4599      RectifyHelpCommand,
4600      RectifyDismissCommand
4601    };
4602
4603  CacheView
4604    *image_view;
4605
4606  char
4607    command[MagickPathExtent],
4608    text[MagickPathExtent];
4609
4610  Cursor
4611    cursor;
4612
4613  int
4614    id,
4615    x,
4616    y;
4617
4618  KeySym
4619    key_symbol;
4620
4621  Image
4622    *crop_image;
4623
4624  double
4625    scale_factor;
4626
4627  RectangleInfo
4628    crop_info,
4629    highlight_info;
4630
4631  register Quantum
4632    *q;
4633
4634  unsigned int
4635    height,
4636    width;
4637
4638  size_t
4639    state;
4640
4641  XEvent
4642    event;
4643
4644  /*
4645    Map Command widget.
4646  */
4647  switch (mode)
4648  {
4649    case CopyMode:
4650    {
4651      (void) CloneString(&windows->command.name,"Copy");
4652      break;
4653    }
4654    case CropMode:
4655    {
4656      (void) CloneString(&windows->command.name,"Crop");
4657      break;
4658    }
4659    case CutMode:
4660    {
4661      (void) CloneString(&windows->command.name,"Cut");
4662      break;
4663    }
4664  }
4665  RectifyModeMenu[0]=windows->command.name;
4666  windows->command.data=0;
4667  (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4668  (void) XMapRaised(display,windows->command.id);
4669  XClientMessage(display,windows->image.id,windows->im_protocols,
4670    windows->im_update_widget,CurrentTime);
4671  /*
4672    Track pointer until button 1 is pressed.
4673  */
4674  XQueryPosition(display,windows->image.id,&x,&y);
4675  (void) XSelectInput(display,windows->image.id,
4676    windows->image.attributes.event_mask | PointerMotionMask);
4677  crop_info.x=(ssize_t) windows->image.x+x;
4678  crop_info.y=(ssize_t) windows->image.y+y;
4679  crop_info.width=0;
4680  crop_info.height=0;
4681  cursor=XCreateFontCursor(display,XC_fleur);
4682  state=DefaultState;
4683  do
4684  {
4685    if (IfMagickTrue(windows->info.mapped) )
4686      {
4687        /*
4688          Display pointer position.
4689        */
4690        (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
4691          (long) crop_info.x,(long) crop_info.y);
4692        XInfoWidget(display,windows,text);
4693      }
4694    /*
4695      Wait for next event.
4696    */
4697    XScreenEvent(display,windows,&event,exception);
4698    if (event.xany.window == windows->command.id)
4699      {
4700        /*
4701          Select a command from the Command widget.
4702        */
4703        id=XCommandWidget(display,windows,CropModeMenu,&event);
4704        if (id < 0)
4705          continue;
4706        switch (CropCommands[id])
4707        {
4708          case CropHelpCommand:
4709          {
4710            switch (mode)
4711            {
4712              case CopyMode:
4713              {
4714                XTextViewWidget(display,resource_info,windows,MagickFalse,
4715                  "Help Viewer - Image Copy",ImageCopyHelp);
4716                break;
4717              }
4718              case CropMode:
4719              {
4720                XTextViewWidget(display,resource_info,windows,MagickFalse,
4721                  "Help Viewer - Image Crop",ImageCropHelp);
4722                break;
4723              }
4724              case CutMode:
4725              {
4726                XTextViewWidget(display,resource_info,windows,MagickFalse,
4727                  "Help Viewer - Image Cut",ImageCutHelp);
4728                break;
4729              }
4730            }
4731            break;
4732          }
4733          case CropDismissCommand:
4734          {
4735            /*
4736              Prematurely exit.
4737            */
4738            state|=EscapeState;
4739            state|=ExitState;
4740            break;
4741          }
4742          default:
4743            break;
4744        }
4745        continue;
4746      }
4747    switch (event.type)
4748    {
4749      case ButtonPress:
4750      {
4751        if (event.xbutton.button != Button1)
4752          break;
4753        if (event.xbutton.window != windows->image.id)
4754          break;
4755        /*
4756          Note first corner of cropping rectangle-- exit loop.
4757        */
4758        (void) XCheckDefineCursor(display,windows->image.id,cursor);
4759        crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4760        crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4761        state|=ExitState;
4762        break;
4763      }
4764      case ButtonRelease:
4765        break;
4766      case Expose:
4767        break;
4768      case KeyPress:
4769      {
4770        if (event.xkey.window != windows->image.id)
4771          break;
4772        /*
4773          Respond to a user key press.
4774        */
4775        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4776          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4777        switch ((int) key_symbol)
4778        {
4779          case XK_Escape:
4780          case XK_F20:
4781          {
4782            /*
4783              Prematurely exit.
4784            */
4785            state|=EscapeState;
4786            state|=ExitState;
4787            break;
4788          }
4789          case XK_F1:
4790          case XK_Help:
4791          {
4792            switch (mode)
4793            {
4794              case CopyMode:
4795              {
4796                XTextViewWidget(display,resource_info,windows,MagickFalse,
4797                  "Help Viewer - Image Copy",ImageCopyHelp);
4798                break;
4799              }
4800              case CropMode:
4801              {
4802                XTextViewWidget(display,resource_info,windows,MagickFalse,
4803                  "Help Viewer - Image Crop",ImageCropHelp);
4804                break;
4805              }
4806              case CutMode:
4807              {
4808                XTextViewWidget(display,resource_info,windows,MagickFalse,
4809                  "Help Viewer - Image Cut",ImageCutHelp);
4810                break;
4811              }
4812            }
4813            break;
4814          }
4815          default:
4816          {
4817            (void) XBell(display,0);
4818            break;
4819          }
4820        }
4821        break;
4822      }
4823      case MotionNotify:
4824      {
4825        if (event.xmotion.window != windows->image.id)
4826          break;
4827        /*
4828          Map and unmap Info widget as text cursor crosses its boundaries.
4829        */
4830        x=event.xmotion.x;
4831        y=event.xmotion.y;
4832        if (IfMagickTrue(windows->info.mapped) )
4833          {
4834            if ((x < (int) (windows->info.x+windows->info.width)) &&
4835                (y < (int) (windows->info.y+windows->info.height)))
4836              (void) XWithdrawWindow(display,windows->info.id,
4837                windows->info.screen);
4838          }
4839        else
4840          if ((x > (int) (windows->info.x+windows->info.width)) ||
4841              (y > (int) (windows->info.y+windows->info.height)))
4842            (void) XMapWindow(display,windows->info.id);
4843        crop_info.x=(ssize_t) windows->image.x+x;
4844        crop_info.y=(ssize_t) windows->image.y+y;
4845        break;
4846      }
4847      default:
4848        break;
4849    }
4850  } while ((state & ExitState) == 0);
4851  (void) XSelectInput(display,windows->image.id,
4852    windows->image.attributes.event_mask);
4853  if ((state & EscapeState) != 0)
4854    {
4855      /*
4856        User want to exit without cropping.
4857      */
4858      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4859      (void) XFreeCursor(display,cursor);
4860      return(MagickTrue);
4861    }
4862  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4863  do
4864  {
4865    /*
4866      Size rectangle as pointer moves until the mouse button is released.
4867    */
4868    x=(int) crop_info.x;
4869    y=(int) crop_info.y;
4870    crop_info.width=0;
4871    crop_info.height=0;
4872    state=DefaultState;
4873    do
4874    {
4875      highlight_info=crop_info;
4876      highlight_info.x=crop_info.x-windows->image.x;
4877      highlight_info.y=crop_info.y-windows->image.y;
4878      if ((highlight_info.width > 3) && (highlight_info.height > 3))
4879        {
4880          /*
4881            Display info and draw cropping rectangle.
4882          */
4883          if (IfMagickFalse(windows->info.mapped) )
4884            (void) XMapWindow(display,windows->info.id);
4885          (void) FormatLocaleString(text,MagickPathExtent,
4886            " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4887            crop_info.height,(double) crop_info.x,(double) crop_info.y);
4888          XInfoWidget(display,windows,text);
4889          XHighlightRectangle(display,windows->image.id,
4890            windows->image.highlight_context,&highlight_info);
4891        }
4892      else
4893        if (IfMagickTrue(windows->info.mapped) )
4894          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4895      /*
4896        Wait for next event.
4897      */
4898      XScreenEvent(display,windows,&event,exception);
4899      if ((highlight_info.width > 3) && (highlight_info.height > 3))
4900        XHighlightRectangle(display,windows->image.id,
4901          windows->image.highlight_context,&highlight_info);
4902      switch (event.type)
4903      {
4904        case ButtonPress:
4905        {
4906          crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4907          crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4908          break;
4909        }
4910        case ButtonRelease:
4911        {
4912          /*
4913            User has committed to cropping rectangle.
4914          */
4915          crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4916          crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4917          XSetCursorState(display,windows,MagickFalse);
4918          state|=ExitState;
4919          windows->command.data=0;
4920          (void) XCommandWidget(display,windows,RectifyModeMenu,
4921            (XEvent *) NULL);
4922          break;
4923        }
4924        case Expose:
4925          break;
4926        case MotionNotify:
4927        {
4928          crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4929          crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4930        }
4931        default:
4932          break;
4933      }
4934      if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4935          ((state & ExitState) != 0))
4936        {
4937          /*
4938            Check boundary conditions.
4939          */
4940          if (crop_info.x < 0)
4941            crop_info.x=0;
4942          else
4943            if (crop_info.x > (ssize_t) windows->image.ximage->width)
4944              crop_info.x=(ssize_t) windows->image.ximage->width;
4945          if ((int) crop_info.x < x)
4946            crop_info.width=(unsigned int) (x-crop_info.x);
4947          else
4948            {
4949              crop_info.width=(unsigned int) (crop_info.x-x);
4950              crop_info.x=(ssize_t) x;
4951            }
4952          if (crop_info.y < 0)
4953            crop_info.y=0;
4954          else
4955            if (crop_info.y > (ssize_t) windows->image.ximage->height)
4956              crop_info.y=(ssize_t) windows->image.ximage->height;
4957          if ((int) crop_info.y < y)
4958            crop_info.height=(unsigned int) (y-crop_info.y);
4959          else
4960            {
4961              crop_info.height=(unsigned int) (crop_info.y-y);
4962              crop_info.y=(ssize_t) y;
4963            }
4964        }
4965    } while ((state & ExitState) == 0);
4966    /*
4967      Wait for user to grab a corner of the rectangle or press return.
4968    */
4969    state=DefaultState;
4970    (void) XMapWindow(display,windows->info.id);
4971    do
4972    {
4973      if (IfMagickTrue(windows->info.mapped) )
4974        {
4975          /*
4976            Display pointer position.
4977          */
4978          (void) FormatLocaleString(text,MagickPathExtent,
4979            " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4980            crop_info.height,(double) crop_info.x,(double) crop_info.y);
4981          XInfoWidget(display,windows,text);
4982        }
4983      highlight_info=crop_info;
4984      highlight_info.x=crop_info.x-windows->image.x;
4985      highlight_info.y=crop_info.y-windows->image.y;
4986      if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4987        {
4988          state|=EscapeState;
4989          state|=ExitState;
4990          break;
4991        }
4992      XHighlightRectangle(display,windows->image.id,
4993        windows->image.highlight_context,&highlight_info);
4994      XScreenEvent(display,windows,&event,exception);
4995      if (event.xany.window == windows->command.id)
4996        {
4997          /*
4998            Select a command from the Command widget.
4999          */
5000          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5001          id=XCommandWidget(display,windows,RectifyModeMenu,&event);
5002          (void) XSetFunction(display,windows->image.highlight_context,
5003            GXinvert);
5004          XHighlightRectangle(display,windows->image.id,
5005            windows->image.highlight_context,&highlight_info);
5006          if (id >= 0)
5007            switch (RectifyCommands[id])
5008            {
5009              case RectifyCopyCommand:
5010              {
5011                state|=ExitState;
5012                break;
5013              }
5014              case RectifyHelpCommand:
5015              {
5016                (void) XSetFunction(display,windows->image.highlight_context,
5017                  GXcopy);
5018                switch (mode)
5019                {
5020                  case CopyMode:
5021                  {
5022                    XTextViewWidget(display,resource_info,windows,MagickFalse,
5023                      "Help Viewer - Image Copy",ImageCopyHelp);
5024                    break;
5025                  }
5026                  case CropMode:
5027                  {
5028                    XTextViewWidget(display,resource_info,windows,MagickFalse,
5029                      "Help Viewer - Image Crop",ImageCropHelp);
5030                    break;
5031                  }
5032                  case CutMode:
5033                  {
5034                    XTextViewWidget(display,resource_info,windows,MagickFalse,
5035                      "Help Viewer - Image Cut",ImageCutHelp);
5036                    break;
5037                  }
5038                }
5039                (void) XSetFunction(display,windows->image.highlight_context,
5040                  GXinvert);
5041                break;
5042              }
5043              case RectifyDismissCommand:
5044              {
5045                /*
5046                  Prematurely exit.
5047                */
5048                state|=EscapeState;
5049                state|=ExitState;
5050                break;
5051              }
5052              default:
5053                break;
5054            }
5055          continue;
5056        }
5057      XHighlightRectangle(display,windows->image.id,
5058        windows->image.highlight_context,&highlight_info);
5059      switch (event.type)
5060      {
5061        case ButtonPress:
5062        {
5063          if (event.xbutton.button != Button1)
5064            break;
5065          if (event.xbutton.window != windows->image.id)
5066            break;
5067          x=windows->image.x+event.xbutton.x;
5068          y=windows->image.y+event.xbutton.y;
5069          if ((x < (int) (crop_info.x+RoiDelta)) &&
5070              (x > (int) (crop_info.x-RoiDelta)) &&
5071              (y < (int) (crop_info.y+RoiDelta)) &&
5072              (y > (int) (crop_info.y-RoiDelta)))
5073            {
5074              crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5075              crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5076              state|=UpdateConfigurationState;
5077              break;
5078            }
5079          if ((x < (int) (crop_info.x+RoiDelta)) &&
5080              (x > (int) (crop_info.x-RoiDelta)) &&
5081              (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5082              (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5083            {
5084              crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5085              state|=UpdateConfigurationState;
5086              break;
5087            }
5088          if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5089              (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5090              (y < (int) (crop_info.y+RoiDelta)) &&
5091              (y > (int) (crop_info.y-RoiDelta)))
5092            {
5093              crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5094              state|=UpdateConfigurationState;
5095              break;
5096            }
5097          if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5098              (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5099              (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5100              (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5101            {
5102              state|=UpdateConfigurationState;
5103              break;
5104            }
5105        }
5106        case ButtonRelease:
5107        {
5108          if (event.xbutton.window == windows->pan.id)
5109            if ((highlight_info.x != crop_info.x-windows->image.x) ||
5110                (highlight_info.y != crop_info.y-windows->image.y))
5111              XHighlightRectangle(display,windows->image.id,
5112                windows->image.highlight_context,&highlight_info);
5113          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5114            event.xbutton.time);
5115          break;
5116        }
5117        case Expose:
5118        {
5119          if (event.xexpose.window == windows->image.id)
5120            if (event.xexpose.count == 0)
5121              {
5122                event.xexpose.x=(int) highlight_info.x;
5123                event.xexpose.y=(int) highlight_info.y;
5124                event.xexpose.width=(int) highlight_info.width;
5125                event.xexpose.height=(int) highlight_info.height;
5126                XRefreshWindow(display,&windows->image,&event);
5127              }
5128          if (event.xexpose.window == windows->info.id)
5129            if (event.xexpose.count == 0)
5130              XInfoWidget(display,windows,text);
5131          break;
5132        }
5133        case KeyPress:
5134        {
5135          if (event.xkey.window != windows->image.id)
5136            break;
5137          /*
5138            Respond to a user key press.
5139          */
5140          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5141            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5142          switch ((int) key_symbol)
5143          {
5144            case XK_Escape:
5145            case XK_F20:
5146              state|=EscapeState;
5147            case XK_Return:
5148            {
5149              state|=ExitState;
5150              break;
5151            }
5152            case XK_Home:
5153            case XK_KP_Home:
5154            {
5155              crop_info.x=(ssize_t) (windows->image.width/2L-crop_info.width/
5156                2L);
5157              crop_info.y=(ssize_t) (windows->image.height/2L-crop_info.height/
5158                2L);
5159              break;
5160            }
5161            case XK_Left:
5162            case XK_KP_Left:
5163            {
5164              crop_info.x--;
5165              break;
5166            }
5167            case XK_Up:
5168            case XK_KP_Up:
5169            case XK_Next:
5170            {
5171              crop_info.y--;
5172              break;
5173            }
5174            case XK_Right:
5175            case XK_KP_Right:
5176            {
5177              crop_info.x++;
5178              break;
5179            }
5180            case XK_Prior:
5181            case XK_Down:
5182            case XK_KP_Down:
5183            {
5184              crop_info.y++;
5185              break;
5186            }
5187            case XK_F1:
5188            case XK_Help:
5189            {
5190              (void) XSetFunction(display,windows->image.highlight_context,
5191                GXcopy);
5192              switch (mode)
5193              {
5194                case CopyMode:
5195                {
5196                  XTextViewWidget(display,resource_info,windows,MagickFalse,
5197                    "Help Viewer - Image Copy",ImageCopyHelp);
5198                  break;
5199                }
5200                case CropMode:
5201                {
5202                  XTextViewWidget(display,resource_info,windows,MagickFalse,
5203                    "Help Viewer - Image Cropg",ImageCropHelp);
5204                  break;
5205                }
5206                case CutMode:
5207                {
5208                  XTextViewWidget(display,resource_info,windows,MagickFalse,
5209                    "Help Viewer - Image Cutg",ImageCutHelp);
5210                  break;
5211                }
5212              }
5213              (void) XSetFunction(display,windows->image.highlight_context,
5214                GXinvert);
5215              break;
5216            }
5217            default:
5218            {
5219              (void) XBell(display,0);
5220              break;
5221            }
5222          }
5223          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5224            event.xkey.time);
5225          break;
5226        }
5227        case KeyRelease:
5228          break;
5229        case MotionNotify:
5230        {
5231          if (event.xmotion.window != windows->image.id)
5232            break;
5233          /*
5234            Map and unmap Info widget as text cursor crosses its boundaries.
5235          */
5236          x=event.xmotion.x;
5237          y=event.xmotion.y;
5238          if (IfMagickTrue(windows->info.mapped) )
5239            {
5240              if ((x < (int) (windows->info.x+windows->info.width)) &&
5241                  (y < (int) (windows->info.y+windows->info.height)))
5242                (void) XWithdrawWindow(display,windows->info.id,
5243                  windows->info.screen);
5244            }
5245          else
5246            if ((x > (int) (windows->info.x+windows->info.width)) ||
5247                (y > (int) (windows->info.y+windows->info.height)))
5248              (void) XMapWindow(display,windows->info.id);
5249          crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5250          crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5251          break;
5252        }
5253        case SelectionRequest:
5254        {
5255          XSelectionEvent
5256            notify;
5257
5258          XSelectionRequestEvent
5259            *request;
5260
5261          /*
5262            Set primary selection.
5263          */
5264          (void) FormatLocaleString(text,MagickPathExtent,
5265            "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5266            crop_info.height,(double) crop_info.x,(double) crop_info.y);
5267          request=(&(event.xselectionrequest));
5268          (void) XChangeProperty(request->display,request->requestor,
5269            request->property,request->target,8,PropModeReplace,
5270            (unsigned char *) text,(int) strlen(text));
5271          notify.type=SelectionNotify;
5272          notify.display=request->display;
5273          notify.requestor=request->requestor;
5274          notify.selection=request->selection;
5275          notify.target=request->target;
5276          notify.time=request->time;
5277          if (request->property == None)
5278            notify.property=request->target;
5279          else
5280            notify.property=request->property;
5281          (void) XSendEvent(request->display,request->requestor,False,0,
5282            (XEvent *) &notify);
5283        }
5284        default:
5285          break;
5286      }
5287      if ((state & UpdateConfigurationState) != 0)
5288        {
5289          (void) XPutBackEvent(display,&event);
5290          (void) XCheckDefineCursor(display,windows->image.id,cursor);
5291          break;
5292        }
5293    } while ((state & ExitState) == 0);
5294  } while ((state & ExitState) == 0);
5295  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5296  XSetCursorState(display,windows,MagickFalse);
5297  if ((state & EscapeState) != 0)
5298    return(MagickTrue);
5299  if (mode == CropMode)
5300    if (((int) crop_info.width != windows->image.ximage->width) ||
5301        ((int) crop_info.height != windows->image.ximage->height))
5302      {
5303        /*
5304          Reconfigure Image window as defined by cropping rectangle.
5305        */
5306        XSetCropGeometry(display,windows,&crop_info,image);
5307        windows->image.window_changes.width=(int) crop_info.width;
5308        windows->image.window_changes.height=(int) crop_info.height;
5309        (void) XConfigureImage(display,resource_info,windows,image,exception);
5310        return(MagickTrue);
5311      }
5312  /*
5313    Copy image before applying image transforms.
5314  */
5315  XSetCursorState(display,windows,MagickTrue);
5316  XCheckRefreshWindows(display,windows);
5317  width=(unsigned int) image->columns;
5318  height=(unsigned int) image->rows;
5319  x=0;
5320  y=0;
5321  if (windows->image.crop_geometry != (char *) NULL)
5322    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5323  scale_factor=(double) width/windows->image.ximage->width;
5324  crop_info.x+=x;
5325  crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5326  crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5327  scale_factor=(double) height/windows->image.ximage->height;
5328  crop_info.y+=y;
5329  crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5330  crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5331  crop_image=CropImage(image,&crop_info,exception);
5332  XSetCursorState(display,windows,MagickFalse);
5333  if (crop_image == (Image *) NULL)
5334    return(MagickFalse);
5335  if (resource_info->copy_image != (Image *) NULL)
5336    resource_info->copy_image=DestroyImage(resource_info->copy_image);
5337  resource_info->copy_image=crop_image;
5338  if (mode == CopyMode)
5339    {
5340      (void) XConfigureImage(display,resource_info,windows,image,exception);
5341      return(MagickTrue);
5342    }
5343  /*
5344    Cut image.
5345  */
5346  if (IfMagickFalse(SetImageStorageClass(image,DirectClass,exception)) )
5347    return(MagickFalse);
5348  image->alpha_trait=BlendPixelTrait;
5349  image_view=AcquireAuthenticCacheView(image,exception);
5350  for (y=0; y < (int) crop_info.height; y++)
5351  {
5352    q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5353      crop_info.width,1,exception);
5354    if (q == (Quantum *) NULL)
5355      break;
5356    for (x=0; x < (int) crop_info.width; x++)
5357    {
5358      SetPixelAlpha(image,TransparentAlpha,q);
5359      q+=GetPixelChannels(image);
5360    }
5361    if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
5362      break;
5363  }
5364  image_view=DestroyCacheView(image_view);
5365  /*
5366    Update image configuration.
5367  */
5368  XConfigureImageColormap(display,resource_info,windows,image,exception);
5369  (void) XConfigureImage(display,resource_info,windows,image,exception);
5370  return(MagickTrue);
5371}
5372
5373/*
5374%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5375%                                                                             %
5376%                                                                             %
5377%                                                                             %
5378+   X D r a w I m a g e                                                       %
5379%                                                                             %
5380%                                                                             %
5381%                                                                             %
5382%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5383%
5384%  XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5385%  the image.
5386%
5387%  The format of the XDrawEditImage method is:
5388%
5389%      MagickBooleanType XDrawEditImage(Display *display,
5390%        XResourceInfo *resource_info,XWindows *windows,Image **image,
5391%        ExceptionInfo *exception)
5392%
5393%  A description of each parameter follows:
5394%
5395%    o display: Specifies a connection to an X server; returned from
5396%      XOpenDisplay.
5397%
5398%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5399%
5400%    o windows: Specifies a pointer to a XWindows structure.
5401%
5402%    o image: the image.
5403%
5404%    o exception: return any errors or warnings in this structure.
5405%
5406*/
5407static MagickBooleanType XDrawEditImage(Display *display,
5408  XResourceInfo *resource_info,XWindows *windows,Image **image,
5409  ExceptionInfo *exception)
5410{
5411  static const char
5412    *DrawMenu[] =
5413    {
5414      "Element",
5415      "Color",
5416      "Stipple",
5417      "Width",
5418      "Undo",
5419      "Help",
5420      "Dismiss",
5421      (char *) NULL
5422    };
5423
5424  static ElementType
5425    element = PointElement;
5426
5427  static const ModeType
5428    DrawCommands[] =
5429    {
5430      DrawElementCommand,
5431      DrawColorCommand,
5432      DrawStippleCommand,
5433      DrawWidthCommand,
5434      DrawUndoCommand,
5435      DrawHelpCommand,
5436      DrawDismissCommand
5437    };
5438
5439  static Pixmap
5440    stipple = (Pixmap) NULL;
5441
5442  static unsigned int
5443    pen_id = 0,
5444    line_width = 1;
5445
5446  char
5447    command[MagickPathExtent],
5448    text[MagickPathExtent];
5449
5450  Cursor
5451    cursor;
5452
5453  int
5454    entry,
5455    id,
5456    number_coordinates,
5457    x,
5458    y;
5459
5460  double
5461    degrees;
5462
5463  MagickStatusType
5464    status;
5465
5466  RectangleInfo
5467    rectangle_info;
5468
5469  register int
5470    i;
5471
5472  unsigned int
5473    distance,
5474    height,
5475    max_coordinates,
5476    width;
5477
5478  size_t
5479    state;
5480
5481  Window
5482    root_window;
5483
5484  XDrawInfo
5485    draw_info;
5486
5487  XEvent
5488    event;
5489
5490  XPoint
5491    *coordinate_info;
5492
5493  XSegment
5494    line_info;
5495
5496  /*
5497    Allocate polygon info.
5498  */
5499  max_coordinates=2048;
5500  coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5501    sizeof(*coordinate_info));
5502  if (coordinate_info == (XPoint *) NULL)
5503    {
5504      (void) ThrowMagickException(exception,GetMagickModule(),
5505        ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5506      return(MagickFalse);
5507    }
5508  /*
5509    Map Command widget.
5510  */
5511  (void) CloneString(&windows->command.name,"Draw");
5512  windows->command.data=4;
5513  (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5514  (void) XMapRaised(display,windows->command.id);
5515  XClientMessage(display,windows->image.id,windows->im_protocols,
5516    windows->im_update_widget,CurrentTime);
5517  /*
5518    Wait for first button press.
5519  */
5520  root_window=XRootWindow(display,XDefaultScreen(display));
5521  draw_info.stencil=OpaqueStencil;
5522  status=MagickTrue;
5523  cursor=XCreateFontCursor(display,XC_tcross);
5524  for ( ; ; )
5525  {
5526    XQueryPosition(display,windows->image.id,&x,&y);
5527    (void) XSelectInput(display,windows->image.id,
5528      windows->image.attributes.event_mask | PointerMotionMask);
5529    (void) XCheckDefineCursor(display,windows->image.id,cursor);
5530    state=DefaultState;
5531    do
5532    {
5533      if (IfMagickTrue(windows->info.mapped) )
5534        {
5535          /*
5536            Display pointer position.
5537          */
5538          (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
5539            x+windows->image.x,y+windows->image.y);
5540          XInfoWidget(display,windows,text);
5541        }
5542      /*
5543        Wait for next event.
5544      */
5545      XScreenEvent(display,windows,&event,exception);
5546      if (event.xany.window == windows->command.id)
5547        {
5548          /*
5549            Select a command from the Command widget.
5550          */
5551          id=XCommandWidget(display,windows,DrawMenu,&event);
5552          if (id < 0)
5553            continue;
5554          switch (DrawCommands[id])
5555          {
5556            case DrawElementCommand:
5557            {
5558              static const char
5559                *Elements[] =
5560                {
5561                  "point",
5562                  "line",
5563                  "rectangle",
5564                  "fill rectangle",
5565                  "circle",
5566                  "fill circle",
5567                  "ellipse",
5568                  "fill ellipse",
5569                  "polygon",
5570                  "fill polygon",
5571                  (char *) NULL,
5572                };
5573
5574              /*
5575                Select a command from the pop-up menu.
5576              */
5577              element=(ElementType) (XMenuWidget(display,windows,
5578                DrawMenu[id],Elements,command)+1);
5579              break;
5580            }
5581            case DrawColorCommand:
5582            {
5583              const char
5584                *ColorMenu[MaxNumberPens+1];
5585
5586              int
5587                pen_number;
5588
5589              MagickBooleanType
5590                transparent;
5591
5592              XColor
5593                color;
5594
5595              /*
5596                Initialize menu selections.
5597              */
5598              for (i=0; i < (int) (MaxNumberPens-2); i++)
5599                ColorMenu[i]=resource_info->pen_colors[i];
5600              ColorMenu[MaxNumberPens-2]="transparent";
5601              ColorMenu[MaxNumberPens-1]="Browser...";
5602              ColorMenu[MaxNumberPens]=(char *) NULL;
5603              /*
5604                Select a pen color from the pop-up menu.
5605              */
5606              pen_number=XMenuWidget(display,windows,DrawMenu[id],
5607                (const char **) ColorMenu,command);
5608              if (pen_number < 0)
5609                break;
5610              transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5611                MagickFalse;
5612              if (IfMagickTrue(transparent) )
5613                {
5614                  draw_info.stencil=TransparentStencil;
5615                  break;
5616                }
5617              if (pen_number == (MaxNumberPens-1))
5618                {
5619                  static char
5620                    color_name[MagickPathExtent] = "gray";
5621
5622                  /*
5623                    Select a pen color from a dialog.
5624                  */
5625                  resource_info->pen_colors[pen_number]=color_name;
5626                  XColorBrowserWidget(display,windows,"Select",color_name);
5627                  if (*color_name == '\0')
5628                    break;
5629                }
5630              /*
5631                Set pen color.
5632              */
5633              (void) XParseColor(display,windows->map_info->colormap,
5634                resource_info->pen_colors[pen_number],&color);
5635              XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5636                (unsigned int) MaxColors,&color);
5637              windows->pixel_info->pen_colors[pen_number]=color;
5638              pen_id=(unsigned int) pen_number;
5639              draw_info.stencil=OpaqueStencil;
5640              break;
5641            }
5642            case DrawStippleCommand:
5643            {
5644              Image
5645                *stipple_image;
5646
5647              ImageInfo
5648                *image_info;
5649
5650              int
5651                status;
5652
5653              static char
5654                filename[MagickPathExtent] = "\0";
5655
5656              static const char
5657                *StipplesMenu[] =
5658                {
5659                  "Brick",
5660                  "Diagonal",
5661                  "Scales",
5662                  "Vertical",
5663                  "Wavy",
5664                  "Translucent",
5665                  "Opaque",
5666                  (char *) NULL,
5667                  (char *) NULL,
5668                };
5669
5670              /*
5671                Select a command from the pop-up menu.
5672              */
5673              StipplesMenu[7]="Open...";
5674              entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5675                command);
5676              if (entry < 0)
5677                break;
5678              if (stipple != (Pixmap) NULL)
5679                (void) XFreePixmap(display,stipple);
5680              stipple=(Pixmap) NULL;
5681              if (entry != 7)
5682                {
5683                  switch (entry)
5684                  {
5685                    case 0:
5686                    {
5687                      stipple=XCreateBitmapFromData(display,root_window,
5688                        (char *) BricksBitmap,BricksWidth,BricksHeight);
5689                      break;
5690                    }
5691                    case 1:
5692                    {
5693                      stipple=XCreateBitmapFromData(display,root_window,
5694                        (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5695                      break;
5696                    }
5697                    case 2:
5698                    {
5699                      stipple=XCreateBitmapFromData(display,root_window,
5700                        (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5701                      break;
5702                    }
5703                    case 3:
5704                    {
5705                      stipple=XCreateBitmapFromData(display,root_window,
5706                        (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5707                      break;
5708                    }
5709                    case 4:
5710                    {
5711                      stipple=XCreateBitmapFromData(display,root_window,
5712                        (char *) WavyBitmap,WavyWidth,WavyHeight);
5713                      break;
5714                    }
5715                    case 5:
5716                    {
5717                      stipple=XCreateBitmapFromData(display,root_window,
5718                        (char *) HighlightBitmap,HighlightWidth,
5719                        HighlightHeight);
5720                      break;
5721                    }
5722                    case 6:
5723                    default:
5724                    {
5725                      stipple=XCreateBitmapFromData(display,root_window,
5726                        (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5727                      break;
5728                    }
5729                  }
5730                  break;
5731                }
5732              XFileBrowserWidget(display,windows,"Stipple",filename);
5733              if (*filename == '\0')
5734                break;
5735              /*
5736                Read image.
5737              */
5738              XSetCursorState(display,windows,MagickTrue);
5739              XCheckRefreshWindows(display,windows);
5740              image_info=AcquireImageInfo();
5741              (void) CopyMagickString(image_info->filename,filename,
5742                MagickPathExtent);
5743              stipple_image=ReadImage(image_info,exception);
5744              CatchException(exception);
5745              XSetCursorState(display,windows,MagickFalse);
5746              if (stipple_image == (Image *) NULL)
5747                break;
5748              (void) AcquireUniqueFileResource(filename);
5749              (void) FormatLocaleString(stipple_image->filename,MagickPathExtent,
5750                "xbm:%s",filename);
5751              (void) WriteImage(image_info,stipple_image,exception);
5752              stipple_image=DestroyImage(stipple_image);
5753              image_info=DestroyImageInfo(image_info);
5754              status=XReadBitmapFile(display,root_window,filename,&width,
5755                &height,&stipple,&x,&y);
5756              (void) RelinquishUniqueFileResource(filename);
5757              if ((status != BitmapSuccess) != 0)
5758                XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5759                  filename);
5760              break;
5761            }
5762            case DrawWidthCommand:
5763            {
5764              static char
5765                width[MagickPathExtent] = "0";
5766
5767              static const char
5768                *WidthsMenu[] =
5769                {
5770                  "1",
5771                  "2",
5772                  "4",
5773                  "8",
5774                  "16",
5775                  "Dialog...",
5776                  (char *) NULL,
5777                };
5778
5779              /*
5780                Select a command from the pop-up menu.
5781              */
5782              entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5783                command);
5784              if (entry < 0)
5785                break;
5786              if (entry != 5)
5787                {
5788                  line_width=(unsigned int) StringToUnsignedLong(
5789                    WidthsMenu[entry]);
5790                  break;
5791                }
5792              (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5793                width);
5794              if (*width == '\0')
5795                break;
5796              line_width=(unsigned int) StringToUnsignedLong(width);
5797              break;
5798            }
5799            case DrawUndoCommand:
5800            {
5801              (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5802                image,exception);
5803              break;
5804            }
5805            case DrawHelpCommand:
5806            {
5807              XTextViewWidget(display,resource_info,windows,MagickFalse,
5808                "Help Viewer - Image Rotation",ImageDrawHelp);
5809              (void) XCheckDefineCursor(display,windows->image.id,cursor);
5810              break;
5811            }
5812            case DrawDismissCommand:
5813            {
5814              /*
5815                Prematurely exit.
5816              */
5817              state|=EscapeState;
5818              state|=ExitState;
5819              break;
5820            }
5821            default:
5822              break;
5823          }
5824          (void) XCheckDefineCursor(display,windows->image.id,cursor);
5825          continue;
5826        }
5827      switch (event.type)
5828      {
5829        case ButtonPress:
5830        {
5831          if (event.xbutton.button != Button1)
5832            break;
5833          if (event.xbutton.window != windows->image.id)
5834            break;
5835          /*
5836            exit loop.
5837          */
5838          x=event.xbutton.x;
5839          y=event.xbutton.y;
5840          state|=ExitState;
5841          break;
5842        }
5843        case ButtonRelease:
5844          break;
5845        case Expose:
5846          break;
5847        case KeyPress:
5848        {
5849          KeySym
5850            key_symbol;
5851
5852          if (event.xkey.window != windows->image.id)
5853            break;
5854          /*
5855            Respond to a user key press.
5856          */
5857          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5858            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5859          switch ((int) key_symbol)
5860          {
5861            case XK_Escape:
5862            case XK_F20:
5863            {
5864              /*
5865                Prematurely exit.
5866              */
5867              state|=EscapeState;
5868              state|=ExitState;
5869              break;
5870            }
5871            case XK_F1:
5872            case XK_Help:
5873            {
5874              XTextViewWidget(display,resource_info,windows,MagickFalse,
5875                "Help Viewer - Image Rotation",ImageDrawHelp);
5876              break;
5877            }
5878            default:
5879            {
5880              (void) XBell(display,0);
5881              break;
5882            }
5883          }
5884          break;
5885        }
5886        case MotionNotify:
5887        {
5888          /*
5889            Map and unmap Info widget as text cursor crosses its boundaries.
5890          */
5891          x=event.xmotion.x;
5892          y=event.xmotion.y;
5893          if (IfMagickTrue(windows->info.mapped) )
5894            {
5895              if ((x < (int) (windows->info.x+windows->info.width)) &&
5896                  (y < (int) (windows->info.y+windows->info.height)))
5897                (void) XWithdrawWindow(display,windows->info.id,
5898                  windows->info.screen);
5899            }
5900          else
5901            if ((x > (int) (windows->info.x+windows->info.width)) ||
5902                (y > (int) (windows->info.y+windows->info.height)))
5903              (void) XMapWindow(display,windows->info.id);
5904          break;
5905        }
5906      }
5907    } while ((state & ExitState) == 0);
5908    (void) XSelectInput(display,windows->image.id,
5909      windows->image.attributes.event_mask);
5910    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5911    if ((state & EscapeState) != 0)
5912      break;
5913    /*
5914      Draw element as pointer moves until the button is released.
5915    */
5916    distance=0;
5917    degrees=0.0;
5918    line_info.x1=x;
5919    line_info.y1=y;
5920    line_info.x2=x;
5921    line_info.y2=y;
5922    rectangle_info.x=(ssize_t) x;
5923    rectangle_info.y=(ssize_t) y;
5924    rectangle_info.width=0;
5925    rectangle_info.height=0;
5926    number_coordinates=1;
5927    coordinate_info->x=x;
5928    coordinate_info->y=y;
5929    (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5930    state=DefaultState;
5931    do
5932    {
5933      switch (element)
5934      {
5935        case PointElement:
5936        default:
5937        {
5938          if (number_coordinates > 1)
5939            {
5940              (void) XDrawLines(display,windows->image.id,
5941                windows->image.highlight_context,coordinate_info,
5942                number_coordinates,CoordModeOrigin);
5943              (void) FormatLocaleString(text,MagickPathExtent," %+d%+d",
5944                coordinate_info[number_coordinates-1].x,
5945                coordinate_info[number_coordinates-1].y);
5946              XInfoWidget(display,windows,text);
5947            }
5948          break;
5949        }
5950        case LineElement:
5951        {
5952          if (distance > 9)
5953            {
5954              /*
5955                Display angle of the line.
5956              */
5957              degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5958                line_info.y1),(double) (line_info.x2-line_info.x1)));
5959              (void) FormatLocaleString(text,MagickPathExtent," %g",
5960                (double) degrees);
5961              XInfoWidget(display,windows,text);
5962              XHighlightLine(display,windows->image.id,
5963                windows->image.highlight_context,&line_info);
5964            }
5965          else
5966            if (IfMagickTrue(windows->info.mapped) )
5967              (void) XWithdrawWindow(display,windows->info.id,
5968                windows->info.screen);
5969          break;
5970        }
5971        case RectangleElement:
5972        case FillRectangleElement:
5973        {
5974          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5975            {
5976              /*
5977                Display info and draw drawing rectangle.
5978              */
5979              (void) FormatLocaleString(text,MagickPathExtent,
5980                " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5981                (double) rectangle_info.height,(double) rectangle_info.x,
5982                (double) rectangle_info.y);
5983              XInfoWidget(display,windows,text);
5984              XHighlightRectangle(display,windows->image.id,
5985                windows->image.highlight_context,&rectangle_info);
5986            }
5987          else
5988            if (IfMagickTrue(windows->info.mapped) )
5989              (void) XWithdrawWindow(display,windows->info.id,
5990                windows->info.screen);
5991          break;
5992        }
5993        case CircleElement:
5994        case FillCircleElement:
5995        case EllipseElement:
5996        case FillEllipseElement:
5997        {
5998          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5999            {
6000              /*
6001                Display info and draw drawing rectangle.
6002              */
6003              (void) FormatLocaleString(text,MagickPathExtent,
6004                " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
6005                (double) rectangle_info.height,(double) rectangle_info.x,
6006                (double) rectangle_info.y);
6007              XInfoWidget(display,windows,text);
6008              XHighlightEllipse(display,windows->image.id,
6009                windows->image.highlight_context,&rectangle_info);
6010            }
6011          else
6012            if (IfMagickTrue(windows->info.mapped) )
6013              (void) XWithdrawWindow(display,windows->info.id,
6014                windows->info.screen);
6015          break;
6016        }
6017        case PolygonElement:
6018        case FillPolygonElement:
6019        {
6020          if (number_coordinates > 1)
6021            (void) XDrawLines(display,windows->image.id,
6022              windows->image.highlight_context,coordinate_info,
6023              number_coordinates,CoordModeOrigin);
6024          if (distance > 9)
6025            {
6026              /*
6027                Display angle of the line.
6028              */
6029              degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
6030                line_info.y1),(double) (line_info.x2-line_info.x1)));
6031              (void) FormatLocaleString(text,MagickPathExtent," %g",
6032                (double) degrees);
6033              XInfoWidget(display,windows,text);
6034              XHighlightLine(display,windows->image.id,
6035                windows->image.highlight_context,&line_info);
6036            }
6037          else
6038            if (IfMagickTrue(windows->info.mapped) )
6039              (void) XWithdrawWindow(display,windows->info.id,
6040                windows->info.screen);
6041          break;
6042        }
6043      }
6044      /*
6045        Wait for next event.
6046      */
6047      XScreenEvent(display,windows,&event,exception);
6048      switch (element)
6049      {
6050        case PointElement:
6051        default:
6052        {
6053          if (number_coordinates > 1)
6054            (void) XDrawLines(display,windows->image.id,
6055              windows->image.highlight_context,coordinate_info,
6056              number_coordinates,CoordModeOrigin);
6057          break;
6058        }
6059        case LineElement:
6060        {
6061          if (distance > 9)
6062            XHighlightLine(display,windows->image.id,
6063              windows->image.highlight_context,&line_info);
6064          break;
6065        }
6066        case RectangleElement:
6067        case FillRectangleElement:
6068        {
6069          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6070            XHighlightRectangle(display,windows->image.id,
6071              windows->image.highlight_context,&rectangle_info);
6072          break;
6073        }
6074        case CircleElement:
6075        case FillCircleElement:
6076        case EllipseElement:
6077        case FillEllipseElement:
6078        {
6079          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6080            XHighlightEllipse(display,windows->image.id,
6081              windows->image.highlight_context,&rectangle_info);
6082          break;
6083        }
6084        case PolygonElement:
6085        case FillPolygonElement:
6086        {
6087          if (number_coordinates > 1)
6088            (void) XDrawLines(display,windows->image.id,
6089              windows->image.highlight_context,coordinate_info,
6090              number_coordinates,CoordModeOrigin);
6091          if (distance > 9)
6092            XHighlightLine(display,windows->image.id,
6093              windows->image.highlight_context,&line_info);
6094          break;
6095        }
6096      }
6097      switch (event.type)
6098      {
6099        case ButtonPress:
6100          break;
6101        case ButtonRelease:
6102        {
6103          /*
6104            User has committed to element.
6105          */
6106          line_info.x2=event.xbutton.x;
6107          line_info.y2=event.xbutton.y;
6108          rectangle_info.x=(ssize_t) event.xbutton.x;
6109          rectangle_info.y=(ssize_t) event.xbutton.y;
6110          coordinate_info[number_coordinates].x=event.xbutton.x;
6111          coordinate_info[number_coordinates].y=event.xbutton.y;
6112          if (((element != PolygonElement) &&
6113               (element != FillPolygonElement)) || (distance <= 9))
6114            {
6115              state|=ExitState;
6116              break;
6117            }
6118          number_coordinates++;
6119          if (number_coordinates < (int) max_coordinates)
6120            {
6121              line_info.x1=event.xbutton.x;
6122              line_info.y1=event.xbutton.y;
6123              break;
6124            }
6125          max_coordinates<<=1;
6126          coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6127            max_coordinates,sizeof(*coordinate_info));
6128          if (coordinate_info == (XPoint *) NULL)
6129            (void) ThrowMagickException(exception,GetMagickModule(),
6130              ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6131          break;
6132        }
6133        case Expose:
6134          break;
6135        case MotionNotify:
6136        {
6137          if (event.xmotion.window != windows->image.id)
6138            break;
6139          if (element != PointElement)
6140            {
6141              line_info.x2=event.xmotion.x;
6142              line_info.y2=event.xmotion.y;
6143              rectangle_info.x=(ssize_t) event.xmotion.x;
6144              rectangle_info.y=(ssize_t) event.xmotion.y;
6145              break;
6146            }
6147          coordinate_info[number_coordinates].x=event.xbutton.x;
6148          coordinate_info[number_coordinates].y=event.xbutton.y;
6149          number_coordinates++;
6150          if (number_coordinates < (int) max_coordinates)
6151            break;
6152          max_coordinates<<=1;
6153          coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6154            max_coordinates,sizeof(*coordinate_info));
6155          if (coordinate_info == (XPoint *) NULL)
6156            (void) ThrowMagickException(exception,GetMagickModule(),
6157              ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6158          break;
6159        }
6160        default:
6161          break;
6162      }
6163      /*
6164        Check boundary conditions.
6165      */
6166      if (line_info.x2 < 0)
6167        line_info.x2=0;
6168      else
6169        if (line_info.x2 > (int) windows->image.width)
6170          line_info.x2=(short) windows->image.width;
6171      if (line_info.y2 < 0)
6172        line_info.y2=0;
6173      else
6174        if (line_info.y2 > (int) windows->image.height)
6175          line_info.y2=(short) windows->image.height;
6176      distance=(unsigned int)
6177        (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6178         ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6179      if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6180          ((state & ExitState) != 0))
6181        {
6182          if (rectangle_info.x < 0)
6183            rectangle_info.x=0;
6184          else
6185            if (rectangle_info.x > (ssize_t) windows->image.width)
6186              rectangle_info.x=(ssize_t) windows->image.width;
6187          if ((int) rectangle_info.x < x)
6188            rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6189          else
6190            {
6191              rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6192              rectangle_info.x=(ssize_t) x;
6193            }
6194          if (rectangle_info.y < 0)
6195            rectangle_info.y=0;
6196          else
6197            if (rectangle_info.y > (ssize_t) windows->image.height)
6198              rectangle_info.y=(ssize_t) windows->image.height;
6199          if ((int) rectangle_info.y < y)
6200            rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6201          else
6202            {
6203              rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6204              rectangle_info.y=(ssize_t) y;
6205            }
6206        }
6207    } while ((state & ExitState) == 0);
6208    (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6209    if ((element == PointElement) || (element == PolygonElement) ||
6210        (element == FillPolygonElement))
6211      {
6212        /*
6213          Determine polygon bounding box.
6214        */
6215        rectangle_info.x=(ssize_t) coordinate_info->x;
6216        rectangle_info.y=(ssize_t) coordinate_info->y;
6217        x=coordinate_info->x;
6218        y=coordinate_info->y;
6219        for (i=1; i < number_coordinates; i++)
6220        {
6221          if (coordinate_info[i].x > x)
6222            x=coordinate_info[i].x;
6223          if (coordinate_info[i].y > y)
6224            y=coordinate_info[i].y;
6225          if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6226            rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6227          if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6228            rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6229        }
6230        rectangle_info.width=(size_t) (x-rectangle_info.x);
6231        rectangle_info.height=(size_t) (y-rectangle_info.y);
6232        for (i=0; i < number_coordinates; i++)
6233        {
6234          coordinate_info[i].x-=rectangle_info.x;
6235          coordinate_info[i].y-=rectangle_info.y;
6236        }
6237      }
6238    else
6239      if (distance <= 9)
6240        continue;
6241      else
6242        if ((element == RectangleElement) ||
6243            (element == CircleElement) || (element == EllipseElement))
6244          {
6245            rectangle_info.width--;
6246            rectangle_info.height--;
6247          }
6248    /*
6249      Drawing is relative to image configuration.
6250    */
6251    draw_info.x=(int) rectangle_info.x;
6252    draw_info.y=(int) rectangle_info.y;
6253    (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6254      image,exception);
6255    width=(unsigned int) (*image)->columns;
6256    height=(unsigned int) (*image)->rows;
6257    x=0;
6258    y=0;
6259    if (windows->image.crop_geometry != (char *) NULL)
6260      (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6261    draw_info.x+=windows->image.x-(line_width/2);
6262    if (draw_info.x < 0)
6263      draw_info.x=0;
6264    draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6265    draw_info.y+=windows->image.y-(line_width/2);
6266    if (draw_info.y < 0)
6267      draw_info.y=0;
6268    draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6269    draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6270    if (draw_info.width > (unsigned int) (*image)->columns)
6271      draw_info.width=(unsigned int) (*image)->columns;
6272    draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6273    if (draw_info.height > (unsigned int) (*image)->rows)
6274      draw_info.height=(unsigned int) (*image)->rows;
6275    (void) FormatLocaleString(draw_info.geometry,MagickPathExtent,"%ux%u%+d%+d",
6276      width*draw_info.width/windows->image.ximage->width,
6277      height*draw_info.height/windows->image.ximage->height,
6278      draw_info.x+x,draw_info.y+y);
6279    /*
6280      Initialize drawing attributes.
6281    */
6282    draw_info.degrees=0.0;
6283    draw_info.element=element;
6284    draw_info.stipple=stipple;
6285    draw_info.line_width=line_width;
6286    draw_info.line_info=line_info;
6287    if (line_info.x1 > (int) (line_width/2))
6288      draw_info.line_info.x1=(short) line_width/2;
6289    if (line_info.y1 > (int) (line_width/2))
6290      draw_info.line_info.y1=(short) line_width/2;
6291    draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6292    draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6293    if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6294      {
6295        draw_info.line_info.x2=(-draw_info.line_info.x2);
6296        draw_info.line_info.y2=(-draw_info.line_info.y2);
6297      }
6298    if (draw_info.line_info.x2 < 0)
6299      {
6300        draw_info.line_info.x2=(-draw_info.line_info.x2);
6301        Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6302      }
6303    if (draw_info.line_info.y2 < 0)
6304      {
6305        draw_info.line_info.y2=(-draw_info.line_info.y2);
6306        Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6307      }
6308    draw_info.rectangle_info=rectangle_info;
6309    if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6310      draw_info.rectangle_info.x=(ssize_t) line_width/2;
6311    if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6312      draw_info.rectangle_info.y=(ssize_t) line_width/2;
6313    draw_info.number_coordinates=(unsigned int) number_coordinates;
6314    draw_info.coordinate_info=coordinate_info;
6315    windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6316    /*
6317      Draw element on image.
6318    */
6319    XSetCursorState(display,windows,MagickTrue);
6320    XCheckRefreshWindows(display,windows);
6321    status=XDrawImage(display,windows->pixel_info,&draw_info,*image,exception);
6322    XSetCursorState(display,windows,MagickFalse);
6323    /*
6324      Update image colormap and return to image drawing.
6325    */
6326    XConfigureImageColormap(display,resource_info,windows,*image,exception);
6327    (void) XConfigureImage(display,resource_info,windows,*image,exception);
6328  }
6329  XSetCursorState(display,windows,MagickFalse);
6330  coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6331  return(IsMagickTrue(status));
6332}
6333
6334/*
6335%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6336%                                                                             %
6337%                                                                             %
6338%                                                                             %
6339+   X D r a w P a n R e c t a n g l e                                         %
6340%                                                                             %
6341%                                                                             %
6342%                                                                             %
6343%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6344%
6345%  XDrawPanRectangle() draws a rectangle in the pan window.  The pan window
6346%  displays a zoom image and the rectangle shows which portion of the image is
6347%  displayed in the Image window.
6348%
6349%  The format of the XDrawPanRectangle method is:
6350%
6351%      XDrawPanRectangle(Display *display,XWindows *windows)
6352%
6353%  A description of each parameter follows:
6354%
6355%    o display: Specifies a connection to an X server;  returned from
6356%      XOpenDisplay.
6357%
6358%    o windows: Specifies a pointer to a XWindows structure.
6359%
6360*/
6361static void XDrawPanRectangle(Display *display,XWindows *windows)
6362{
6363  double
6364    scale_factor;
6365
6366  RectangleInfo
6367    highlight_info;
6368
6369  /*
6370    Determine dimensions of the panning rectangle.
6371  */
6372  scale_factor=(double) windows->pan.width/windows->image.ximage->width;
6373  highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6374  highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6375  scale_factor=(double)
6376    windows->pan.height/windows->image.ximage->height;
6377  highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6378  highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6379  /*
6380    Display the panning rectangle.
6381  */
6382  (void) XClearWindow(display,windows->pan.id);
6383  XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6384    &highlight_info);
6385}
6386
6387/*
6388%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6389%                                                                             %
6390%                                                                             %
6391%                                                                             %
6392+   X I m a g e C a c h e                                                     %
6393%                                                                             %
6394%                                                                             %
6395%                                                                             %
6396%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6397%
6398%  XImageCache() handles the creation, manipulation, and destruction of the
6399%  image cache (undo and redo buffers).
6400%
6401%  The format of the XImageCache method is:
6402%
6403%      void XImageCache(Display *display,XResourceInfo *resource_info,
6404%        XWindows *windows,const CommandType command,Image **image,
6405%        ExceptionInfo *exception)
6406%
6407%  A description of each parameter follows:
6408%
6409%    o display: Specifies a connection to an X server; returned from
6410%      XOpenDisplay.
6411%
6412%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6413%
6414%    o windows: Specifies a pointer to a XWindows structure.
6415%
6416%    o command: Specifies a command to perform.
6417%
6418%    o image: the image;  XImageCache may transform the image and return a new
6419%      image pointer.
6420%
6421%    o exception: return any errors or warnings in this structure.
6422%
6423*/
6424static void XImageCache(Display *display,XResourceInfo *resource_info,
6425  XWindows *windows,const CommandType command,Image **image,
6426  ExceptionInfo *exception)
6427{
6428  Image
6429    *cache_image;
6430
6431  static Image
6432    *redo_image = (Image *) NULL,
6433    *undo_image = (Image *) NULL;
6434
6435  switch (command)
6436  {
6437    case FreeBuffersCommand:
6438    {
6439      /*
6440        Free memory from the undo and redo cache.
6441      */
6442      while (undo_image != (Image *) NULL)
6443      {
6444        cache_image=undo_image;
6445        undo_image=GetPreviousImageInList(undo_image);
6446        cache_image->list=DestroyImage(cache_image->list);
6447        cache_image=DestroyImage(cache_image);
6448      }
6449      undo_image=NewImageList();
6450      if (redo_image != (Image *) NULL)
6451        redo_image=DestroyImage(redo_image);
6452      redo_image=NewImageList();
6453      return;
6454    }
6455    case UndoCommand:
6456    {
6457      char
6458        image_geometry[MagickPathExtent];
6459
6460      /*
6461        Undo the last image transformation.
6462      */
6463      if (undo_image == (Image *) NULL)
6464        {
6465          (void) XBell(display,0);
6466          return;
6467        }
6468      cache_image=undo_image;
6469      undo_image=GetPreviousImageInList(undo_image);
6470      windows->image.window_changes.width=(int) cache_image->columns;
6471      windows->image.window_changes.height=(int) cache_image->rows;
6472      (void) FormatLocaleString(image_geometry,MagickPathExtent,"%dx%d!",
6473        windows->image.ximage->width,windows->image.ximage->height);
6474      (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
6475        exception);
6476      if (windows->image.crop_geometry != (char *) NULL)
6477        windows->image.crop_geometry=(char *) RelinquishMagickMemory(
6478          windows->image.crop_geometry);
6479      windows->image.crop_geometry=cache_image->geometry;
6480      if (redo_image != (Image *) NULL)
6481        redo_image=DestroyImage(redo_image);
6482      redo_image=(*image);
6483      *image=cache_image->list;
6484      cache_image=DestroyImage(cache_image);
6485      if (IfMagickTrue(windows->image.orphan) )
6486        return;
6487      XConfigureImageColormap(display,resource_info,windows,*image,exception);
6488      (void) XConfigureImage(display,resource_info,windows,*image,exception);
6489      return;
6490    }
6491    case CutCommand:
6492    case PasteCommand:
6493    case ApplyCommand:
6494    case HalfSizeCommand:
6495    case OriginalSizeCommand:
6496    case DoubleSizeCommand:
6497    case ResizeCommand:
6498    case TrimCommand:
6499    case CropCommand:
6500    case ChopCommand:
6501    case FlipCommand:
6502    case FlopCommand:
6503    case RotateRightCommand:
6504    case RotateLeftCommand:
6505    case RotateCommand:
6506    case ShearCommand:
6507    case RollCommand:
6508    case NegateCommand:
6509    case ContrastStretchCommand:
6510    case SigmoidalContrastCommand:
6511    case NormalizeCommand:
6512    case EqualizeCommand:
6513    case HueCommand:
6514    case SaturationCommand:
6515    case BrightnessCommand:
6516    case GammaCommand:
6517    case SpiffCommand:
6518    case DullCommand:
6519    case GrayscaleCommand:
6520    case MapCommand:
6521    case QuantizeCommand:
6522    case DespeckleCommand:
6523    case EmbossCommand:
6524    case ReduceNoiseCommand:
6525    case AddNoiseCommand:
6526    case SharpenCommand:
6527    case BlurCommand:
6528    case ThresholdCommand:
6529    case EdgeDetectCommand:
6530    case SpreadCommand:
6531    case ShadeCommand:
6532    case RaiseCommand:
6533    case SegmentCommand:
6534    case SolarizeCommand:
6535    case SepiaToneCommand:
6536    case SwirlCommand:
6537    case ImplodeCommand:
6538    case VignetteCommand:
6539    case WaveCommand:
6540    case OilPaintCommand:
6541    case CharcoalDrawCommand:
6542    case AnnotateCommand:
6543    case AddBorderCommand:
6544    case AddFrameCommand:
6545    case CompositeCommand:
6546    case CommentCommand:
6547    case LaunchCommand:
6548    case RegionofInterestCommand:
6549    case SaveToUndoBufferCommand:
6550    case RedoCommand:
6551    {
6552      Image
6553        *previous_image;
6554
6555      ssize_t
6556        bytes;
6557
6558      bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelInfo));
6559      if (undo_image != (Image *) NULL)
6560        {
6561          /*
6562            Ensure the undo cache has enough memory available.
6563          */
6564          previous_image=undo_image;
6565          while (previous_image != (Image *) NULL)
6566          {
6567            bytes+=previous_image->list->columns*previous_image->list->rows*
6568              sizeof(PixelInfo);
6569            if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6570              {
6571                previous_image=GetPreviousImageInList(previous_image);
6572                continue;
6573              }
6574            bytes-=previous_image->list->columns*previous_image->list->rows*
6575              sizeof(PixelInfo);
6576            if (previous_image == undo_image)
6577              undo_image=NewImageList();
6578            else
6579              previous_image->next->previous=NewImageList();
6580            break;
6581          }
6582          while (previous_image != (Image *) NULL)
6583          {
6584            /*
6585              Delete any excess memory from undo cache.
6586            */
6587            cache_image=previous_image;
6588            previous_image=GetPreviousImageInList(previous_image);
6589            cache_image->list=DestroyImage(cache_image->list);
6590            cache_image=DestroyImage(cache_image);
6591          }
6592        }
6593      if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6594        break;
6595      /*
6596        Save image before transformations are applied.
6597      */
6598      cache_image=AcquireImage((ImageInfo *) NULL,exception);
6599      if (cache_image == (Image *) NULL)
6600        break;
6601      XSetCursorState(display,windows,MagickTrue);
6602      XCheckRefreshWindows(display,windows);
6603      cache_image->list=CloneImage(*image,0,0,MagickTrue,exception);
6604      XSetCursorState(display,windows,MagickFalse);
6605      if (cache_image->list == (Image *) NULL)
6606        {
6607          cache_image=DestroyImage(cache_image);
6608          break;
6609        }
6610      cache_image->columns=(size_t) windows->image.ximage->width;
6611      cache_image->rows=(size_t) windows->image.ximage->height;
6612      cache_image->geometry=windows->image.crop_geometry;
6613      if (windows->image.crop_geometry != (char *) NULL)
6614        {
6615          cache_image->geometry=AcquireString((char *) NULL);
6616          (void) CopyMagickString(cache_image->geometry,
6617            windows->image.crop_geometry,MagickPathExtent);
6618        }
6619      if (undo_image == (Image *) NULL)
6620        {
6621          undo_image=cache_image;
6622          break;
6623        }
6624      undo_image->next=cache_image;
6625      undo_image->next->previous=undo_image;
6626      undo_image=undo_image->next;
6627      break;
6628    }
6629    default:
6630      break;
6631  }
6632  if (command == RedoCommand)
6633    {
6634      /*
6635        Redo the last image transformation.
6636      */
6637      if (redo_image == (Image *) NULL)
6638        {
6639          (void) XBell(display,0);
6640          return;
6641        }
6642      windows->image.window_changes.width=(int) redo_image->columns;
6643      windows->image.window_changes.height=(int) redo_image->rows;
6644      if (windows->image.crop_geometry != (char *) NULL)
6645        windows->image.crop_geometry=(char *)
6646          RelinquishMagickMemory(windows->image.crop_geometry);
6647      windows->image.crop_geometry=redo_image->geometry;
6648      *image=DestroyImage(*image);
6649      *image=redo_image;
6650      redo_image=NewImageList();
6651      if (IfMagickTrue(windows->image.orphan) )
6652        return;
6653      XConfigureImageColormap(display,resource_info,windows,*image,exception);
6654      (void) XConfigureImage(display,resource_info,windows,*image,exception);
6655      return;
6656    }
6657  if (command != InfoCommand)
6658    return;
6659  /*
6660    Display image info.
6661  */
6662  XSetCursorState(display,windows,MagickTrue);
6663  XCheckRefreshWindows(display,windows);
6664  XDisplayImageInfo(display,resource_info,windows,undo_image,*image,exception);
6665  XSetCursorState(display,windows,MagickFalse);
6666}
6667
6668/*
6669%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6670%                                                                             %
6671%                                                                             %
6672%                                                                             %
6673+   X I m a g e W i n d o w C o m m a n d                                     %
6674%                                                                             %
6675%                                                                             %
6676%                                                                             %
6677%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6678%
6679%  XImageWindowCommand() makes a transform to the image or Image window as
6680%  specified by a user menu button or keyboard command.
6681%
6682%  The format of the XImageWindowCommand method is:
6683%
6684%      CommandType XImageWindowCommand(Display *display,
6685%        XResourceInfo *resource_info,XWindows *windows,
6686%        const MagickStatusType state,KeySym key_symbol,Image **image,
6687%        ExceptionInfo *exception)
6688%
6689%  A description of each parameter follows:
6690%
6691%    o nexus:  Method XImageWindowCommand returns an image when the
6692%      user chooses 'Open Image' from the command menu.  Otherwise a null
6693%      image is returned.
6694%
6695%    o display: Specifies a connection to an X server; returned from
6696%      XOpenDisplay.
6697%
6698%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6699%
6700%    o windows: Specifies a pointer to a XWindows structure.
6701%
6702%    o state: key mask.
6703%
6704%    o key_symbol: Specifies a command to perform.
6705%
6706%    o image: the image;  XImageWIndowCommand may transform the image and
6707%      return a new image pointer.
6708%
6709%    o exception: return any errors or warnings in this structure.
6710%
6711*/
6712static CommandType XImageWindowCommand(Display *display,
6713  XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6714  KeySym key_symbol,Image **image,ExceptionInfo *exception)
6715{
6716  static char
6717    delta[MagickPathExtent] = "";
6718
6719  static const char
6720    Digits[] = "01234567890";
6721
6722  static KeySym
6723    last_symbol = XK_0;
6724
6725  if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6726    {
6727      if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6728        {
6729          *delta='\0';
6730          resource_info->quantum=1;
6731        }
6732      last_symbol=key_symbol;
6733      delta[strlen(delta)+1]='\0';
6734      delta[strlen(delta)]=Digits[key_symbol-XK_0];
6735      resource_info->quantum=StringToLong(delta);
6736      return(NullCommand);
6737    }
6738  last_symbol=key_symbol;
6739  if (resource_info->immutable)
6740    {
6741      /*
6742        Virtual image window has a restricted command set.
6743      */
6744      switch (key_symbol)
6745      {
6746        case XK_question:
6747          return(InfoCommand);
6748        case XK_p:
6749        case XK_Print:
6750          return(PrintCommand);
6751        case XK_space:
6752          return(NextCommand);
6753        case XK_q:
6754        case XK_Escape:
6755          return(QuitCommand);
6756        default:
6757          break;
6758      }
6759      return(NullCommand);
6760    }
6761  switch ((int) key_symbol)
6762  {
6763    case XK_o:
6764    {
6765      if ((state & ControlMask) == 0)
6766        break;
6767      return(OpenCommand);
6768    }
6769    case XK_space:
6770      return(NextCommand);
6771    case XK_BackSpace:
6772      return(FormerCommand);
6773    case XK_s:
6774    {
6775      if ((state & Mod1Mask) != 0)
6776        return(SwirlCommand);
6777      if ((state & ControlMask) == 0)
6778        return(ShearCommand);
6779      return(SaveCommand);
6780    }
6781    case XK_p:
6782    case XK_Print:
6783    {
6784      if ((state & Mod1Mask) != 0)
6785        return(OilPaintCommand);
6786      if ((state & Mod4Mask) != 0)
6787        return(ColorCommand);
6788      if ((state & ControlMask) == 0)
6789        return(NullCommand);
6790      return(PrintCommand);
6791    }
6792    case XK_d:
6793    {
6794      if ((state & Mod4Mask) != 0)
6795        return(DrawCommand);
6796      if ((state & ControlMask) == 0)
6797        return(NullCommand);
6798      return(DeleteCommand);
6799    }
6800    case XK_Select:
6801    {
6802      if ((state & ControlMask) == 0)
6803        return(NullCommand);
6804      return(SelectCommand);
6805    }
6806    case XK_n:
6807    {
6808      if ((state & ControlMask) == 0)
6809        return(NullCommand);
6810      return(NewCommand);
6811    }
6812    case XK_q:
6813    case XK_Escape:
6814      return(QuitCommand);
6815    case XK_z:
6816    case XK_Undo:
6817    {
6818      if ((state & ControlMask) == 0)
6819        return(NullCommand);
6820      return(UndoCommand);
6821    }
6822    case XK_r:
6823    case XK_Redo:
6824    {
6825      if ((state & ControlMask) == 0)
6826        return(RollCommand);
6827      return(RedoCommand);
6828    }
6829    case XK_x:
6830    {
6831      if ((state & ControlMask) == 0)
6832        return(NullCommand);
6833      return(CutCommand);
6834    }
6835    case XK_c:
6836    {
6837      if ((state & Mod1Mask) != 0)
6838        return(CharcoalDrawCommand);
6839      if ((state & ControlMask) == 0)
6840        return(CropCommand);
6841      return(CopyCommand);
6842    }
6843    case XK_v:
6844    case XK_Insert:
6845    {
6846      if ((state & Mod4Mask) != 0)
6847        return(CompositeCommand);
6848      if ((state & ControlMask) == 0)
6849        return(FlipCommand);
6850      return(PasteCommand);
6851    }
6852    case XK_less:
6853      return(HalfSizeCommand);
6854    case XK_minus:
6855      return(OriginalSizeCommand);
6856    case XK_greater:
6857      return(DoubleSizeCommand);
6858    case XK_percent:
6859      return(ResizeCommand);
6860    case XK_at:
6861      return(RefreshCommand);
6862    case XK_bracketleft:
6863      return(ChopCommand);
6864    case XK_h:
6865      return(FlopCommand);
6866    case XK_slash:
6867      return(RotateRightCommand);
6868    case XK_backslash:
6869      return(RotateLeftCommand);
6870    case XK_asterisk:
6871      return(RotateCommand);
6872    case XK_t:
6873      return(TrimCommand);
6874    case XK_H:
6875      return(HueCommand);
6876    case XK_S:
6877      return(SaturationCommand);
6878    case XK_L:
6879      return(BrightnessCommand);
6880    case XK_G:
6881      return(GammaCommand);
6882    case XK_C:
6883      return(SpiffCommand);
6884    case XK_Z:
6885      return(DullCommand);
6886    case XK_N:
6887      return(NormalizeCommand);
6888    case XK_equal:
6889      return(EqualizeCommand);
6890    case XK_asciitilde:
6891      return(NegateCommand);
6892    case XK_period:
6893      return(GrayscaleCommand);
6894    case XK_numbersign:
6895      return(QuantizeCommand);
6896    case XK_F2:
6897      return(DespeckleCommand);
6898    case XK_F3:
6899      return(EmbossCommand);
6900    case XK_F4:
6901      return(ReduceNoiseCommand);
6902    case XK_F5:
6903      return(AddNoiseCommand);
6904    case XK_F6:
6905      return(SharpenCommand);
6906    case XK_F7:
6907      return(BlurCommand);
6908    case XK_F8:
6909      return(ThresholdCommand);
6910    case XK_F9:
6911      return(EdgeDetectCommand);
6912    case XK_F10:
6913      return(SpreadCommand);
6914    case XK_F11:
6915      return(ShadeCommand);
6916    case XK_F12:
6917      return(RaiseCommand);
6918    case XK_F13:
6919      return(SegmentCommand);
6920    case XK_i:
6921    {
6922      if ((state & Mod1Mask) == 0)
6923        return(NullCommand);
6924      return(ImplodeCommand);
6925    }
6926    case XK_w:
6927    {
6928      if ((state & Mod1Mask) == 0)
6929        return(NullCommand);
6930      return(WaveCommand);
6931    }
6932    case XK_m:
6933    {
6934      if ((state & Mod4Mask) == 0)
6935        return(NullCommand);
6936      return(MatteCommand);
6937    }
6938    case XK_b:
6939    {
6940      if ((state & Mod4Mask) == 0)
6941        return(NullCommand);
6942      return(AddBorderCommand);
6943    }
6944    case XK_f:
6945    {
6946      if ((state & Mod4Mask) == 0)
6947        return(NullCommand);
6948      return(AddFrameCommand);
6949    }
6950    case XK_exclam:
6951    {
6952      if ((state & Mod4Mask) == 0)
6953        return(NullCommand);
6954      return(CommentCommand);
6955    }
6956    case XK_a:
6957    {
6958      if ((state & Mod1Mask) != 0)
6959        return(ApplyCommand);
6960      if ((state & Mod4Mask) != 0)
6961        return(AnnotateCommand);
6962      if ((state & ControlMask) == 0)
6963        return(NullCommand);
6964      return(RegionofInterestCommand);
6965    }
6966    case XK_question:
6967      return(InfoCommand);
6968    case XK_plus:
6969      return(ZoomCommand);
6970    case XK_P:
6971    {
6972      if ((state & ShiftMask) == 0)
6973        return(NullCommand);
6974      return(ShowPreviewCommand);
6975    }
6976    case XK_Execute:
6977      return(LaunchCommand);
6978    case XK_F1:
6979      return(HelpCommand);
6980    case XK_Find:
6981      return(BrowseDocumentationCommand);
6982    case XK_Menu:
6983    {
6984      (void) XMapRaised(display,windows->command.id);
6985      return(NullCommand);
6986    }
6987    case XK_Next:
6988    case XK_Prior:
6989    case XK_Home:
6990    case XK_KP_Home:
6991    {
6992      XTranslateImage(display,windows,*image,key_symbol);
6993      return(NullCommand);
6994    }
6995    case XK_Up:
6996    case XK_KP_Up:
6997    case XK_Down:
6998    case XK_KP_Down:
6999    case XK_Left:
7000    case XK_KP_Left:
7001    case XK_Right:
7002    case XK_KP_Right:
7003    {
7004      if ((state & Mod1Mask) != 0)
7005        {
7006          RectangleInfo
7007            crop_info;
7008
7009          /*
7010            Trim one pixel from edge of image.
7011          */
7012          crop_info.x=0;
7013          crop_info.y=0;
7014          crop_info.width=(size_t) windows->image.ximage->width;
7015          crop_info.height=(size_t) windows->image.ximage->height;
7016          if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
7017            {
7018              if (resource_info->quantum >= (int) crop_info.height)
7019                resource_info->quantum=(int) crop_info.height-1;
7020              crop_info.height-=resource_info->quantum;
7021            }
7022          if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
7023            {
7024              if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
7025                resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
7026              crop_info.y+=resource_info->quantum;
7027              crop_info.height-=resource_info->quantum;
7028            }
7029          if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
7030            {
7031              if (resource_info->quantum >= (int) crop_info.width)
7032                resource_info->quantum=(int) crop_info.width-1;
7033              crop_info.width-=resource_info->quantum;
7034            }
7035          if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
7036            {
7037              if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
7038                resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
7039              crop_info.x+=resource_info->quantum;
7040              crop_info.width-=resource_info->quantum;
7041            }
7042          if ((int) (windows->image.x+windows->image.width) >
7043              (int) crop_info.width)
7044            windows->image.x=(int) (crop_info.width-windows->image.width);
7045          if ((int) (windows->image.y+windows->image.height) >
7046              (int) crop_info.height)
7047            windows->image.y=(int) (crop_info.height-windows->image.height);
7048          XSetCropGeometry(display,windows,&crop_info,*image);
7049          windows->image.window_changes.width=(int) crop_info.width;
7050          windows->image.window_changes.height=(int) crop_info.height;
7051          (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
7052          (void) XConfigureImage(display,resource_info,windows,*image,
7053            exception);
7054          return(NullCommand);
7055        }
7056      XTranslateImage(display,windows,*image,key_symbol);
7057      return(NullCommand);
7058    }
7059    default:
7060      return(NullCommand);
7061  }
7062  return(NullCommand);
7063}
7064
7065/*
7066%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7067%                                                                             %
7068%                                                                             %
7069%                                                                             %
7070+   X M a g i c k C o m m a n d                                               %
7071%                                                                             %
7072%                                                                             %
7073%                                                                             %
7074%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7075%
7076%  XMagickCommand() makes a transform to the image or Image window as
7077%  specified by a user menu button or keyboard command.
7078%
7079%  The format of the XMagickCommand method is:
7080%
7081%      Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7082%        XWindows *windows,const CommandType command,Image **image,
7083%        ExceptionInfo *exception)
7084%
7085%  A description of each parameter follows:
7086%
7087%    o display: Specifies a connection to an X server; returned from
7088%      XOpenDisplay.
7089%
7090%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7091%
7092%    o windows: Specifies a pointer to a XWindows structure.
7093%
7094%    o command: Specifies a command to perform.
7095%
7096%    o image: the image;  XMagickCommand may transform the image and return a
7097%      new image pointer.
7098%
7099%    o exception: return any errors or warnings in this structure.
7100%
7101*/
7102static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7103  XWindows *windows,const CommandType command,Image **image,
7104  ExceptionInfo *exception)
7105{
7106  char
7107    filename[MagickPathExtent],
7108    geometry[MagickPathExtent],
7109    modulate_factors[MagickPathExtent];
7110
7111  GeometryInfo
7112    geometry_info;
7113
7114  Image
7115    *nexus;
7116
7117  ImageInfo
7118    *image_info;
7119
7120  int
7121    x,
7122    y;
7123
7124  MagickStatusType
7125    flags,
7126    status;
7127
7128  QuantizeInfo
7129    quantize_info;
7130
7131  RectangleInfo
7132    page_geometry;
7133
7134  register int
7135    i;
7136
7137  static char
7138    color[MagickPathExtent] = "gray";
7139
7140  unsigned int
7141    height,
7142    width;
7143
7144  /*
7145    Process user command.
7146  */
7147  XCheckRefreshWindows(display,windows);
7148  XImageCache(display,resource_info,windows,command,image,exception);
7149  nexus=NewImageList();
7150  windows->image.window_changes.width=windows->image.ximage->width;
7151  windows->image.window_changes.height=windows->image.ximage->height;
7152  image_info=CloneImageInfo(resource_info->image_info);
7153  SetGeometryInfo(&geometry_info);
7154  GetQuantizeInfo(&quantize_info);
7155  switch (command)
7156  {
7157    case OpenCommand:
7158    {
7159      /*
7160        Load image.
7161      */
7162      nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7163      break;
7164    }
7165    case NextCommand:
7166    {
7167      /*
7168        Display next image.
7169      */
7170      for (i=0; i < resource_info->quantum; i++)
7171        XClientMessage(display,windows->image.id,windows->im_protocols,
7172          windows->im_next_image,CurrentTime);
7173      break;
7174    }
7175    case FormerCommand:
7176    {
7177      /*
7178        Display former image.
7179      */
7180      for (i=0; i < resource_info->quantum; i++)
7181        XClientMessage(display,windows->image.id,windows->im_protocols,
7182          windows->im_former_image,CurrentTime);
7183      break;
7184    }
7185    case SelectCommand:
7186    {
7187      int
7188        status;
7189
7190      /*
7191        Select image.
7192      */
7193      if (*resource_info->home_directory == '\0')
7194        (void) CopyMagickString(resource_info->home_directory,".",
7195          MagickPathExtent);
7196      status=chdir(resource_info->home_directory);
7197      if (status == -1)
7198        (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
7199          "UnableToOpenFile","%s",resource_info->home_directory);
7200      nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7201      break;
7202    }
7203    case SaveCommand:
7204    {
7205      /*
7206        Save image.
7207      */
7208      status=XSaveImage(display,resource_info,windows,*image,exception);
7209      if (IfMagickFalse(status) )
7210        {
7211          char
7212            message[MagickPathExtent];
7213
7214          (void) FormatLocaleString(message,MagickPathExtent,"%s:%s",
7215            exception->reason != (char *) NULL ? exception->reason : "",
7216            exception->description != (char *) NULL ? exception->description :
7217            "");
7218          XNoticeWidget(display,windows,"Unable to save file:",message);
7219          break;
7220        }
7221      break;
7222    }
7223    case PrintCommand:
7224    {
7225      /*
7226        Print image.
7227      */
7228      status=XPrintImage(display,resource_info,windows,*image,exception);
7229      if (IfMagickFalse(status) )
7230        {
7231          char
7232            message[MagickPathExtent];
7233
7234          (void) FormatLocaleString(message,MagickPathExtent,"%s:%s",
7235            exception->reason != (char *) NULL ? exception->reason : "",
7236            exception->description != (char *) NULL ? exception->description :
7237            "");
7238          XNoticeWidget(display,windows,"Unable to print file:",message);
7239          break;
7240        }
7241      break;
7242    }
7243    case DeleteCommand:
7244    {
7245      static char
7246        filename[MagickPathExtent] = "\0";
7247
7248      /*
7249        Delete image file.
7250      */
7251      XFileBrowserWidget(display,windows,"Delete",filename);
7252      if (*filename == '\0')
7253        break;
7254      status=ShredFile(filename);
7255      if (IfMagickTrue(status) )
7256        XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7257      break;
7258    }
7259    case NewCommand:
7260    {
7261      int
7262        status;
7263
7264      static char
7265        color[MagickPathExtent] = "gray",
7266        geometry[MagickPathExtent] = "640x480";
7267
7268      static const char
7269        *format = "gradient";
7270
7271      /*
7272        Query user for canvas geometry.
7273      */
7274      status=XDialogWidget(display,windows,"New","Enter image geometry:",
7275        geometry);
7276      if (*geometry == '\0')
7277        break;
7278      if (status == 0)
7279        format="xc";
7280      XColorBrowserWidget(display,windows,"Select",color);
7281      if (*color == '\0')
7282        break;
7283      /*
7284        Create canvas.
7285      */
7286      (void) FormatLocaleString(image_info->filename,MagickPathExtent,
7287        "%s:%s",format,color);
7288      (void) CloneString(&image_info->size,geometry);
7289      nexus=ReadImage(image_info,exception);
7290      CatchException(exception);
7291      XClientMessage(display,windows->image.id,windows->im_protocols,
7292        windows->im_next_image,CurrentTime);
7293      break;
7294    }
7295    case VisualDirectoryCommand:
7296    {
7297      /*
7298        Visual Image directory.
7299      */
7300      nexus=XVisualDirectoryImage(display,resource_info,windows,exception);
7301      break;
7302    }
7303    case QuitCommand:
7304    {
7305      /*
7306        exit program.
7307      */
7308      if (IfMagickFalse(resource_info->confirm_exit) )
7309        XClientMessage(display,windows->image.id,windows->im_protocols,
7310          windows->im_exit,CurrentTime);
7311      else
7312        {
7313          int
7314            status;
7315
7316          /*
7317            Confirm program exit.
7318          */
7319          status=XConfirmWidget(display,windows,"Do you really want to exit",
7320            resource_info->client_name);
7321          if (status > 0)
7322            XClientMessage(display,windows->image.id,windows->im_protocols,
7323              windows->im_exit,CurrentTime);
7324        }
7325      break;
7326    }
7327    case CutCommand:
7328    {
7329      /*
7330        Cut image.
7331      */
7332      (void) XCropImage(display,resource_info,windows,*image,CutMode,exception);
7333      break;
7334    }
7335    case CopyCommand:
7336    {
7337      /*
7338        Copy image.
7339      */
7340      (void) XCropImage(display,resource_info,windows,*image,CopyMode,
7341        exception);
7342      break;
7343    }
7344    case PasteCommand:
7345    {
7346      /*
7347        Paste image.
7348      */
7349      status=XPasteImage(display,resource_info,windows,*image,exception);
7350      if (IfMagickFalse(status) )
7351        {
7352          XNoticeWidget(display,windows,"Unable to paste X image",
7353            (*image)->filename);
7354          break;
7355        }
7356      break;
7357    }
7358    case HalfSizeCommand:
7359    {
7360      /*
7361        Half image size.
7362      */
7363      windows->image.window_changes.width=windows->image.ximage->width/2;
7364      windows->image.window_changes.height=windows->image.ximage->height/2;
7365      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7366      break;
7367    }
7368    case OriginalSizeCommand:
7369    {
7370      /*
7371        Original image size.
7372      */
7373      windows->image.window_changes.width=(int) (*image)->columns;
7374      windows->image.window_changes.height=(int) (*image)->rows;
7375      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7376      break;
7377    }
7378    case DoubleSizeCommand:
7379    {
7380      /*
7381        Double the image size.
7382      */
7383      windows->image.window_changes.width=windows->image.ximage->width << 1;
7384      windows->image.window_changes.height=windows->image.ximage->height << 1;
7385      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7386      break;
7387    }
7388    case ResizeCommand:
7389    {
7390      int
7391        status;
7392
7393      size_t
7394        height,
7395        width;
7396
7397      ssize_t
7398        x,
7399        y;
7400
7401      /*
7402        Resize image.
7403      */
7404      width=(size_t) windows->image.ximage->width;
7405      height=(size_t) windows->image.ximage->height;
7406      x=0;
7407      y=0;
7408      (void) FormatLocaleString(geometry,MagickPathExtent,"%.20gx%.20g+0+0",
7409        (double) width,(double) height);
7410      status=XDialogWidget(display,windows,"Resize",
7411        "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7412      if (*geometry == '\0')
7413        break;
7414      if (status == 0)
7415        (void) ConcatenateMagickString(geometry,"!",MagickPathExtent);
7416      (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7417      windows->image.window_changes.width=(int) width;
7418      windows->image.window_changes.height=(int) height;
7419      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7420      break;
7421    }
7422    case ApplyCommand:
7423    {
7424      char
7425        image_geometry[MagickPathExtent];
7426
7427      if ((windows->image.crop_geometry == (char *) NULL) &&
7428          ((int) (*image)->columns == windows->image.ximage->width) &&
7429          ((int) (*image)->rows == windows->image.ximage->height))
7430        break;
7431      /*
7432        Apply size transforms to image.
7433      */
7434      XSetCursorState(display,windows,MagickTrue);
7435      XCheckRefreshWindows(display,windows);
7436      /*
7437        Crop and/or scale displayed image.
7438      */
7439      (void) FormatLocaleString(image_geometry,MagickPathExtent,"%dx%d!",
7440        windows->image.ximage->width,windows->image.ximage->height);
7441      (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
7442        exception);
7443      if (windows->image.crop_geometry != (char *) NULL)
7444        windows->image.crop_geometry=(char *) RelinquishMagickMemory(
7445          windows->image.crop_geometry);
7446      windows->image.x=0;
7447      windows->image.y=0;
7448      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7449      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7450      break;
7451    }
7452    case RefreshCommand:
7453    {
7454      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7455      break;
7456    }
7457    case RestoreCommand:
7458    {
7459      /*
7460        Restore Image window to its original size.
7461      */
7462      if ((windows->image.width == (unsigned int) (*image)->columns) &&
7463          (windows->image.height == (unsigned int) (*image)->rows) &&
7464          (windows->image.crop_geometry == (char *) NULL))
7465        {
7466          (void) XBell(display,0);
7467          break;
7468        }
7469      windows->image.window_changes.width=(int) (*image)->columns;
7470      windows->image.window_changes.height=(int) (*image)->rows;
7471      if (windows->image.crop_geometry != (char *) NULL)
7472        {
7473          windows->image.crop_geometry=(char *)
7474            RelinquishMagickMemory(windows->image.crop_geometry);
7475          windows->image.crop_geometry=(char *) NULL;
7476          windows->image.x=0;
7477          windows->image.y=0;
7478        }
7479      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7480      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7481      break;
7482    }
7483    case CropCommand:
7484    {
7485      /*
7486        Crop image.
7487      */
7488      (void) XCropImage(display,resource_info,windows,*image,CropMode,
7489        exception);
7490      break;
7491    }
7492    case ChopCommand:
7493    {
7494      /*
7495        Chop image.
7496      */
7497      status=XChopImage(display,resource_info,windows,image,exception);
7498      if (IfMagickFalse(status) )
7499        {
7500          XNoticeWidget(display,windows,"Unable to cut X image",
7501            (*image)->filename);
7502          break;
7503        }
7504      break;
7505    }
7506    case FlopCommand:
7507    {
7508      Image
7509        *flop_image;
7510
7511      /*
7512        Flop image scanlines.
7513      */
7514      XSetCursorState(display,windows,MagickTrue);
7515      XCheckRefreshWindows(display,windows);
7516      flop_image=FlopImage(*image,exception);
7517      if (flop_image != (Image *) NULL)
7518        {
7519          *image=DestroyImage(*image);
7520          *image=flop_image;
7521        }
7522      CatchException(exception);
7523      XSetCursorState(display,windows,MagickFalse);
7524      if (windows->image.crop_geometry != (char *) NULL)
7525        {
7526          /*
7527            Flop crop geometry.
7528          */
7529          width=(unsigned int) (*image)->columns;
7530          height=(unsigned int) (*image)->rows;
7531          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7532            &width,&height);
7533          (void) FormatLocaleString(windows->image.crop_geometry,MagickPathExtent,
7534            "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7535        }
7536      if (IfMagickTrue(windows->image.orphan) )
7537        break;
7538      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7539      break;
7540    }
7541    case FlipCommand:
7542    {
7543      Image
7544        *flip_image;
7545
7546      /*
7547        Flip image scanlines.
7548      */
7549      XSetCursorState(display,windows,MagickTrue);
7550      XCheckRefreshWindows(display,windows);
7551      flip_image=FlipImage(*image,exception);
7552      if (flip_image != (Image *) NULL)
7553        {
7554          *image=DestroyImage(*image);
7555          *image=flip_image;
7556        }
7557      CatchException(exception);
7558      XSetCursorState(display,windows,MagickFalse);
7559      if (windows->image.crop_geometry != (char *) NULL)
7560        {
7561          /*
7562            Flip crop geometry.
7563          */
7564          width=(unsigned int) (*image)->columns;
7565          height=(unsigned int) (*image)->rows;
7566          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7567            &width,&height);
7568          (void) FormatLocaleString(windows->image.crop_geometry,MagickPathExtent,
7569            "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7570        }
7571      if (IfMagickTrue(windows->image.orphan) )
7572        break;
7573      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7574      break;
7575    }
7576    case RotateRightCommand:
7577    {
7578      /*
7579        Rotate image 90 degrees clockwise.
7580      */
7581      status=XRotateImage(display,resource_info,windows,90.0,image,exception);
7582      if (IfMagickFalse(status) )
7583        {
7584          XNoticeWidget(display,windows,"Unable to rotate X image",
7585            (*image)->filename);
7586          break;
7587        }
7588      break;
7589    }
7590    case RotateLeftCommand:
7591    {
7592      /*
7593        Rotate image 90 degrees counter-clockwise.
7594      */
7595      status=XRotateImage(display,resource_info,windows,-90.0,image,exception);
7596      if (IfMagickFalse(status) )
7597        {
7598          XNoticeWidget(display,windows,"Unable to rotate X image",
7599            (*image)->filename);
7600          break;
7601        }
7602      break;
7603    }
7604    case RotateCommand:
7605    {
7606      /*
7607        Rotate image.
7608      */
7609      status=XRotateImage(display,resource_info,windows,0.0,image,exception);
7610      if (IfMagickFalse(status) )
7611        {
7612          XNoticeWidget(display,windows,"Unable to rotate X image",
7613            (*image)->filename);
7614          break;
7615        }
7616      break;
7617    }
7618    case ShearCommand:
7619    {
7620      Image
7621        *shear_image;
7622
7623      static char
7624        geometry[MagickPathExtent] = "45.0x45.0";
7625
7626      /*
7627        Query user for shear color and geometry.
7628      */
7629      XColorBrowserWidget(display,windows,"Select",color);
7630      if (*color == '\0')
7631        break;
7632      (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7633        geometry);
7634      if (*geometry == '\0')
7635        break;
7636      /*
7637        Shear image.
7638      */
7639      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7640        exception);
7641      XSetCursorState(display,windows,MagickTrue);
7642      XCheckRefreshWindows(display,windows);
7643      (void) QueryColorCompliance(color,AllCompliance,
7644        &(*image)->background_color,exception);
7645      flags=ParseGeometry(geometry,&geometry_info);
7646      if ((flags & SigmaValue) == 0)
7647        geometry_info.sigma=geometry_info.rho;
7648      shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7649        exception);
7650      if (shear_image != (Image *) NULL)
7651        {
7652          *image=DestroyImage(*image);
7653          *image=shear_image;
7654        }
7655      CatchException(exception);
7656      XSetCursorState(display,windows,MagickFalse);
7657      if (IfMagickTrue(windows->image.orphan) )
7658        break;
7659      windows->image.window_changes.width=(int) (*image)->columns;
7660      windows->image.window_changes.height=(int) (*image)->rows;
7661      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7662      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7663      break;
7664    }
7665    case RollCommand:
7666    {
7667      Image
7668        *roll_image;
7669
7670      static char
7671        geometry[MagickPathExtent] = "+2+2";
7672
7673      /*
7674        Query user for the roll geometry.
7675      */
7676      (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7677        geometry);
7678      if (*geometry == '\0')
7679        break;
7680      /*
7681        Roll image.
7682      */
7683      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7684        exception);
7685      XSetCursorState(display,windows,MagickTrue);
7686      XCheckRefreshWindows(display,windows);
7687      (void) ParsePageGeometry(*image,geometry,&page_geometry,
7688        exception);
7689      roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7690        exception);
7691      if (roll_image != (Image *) NULL)
7692        {
7693          *image=DestroyImage(*image);
7694          *image=roll_image;
7695        }
7696      CatchException(exception);
7697      XSetCursorState(display,windows,MagickFalse);
7698      if (IfMagickTrue(windows->image.orphan) )
7699        break;
7700      windows->image.window_changes.width=(int) (*image)->columns;
7701      windows->image.window_changes.height=(int) (*image)->rows;
7702      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7703      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7704      break;
7705    }
7706    case TrimCommand:
7707    {
7708      static char
7709        fuzz[MagickPathExtent];
7710
7711      /*
7712        Query user for the fuzz factor.
7713      */
7714      (void) FormatLocaleString(fuzz,MagickPathExtent,"%g%%",100.0*
7715        (*image)->fuzz/(QuantumRange+1.0));
7716      (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7717      if (*fuzz == '\0')
7718        break;
7719      (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+1.0);
7720      /*
7721        Trim image.
7722      */
7723      status=XTrimImage(display,resource_info,windows,*image,exception);
7724      if (IfMagickFalse(status) )
7725        {
7726          XNoticeWidget(display,windows,"Unable to trim X image",
7727            (*image)->filename);
7728          break;
7729        }
7730      break;
7731    }
7732    case HueCommand:
7733    {
7734      static char
7735        hue_percent[MagickPathExtent] = "110";
7736
7737      /*
7738        Query user for percent hue change.
7739      */
7740      (void) XDialogWidget(display,windows,"Apply",
7741        "Enter percent change in image hue (0-200):",hue_percent);
7742      if (*hue_percent == '\0')
7743        break;
7744      /*
7745        Vary the image hue.
7746      */
7747      XSetCursorState(display,windows,MagickTrue);
7748      XCheckRefreshWindows(display,windows);
7749      (void) CopyMagickString(modulate_factors,"100.0/100.0/",MagickPathExtent);
7750      (void) ConcatenateMagickString(modulate_factors,hue_percent,
7751        MagickPathExtent);
7752      (void) ModulateImage(*image,modulate_factors,exception);
7753      XSetCursorState(display,windows,MagickFalse);
7754      if (IfMagickTrue(windows->image.orphan) )
7755        break;
7756      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7757      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7758      break;
7759    }
7760    case SaturationCommand:
7761    {
7762      static char
7763        saturation_percent[MagickPathExtent] = "110";
7764
7765      /*
7766        Query user for percent saturation change.
7767      */
7768      (void) XDialogWidget(display,windows,"Apply",
7769        "Enter percent change in color saturation (0-200):",saturation_percent);
7770      if (*saturation_percent == '\0')
7771        break;
7772      /*
7773        Vary color saturation.
7774      */
7775      XSetCursorState(display,windows,MagickTrue);
7776      XCheckRefreshWindows(display,windows);
7777      (void) CopyMagickString(modulate_factors,"100.0/",MagickPathExtent);
7778      (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7779        MagickPathExtent);
7780      (void) ModulateImage(*image,modulate_factors,exception);
7781      XSetCursorState(display,windows,MagickFalse);
7782      if (IfMagickTrue(windows->image.orphan) )
7783        break;
7784      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7785      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7786      break;
7787    }
7788    case BrightnessCommand:
7789    {
7790      static char
7791        brightness_percent[MagickPathExtent] = "110";
7792
7793      /*
7794        Query user for percent brightness change.
7795      */
7796      (void) XDialogWidget(display,windows,"Apply",
7797        "Enter percent change in color brightness (0-200):",brightness_percent);
7798      if (*brightness_percent == '\0')
7799        break;
7800      /*
7801        Vary the color brightness.
7802      */
7803      XSetCursorState(display,windows,MagickTrue);
7804      XCheckRefreshWindows(display,windows);
7805      (void) CopyMagickString(modulate_factors,brightness_percent,
7806        MagickPathExtent);
7807      (void) ModulateImage(*image,modulate_factors,exception);
7808      XSetCursorState(display,windows,MagickFalse);
7809      if (IfMagickTrue(windows->image.orphan) )
7810        break;
7811      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7812      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7813      break;
7814    }
7815    case GammaCommand:
7816    {
7817      static char
7818        factor[MagickPathExtent] = "1.6";
7819
7820      /*
7821        Query user for gamma value.
7822      */
7823      (void) XDialogWidget(display,windows,"Gamma",
7824        "Enter gamma value (e.g. 1.2):",factor);
7825      if (*factor == '\0')
7826        break;
7827      /*
7828        Gamma correct image.
7829      */
7830      XSetCursorState(display,windows,MagickTrue);
7831      XCheckRefreshWindows(display,windows);
7832      (void) GammaImage(*image,strtod(factor,(char **) NULL),exception);
7833      XSetCursorState(display,windows,MagickFalse);
7834      if (IfMagickTrue(windows->image.orphan) )
7835        break;
7836      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7837      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7838      break;
7839    }
7840    case SpiffCommand:
7841    {
7842      /*
7843        Sharpen the image contrast.
7844      */
7845      XSetCursorState(display,windows,MagickTrue);
7846      XCheckRefreshWindows(display,windows);
7847      (void) ContrastImage(*image,MagickTrue,exception);
7848      XSetCursorState(display,windows,MagickFalse);
7849      if (IfMagickTrue(windows->image.orphan) )
7850        break;
7851      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7852      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7853      break;
7854    }
7855    case DullCommand:
7856    {
7857      /*
7858        Dull the image contrast.
7859      */
7860      XSetCursorState(display,windows,MagickTrue);
7861      XCheckRefreshWindows(display,windows);
7862      (void) ContrastImage(*image,MagickFalse,exception);
7863      XSetCursorState(display,windows,MagickFalse);
7864      if (IfMagickTrue(windows->image.orphan) )
7865        break;
7866      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7867      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7868      break;
7869    }
7870    case ContrastStretchCommand:
7871    {
7872      double
7873        black_point,
7874        white_point;
7875
7876      static char
7877        levels[MagickPathExtent] = "1%";
7878
7879      /*
7880        Query user for gamma value.
7881      */
7882      (void) XDialogWidget(display,windows,"Contrast Stretch",
7883        "Enter black and white points:",levels);
7884      if (*levels == '\0')
7885        break;
7886      /*
7887        Contrast stretch image.
7888      */
7889      XSetCursorState(display,windows,MagickTrue);
7890      XCheckRefreshWindows(display,windows);
7891      flags=ParseGeometry(levels,&geometry_info);
7892      black_point=geometry_info.rho;
7893      white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7894      if ((flags & PercentValue) != 0)
7895        {
7896          black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7897          white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7898        }
7899      white_point=(double) (*image)->columns*(*image)->rows-white_point;
7900      (void) ContrastStretchImage(*image,black_point,white_point,
7901        exception);
7902      XSetCursorState(display,windows,MagickFalse);
7903      if (IfMagickTrue(windows->image.orphan) )
7904        break;
7905      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7906      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7907      break;
7908    }
7909    case SigmoidalContrastCommand:
7910    {
7911      GeometryInfo
7912        geometry_info;
7913
7914      MagickStatusType
7915        flags;
7916
7917      static char
7918        levels[MagickPathExtent] = "3x50%";
7919
7920      /*
7921        Query user for gamma value.
7922      */
7923      (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7924        "Enter contrast and midpoint:",levels);
7925      if (*levels == '\0')
7926        break;
7927      /*
7928        Contrast stretch image.
7929      */
7930      XSetCursorState(display,windows,MagickTrue);
7931      XCheckRefreshWindows(display,windows);
7932      flags=ParseGeometry(levels,&geometry_info);
7933      if ((flags & SigmaValue) == 0)
7934        geometry_info.sigma=1.0*QuantumRange/2.0;
7935      if ((flags & PercentValue) != 0)
7936        geometry_info.sigma=1.0*QuantumRange*geometry_info.sigma/100.0;
7937      (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
7938        geometry_info.sigma,exception);
7939      XSetCursorState(display,windows,MagickFalse);
7940      if (IfMagickTrue(windows->image.orphan) )
7941        break;
7942      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7943      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7944      break;
7945    }
7946    case NormalizeCommand:
7947    {
7948      /*
7949        Perform histogram normalization on the image.
7950      */
7951      XSetCursorState(display,windows,MagickTrue);
7952      XCheckRefreshWindows(display,windows);
7953      (void) NormalizeImage(*image,exception);
7954      XSetCursorState(display,windows,MagickFalse);
7955      if (IfMagickTrue(windows->image.orphan) )
7956        break;
7957      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7958      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7959      break;
7960    }
7961    case EqualizeCommand:
7962    {
7963      /*
7964        Perform histogram equalization on the image.
7965      */
7966      XSetCursorState(display,windows,MagickTrue);
7967      XCheckRefreshWindows(display,windows);
7968      (void) EqualizeImage(*image,exception);
7969      XSetCursorState(display,windows,MagickFalse);
7970      if (IfMagickTrue(windows->image.orphan) )
7971        break;
7972      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7973      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7974      break;
7975    }
7976    case NegateCommand:
7977    {
7978      /*
7979        Negate colors in image.
7980      */
7981      XSetCursorState(display,windows,MagickTrue);
7982      XCheckRefreshWindows(display,windows);
7983      (void) NegateImage(*image,MagickFalse,exception);
7984      XSetCursorState(display,windows,MagickFalse);
7985      if (IfMagickTrue(windows->image.orphan) )
7986        break;
7987      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7988      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7989      break;
7990    }
7991    case GrayscaleCommand:
7992    {
7993      /*
7994        Convert image to grayscale.
7995      */
7996      XSetCursorState(display,windows,MagickTrue);
7997      XCheckRefreshWindows(display,windows);
7998      (void) SetImageType(*image,(*image)->alpha_trait == UndefinedPixelTrait ?
7999        GrayscaleType : GrayscaleAlphaType,exception);
8000      XSetCursorState(display,windows,MagickFalse);
8001      if (IfMagickTrue(windows->image.orphan) )
8002        break;
8003      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8004      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8005      break;
8006    }
8007    case MapCommand:
8008    {
8009      Image
8010        *affinity_image;
8011
8012      static char
8013        filename[MagickPathExtent] = "\0";
8014
8015      /*
8016        Request image file name from user.
8017      */
8018      XFileBrowserWidget(display,windows,"Map",filename);
8019      if (*filename == '\0')
8020        break;
8021      /*
8022        Map image.
8023      */
8024      XSetCursorState(display,windows,MagickTrue);
8025      XCheckRefreshWindows(display,windows);
8026      (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
8027      affinity_image=ReadImage(image_info,exception);
8028      if (affinity_image != (Image *) NULL)
8029        {
8030          (void) RemapImage(&quantize_info,*image,affinity_image,exception);
8031          affinity_image=DestroyImage(affinity_image);
8032        }
8033      CatchException(exception);
8034      XSetCursorState(display,windows,MagickFalse);
8035      if (IfMagickTrue(windows->image.orphan) )
8036        break;
8037      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8038      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8039      break;
8040    }
8041    case QuantizeCommand:
8042    {
8043      int
8044        status;
8045
8046      static char
8047        colors[MagickPathExtent] = "256";
8048
8049      /*
8050        Query user for maximum number of colors.
8051      */
8052      status=XDialogWidget(display,windows,"Quantize",
8053        "Maximum number of colors:",colors);
8054      if (*colors == '\0')
8055        break;
8056      /*
8057        Color reduce the image.
8058      */
8059      XSetCursorState(display,windows,MagickTrue);
8060      XCheckRefreshWindows(display,windows);
8061      quantize_info.number_colors=StringToUnsignedLong(colors);
8062      quantize_info.dither_method=status != 0 ? RiemersmaDitherMethod :
8063        NoDitherMethod;
8064      (void) QuantizeImage(&quantize_info,*image,exception);
8065      XSetCursorState(display,windows,MagickFalse);
8066      if (IfMagickTrue(windows->image.orphan) )
8067        break;
8068      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8069      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8070      break;
8071    }
8072    case DespeckleCommand:
8073    {
8074      Image
8075        *despeckle_image;
8076
8077      /*
8078        Despeckle image.
8079      */
8080      XSetCursorState(display,windows,MagickTrue);
8081      XCheckRefreshWindows(display,windows);
8082      despeckle_image=DespeckleImage(*image,exception);
8083      if (despeckle_image != (Image *) NULL)
8084        {
8085          *image=DestroyImage(*image);
8086          *image=despeckle_image;
8087        }
8088      CatchException(exception);
8089      XSetCursorState(display,windows,MagickFalse);
8090      if (IfMagickTrue(windows->image.orphan) )
8091        break;
8092      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8093      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8094      break;
8095    }
8096    case EmbossCommand:
8097    {
8098      Image
8099        *emboss_image;
8100
8101      static char
8102        radius[MagickPathExtent] = "0.0x1.0";
8103
8104      /*
8105        Query user for emboss radius.
8106      */
8107      (void) XDialogWidget(display,windows,"Emboss",
8108        "Enter the emboss radius and standard deviation:",radius);
8109      if (*radius == '\0')
8110        break;
8111      /*
8112        Reduce noise in the image.
8113      */
8114      XSetCursorState(display,windows,MagickTrue);
8115      XCheckRefreshWindows(display,windows);
8116      flags=ParseGeometry(radius,&geometry_info);
8117      if ((flags & SigmaValue) == 0)
8118        geometry_info.sigma=1.0;
8119      emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8120        exception);
8121      if (emboss_image != (Image *) NULL)
8122        {
8123          *image=DestroyImage(*image);
8124          *image=emboss_image;
8125        }
8126      CatchException(exception);
8127      XSetCursorState(display,windows,MagickFalse);
8128      if (IfMagickTrue(windows->image.orphan) )
8129        break;
8130      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8131      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8132      break;
8133    }
8134    case ReduceNoiseCommand:
8135    {
8136      Image
8137        *noise_image;
8138
8139      static char
8140        radius[MagickPathExtent] = "0";
8141
8142      /*
8143        Query user for noise radius.
8144      */
8145      (void) XDialogWidget(display,windows,"Reduce Noise",
8146        "Enter the noise radius:",radius);
8147      if (*radius == '\0')
8148        break;
8149      /*
8150        Reduce noise in the image.
8151      */
8152      XSetCursorState(display,windows,MagickTrue);
8153      XCheckRefreshWindows(display,windows);
8154      flags=ParseGeometry(radius,&geometry_info);
8155      noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8156        geometry_info.rho,(size_t) geometry_info.rho,exception);
8157      if (noise_image != (Image *) NULL)
8158        {
8159          *image=DestroyImage(*image);
8160          *image=noise_image;
8161        }
8162      CatchException(exception);
8163      XSetCursorState(display,windows,MagickFalse);
8164      if (IfMagickTrue(windows->image.orphan) )
8165        break;
8166      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8167      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8168      break;
8169    }
8170    case AddNoiseCommand:
8171    {
8172      char
8173        **noises;
8174
8175      Image
8176        *noise_image;
8177
8178      static char
8179        noise_type[MagickPathExtent] = "Gaussian";
8180
8181      /*
8182        Add noise to the image.
8183      */
8184      noises=GetCommandOptions(MagickNoiseOptions);
8185      if (noises == (char **) NULL)
8186        break;
8187      XListBrowserWidget(display,windows,&windows->widget,
8188        (const char **) noises,"Add Noise",
8189        "Select a type of noise to add to your image:",noise_type);
8190      noises=DestroyStringList(noises);
8191      if (*noise_type == '\0')
8192        break;
8193      XSetCursorState(display,windows,MagickTrue);
8194      XCheckRefreshWindows(display,windows);
8195      noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8196        MagickNoiseOptions,MagickFalse,noise_type),1.0,exception);
8197      if (noise_image != (Image *) NULL)
8198        {
8199          *image=DestroyImage(*image);
8200          *image=noise_image;
8201        }
8202      CatchException(exception);
8203      XSetCursorState(display,windows,MagickFalse);
8204      if (IfMagickTrue(windows->image.orphan) )
8205        break;
8206      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8207      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8208      break;
8209    }
8210    case SharpenCommand:
8211    {
8212      Image
8213        *sharp_image;
8214
8215      static char
8216        radius[MagickPathExtent] = "0.0x1.0";
8217
8218      /*
8219        Query user for sharpen radius.
8220      */
8221      (void) XDialogWidget(display,windows,"Sharpen",
8222        "Enter the sharpen radius and standard deviation:",radius);
8223      if (*radius == '\0')
8224        break;
8225      /*
8226        Sharpen image scanlines.
8227      */
8228      XSetCursorState(display,windows,MagickTrue);
8229      XCheckRefreshWindows(display,windows);
8230      flags=ParseGeometry(radius,&geometry_info);
8231      sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8232        exception);
8233      if (sharp_image != (Image *) NULL)
8234        {
8235          *image=DestroyImage(*image);
8236          *image=sharp_image;
8237        }
8238      CatchException(exception);
8239      XSetCursorState(display,windows,MagickFalse);
8240      if (IfMagickTrue(windows->image.orphan) )
8241        break;
8242      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8243      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8244      break;
8245    }
8246    case BlurCommand:
8247    {
8248      Image
8249        *blur_image;
8250
8251      static char
8252        radius[MagickPathExtent] = "0.0x1.0";
8253
8254      /*
8255        Query user for blur radius.
8256      */
8257      (void) XDialogWidget(display,windows,"Blur",
8258        "Enter the blur radius and standard deviation:",radius);
8259      if (*radius == '\0')
8260        break;
8261      /*
8262        Blur an image.
8263      */
8264      XSetCursorState(display,windows,MagickTrue);
8265      XCheckRefreshWindows(display,windows);
8266      flags=ParseGeometry(radius,&geometry_info);
8267      blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8268        exception);
8269      if (blur_image != (Image *) NULL)
8270        {
8271          *image=DestroyImage(*image);
8272          *image=blur_image;
8273        }
8274      CatchException(exception);
8275      XSetCursorState(display,windows,MagickFalse);
8276      if (IfMagickTrue(windows->image.orphan) )
8277        break;
8278      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8279      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8280      break;
8281    }
8282    case ThresholdCommand:
8283    {
8284      double
8285        threshold;
8286
8287      static char
8288        factor[MagickPathExtent] = "128";
8289
8290      /*
8291        Query user for threshold value.
8292      */
8293      (void) XDialogWidget(display,windows,"Threshold",
8294        "Enter threshold value:",factor);
8295      if (*factor == '\0')
8296        break;
8297      /*
8298        Gamma correct image.
8299      */
8300      XSetCursorState(display,windows,MagickTrue);
8301      XCheckRefreshWindows(display,windows);
8302      threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8303      (void) BilevelImage(*image,threshold,exception);
8304      XSetCursorState(display,windows,MagickFalse);
8305      if (IfMagickTrue(windows->image.orphan) )
8306        break;
8307      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8308      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8309      break;
8310    }
8311    case EdgeDetectCommand:
8312    {
8313      Image
8314        *edge_image;
8315
8316      static char
8317        radius[MagickPathExtent] = "0";
8318
8319      /*
8320        Query user for edge factor.
8321      */
8322      (void) XDialogWidget(display,windows,"Detect Edges",
8323        "Enter the edge detect radius:",radius);
8324      if (*radius == '\0')
8325        break;
8326      /*
8327        Detect edge in image.
8328      */
8329      XSetCursorState(display,windows,MagickTrue);
8330      XCheckRefreshWindows(display,windows);
8331      flags=ParseGeometry(radius,&geometry_info);
8332      edge_image=EdgeImage(*image,geometry_info.rho,exception);
8333      if (edge_image != (Image *) NULL)
8334        {
8335          *image=DestroyImage(*image);
8336          *image=edge_image;
8337        }
8338      CatchException(exception);
8339      XSetCursorState(display,windows,MagickFalse);
8340      if (IfMagickTrue(windows->image.orphan) )
8341        break;
8342      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8343      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8344      break;
8345    }
8346    case SpreadCommand:
8347    {
8348      Image
8349        *spread_image;
8350
8351      static char
8352        amount[MagickPathExtent] = "2";
8353
8354      /*
8355        Query user for spread amount.
8356      */
8357      (void) XDialogWidget(display,windows,"Spread",
8358        "Enter the displacement amount:",amount);
8359      if (*amount == '\0')
8360        break;
8361      /*
8362        Displace image pixels by a random amount.
8363      */
8364      XSetCursorState(display,windows,MagickTrue);
8365      XCheckRefreshWindows(display,windows);
8366      flags=ParseGeometry(amount,&geometry_info);
8367      spread_image=EdgeImage(*image,geometry_info.rho,exception);
8368      if (spread_image != (Image *) NULL)
8369        {
8370          *image=DestroyImage(*image);
8371          *image=spread_image;
8372        }
8373      CatchException(exception);
8374      XSetCursorState(display,windows,MagickFalse);
8375      if (IfMagickTrue(windows->image.orphan) )
8376        break;
8377      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8378      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8379      break;
8380    }
8381    case ShadeCommand:
8382    {
8383      Image
8384        *shade_image;
8385
8386      int
8387        status;
8388
8389      static char
8390        geometry[MagickPathExtent] = "30x30";
8391
8392      /*
8393        Query user for the shade geometry.
8394      */
8395      status=XDialogWidget(display,windows,"Shade",
8396        "Enter the azimuth and elevation of the light source:",geometry);
8397      if (*geometry == '\0')
8398        break;
8399      /*
8400        Shade image pixels.
8401      */
8402      XSetCursorState(display,windows,MagickTrue);
8403      XCheckRefreshWindows(display,windows);
8404      flags=ParseGeometry(geometry,&geometry_info);
8405      if ((flags & SigmaValue) == 0)
8406        geometry_info.sigma=1.0;
8407      shade_image=ShadeImage(*image,IsMagickTrue(status),
8408        geometry_info.rho,geometry_info.sigma,exception);
8409      if (shade_image != (Image *) NULL)
8410        {
8411          *image=DestroyImage(*image);
8412          *image=shade_image;
8413        }
8414      CatchException(exception);
8415      XSetCursorState(display,windows,MagickFalse);
8416      if (IfMagickTrue(windows->image.orphan) )
8417        break;
8418      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8419      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8420      break;
8421    }
8422    case RaiseCommand:
8423    {
8424      static char
8425        bevel_width[MagickPathExtent] = "10";
8426
8427      /*
8428        Query user for bevel width.
8429      */
8430      (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8431      if (*bevel_width == '\0')
8432        break;
8433      /*
8434        Raise an image.
8435      */
8436      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8437        exception);
8438      XSetCursorState(display,windows,MagickTrue);
8439      XCheckRefreshWindows(display,windows);
8440      (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8441        exception);
8442      (void) RaiseImage(*image,&page_geometry,MagickTrue,exception);
8443      XSetCursorState(display,windows,MagickFalse);
8444      if (IfMagickTrue(windows->image.orphan) )
8445        break;
8446      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8447      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8448      break;
8449    }
8450    case SegmentCommand:
8451    {
8452      static char
8453        threshold[MagickPathExtent] = "1.0x1.5";
8454
8455      /*
8456        Query user for smoothing threshold.
8457      */
8458      (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8459        threshold);
8460      if (*threshold == '\0')
8461        break;
8462      /*
8463        Segment an image.
8464      */
8465      XSetCursorState(display,windows,MagickTrue);
8466      XCheckRefreshWindows(display,windows);
8467      flags=ParseGeometry(threshold,&geometry_info);
8468      if ((flags & SigmaValue) == 0)
8469        geometry_info.sigma=1.0;
8470      (void) SegmentImage(*image,sRGBColorspace,MagickFalse,geometry_info.rho,
8471        geometry_info.sigma,exception);
8472      XSetCursorState(display,windows,MagickFalse);
8473      if (IfMagickTrue(windows->image.orphan) )
8474        break;
8475      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8476      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8477      break;
8478    }
8479    case SepiaToneCommand:
8480    {
8481      double
8482        threshold;
8483
8484      Image
8485        *sepia_image;
8486
8487      static char
8488        factor[MagickPathExtent] = "80%";
8489
8490      /*
8491        Query user for sepia-tone factor.
8492      */
8493      (void) XDialogWidget(display,windows,"Sepia Tone",
8494        "Enter the sepia tone factor (0 - 99.9%):",factor);
8495      if (*factor == '\0')
8496        break;
8497      /*
8498        Sepia tone image pixels.
8499      */
8500      XSetCursorState(display,windows,MagickTrue);
8501      XCheckRefreshWindows(display,windows);
8502      threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8503      sepia_image=SepiaToneImage(*image,threshold,exception);
8504      if (sepia_image != (Image *) NULL)
8505        {
8506          *image=DestroyImage(*image);
8507          *image=sepia_image;
8508        }
8509      CatchException(exception);
8510      XSetCursorState(display,windows,MagickFalse);
8511      if (IfMagickTrue(windows->image.orphan) )
8512        break;
8513      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8514      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8515      break;
8516    }
8517    case SolarizeCommand:
8518    {
8519      double
8520        threshold;
8521
8522      static char
8523        factor[MagickPathExtent] = "60%";
8524
8525      /*
8526        Query user for solarize factor.
8527      */
8528      (void) XDialogWidget(display,windows,"Solarize",
8529        "Enter the solarize factor (0 - 99.9%):",factor);
8530      if (*factor == '\0')
8531        break;
8532      /*
8533        Solarize image pixels.
8534      */
8535      XSetCursorState(display,windows,MagickTrue);
8536      XCheckRefreshWindows(display,windows);
8537      threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8538      (void) SolarizeImage(*image,threshold,exception);
8539      XSetCursorState(display,windows,MagickFalse);
8540      if (IfMagickTrue(windows->image.orphan) )
8541        break;
8542      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8543      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8544      break;
8545    }
8546    case SwirlCommand:
8547    {
8548      Image
8549        *swirl_image;
8550
8551      static char
8552        degrees[MagickPathExtent] = "60";
8553
8554      /*
8555        Query user for swirl angle.
8556      */
8557      (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8558        degrees);
8559      if (*degrees == '\0')
8560        break;
8561      /*
8562        Swirl image pixels about the center.
8563      */
8564      XSetCursorState(display,windows,MagickTrue);
8565      XCheckRefreshWindows(display,windows);
8566      flags=ParseGeometry(degrees,&geometry_info);
8567      swirl_image=SwirlImage(*image,geometry_info.rho,(*image)->interpolate,
8568        exception);
8569      if (swirl_image != (Image *) NULL)
8570        {
8571          *image=DestroyImage(*image);
8572          *image=swirl_image;
8573        }
8574      CatchException(exception);
8575      XSetCursorState(display,windows,MagickFalse);
8576      if (IfMagickTrue(windows->image.orphan) )
8577        break;
8578      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8579      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8580      break;
8581    }
8582    case ImplodeCommand:
8583    {
8584      Image
8585        *implode_image;
8586
8587      static char
8588        factor[MagickPathExtent] = "0.3";
8589
8590      /*
8591        Query user for implode factor.
8592      */
8593      (void) XDialogWidget(display,windows,"Implode",
8594        "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8595      if (*factor == '\0')
8596        break;
8597      /*
8598        Implode image pixels about the center.
8599      */
8600      XSetCursorState(display,windows,MagickTrue);
8601      XCheckRefreshWindows(display,windows);
8602      flags=ParseGeometry(factor,&geometry_info);
8603      implode_image=ImplodeImage(*image,geometry_info.rho,(*image)->interpolate,
8604        exception);
8605      if (implode_image != (Image *) NULL)
8606        {
8607          *image=DestroyImage(*image);
8608          *image=implode_image;
8609        }
8610      CatchException(exception);
8611      XSetCursorState(display,windows,MagickFalse);
8612      if (IfMagickTrue(windows->image.orphan) )
8613        break;
8614      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8615      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8616      break;
8617    }
8618    case VignetteCommand:
8619    {
8620      Image
8621        *vignette_image;
8622
8623      static char
8624        geometry[MagickPathExtent] = "0x20";
8625
8626      /*
8627        Query user for the vignette geometry.
8628      */
8629      (void) XDialogWidget(display,windows,"Vignette",
8630        "Enter the radius, sigma, and x and y offsets:",geometry);
8631      if (*geometry == '\0')
8632        break;
8633      /*
8634        Soften the edges of the image in vignette style
8635      */
8636      XSetCursorState(display,windows,MagickTrue);
8637      XCheckRefreshWindows(display,windows);
8638      flags=ParseGeometry(geometry,&geometry_info);
8639      if ((flags & SigmaValue) == 0)
8640        geometry_info.sigma=1.0;
8641      if ((flags & XiValue) == 0)
8642        geometry_info.xi=0.1*(*image)->columns;
8643      if ((flags & PsiValue) == 0)
8644        geometry_info.psi=0.1*(*image)->rows;
8645      vignette_image=VignetteImage(*image,geometry_info.rho,0.0,(ssize_t)
8646        ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-0.5),
8647        exception);
8648      if (vignette_image != (Image *) NULL)
8649        {
8650          *image=DestroyImage(*image);
8651          *image=vignette_image;
8652        }
8653      CatchException(exception);
8654      XSetCursorState(display,windows,MagickFalse);
8655      if (IfMagickTrue(windows->image.orphan) )
8656        break;
8657      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8658      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8659      break;
8660    }
8661    case WaveCommand:
8662    {
8663      Image
8664        *wave_image;
8665
8666      static char
8667        geometry[MagickPathExtent] = "25x150";
8668
8669      /*
8670        Query user for the wave geometry.
8671      */
8672      (void) XDialogWidget(display,windows,"Wave",
8673        "Enter the amplitude and length of the wave:",geometry);
8674      if (*geometry == '\0')
8675        break;
8676      /*
8677        Alter an image along a sine wave.
8678      */
8679      XSetCursorState(display,windows,MagickTrue);
8680      XCheckRefreshWindows(display,windows);
8681      flags=ParseGeometry(geometry,&geometry_info);
8682      if ((flags & SigmaValue) == 0)
8683        geometry_info.sigma=1.0;
8684      wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8685        (*image)->interpolate,exception);
8686      if (wave_image != (Image *) NULL)
8687        {
8688          *image=DestroyImage(*image);
8689          *image=wave_image;
8690        }
8691      CatchException(exception);
8692      XSetCursorState(display,windows,MagickFalse);
8693      if (IfMagickTrue(windows->image.orphan) )
8694        break;
8695      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8696      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8697      break;
8698    }
8699    case OilPaintCommand:
8700    {
8701      Image
8702        *paint_image;
8703
8704      static char
8705        radius[MagickPathExtent] = "0";
8706
8707      /*
8708        Query user for circular neighborhood radius.
8709      */
8710      (void) XDialogWidget(display,windows,"Oil Paint",
8711        "Enter the mask radius:",radius);
8712      if (*radius == '\0')
8713        break;
8714      /*
8715        OilPaint image scanlines.
8716      */
8717      XSetCursorState(display,windows,MagickTrue);
8718      XCheckRefreshWindows(display,windows);
8719      flags=ParseGeometry(radius,&geometry_info);
8720      paint_image=OilPaintImage(*image,geometry_info.rho,geometry_info.sigma,
8721        exception);
8722      if (paint_image != (Image *) NULL)
8723        {
8724          *image=DestroyImage(*image);
8725          *image=paint_image;
8726        }
8727      CatchException(exception);
8728      XSetCursorState(display,windows,MagickFalse);
8729      if (IfMagickTrue(windows->image.orphan) )
8730        break;
8731      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8732      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8733      break;
8734    }
8735    case CharcoalDrawCommand:
8736    {
8737      Image
8738        *charcoal_image;
8739
8740      static char
8741        radius[MagickPathExtent] = "0x1";
8742
8743      /*
8744        Query user for charcoal radius.
8745      */
8746      (void) XDialogWidget(display,windows,"Charcoal Draw",
8747        "Enter the charcoal radius and sigma:",radius);
8748      if (*radius == '\0')
8749        break;
8750      /*
8751        Charcoal the image.
8752      */
8753      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8754        exception);
8755      XSetCursorState(display,windows,MagickTrue);
8756      XCheckRefreshWindows(display,windows);
8757      flags=ParseGeometry(radius,&geometry_info);
8758      if ((flags & SigmaValue) == 0)
8759        geometry_info.sigma=geometry_info.rho;
8760      charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8761        exception);
8762      if (charcoal_image != (Image *) NULL)
8763        {
8764          *image=DestroyImage(*image);
8765          *image=charcoal_image;
8766        }
8767      CatchException(exception);
8768      XSetCursorState(display,windows,MagickFalse);
8769      if (IfMagickTrue(windows->image.orphan) )
8770        break;
8771      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8772      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8773      break;
8774    }
8775    case AnnotateCommand:
8776    {
8777      /*
8778        Annotate the image with text.
8779      */
8780      status=XAnnotateEditImage(display,resource_info,windows,*image,exception);
8781      if (IfMagickFalse(status) )
8782        {
8783          XNoticeWidget(display,windows,"Unable to annotate X image",
8784            (*image)->filename);
8785          break;
8786        }
8787      break;
8788    }
8789    case DrawCommand:
8790    {
8791      /*
8792        Draw image.
8793      */
8794      status=XDrawEditImage(display,resource_info,windows,image,exception);
8795      if (IfMagickFalse(status) )
8796        {
8797          XNoticeWidget(display,windows,"Unable to draw on the X image",
8798            (*image)->filename);
8799          break;
8800        }
8801      break;
8802    }
8803    case ColorCommand:
8804    {
8805      /*
8806        Color edit.
8807      */
8808      status=XColorEditImage(display,resource_info,windows,image,exception);
8809      if (IfMagickFalse(status) )
8810        {
8811          XNoticeWidget(display,windows,"Unable to pixel edit X image",
8812            (*image)->filename);
8813          break;
8814        }
8815      break;
8816    }
8817    case MatteCommand:
8818    {
8819      /*
8820        Matte edit.
8821      */
8822      status=XMatteEditImage(display,resource_info,windows,image,exception);
8823      if (IfMagickFalse(status) )
8824        {
8825          XNoticeWidget(display,windows,"Unable to matte edit X image",
8826            (*image)->filename);
8827          break;
8828        }
8829      break;
8830    }
8831    case CompositeCommand:
8832    {
8833      /*
8834        Composite image.
8835      */
8836      status=XCompositeImage(display,resource_info,windows,*image,
8837        exception);
8838      if (IfMagickFalse(status) )
8839        {
8840          XNoticeWidget(display,windows,"Unable to composite X image",
8841            (*image)->filename);
8842          break;
8843        }
8844      break;
8845    }
8846    case AddBorderCommand:
8847    {
8848      Image
8849        *border_image;
8850
8851      static char
8852        geometry[MagickPathExtent] = "6x6";
8853
8854      /*
8855        Query user for border color and geometry.
8856      */
8857      XColorBrowserWidget(display,windows,"Select",color);
8858      if (*color == '\0')
8859        break;
8860      (void) XDialogWidget(display,windows,"Add Border",
8861        "Enter border geometry:",geometry);
8862      if (*geometry == '\0')
8863        break;
8864      /*
8865        Add a border to the image.
8866      */
8867      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8868        exception);
8869      XSetCursorState(display,windows,MagickTrue);
8870      XCheckRefreshWindows(display,windows);
8871      (void) QueryColorCompliance(color,AllCompliance,&(*image)->border_color,
8872        exception);
8873      (void) ParsePageGeometry(*image,geometry,&page_geometry,
8874        exception);
8875      border_image=BorderImage(*image,&page_geometry,(*image)->compose,
8876        exception);
8877      if (border_image != (Image *) NULL)
8878        {
8879          *image=DestroyImage(*image);
8880          *image=border_image;
8881        }
8882      CatchException(exception);
8883      XSetCursorState(display,windows,MagickFalse);
8884      if (IfMagickTrue(windows->image.orphan) )
8885        break;
8886      windows->image.window_changes.width=(int) (*image)->columns;
8887      windows->image.window_changes.height=(int) (*image)->rows;
8888      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8889      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8890      break;
8891    }
8892    case AddFrameCommand:
8893    {
8894      FrameInfo
8895        frame_info;
8896
8897      Image
8898        *frame_image;
8899
8900      static char
8901        geometry[MagickPathExtent] = "6x6";
8902
8903      /*
8904        Query user for frame color and geometry.
8905      */
8906      XColorBrowserWidget(display,windows,"Select",color);
8907      if (*color == '\0')
8908        break;
8909      (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8910        geometry);
8911      if (*geometry == '\0')
8912        break;
8913      /*
8914        Surround image with an ornamental border.
8915      */
8916      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8917        exception);
8918      XSetCursorState(display,windows,MagickTrue);
8919      XCheckRefreshWindows(display,windows);
8920      (void) QueryColorCompliance(color,AllCompliance,&(*image)->matte_color,
8921        exception);
8922      (void) ParsePageGeometry(*image,geometry,&page_geometry,
8923        exception);
8924      frame_info.width=page_geometry.width;
8925      frame_info.height=page_geometry.height;
8926      frame_info.outer_bevel=page_geometry.x;
8927      frame_info.inner_bevel=page_geometry.y;
8928      frame_info.x=(ssize_t) frame_info.width;
8929      frame_info.y=(ssize_t) frame_info.height;
8930      frame_info.width=(*image)->columns+2*frame_info.width;
8931      frame_info.height=(*image)->rows+2*frame_info.height;
8932      frame_image=FrameImage(*image,&frame_info,(*image)->compose,exception);
8933      if (frame_image != (Image *) NULL)
8934        {
8935          *image=DestroyImage(*image);
8936          *image=frame_image;
8937        }
8938      CatchException(exception);
8939      XSetCursorState(display,windows,MagickFalse);
8940      if (IfMagickTrue(windows->image.orphan) )
8941        break;
8942      windows->image.window_changes.width=(int) (*image)->columns;
8943      windows->image.window_changes.height=(int) (*image)->rows;
8944      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8945      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8946      break;
8947    }
8948    case CommentCommand:
8949    {
8950      const char
8951        *value;
8952
8953      FILE
8954        *file;
8955
8956      int
8957        unique_file;
8958
8959      /*
8960        Edit image comment.
8961      */
8962      unique_file=AcquireUniqueFileResource(image_info->filename);
8963      if (unique_file == -1)
8964        XNoticeWidget(display,windows,"Unable to edit image comment",
8965          image_info->filename);
8966      value=GetImageProperty(*image,"comment",exception);
8967      if (value == (char *) NULL)
8968        unique_file=close(unique_file)-1;
8969      else
8970        {
8971          register const char
8972            *p;
8973
8974          file=fdopen(unique_file,"w");
8975          if (file == (FILE *) NULL)
8976            {
8977              XNoticeWidget(display,windows,"Unable to edit image comment",
8978                image_info->filename);
8979              break;
8980            }
8981          for (p=value; *p != '\0'; p++)
8982            (void) fputc((int) *p,file);
8983          (void) fputc('\n',file);
8984          (void) fclose(file);
8985        }
8986      XSetCursorState(display,windows,MagickTrue);
8987      XCheckRefreshWindows(display,windows);
8988      status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
8989        exception);
8990      if (IfMagickFalse(status) )
8991        XNoticeWidget(display,windows,"Unable to edit image comment",
8992          (char *) NULL);
8993      else
8994        {
8995          char
8996            *comment;
8997
8998          comment=FileToString(image_info->filename,~0UL,exception);
8999          if (comment != (char *) NULL)
9000            {
9001              (void) SetImageProperty(*image,"comment",comment,exception);
9002              (*image)->taint=MagickTrue;
9003            }
9004        }
9005      (void) RelinquishUniqueFileResource(image_info->filename);
9006      XSetCursorState(display,windows,MagickFalse);
9007      break;
9008    }
9009    case LaunchCommand:
9010    {
9011      /*
9012        Launch program.
9013      */
9014      XSetCursorState(display,windows,MagickTrue);
9015      XCheckRefreshWindows(display,windows);
9016      (void) AcquireUniqueFilename(filename);
9017      (void) FormatLocaleString((*image)->filename,MagickPathExtent,"launch:%s",
9018        filename);
9019      status=WriteImage(image_info,*image,exception);
9020      if (IfMagickFalse(status) )
9021        XNoticeWidget(display,windows,"Unable to launch image editor",
9022          (char *) NULL);
9023      else
9024        {
9025          nexus=ReadImage(resource_info->image_info,exception);
9026          CatchException(exception);
9027          XClientMessage(display,windows->image.id,windows->im_protocols,
9028            windows->im_next_image,CurrentTime);
9029        }
9030      (void) RelinquishUniqueFileResource(filename);
9031      XSetCursorState(display,windows,MagickFalse);
9032      break;
9033    }
9034    case RegionofInterestCommand:
9035    {
9036      /*
9037        Apply an image processing technique to a region of interest.
9038      */
9039      (void) XROIImage(display,resource_info,windows,image,exception);
9040      break;
9041    }
9042    case InfoCommand:
9043      break;
9044    case ZoomCommand:
9045    {
9046      /*
9047        Zoom image.
9048      */
9049      if (IfMagickTrue(windows->magnify.mapped) )
9050        (void) XRaiseWindow(display,windows->magnify.id);
9051      else
9052        {
9053          /*
9054            Make magnify image.
9055          */
9056          XSetCursorState(display,windows,MagickTrue);
9057          (void) XMapRaised(display,windows->magnify.id);
9058          XSetCursorState(display,windows,MagickFalse);
9059        }
9060      break;
9061    }
9062    case ShowPreviewCommand:
9063    {
9064      char
9065        **previews;
9066
9067      Image
9068        *preview_image;
9069
9070      static char
9071        preview_type[MagickPathExtent] = "Gamma";
9072
9073      /*
9074        Select preview type from menu.
9075      */
9076      previews=GetCommandOptions(MagickPreviewOptions);
9077      if (previews == (char **) NULL)
9078        break;
9079      XListBrowserWidget(display,windows,&windows->widget,
9080        (const char **) previews,"Preview",
9081        "Select an enhancement, effect, or F/X:",preview_type);
9082      previews=DestroyStringList(previews);
9083      if (*preview_type == '\0')
9084        break;
9085      /*
9086        Show image preview.
9087      */
9088      XSetCursorState(display,windows,MagickTrue);
9089      XCheckRefreshWindows(display,windows);
9090      image_info->preview_type=(PreviewType)
9091        ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
9092      image_info->group=(ssize_t) windows->image.id;
9093      (void) DeleteImageProperty(*image,"label");
9094      (void) SetImageProperty(*image,"label","Preview",exception);
9095      (void) AcquireUniqueFilename(filename);
9096      (void) FormatLocaleString((*image)->filename,MagickPathExtent,"preview:%s",
9097        filename);
9098      status=WriteImage(image_info,*image,exception);
9099      (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
9100      preview_image=ReadImage(image_info,exception);
9101      (void) RelinquishUniqueFileResource(filename);
9102      if (preview_image == (Image *) NULL)
9103        break;
9104      (void) FormatLocaleString(preview_image->filename,MagickPathExtent,"show:%s",
9105        filename);
9106      status=WriteImage(image_info,preview_image,exception);
9107      preview_image=DestroyImage(preview_image);
9108      if (IfMagickFalse(status) )
9109        XNoticeWidget(display,windows,"Unable to show image preview",
9110          (*image)->filename);
9111      XDelay(display,1500);
9112      XSetCursorState(display,windows,MagickFalse);
9113      break;
9114    }
9115    case ShowHistogramCommand:
9116    {
9117      Image
9118        *histogram_image;
9119
9120      /*
9121        Show image histogram.
9122      */
9123      XSetCursorState(display,windows,MagickTrue);
9124      XCheckRefreshWindows(display,windows);
9125      image_info->group=(ssize_t) windows->image.id;
9126      (void) DeleteImageProperty(*image,"label");
9127      (void) SetImageProperty(*image,"label","Histogram",exception);
9128      (void) AcquireUniqueFilename(filename);
9129      (void) FormatLocaleString((*image)->filename,MagickPathExtent,"histogram:%s",
9130        filename);
9131      status=WriteImage(image_info,*image,exception);
9132      (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
9133      histogram_image=ReadImage(image_info,exception);
9134      (void) RelinquishUniqueFileResource(filename);
9135      if (histogram_image == (Image *) NULL)
9136        break;
9137      (void) FormatLocaleString(histogram_image->filename,MagickPathExtent,
9138        "show:%s",filename);
9139      status=WriteImage(image_info,histogram_image,exception);
9140      histogram_image=DestroyImage(histogram_image);
9141      if (IfMagickFalse(status) )
9142        XNoticeWidget(display,windows,"Unable to show histogram",
9143          (*image)->filename);
9144      XDelay(display,1500);
9145      XSetCursorState(display,windows,MagickFalse);
9146      break;
9147    }
9148    case ShowMatteCommand:
9149    {
9150      Image
9151        *matte_image;
9152
9153      if ((*image)->alpha_trait == UndefinedPixelTrait)
9154        {
9155          XNoticeWidget(display,windows,
9156            "Image does not have any matte information",(*image)->filename);
9157          break;
9158        }
9159      /*
9160        Show image matte.
9161      */
9162      XSetCursorState(display,windows,MagickTrue);
9163      XCheckRefreshWindows(display,windows);
9164      image_info->group=(ssize_t) windows->image.id;
9165      (void) DeleteImageProperty(*image,"label");
9166      (void) SetImageProperty(*image,"label","Matte",exception);
9167      (void) AcquireUniqueFilename(filename);
9168      (void) FormatLocaleString((*image)->filename,MagickPathExtent,"matte:%s",
9169        filename);
9170      status=WriteImage(image_info,*image,exception);
9171      (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
9172      matte_image=ReadImage(image_info,exception);
9173      (void) RelinquishUniqueFileResource(filename);
9174      if (matte_image == (Image *) NULL)
9175        break;
9176      (void) FormatLocaleString(matte_image->filename,MagickPathExtent,"show:%s",
9177        filename);
9178      status=WriteImage(image_info,matte_image,exception);
9179      matte_image=DestroyImage(matte_image);
9180      if (IfMagickFalse(status) )
9181        XNoticeWidget(display,windows,"Unable to show matte",
9182          (*image)->filename);
9183      XDelay(display,1500);
9184      XSetCursorState(display,windows,MagickFalse);
9185      break;
9186    }
9187    case BackgroundCommand:
9188    {
9189      /*
9190        Background image.
9191      */
9192      status=XBackgroundImage(display,resource_info,windows,image,exception);
9193      if (IfMagickFalse(status) )
9194        break;
9195      nexus=CloneImage(*image,0,0,MagickTrue,exception);
9196      if (nexus != (Image *) NULL)
9197        XClientMessage(display,windows->image.id,windows->im_protocols,
9198          windows->im_next_image,CurrentTime);
9199      break;
9200    }
9201    case SlideShowCommand:
9202    {
9203      static char
9204        delay[MagickPathExtent] = "5";
9205
9206      /*
9207        Display next image after pausing.
9208      */
9209      (void) XDialogWidget(display,windows,"Slide Show",
9210        "Pause how many 1/100ths of a second between images:",delay);
9211      if (*delay == '\0')
9212        break;
9213      resource_info->delay=StringToUnsignedLong(delay);
9214      XClientMessage(display,windows->image.id,windows->im_protocols,
9215        windows->im_next_image,CurrentTime);
9216      break;
9217    }
9218    case PreferencesCommand:
9219    {
9220      /*
9221        Set user preferences.
9222      */
9223      status=XPreferencesWidget(display,resource_info,windows);
9224      if (IfMagickFalse(status) )
9225        break;
9226      nexus=CloneImage(*image,0,0,MagickTrue,exception);
9227      if (nexus != (Image *) NULL)
9228        XClientMessage(display,windows->image.id,windows->im_protocols,
9229          windows->im_next_image,CurrentTime);
9230      break;
9231    }
9232    case HelpCommand:
9233    {
9234      /*
9235        User requested help.
9236      */
9237      XTextViewWidget(display,resource_info,windows,MagickFalse,
9238        "Help Viewer - Display",DisplayHelp);
9239      break;
9240    }
9241    case BrowseDocumentationCommand:
9242    {
9243      Atom
9244        mozilla_atom;
9245
9246      Window
9247        mozilla_window,
9248        root_window;
9249
9250      /*
9251        Browse the ImageMagick documentation.
9252      */
9253      root_window=XRootWindow(display,XDefaultScreen(display));
9254      mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9255      mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9256      if (mozilla_window != (Window) NULL)
9257        {
9258          char
9259            command[MagickPathExtent],
9260            *url;
9261
9262          /*
9263            Display documentation using Netscape remote control.
9264          */
9265          url=GetMagickHomeURL();
9266          (void) FormatLocaleString(command,MagickPathExtent,
9267            "openurl(%s,new-tab)",url);
9268          url=DestroyString(url);
9269          mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9270          (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9271            8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9272          XSetCursorState(display,windows,MagickFalse);
9273          break;
9274        }
9275      XSetCursorState(display,windows,MagickTrue);
9276      XCheckRefreshWindows(display,windows);
9277      status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9278        exception);
9279      if (IfMagickFalse(status) )
9280        XNoticeWidget(display,windows,"Unable to browse documentation",
9281          (char *) NULL);
9282      XDelay(display,1500);
9283      XSetCursorState(display,windows,MagickFalse);
9284      break;
9285    }
9286    case VersionCommand:
9287    {
9288      XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9289        GetMagickCopyright());
9290      break;
9291    }
9292    case SaveToUndoBufferCommand:
9293      break;
9294    default:
9295    {
9296      (void) XBell(display,0);
9297      break;
9298    }
9299  }
9300  image_info=DestroyImageInfo(image_info);
9301  return(nexus);
9302}
9303
9304/*
9305%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9306%                                                                             %
9307%                                                                             %
9308%                                                                             %
9309+   X M a g n i f y I m a g e                                                 %
9310%                                                                             %
9311%                                                                             %
9312%                                                                             %
9313%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9314%
9315%  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9316%  The magnified portion is displayed in a separate window.
9317%
9318%  The format of the XMagnifyImage method is:
9319%
9320%      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9321%        ExceptionInfo *exception)
9322%
9323%  A description of each parameter follows:
9324%
9325%    o display: Specifies a connection to an X server;  returned from
9326%      XOpenDisplay.
9327%
9328%    o windows: Specifies a pointer to a XWindows structure.
9329%
9330%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
9331%      the entire image is refreshed.
9332%
9333%    o exception: return any errors or warnings in this structure.
9334%
9335*/
9336static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9337  ExceptionInfo *exception)
9338{
9339  char
9340    text[MagickPathExtent];
9341
9342  register int
9343    x,
9344    y;
9345
9346  size_t
9347    state;
9348
9349  /*
9350    Update magnified image until the mouse button is released.
9351  */
9352  (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9353  state=DefaultState;
9354  x=event->xbutton.x;
9355  y=event->xbutton.y;
9356  windows->magnify.x=(int) windows->image.x+x;
9357  windows->magnify.y=(int) windows->image.y+y;
9358  do
9359  {
9360    /*
9361      Map and unmap Info widget as text cursor crosses its boundaries.
9362    */
9363    if (IfMagickTrue(windows->info.mapped) )
9364      {
9365        if ((x < (int) (windows->info.x+windows->info.width)) &&
9366            (y < (int) (windows->info.y+windows->info.height)))
9367          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9368      }
9369    else
9370      if ((x > (int) (windows->info.x+windows->info.width)) ||
9371          (y > (int) (windows->info.y+windows->info.height)))
9372        (void) XMapWindow(display,windows->info.id);
9373    if (IfMagickTrue(windows->info.mapped) )
9374      {
9375        /*
9376          Display pointer position.
9377        */
9378        (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
9379          windows->magnify.x,windows->magnify.y);
9380        XInfoWidget(display,windows,text);
9381      }
9382    /*
9383      Wait for next event.
9384    */
9385    XScreenEvent(display,windows,event,exception);
9386    switch (event->type)
9387    {
9388      case ButtonPress:
9389        break;
9390      case ButtonRelease:
9391      {
9392        /*
9393          User has finished magnifying image.
9394        */
9395        x=event->xbutton.x;
9396        y=event->xbutton.y;
9397        state|=ExitState;
9398        break;
9399      }
9400      case Expose:
9401        break;
9402      case MotionNotify:
9403      {
9404        x=event->xmotion.x;
9405        y=event->xmotion.y;
9406        break;
9407      }
9408      default:
9409        break;
9410    }
9411    /*
9412      Check boundary conditions.
9413    */
9414    if (x < 0)
9415      x=0;
9416    else
9417      if (x >= (int) windows->image.width)
9418        x=(int) windows->image.width-1;
9419    if (y < 0)
9420      y=0;
9421    else
9422     if (y >= (int) windows->image.height)
9423       y=(int) windows->image.height-1;
9424  } while ((state & ExitState) == 0);
9425  /*
9426    Display magnified image.
9427  */
9428  XSetCursorState(display,windows,MagickFalse);
9429}
9430
9431/*
9432%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9433%                                                                             %
9434%                                                                             %
9435%                                                                             %
9436+   X M a g n i f y W i n d o w C o m m a n d                                 %
9437%                                                                             %
9438%                                                                             %
9439%                                                                             %
9440%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9441%
9442%  XMagnifyWindowCommand() moves the image within an Magnify window by one
9443%  pixel as specified by the key symbol.
9444%
9445%  The format of the XMagnifyWindowCommand method is:
9446%
9447%      void XMagnifyWindowCommand(Display *display,XWindows *windows,
9448%        const MagickStatusType state,const KeySym key_symbol,
9449%        ExceptionInfo *exception)
9450%
9451%  A description of each parameter follows:
9452%
9453%    o display: Specifies a connection to an X server; returned from
9454%      XOpenDisplay.
9455%
9456%    o windows: Specifies a pointer to a XWindows structure.
9457%
9458%    o state: key mask.
9459%
9460%    o key_symbol: Specifies a KeySym which indicates which side of the image
9461%      to trim.
9462%
9463%    o exception: return any errors or warnings in this structure.
9464%
9465*/
9466static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9467  const MagickStatusType state,const KeySym key_symbol,ExceptionInfo *exception)
9468{
9469  unsigned int
9470    quantum;
9471
9472  /*
9473    User specified a magnify factor or position.
9474  */
9475  quantum=1;
9476  if ((state & Mod1Mask) != 0)
9477    quantum=10;
9478  switch ((int) key_symbol)
9479  {
9480    case QuitCommand:
9481    {
9482      (void) XWithdrawWindow(display,windows->magnify.id,
9483        windows->magnify.screen);
9484      break;
9485    }
9486    case XK_Home:
9487    case XK_KP_Home:
9488    {
9489      windows->magnify.x=(int) windows->image.width/2;
9490      windows->magnify.y=(int) windows->image.height/2;
9491      break;
9492    }
9493    case XK_Left:
9494    case XK_KP_Left:
9495    {
9496      if (windows->magnify.x > 0)
9497        windows->magnify.x-=quantum;
9498      break;
9499    }
9500    case XK_Up:
9501    case XK_KP_Up:
9502    {
9503      if (windows->magnify.y > 0)
9504        windows->magnify.y-=quantum;
9505      break;
9506    }
9507    case XK_Right:
9508    case XK_KP_Right:
9509    {
9510      if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9511        windows->magnify.x+=quantum;
9512      break;
9513    }
9514    case XK_Down:
9515    case XK_KP_Down:
9516    {
9517      if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9518        windows->magnify.y+=quantum;
9519      break;
9520    }
9521    case XK_0:
9522    case XK_1:
9523    case XK_2:
9524    case XK_3:
9525    case XK_4:
9526    case XK_5:
9527    case XK_6:
9528    case XK_7:
9529    case XK_8:
9530    case XK_9:
9531    {
9532      windows->magnify.data=(key_symbol-XK_0);
9533      break;
9534    }
9535    case XK_KP_0:
9536    case XK_KP_1:
9537    case XK_KP_2:
9538    case XK_KP_3:
9539    case XK_KP_4:
9540    case XK_KP_5:
9541    case XK_KP_6:
9542    case XK_KP_7:
9543    case XK_KP_8:
9544    case XK_KP_9:
9545    {
9546      windows->magnify.data=(key_symbol-XK_KP_0);
9547      break;
9548    }
9549    default:
9550      break;
9551  }
9552  XMakeMagnifyImage(display,windows,exception);
9553}
9554
9555/*
9556%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9557%                                                                             %
9558%                                                                             %
9559%                                                                             %
9560+   X M a k e P a n I m a g e                                                 %
9561%                                                                             %
9562%                                                                             %
9563%                                                                             %
9564%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9565%
9566%  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9567%  icon window.
9568%
9569%  The format of the XMakePanImage method is:
9570%
9571%        void XMakePanImage(Display *display,XResourceInfo *resource_info,
9572%          XWindows *windows,Image *image,ExceptionInfo *exception)
9573%
9574%  A description of each parameter follows:
9575%
9576%    o display: Specifies a connection to an X server;  returned from
9577%      XOpenDisplay.
9578%
9579%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9580%
9581%    o windows: Specifies a pointer to a XWindows structure.
9582%
9583%    o image: the image.
9584%
9585%    o exception: return any errors or warnings in this structure.
9586%
9587*/
9588static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9589  XWindows *windows,Image *image,ExceptionInfo *exception)
9590{
9591  MagickStatusType
9592    status;
9593
9594  /*
9595    Create and display image for panning icon.
9596  */
9597  XSetCursorState(display,windows,MagickTrue);
9598  XCheckRefreshWindows(display,windows);
9599  windows->pan.x=(int) windows->image.x;
9600  windows->pan.y=(int) windows->image.y;
9601  status=XMakeImage(display,resource_info,&windows->pan,image,
9602    windows->pan.width,windows->pan.height,exception);
9603  if (IfMagickFalse(status) )
9604    ThrowXWindowException(ResourceLimitError,
9605     "MemoryAllocationFailed",image->filename);
9606  (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9607    windows->pan.pixmap);
9608  (void) XClearWindow(display,windows->pan.id);
9609  XDrawPanRectangle(display,windows);
9610  XSetCursorState(display,windows,MagickFalse);
9611}
9612
9613/*
9614%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9615%                                                                             %
9616%                                                                             %
9617%                                                                             %
9618+   X M a t t a E d i t I m a g e                                             %
9619%                                                                             %
9620%                                                                             %
9621%                                                                             %
9622%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9623%
9624%  XMatteEditImage() allows the user to interactively change the Matte channel
9625%  of an image.  If the image is PseudoClass it is promoted to DirectClass
9626%  before the matte information is stored.
9627%
9628%  The format of the XMatteEditImage method is:
9629%
9630%      MagickBooleanType XMatteEditImage(Display *display,
9631%        XResourceInfo *resource_info,XWindows *windows,Image **image,
9632%        ExceptionInfo *exception)
9633%
9634%  A description of each parameter follows:
9635%
9636%    o display: Specifies a connection to an X server;  returned from
9637%      XOpenDisplay.
9638%
9639%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9640%
9641%    o windows: Specifies a pointer to a XWindows structure.
9642%
9643%    o image: the image; returned from ReadImage.
9644%
9645%    o exception: return any errors or warnings in this structure.
9646%
9647*/
9648static MagickBooleanType XMatteEditImage(Display *display,
9649  XResourceInfo *resource_info,XWindows *windows,Image **image,
9650  ExceptionInfo *exception)
9651{
9652  static char
9653    matte[MagickPathExtent] = "0";
9654
9655  static const char
9656    *MatteEditMenu[] =
9657    {
9658      "Method",
9659      "Border Color",
9660      "Fuzz",
9661      "Matte Value",
9662      "Undo",
9663      "Help",
9664      "Dismiss",
9665      (char *) NULL
9666    };
9667
9668  static const ModeType
9669    MatteEditCommands[] =
9670    {
9671      MatteEditMethod,
9672      MatteEditBorderCommand,
9673      MatteEditFuzzCommand,
9674      MatteEditValueCommand,
9675      MatteEditUndoCommand,
9676      MatteEditHelpCommand,
9677      MatteEditDismissCommand
9678    };
9679
9680  static PaintMethod
9681    method = PointMethod;
9682
9683  static XColor
9684    border_color = { 0, 0, 0, 0, 0, 0 };
9685
9686  char
9687    command[MagickPathExtent],
9688    text[MagickPathExtent];
9689
9690  Cursor
9691    cursor;
9692
9693  int
9694    entry,
9695    id,
9696    x,
9697    x_offset,
9698    y,
9699    y_offset;
9700
9701  register int
9702    i;
9703
9704  register Quantum
9705    *q;
9706
9707  unsigned int
9708    height,
9709    width;
9710
9711  size_t
9712    state;
9713
9714  XEvent
9715    event;
9716
9717  /*
9718    Map Command widget.
9719  */
9720  (void) CloneString(&windows->command.name,"Matte Edit");
9721  windows->command.data=4;
9722  (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9723  (void) XMapRaised(display,windows->command.id);
9724  XClientMessage(display,windows->image.id,windows->im_protocols,
9725    windows->im_update_widget,CurrentTime);
9726  /*
9727    Make cursor.
9728  */
9729  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9730    resource_info->background_color,resource_info->foreground_color);
9731  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9732  /*
9733    Track pointer until button 1 is pressed.
9734  */
9735  XQueryPosition(display,windows->image.id,&x,&y);
9736  (void) XSelectInput(display,windows->image.id,
9737    windows->image.attributes.event_mask | PointerMotionMask);
9738  state=DefaultState;
9739  do
9740  {
9741    if (IfMagickTrue(windows->info.mapped) )
9742      {
9743        /*
9744          Display pointer position.
9745        */
9746        (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
9747          x+windows->image.x,y+windows->image.y);
9748        XInfoWidget(display,windows,text);
9749      }
9750    /*
9751      Wait for next event.
9752    */
9753    XScreenEvent(display,windows,&event,exception);
9754    if (event.xany.window == windows->command.id)
9755      {
9756        /*
9757          Select a command from the Command widget.
9758        */
9759        id=XCommandWidget(display,windows,MatteEditMenu,&event);
9760        if (id < 0)
9761          {
9762            (void) XCheckDefineCursor(display,windows->image.id,cursor);
9763            continue;
9764          }
9765        switch (MatteEditCommands[id])
9766        {
9767          case MatteEditMethod:
9768          {
9769            char
9770              **methods;
9771
9772            /*
9773              Select a method from the pop-up menu.
9774            */
9775            methods=GetCommandOptions(MagickMethodOptions);
9776            if (methods == (char **) NULL)
9777              break;
9778            entry=XMenuWidget(display,windows,MatteEditMenu[id],
9779              (const char **) methods,command);
9780            if (entry >= 0)
9781              method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9782                MagickFalse,methods[entry]);
9783            methods=DestroyStringList(methods);
9784            break;
9785          }
9786          case MatteEditBorderCommand:
9787          {
9788            const char
9789              *ColorMenu[MaxNumberPens];
9790
9791            int
9792              pen_number;
9793
9794            /*
9795              Initialize menu selections.
9796            */
9797            for (i=0; i < (int) (MaxNumberPens-2); i++)
9798              ColorMenu[i]=resource_info->pen_colors[i];
9799            ColorMenu[MaxNumberPens-2]="Browser...";
9800            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9801            /*
9802              Select a pen color from the pop-up menu.
9803            */
9804            pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9805              (const char **) ColorMenu,command);
9806            if (pen_number < 0)
9807              break;
9808            if (pen_number == (MaxNumberPens-2))
9809              {
9810                static char
9811                  color_name[MagickPathExtent] = "gray";
9812
9813                /*
9814                  Select a pen color from a dialog.
9815                */
9816                resource_info->pen_colors[pen_number]=color_name;
9817                XColorBrowserWidget(display,windows,"Select",color_name);
9818                if (*color_name == '\0')
9819                  break;
9820              }
9821            /*
9822              Set border color.
9823            */
9824            (void) XParseColor(display,windows->map_info->colormap,
9825              resource_info->pen_colors[pen_number],&border_color);
9826            break;
9827          }
9828          case MatteEditFuzzCommand:
9829          {
9830            static char
9831              fuzz[MagickPathExtent];
9832
9833            static const char
9834              *FuzzMenu[] =
9835              {
9836                "0%",
9837                "2%",
9838                "5%",
9839                "10%",
9840                "15%",
9841                "Dialog...",
9842                (char *) NULL,
9843              };
9844
9845            /*
9846              Select a command from the pop-up menu.
9847            */
9848            entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9849              command);
9850            if (entry < 0)
9851              break;
9852            if (entry != 5)
9853              {
9854                (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
9855                  QuantumRange+1.0);
9856                break;
9857              }
9858            (void) CopyMagickString(fuzz,"20%",MagickPathExtent);
9859            (void) XDialogWidget(display,windows,"Ok",
9860              "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9861            if (*fuzz == '\0')
9862              break;
9863            (void) ConcatenateMagickString(fuzz,"%",MagickPathExtent);
9864            (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
9865              1.0);
9866            break;
9867          }
9868          case MatteEditValueCommand:
9869          {
9870            static char
9871              message[MagickPathExtent];
9872
9873            static const char
9874              *MatteMenu[] =
9875              {
9876                "Opaque",
9877                "Transparent",
9878                "Dialog...",
9879                (char *) NULL,
9880              };
9881
9882            /*
9883              Select a command from the pop-up menu.
9884            */
9885            entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9886              command);
9887            if (entry < 0)
9888              break;
9889            if (entry != 2)
9890              {
9891                (void) FormatLocaleString(matte,MagickPathExtent,QuantumFormat,
9892                  OpaqueAlpha);
9893                if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9894                  (void) FormatLocaleString(matte,MagickPathExtent,QuantumFormat,
9895                    (Quantum) TransparentAlpha);
9896                break;
9897              }
9898            (void) FormatLocaleString(message,MagickPathExtent,
9899              "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9900              QuantumRange);
9901            (void) XDialogWidget(display,windows,"Matte",message,matte);
9902            if (*matte == '\0')
9903              break;
9904            break;
9905          }
9906          case MatteEditUndoCommand:
9907          {
9908            (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9909              image,exception);
9910            break;
9911          }
9912          case MatteEditHelpCommand:
9913          {
9914            XTextViewWidget(display,resource_info,windows,MagickFalse,
9915              "Help Viewer - Matte Edit",ImageMatteEditHelp);
9916            break;
9917          }
9918          case MatteEditDismissCommand:
9919          {
9920            /*
9921              Prematurely exit.
9922            */
9923            state|=EscapeState;
9924            state|=ExitState;
9925            break;
9926          }
9927          default:
9928            break;
9929        }
9930        (void) XCheckDefineCursor(display,windows->image.id,cursor);
9931        continue;
9932      }
9933    switch (event.type)
9934    {
9935      case ButtonPress:
9936      {
9937        if (event.xbutton.button != Button1)
9938          break;
9939        if ((event.xbutton.window != windows->image.id) &&
9940            (event.xbutton.window != windows->magnify.id))
9941          break;
9942        /*
9943          Update matte data.
9944        */
9945        x=event.xbutton.x;
9946        y=event.xbutton.y;
9947        (void) XMagickCommand(display,resource_info,windows,
9948          SaveToUndoBufferCommand,image,exception);
9949        state|=UpdateConfigurationState;
9950        break;
9951      }
9952      case ButtonRelease:
9953      {
9954        if (event.xbutton.button != Button1)
9955          break;
9956        if ((event.xbutton.window != windows->image.id) &&
9957            (event.xbutton.window != windows->magnify.id))
9958          break;
9959        /*
9960          Update colormap information.
9961        */
9962        x=event.xbutton.x;
9963        y=event.xbutton.y;
9964        XConfigureImageColormap(display,resource_info,windows,*image,exception);
9965        (void) XConfigureImage(display,resource_info,windows,*image,exception);
9966        XInfoWidget(display,windows,text);
9967        (void) XCheckDefineCursor(display,windows->image.id,cursor);
9968        state&=(~UpdateConfigurationState);
9969        break;
9970      }
9971      case Expose:
9972        break;
9973      case KeyPress:
9974      {
9975        char
9976          command[MagickPathExtent];
9977
9978        KeySym
9979          key_symbol;
9980
9981        if (event.xkey.window == windows->magnify.id)
9982          {
9983            Window
9984              window;
9985
9986            window=windows->magnify.id;
9987            while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9988          }
9989        if (event.xkey.window != windows->image.id)
9990          break;
9991        /*
9992          Respond to a user key press.
9993        */
9994        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
9995          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
9996        switch ((int) key_symbol)
9997        {
9998          case XK_Escape:
9999          case XK_F20:
10000          {
10001            /*
10002              Prematurely exit.
10003            */
10004            state|=ExitState;
10005            break;
10006          }
10007          case XK_F1:
10008          case XK_Help:
10009          {
10010            XTextViewWidget(display,resource_info,windows,MagickFalse,
10011              "Help Viewer - Matte Edit",ImageMatteEditHelp);
10012            break;
10013          }
10014          default:
10015          {
10016            (void) XBell(display,0);
10017            break;
10018          }
10019        }
10020        break;
10021      }
10022      case MotionNotify:
10023      {
10024        /*
10025          Map and unmap Info widget as cursor crosses its boundaries.
10026        */
10027        x=event.xmotion.x;
10028        y=event.xmotion.y;
10029        if (IfMagickTrue(windows->info.mapped) )
10030          {
10031            if ((x < (int) (windows->info.x+windows->info.width)) &&
10032                (y < (int) (windows->info.y+windows->info.height)))
10033              (void) XWithdrawWindow(display,windows->info.id,
10034                windows->info.screen);
10035          }
10036        else
10037          if ((x > (int) (windows->info.x+windows->info.width)) ||
10038              (y > (int) (windows->info.y+windows->info.height)))
10039            (void) XMapWindow(display,windows->info.id);
10040        break;
10041      }
10042      default:
10043        break;
10044    }
10045    if (event.xany.window == windows->magnify.id)
10046      {
10047        x=windows->magnify.x-windows->image.x;
10048        y=windows->magnify.y-windows->image.y;
10049      }
10050    x_offset=x;
10051    y_offset=y;
10052    if ((state & UpdateConfigurationState) != 0)
10053      {
10054        CacheView
10055          *image_view;
10056
10057        int
10058          x,
10059          y;
10060
10061        /*
10062          Matte edit is relative to image configuration.
10063        */
10064        (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
10065          MagickTrue);
10066        XPutPixel(windows->image.ximage,x_offset,y_offset,
10067          windows->pixel_info->background_color.pixel);
10068        width=(unsigned int) (*image)->columns;
10069        height=(unsigned int) (*image)->rows;
10070        x=0;
10071        y=0;
10072        if (windows->image.crop_geometry != (char *) NULL)
10073          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
10074            &height);
10075        x_offset=(int) (width*(windows->image.x+x_offset)/
10076          windows->image.ximage->width+x);
10077        y_offset=(int) (height*(windows->image.y+y_offset)/
10078          windows->image.ximage->height+y);
10079        if ((x_offset < 0) || (y_offset < 0))
10080          continue;
10081        if ((x_offset >= (int) (*image)->columns) ||
10082            (y_offset >= (int) (*image)->rows))
10083          continue;
10084        if (IfMagickFalse(SetImageStorageClass(*image,DirectClass,exception)) )
10085          return(MagickFalse);
10086        if ((*image)->alpha_trait == UndefinedPixelTrait)
10087          (void) SetImageAlphaChannel(*image,OpaqueAlphaChannel,exception);
10088        image_view=AcquireAuthenticCacheView(*image,exception);
10089        switch (method)
10090        {
10091          case PointMethod:
10092          default:
10093          {
10094            /*
10095              Update matte information using point algorithm.
10096            */
10097            q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10098              (ssize_t) y_offset,1,1,exception);
10099            if (q == (Quantum *) NULL)
10100              break;
10101            SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10102            (void) SyncCacheViewAuthenticPixels(image_view,exception);
10103            break;
10104          }
10105          case ReplaceMethod:
10106          {
10107            PixelInfo
10108              pixel,
10109              target;
10110
10111            /*
10112              Update matte information using replace algorithm.
10113            */
10114            (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
10115              x_offset,(ssize_t) y_offset,&target,exception);
10116            for (y=0; y < (int) (*image)->rows; y++)
10117            {
10118              q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10119                (*image)->columns,1,exception);
10120              if (q == (Quantum *) NULL)
10121                break;
10122              for (x=0; x < (int) (*image)->columns; x++)
10123              {
10124                GetPixelInfoPixel(*image,q,&pixel);
10125                if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
10126                  SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10127                q+=GetPixelChannels(*image);
10128              }
10129              if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
10130                break;
10131            }
10132            break;
10133          }
10134          case FloodfillMethod:
10135          case FillToBorderMethod:
10136          {
10137            ChannelType
10138              channel_mask;
10139
10140            DrawInfo
10141              *draw_info;
10142
10143            PixelInfo
10144              target;
10145
10146            /*
10147              Update matte information using floodfill algorithm.
10148            */
10149            (void) GetOneVirtualPixelInfo(*image,
10150              GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
10151              y_offset,&target,exception);
10152            if (method == FillToBorderMethod)
10153              {
10154                target.red=(double) ScaleShortToQuantum(
10155                  border_color.red);
10156                target.green=(double) ScaleShortToQuantum(
10157                  border_color.green);
10158                target.blue=(double) ScaleShortToQuantum(
10159                  border_color.blue);
10160              }
10161            draw_info=CloneDrawInfo(resource_info->image_info,
10162              (DrawInfo *) NULL);
10163            draw_info->fill.alpha=(double) ClampToQuantum(
10164              StringToDouble(matte,(char **) NULL));
10165            channel_mask=SetImageChannelMask(*image,AlphaChannel);
10166            (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10167              x_offset,(ssize_t) y_offset,
10168              IsMagickFalse(method == FloodfillMethod),exception);
10169            (void) SetPixelChannelMask(*image,channel_mask);
10170            draw_info=DestroyDrawInfo(draw_info);
10171            break;
10172          }
10173          case ResetMethod:
10174          {
10175            /*
10176              Update matte information using reset algorithm.
10177            */
10178            if (IfMagickFalse(SetImageStorageClass(*image,DirectClass,exception)) )
10179              return(MagickFalse);
10180            for (y=0; y < (int) (*image)->rows; y++)
10181            {
10182              q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10183                (*image)->columns,1,exception);
10184              if (q == (Quantum *) NULL)
10185                break;
10186              for (x=0; x < (int) (*image)->columns; x++)
10187              {
10188                SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10189                q+=GetPixelChannels(*image);
10190              }
10191              if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
10192                break;
10193            }
10194            if (StringToLong(matte) == (long) OpaqueAlpha)
10195              (*image)->alpha_trait=UndefinedPixelTrait;
10196            break;
10197          }
10198        }
10199        image_view=DestroyCacheView(image_view);
10200        state&=(~UpdateConfigurationState);
10201      }
10202  } while ((state & ExitState) == 0);
10203  (void) XSelectInput(display,windows->image.id,
10204    windows->image.attributes.event_mask);
10205  XSetCursorState(display,windows,MagickFalse);
10206  (void) XFreeCursor(display,cursor);
10207  return(MagickTrue);
10208}
10209
10210/*
10211%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10212%                                                                             %
10213%                                                                             %
10214%                                                                             %
10215+   X O p e n I m a g e                                                       %
10216%                                                                             %
10217%                                                                             %
10218%                                                                             %
10219%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10220%
10221%  XOpenImage() loads an image from a file.
10222%
10223%  The format of the XOpenImage method is:
10224%
10225%     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10226%       XWindows *windows,const unsigned int command)
10227%
10228%  A description of each parameter follows:
10229%
10230%    o display: Specifies a connection to an X server; returned from
10231%      XOpenDisplay.
10232%
10233%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10234%
10235%    o windows: Specifies a pointer to a XWindows structure.
10236%
10237%    o command: A value other than zero indicates that the file is selected
10238%      from the command line argument list.
10239%
10240*/
10241static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10242  XWindows *windows,const MagickBooleanType command)
10243{
10244  const MagickInfo
10245    *magick_info;
10246
10247  ExceptionInfo
10248    *exception;
10249
10250  Image
10251    *nexus;
10252
10253  ImageInfo
10254    *image_info;
10255
10256  static char
10257    filename[MagickPathExtent] = "\0";
10258
10259  /*
10260    Request file name from user.
10261  */
10262  if (IfMagickFalse(command) )
10263    XFileBrowserWidget(display,windows,"Open",filename);
10264  else
10265    {
10266      char
10267        **filelist,
10268        **files;
10269
10270      int
10271        count,
10272        status;
10273
10274      register int
10275        i,
10276        j;
10277
10278      /*
10279        Select next image from the command line.
10280      */
10281      status=XGetCommand(display,windows->image.id,&files,&count);
10282      if (status == 0)
10283        {
10284          ThrowXWindowException(XServerError,"UnableToGetProperty","...");
10285          return((Image *) NULL);
10286        }
10287      filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10288      if (filelist == (char **) NULL)
10289        {
10290          ThrowXWindowException(ResourceLimitError,
10291            "MemoryAllocationFailed","...");
10292          (void) XFreeStringList(files);
10293          return((Image *) NULL);
10294        }
10295      j=0;
10296      for (i=1; i < count; i++)
10297        if (*files[i] != '-')
10298          filelist[j++]=files[i];
10299      filelist[j]=(char *) NULL;
10300      XListBrowserWidget(display,windows,&windows->widget,
10301        (const char **) filelist,"Load","Select Image to Load:",filename);
10302      filelist=(char **) RelinquishMagickMemory(filelist);
10303      (void) XFreeStringList(files);
10304    }
10305  if (*filename == '\0')
10306    return((Image *) NULL);
10307  image_info=CloneImageInfo(resource_info->image_info);
10308  (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10309    (void *) NULL);
10310  (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
10311  exception=AcquireExceptionInfo();
10312  (void) SetImageInfo(image_info,0,exception);
10313  if (LocaleCompare(image_info->magick,"X") == 0)
10314    {
10315      char
10316        seconds[MagickPathExtent];
10317
10318      /*
10319        User may want to delay the X server screen grab.
10320      */
10321      (void) CopyMagickString(seconds,"0",MagickPathExtent);
10322      (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10323        seconds);
10324      if (*seconds == '\0')
10325        return((Image *) NULL);
10326      XDelay(display,(size_t) (1000*StringToLong(seconds)));
10327    }
10328  magick_info=GetMagickInfo(image_info->magick,exception);
10329  if ((magick_info != (const MagickInfo *) NULL) &&
10330      GetMagickRawSupport(magick_info) == MagickTrue)
10331    {
10332      char
10333        geometry[MagickPathExtent];
10334
10335      /*
10336        Request image size from the user.
10337      */
10338      (void) CopyMagickString(geometry,"512x512",MagickPathExtent);
10339      if (image_info->size != (char *) NULL)
10340        (void) CopyMagickString(geometry,image_info->size,MagickPathExtent);
10341      (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10342        geometry);
10343      (void) CloneString(&image_info->size,geometry);
10344    }
10345  /*
10346    Load the image.
10347  */
10348  XSetCursorState(display,windows,MagickTrue);
10349  XCheckRefreshWindows(display,windows);
10350  (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
10351  nexus=ReadImage(image_info,exception);
10352  CatchException(exception);
10353  XSetCursorState(display,windows,MagickFalse);
10354  if (nexus != (Image *) NULL)
10355    XClientMessage(display,windows->image.id,windows->im_protocols,
10356      windows->im_next_image,CurrentTime);
10357  else
10358    {
10359      char
10360        *text,
10361        **textlist;
10362
10363      /*
10364        Unknown image format.
10365      */
10366      text=FileToString(filename,~0UL,exception);
10367      if (text == (char *) NULL)
10368        return((Image *) NULL);
10369      textlist=StringToList(text);
10370      if (textlist != (char **) NULL)
10371        {
10372          char
10373            title[MagickPathExtent];
10374
10375          register int
10376            i;
10377
10378          (void) FormatLocaleString(title,MagickPathExtent,
10379            "Unknown format: %s",filename);
10380          XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10381            (const char **) textlist);
10382          for (i=0; textlist[i] != (char *) NULL; i++)
10383            textlist[i]=DestroyString(textlist[i]);
10384          textlist=(char **) RelinquishMagickMemory(textlist);
10385        }
10386      text=DestroyString(text);
10387    }
10388  exception=DestroyExceptionInfo(exception);
10389  image_info=DestroyImageInfo(image_info);
10390  return(nexus);
10391}
10392
10393/*
10394%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10395%                                                                             %
10396%                                                                             %
10397%                                                                             %
10398+   X P a n I m a g e                                                         %
10399%                                                                             %
10400%                                                                             %
10401%                                                                             %
10402%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10403%
10404%  XPanImage() pans the image until the mouse button is released.
10405%
10406%  The format of the XPanImage method is:
10407%
10408%      void XPanImage(Display *display,XWindows *windows,XEvent *event,
10409%        ExceptionInfo *exception)
10410%
10411%  A description of each parameter follows:
10412%
10413%    o display: Specifies a connection to an X server;  returned from
10414%      XOpenDisplay.
10415%
10416%    o windows: Specifies a pointer to a XWindows structure.
10417%
10418%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10419%      the entire image is refreshed.
10420%
10421%    o exception: return any errors or warnings in this structure.
10422%
10423*/
10424static void XPanImage(Display *display,XWindows *windows,XEvent *event,
10425  ExceptionInfo *exception)
10426{
10427  char
10428    text[MagickPathExtent];
10429
10430  Cursor
10431    cursor;
10432
10433  double
10434    x_factor,
10435    y_factor;
10436
10437  RectangleInfo
10438    pan_info;
10439
10440  size_t
10441    state;
10442
10443  /*
10444    Define cursor.
10445  */
10446  if ((windows->image.ximage->width > (int) windows->image.width) &&
10447      (windows->image.ximage->height > (int) windows->image.height))
10448    cursor=XCreateFontCursor(display,XC_fleur);
10449  else
10450    if (windows->image.ximage->width > (int) windows->image.width)
10451      cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10452    else
10453      if (windows->image.ximage->height > (int) windows->image.height)
10454        cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10455      else
10456        cursor=XCreateFontCursor(display,XC_arrow);
10457  (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10458  /*
10459    Pan image as pointer moves until the mouse button is released.
10460  */
10461  x_factor=(double) windows->image.ximage->width/windows->pan.width;
10462  y_factor=(double) windows->image.ximage->height/windows->pan.height;
10463  pan_info.width=windows->pan.width*windows->image.width/
10464    windows->image.ximage->width;
10465  pan_info.height=windows->pan.height*windows->image.height/
10466    windows->image.ximage->height;
10467  pan_info.x=0;
10468  pan_info.y=0;
10469  state=UpdateConfigurationState;
10470  do
10471  {
10472    switch (event->type)
10473    {
10474      case ButtonPress:
10475      {
10476        /*
10477          User choose an initial pan location.
10478        */
10479        pan_info.x=(ssize_t) event->xbutton.x;
10480        pan_info.y=(ssize_t) event->xbutton.y;
10481        state|=UpdateConfigurationState;
10482        break;
10483      }
10484      case ButtonRelease:
10485      {
10486        /*
10487          User has finished panning the image.
10488        */
10489        pan_info.x=(ssize_t) event->xbutton.x;
10490        pan_info.y=(ssize_t) event->xbutton.y;
10491        state|=UpdateConfigurationState | ExitState;
10492        break;
10493      }
10494      case MotionNotify:
10495      {
10496        pan_info.x=(ssize_t) event->xmotion.x;
10497        pan_info.y=(ssize_t) event->xmotion.y;
10498        state|=UpdateConfigurationState;
10499      }
10500      default:
10501        break;
10502    }
10503    if ((state & UpdateConfigurationState) != 0)
10504      {
10505        /*
10506          Check boundary conditions.
10507        */
10508        if (pan_info.x < (ssize_t) (pan_info.width/2))
10509          pan_info.x=0;
10510        else
10511          pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10512        if (pan_info.x < 0)
10513          pan_info.x=0;
10514        else
10515          if ((int) (pan_info.x+windows->image.width) >
10516              windows->image.ximage->width)
10517            pan_info.x=(ssize_t)
10518              (windows->image.ximage->width-windows->image.width);
10519        if (pan_info.y < (ssize_t) (pan_info.height/2))
10520          pan_info.y=0;
10521        else
10522          pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10523        if (pan_info.y < 0)
10524          pan_info.y=0;
10525        else
10526          if ((int) (pan_info.y+windows->image.height) >
10527              windows->image.ximage->height)
10528            pan_info.y=(ssize_t)
10529              (windows->image.ximage->height-windows->image.height);
10530        if ((windows->image.x != (int) pan_info.x) ||
10531            (windows->image.y != (int) pan_info.y))
10532          {
10533            /*
10534              Display image pan offset.
10535            */
10536            windows->image.x=(int) pan_info.x;
10537            windows->image.y=(int) pan_info.y;
10538            (void) FormatLocaleString(text,MagickPathExtent," %ux%u%+d%+d ",
10539              windows->image.width,windows->image.height,windows->image.x,
10540              windows->image.y);
10541            XInfoWidget(display,windows,text);
10542            /*
10543              Refresh Image window.
10544            */
10545            XDrawPanRectangle(display,windows);
10546            XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10547          }
10548        state&=(~UpdateConfigurationState);
10549      }
10550    /*
10551      Wait for next event.
10552    */
10553    if ((state & ExitState) == 0)
10554      XScreenEvent(display,windows,event,exception);
10555  } while ((state & ExitState) == 0);
10556  /*
10557    Restore cursor.
10558  */
10559  (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10560  (void) XFreeCursor(display,cursor);
10561  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10562}
10563
10564/*
10565%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10566%                                                                             %
10567%                                                                             %
10568%                                                                             %
10569+   X P a s t e I m a g e                                                     %
10570%                                                                             %
10571%                                                                             %
10572%                                                                             %
10573%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10574%
10575%  XPasteImage() pastes an image previously saved with XCropImage in the X
10576%  window image at a location the user chooses with the pointer.
10577%
10578%  The format of the XPasteImage method is:
10579%
10580%      MagickBooleanType XPasteImage(Display *display,
10581%        XResourceInfo *resource_info,XWindows *windows,Image *image,
10582%        ExceptionInfo *exception)
10583%
10584%  A description of each parameter follows:
10585%
10586%    o display: Specifies a connection to an X server;  returned from
10587%      XOpenDisplay.
10588%
10589%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10590%
10591%    o windows: Specifies a pointer to a XWindows structure.
10592%
10593%    o image: the image; returned from ReadImage.
10594%
10595%    o exception: return any errors or warnings in this structure.
10596%
10597*/
10598static MagickBooleanType XPasteImage(Display *display,
10599  XResourceInfo *resource_info,XWindows *windows,Image *image,
10600  ExceptionInfo *exception)
10601{
10602  static const char
10603    *PasteMenu[] =
10604    {
10605      "Operator",
10606      "Help",
10607      "Dismiss",
10608      (char *) NULL
10609    };
10610
10611  static const ModeType
10612    PasteCommands[] =
10613    {
10614      PasteOperatorsCommand,
10615      PasteHelpCommand,
10616      PasteDismissCommand
10617    };
10618
10619  static CompositeOperator
10620    compose = CopyCompositeOp;
10621
10622  char
10623    text[MagickPathExtent];
10624
10625  Cursor
10626    cursor;
10627
10628  Image
10629    *paste_image;
10630
10631  int
10632    entry,
10633    id,
10634    x,
10635    y;
10636
10637  double
10638    scale_factor;
10639
10640  RectangleInfo
10641    highlight_info,
10642    paste_info;
10643
10644  unsigned int
10645    height,
10646    width;
10647
10648  size_t
10649    state;
10650
10651  XEvent
10652    event;
10653
10654  /*
10655    Copy image.
10656  */
10657  if (resource_info->copy_image == (Image *) NULL)
10658    return(MagickFalse);
10659  paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,exception);
10660  /*
10661    Map Command widget.
10662  */
10663  (void) CloneString(&windows->command.name,"Paste");
10664  windows->command.data=1;
10665  (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10666  (void) XMapRaised(display,windows->command.id);
10667  XClientMessage(display,windows->image.id,windows->im_protocols,
10668    windows->im_update_widget,CurrentTime);
10669  /*
10670    Track pointer until button 1 is pressed.
10671  */
10672  XSetCursorState(display,windows,MagickFalse);
10673  XQueryPosition(display,windows->image.id,&x,&y);
10674  (void) XSelectInput(display,windows->image.id,
10675    windows->image.attributes.event_mask | PointerMotionMask);
10676  paste_info.x=(ssize_t) windows->image.x+x;
10677  paste_info.y=(ssize_t) windows->image.y+y;
10678  paste_info.width=0;
10679  paste_info.height=0;
10680  cursor=XCreateFontCursor(display,XC_ul_angle);
10681  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10682  state=DefaultState;
10683  do
10684  {
10685    if (IfMagickTrue(windows->info.mapped) )
10686      {
10687        /*
10688          Display pointer position.
10689        */
10690        (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
10691          (long) paste_info.x,(long) paste_info.y);
10692        XInfoWidget(display,windows,text);
10693      }
10694    highlight_info=paste_info;
10695    highlight_info.x=paste_info.x-windows->image.x;
10696    highlight_info.y=paste_info.y-windows->image.y;
10697    XHighlightRectangle(display,windows->image.id,
10698      windows->image.highlight_context,&highlight_info);
10699    /*
10700      Wait for next event.
10701    */
10702    XScreenEvent(display,windows,&event,exception);
10703    XHighlightRectangle(display,windows->image.id,
10704      windows->image.highlight_context,&highlight_info);
10705    if (event.xany.window == windows->command.id)
10706      {
10707        /*
10708          Select a command from the Command widget.
10709        */
10710        id=XCommandWidget(display,windows,PasteMenu,&event);
10711        if (id < 0)
10712          continue;
10713        switch (PasteCommands[id])
10714        {
10715          case PasteOperatorsCommand:
10716          {
10717            char
10718              command[MagickPathExtent],
10719              **operators;
10720
10721            /*
10722              Select a command from the pop-up menu.
10723            */
10724            operators=GetCommandOptions(MagickComposeOptions);
10725            if (operators == (char **) NULL)
10726              break;
10727            entry=XMenuWidget(display,windows,PasteMenu[id],
10728              (const char **) operators,command);
10729            if (entry >= 0)
10730              compose=(CompositeOperator) ParseCommandOption(
10731                MagickComposeOptions,MagickFalse,operators[entry]);
10732            operators=DestroyStringList(operators);
10733            break;
10734          }
10735          case PasteHelpCommand:
10736          {
10737            XTextViewWidget(display,resource_info,windows,MagickFalse,
10738              "Help Viewer - Image Composite",ImagePasteHelp);
10739            break;
10740          }
10741          case PasteDismissCommand:
10742          {
10743            /*
10744              Prematurely exit.
10745            */
10746            state|=EscapeState;
10747            state|=ExitState;
10748            break;
10749          }
10750          default:
10751            break;
10752        }
10753        continue;
10754      }
10755    switch (event.type)
10756    {
10757      case ButtonPress:
10758      {
10759        if (IfMagickTrue(image->debug) )
10760          (void) LogMagickEvent(X11Event,GetMagickModule(),
10761            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10762            event.xbutton.button,event.xbutton.x,event.xbutton.y);
10763        if (event.xbutton.button != Button1)
10764          break;
10765        if (event.xbutton.window != windows->image.id)
10766          break;
10767        /*
10768          Paste rectangle is relative to image configuration.
10769        */
10770        width=(unsigned int) image->columns;
10771        height=(unsigned int) image->rows;
10772        x=0;
10773        y=0;
10774        if (windows->image.crop_geometry != (char *) NULL)
10775          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10776            &width,&height);
10777        scale_factor=(double) windows->image.ximage->width/width;
10778        paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10779        scale_factor=(double) windows->image.ximage->height/height;
10780        paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10781        (void) XCheckDefineCursor(display,windows->image.id,cursor);
10782        paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10783        paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10784        break;
10785      }
10786      case ButtonRelease:
10787      {
10788        if (IfMagickTrue(image->debug) )
10789          (void) LogMagickEvent(X11Event,GetMagickModule(),
10790            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10791            event.xbutton.button,event.xbutton.x,event.xbutton.y);
10792        if (event.xbutton.button != Button1)
10793          break;
10794        if (event.xbutton.window != windows->image.id)
10795          break;
10796        if ((paste_info.width != 0) && (paste_info.height != 0))
10797          {
10798            /*
10799              User has selected the location of the paste image.
10800            */
10801            paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10802            paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10803            state|=ExitState;
10804          }
10805        break;
10806      }
10807      case Expose:
10808        break;
10809      case KeyPress:
10810      {
10811        char
10812          command[MagickPathExtent];
10813
10814        KeySym
10815          key_symbol;
10816
10817        int
10818          length;
10819
10820        if (event.xkey.window != windows->image.id)
10821          break;
10822        /*
10823          Respond to a user key press.
10824        */
10825        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10826          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10827        *(command+length)='\0';
10828        if (IfMagickTrue(image->debug) )
10829          (void) LogMagickEvent(X11Event,GetMagickModule(),
10830            "Key press: 0x%lx (%s)",(long) key_symbol,command);
10831        switch ((int) key_symbol)
10832        {
10833          case XK_Escape:
10834          case XK_F20:
10835          {
10836            /*
10837              Prematurely exit.
10838            */
10839            paste_image=DestroyImage(paste_image);
10840            state|=EscapeState;
10841            state|=ExitState;
10842            break;
10843          }
10844          case XK_F1:
10845          case XK_Help:
10846          {
10847            (void) XSetFunction(display,windows->image.highlight_context,
10848              GXcopy);
10849            XTextViewWidget(display,resource_info,windows,MagickFalse,
10850              "Help Viewer - Image Composite",ImagePasteHelp);
10851            (void) XSetFunction(display,windows->image.highlight_context,
10852              GXinvert);
10853            break;
10854          }
10855          default:
10856          {
10857            (void) XBell(display,0);
10858            break;
10859          }
10860        }
10861        break;
10862      }
10863      case MotionNotify:
10864      {
10865        /*
10866          Map and unmap Info widget as text cursor crosses its boundaries.
10867        */
10868        x=event.xmotion.x;
10869        y=event.xmotion.y;
10870        if (IfMagickTrue(windows->info.mapped) )
10871          {
10872            if ((x < (int) (windows->info.x+windows->info.width)) &&
10873                (y < (int) (windows->info.y+windows->info.height)))
10874              (void) XWithdrawWindow(display,windows->info.id,
10875                windows->info.screen);
10876          }
10877        else
10878          if ((x > (int) (windows->info.x+windows->info.width)) ||
10879              (y > (int) (windows->info.y+windows->info.height)))
10880            (void) XMapWindow(display,windows->info.id);
10881        paste_info.x=(ssize_t) windows->image.x+x;
10882        paste_info.y=(ssize_t) windows->image.y+y;
10883        break;
10884      }
10885      default:
10886      {
10887        if (IfMagickTrue(image->debug) )
10888          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10889            event.type);
10890        break;
10891      }
10892    }
10893  } while ((state & ExitState) == 0);
10894  (void) XSelectInput(display,windows->image.id,
10895    windows->image.attributes.event_mask);
10896  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10897  XSetCursorState(display,windows,MagickFalse);
10898  (void) XFreeCursor(display,cursor);
10899  if ((state & EscapeState) != 0)
10900    return(MagickTrue);
10901  /*
10902    Image pasting is relative to image configuration.
10903  */
10904  XSetCursorState(display,windows,MagickTrue);
10905  XCheckRefreshWindows(display,windows);
10906  width=(unsigned int) image->columns;
10907  height=(unsigned int) image->rows;
10908  x=0;
10909  y=0;
10910  if (windows->image.crop_geometry != (char *) NULL)
10911    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10912  scale_factor=(double) width/windows->image.ximage->width;
10913  paste_info.x+=x;
10914  paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10915  paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10916  scale_factor=(double) height/windows->image.ximage->height;
10917  paste_info.y+=y;
10918  paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10919  paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10920  /*
10921    Paste image with X Image window.
10922  */
10923  (void) CompositeImage(image,paste_image,compose,MagickTrue,paste_info.x,
10924    paste_info.y,exception);
10925  paste_image=DestroyImage(paste_image);
10926  XSetCursorState(display,windows,MagickFalse);
10927  /*
10928    Update image colormap.
10929  */
10930  XConfigureImageColormap(display,resource_info,windows,image,exception);
10931  (void) XConfigureImage(display,resource_info,windows,image,exception);
10932  return(MagickTrue);
10933}
10934
10935/*
10936%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10937%                                                                             %
10938%                                                                             %
10939%                                                                             %
10940+   X P r i n t I m a g e                                                     %
10941%                                                                             %
10942%                                                                             %
10943%                                                                             %
10944%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10945%
10946%  XPrintImage() prints an image to a Postscript printer.
10947%
10948%  The format of the XPrintImage method is:
10949%
10950%      MagickBooleanType XPrintImage(Display *display,
10951%        XResourceInfo *resource_info,XWindows *windows,Image *image,
10952%        ExceptionInfo *exception)
10953%
10954%  A description of each parameter follows:
10955%
10956%    o display: Specifies a connection to an X server; returned from
10957%      XOpenDisplay.
10958%
10959%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10960%
10961%    o windows: Specifies a pointer to a XWindows structure.
10962%
10963%    o image: the image.
10964%
10965%    o exception: return any errors or warnings in this structure.
10966%
10967*/
10968static MagickBooleanType XPrintImage(Display *display,
10969  XResourceInfo *resource_info,XWindows *windows,Image *image,
10970  ExceptionInfo *exception)
10971{
10972  char
10973    filename[MagickPathExtent],
10974    geometry[MagickPathExtent];
10975
10976  Image
10977    *print_image;
10978
10979  ImageInfo
10980    *image_info;
10981
10982  MagickStatusType
10983    status;
10984
10985  /*
10986    Request Postscript page geometry from user.
10987  */
10988  image_info=CloneImageInfo(resource_info->image_info);
10989  (void) FormatLocaleString(geometry,MagickPathExtent,"Letter");
10990  if (image_info->page != (char *) NULL)
10991    (void) CopyMagickString(geometry,image_info->page,MagickPathExtent);
10992  XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
10993    "Select Postscript Page Geometry:",geometry);
10994  if (*geometry == '\0')
10995    return(MagickTrue);
10996  image_info->page=GetPageGeometry(geometry);
10997  /*
10998    Apply image transforms.
10999  */
11000  XSetCursorState(display,windows,MagickTrue);
11001  XCheckRefreshWindows(display,windows);
11002  print_image=CloneImage(image,0,0,MagickTrue,exception);
11003  if (print_image == (Image *) NULL)
11004    return(MagickFalse);
11005  (void) FormatLocaleString(geometry,MagickPathExtent,"%dx%d!",
11006    windows->image.ximage->width,windows->image.ximage->height);
11007  (void) TransformImage(&print_image,windows->image.crop_geometry,geometry,
11008    exception);
11009  /*
11010    Print image.
11011  */
11012  (void) AcquireUniqueFilename(filename);
11013  (void) FormatLocaleString(print_image->filename,MagickPathExtent,"print:%s",
11014    filename);
11015  status=WriteImage(image_info,print_image,exception);
11016  (void) RelinquishUniqueFileResource(filename);
11017  print_image=DestroyImage(print_image);
11018  image_info=DestroyImageInfo(image_info);
11019  XSetCursorState(display,windows,MagickFalse);
11020  return(IsMagickTrue(status));
11021}
11022
11023/*
11024%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11025%                                                                             %
11026%                                                                             %
11027%                                                                             %
11028+   X R O I I m a g e                                                         %
11029%                                                                             %
11030%                                                                             %
11031%                                                                             %
11032%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11033%
11034%  XROIImage() applies an image processing technique to a region of interest.
11035%
11036%  The format of the XROIImage method is:
11037%
11038%      MagickBooleanType XROIImage(Display *display,
11039%        XResourceInfo *resource_info,XWindows *windows,Image **image,
11040%        ExceptionInfo *exception)
11041%
11042%  A description of each parameter follows:
11043%
11044%    o display: Specifies a connection to an X server; returned from
11045%      XOpenDisplay.
11046%
11047%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11048%
11049%    o windows: Specifies a pointer to a XWindows structure.
11050%
11051%    o image: the image; returned from ReadImage.
11052%
11053%    o exception: return any errors or warnings in this structure.
11054%
11055*/
11056static MagickBooleanType XROIImage(Display *display,
11057  XResourceInfo *resource_info,XWindows *windows,Image **image,
11058  ExceptionInfo *exception)
11059{
11060#define ApplyMenus  7
11061
11062  static const char
11063    *ROIMenu[] =
11064    {
11065      "Help",
11066      "Dismiss",
11067      (char *) NULL
11068    },
11069    *ApplyMenu[] =
11070    {
11071      "File",
11072      "Edit",
11073      "Transform",
11074      "Enhance",
11075      "Effects",
11076      "F/X",
11077      "Miscellany",
11078      "Help",
11079      "Dismiss",
11080      (char *) NULL
11081    },
11082    *FileMenu[] =
11083    {
11084      "Save...",
11085      "Print...",
11086      (char *) NULL
11087    },
11088    *EditMenu[] =
11089    {
11090      "Undo",
11091      "Redo",
11092      (char *) NULL
11093    },
11094    *TransformMenu[] =
11095    {
11096      "Flop",
11097      "Flip",
11098      "Rotate Right",
11099      "Rotate Left",
11100      (char *) NULL
11101    },
11102    *EnhanceMenu[] =
11103    {
11104      "Hue...",
11105      "Saturation...",
11106      "Brightness...",
11107      "Gamma...",
11108      "Spiff",
11109      "Dull",
11110      "Contrast Stretch...",
11111      "Sigmoidal Contrast...",
11112      "Normalize",
11113      "Equalize",
11114      "Negate",
11115      "Grayscale",
11116      "Map...",
11117      "Quantize...",
11118      (char *) NULL
11119    },
11120    *EffectsMenu[] =
11121    {
11122      "Despeckle",
11123      "Emboss",
11124      "Reduce Noise",
11125      "Add Noise",
11126      "Sharpen...",
11127      "Blur...",
11128      "Threshold...",
11129      "Edge Detect...",
11130      "Spread...",
11131      "Shade...",
11132      "Raise...",
11133      "Segment...",
11134      (char *) NULL
11135    },
11136    *FXMenu[] =
11137    {
11138      "Solarize...",
11139      "Sepia Tone...",
11140      "Swirl...",
11141      "Implode...",
11142      "Vignette...",
11143      "Wave...",
11144      "Oil Paint...",
11145      "Charcoal Draw...",
11146      (char *) NULL
11147    },
11148    *MiscellanyMenu[] =
11149    {
11150      "Image Info",
11151      "Zoom Image",
11152      "Show Preview...",
11153      "Show Histogram",
11154      "Show Matte",
11155      (char *) NULL
11156    };
11157
11158  static const char
11159    **Menus[ApplyMenus] =
11160    {
11161      FileMenu,
11162      EditMenu,
11163      TransformMenu,
11164      EnhanceMenu,
11165      EffectsMenu,
11166      FXMenu,
11167      MiscellanyMenu
11168    };
11169
11170  static const CommandType
11171    ApplyCommands[] =
11172    {
11173      NullCommand,
11174      NullCommand,
11175      NullCommand,
11176      NullCommand,
11177      NullCommand,
11178      NullCommand,
11179      NullCommand,
11180      HelpCommand,
11181      QuitCommand
11182    },
11183    FileCommands[] =
11184    {
11185      SaveCommand,
11186      PrintCommand
11187    },
11188    EditCommands[] =
11189    {
11190      UndoCommand,
11191      RedoCommand
11192    },
11193    TransformCommands[] =
11194    {
11195      FlopCommand,
11196      FlipCommand,
11197      RotateRightCommand,
11198      RotateLeftCommand
11199    },
11200    EnhanceCommands[] =
11201    {
11202      HueCommand,
11203      SaturationCommand,
11204      BrightnessCommand,
11205      GammaCommand,
11206      SpiffCommand,
11207      DullCommand,
11208      ContrastStretchCommand,
11209      SigmoidalContrastCommand,
11210      NormalizeCommand,
11211      EqualizeCommand,
11212      NegateCommand,
11213      GrayscaleCommand,
11214      MapCommand,
11215      QuantizeCommand
11216    },
11217    EffectsCommands[] =
11218    {
11219      DespeckleCommand,
11220      EmbossCommand,
11221      ReduceNoiseCommand,
11222      AddNoiseCommand,
11223      SharpenCommand,
11224      BlurCommand,
11225      EdgeDetectCommand,
11226      SpreadCommand,
11227      ShadeCommand,
11228      RaiseCommand,
11229      SegmentCommand
11230    },
11231    FXCommands[] =
11232    {
11233      SolarizeCommand,
11234      SepiaToneCommand,
11235      SwirlCommand,
11236      ImplodeCommand,
11237      VignetteCommand,
11238      WaveCommand,
11239      OilPaintCommand,
11240      CharcoalDrawCommand
11241    },
11242    MiscellanyCommands[] =
11243    {
11244      InfoCommand,
11245      ZoomCommand,
11246      ShowPreviewCommand,
11247      ShowHistogramCommand,
11248      ShowMatteCommand
11249    },
11250    ROICommands[] =
11251    {
11252      ROIHelpCommand,
11253      ROIDismissCommand
11254    };
11255
11256  static const CommandType
11257    *Commands[ApplyMenus] =
11258    {
11259      FileCommands,
11260      EditCommands,
11261      TransformCommands,
11262      EnhanceCommands,
11263      EffectsCommands,
11264      FXCommands,
11265      MiscellanyCommands
11266    };
11267
11268  char
11269    command[MagickPathExtent],
11270    text[MagickPathExtent];
11271
11272  CommandType
11273    command_type;
11274
11275  Cursor
11276    cursor;
11277
11278  Image
11279    *roi_image;
11280
11281  int
11282    entry,
11283    id,
11284    x,
11285    y;
11286
11287  double
11288    scale_factor;
11289
11290  MagickProgressMonitor
11291    progress_monitor;
11292
11293  RectangleInfo
11294    crop_info,
11295    highlight_info,
11296    roi_info;
11297
11298  unsigned int
11299    height,
11300    width;
11301
11302  size_t
11303    state;
11304
11305  XEvent
11306    event;
11307
11308  /*
11309    Map Command widget.
11310  */
11311  (void) CloneString(&windows->command.name,"ROI");
11312  windows->command.data=0;
11313  (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11314  (void) XMapRaised(display,windows->command.id);
11315  XClientMessage(display,windows->image.id,windows->im_protocols,
11316    windows->im_update_widget,CurrentTime);
11317  /*
11318    Track pointer until button 1 is pressed.
11319  */
11320  XQueryPosition(display,windows->image.id,&x,&y);
11321  (void) XSelectInput(display,windows->image.id,
11322    windows->image.attributes.event_mask | PointerMotionMask);
11323  roi_info.x=(ssize_t) windows->image.x+x;
11324  roi_info.y=(ssize_t) windows->image.y+y;
11325  roi_info.width=0;
11326  roi_info.height=0;
11327  cursor=XCreateFontCursor(display,XC_fleur);
11328  state=DefaultState;
11329  do
11330  {
11331    if (IfMagickTrue(windows->info.mapped) )
11332      {
11333        /*
11334          Display pointer position.
11335        */
11336        (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
11337          (long) roi_info.x,(long) roi_info.y);
11338        XInfoWidget(display,windows,text);
11339      }
11340    /*
11341      Wait for next event.
11342    */
11343    XScreenEvent(display,windows,&event,exception);
11344    if (event.xany.window == windows->command.id)
11345      {
11346        /*
11347          Select a command from the Command widget.
11348        */
11349        id=XCommandWidget(display,windows,ROIMenu,&event);
11350        if (id < 0)
11351          continue;
11352        switch (ROICommands[id])
11353        {
11354          case ROIHelpCommand:
11355          {
11356            XTextViewWidget(display,resource_info,windows,MagickFalse,
11357              "Help Viewer - Region of Interest",ImageROIHelp);
11358            break;
11359          }
11360          case ROIDismissCommand:
11361          {
11362            /*
11363              Prematurely exit.
11364            */
11365            state|=EscapeState;
11366            state|=ExitState;
11367            break;
11368          }
11369          default:
11370            break;
11371        }
11372        continue;
11373      }
11374    switch (event.type)
11375    {
11376      case ButtonPress:
11377      {
11378        if (event.xbutton.button != Button1)
11379          break;
11380        if (event.xbutton.window != windows->image.id)
11381          break;
11382        /*
11383          Note first corner of region of interest rectangle-- exit loop.
11384        */
11385        (void) XCheckDefineCursor(display,windows->image.id,cursor);
11386        roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11387        roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11388        state|=ExitState;
11389        break;
11390      }
11391      case ButtonRelease:
11392        break;
11393      case Expose:
11394        break;
11395      case KeyPress:
11396      {
11397        KeySym
11398          key_symbol;
11399
11400        if (event.xkey.window != windows->image.id)
11401          break;
11402        /*
11403          Respond to a user key press.
11404        */
11405        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11406          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11407        switch ((int) key_symbol)
11408        {
11409          case XK_Escape:
11410          case XK_F20:
11411          {
11412            /*
11413              Prematurely exit.
11414            */
11415            state|=EscapeState;
11416            state|=ExitState;
11417            break;
11418          }
11419          case XK_F1:
11420          case XK_Help:
11421          {
11422            XTextViewWidget(display,resource_info,windows,MagickFalse,
11423              "Help Viewer - Region of Interest",ImageROIHelp);
11424            break;
11425          }
11426          default:
11427          {
11428            (void) XBell(display,0);
11429            break;
11430          }
11431        }
11432        break;
11433      }
11434      case MotionNotify:
11435      {
11436        /*
11437          Map and unmap Info widget as text cursor crosses its boundaries.
11438        */
11439        x=event.xmotion.x;
11440        y=event.xmotion.y;
11441        if (IfMagickTrue(windows->info.mapped) )
11442          {
11443            if ((x < (int) (windows->info.x+windows->info.width)) &&
11444                (y < (int) (windows->info.y+windows->info.height)))
11445              (void) XWithdrawWindow(display,windows->info.id,
11446                windows->info.screen);
11447          }
11448        else
11449          if ((x > (int) (windows->info.x+windows->info.width)) ||
11450              (y > (int) (windows->info.y+windows->info.height)))
11451            (void) XMapWindow(display,windows->info.id);
11452        roi_info.x=(ssize_t) windows->image.x+x;
11453        roi_info.y=(ssize_t) windows->image.y+y;
11454        break;
11455      }
11456      default:
11457        break;
11458    }
11459  } while ((state & ExitState) == 0);
11460  (void) XSelectInput(display,windows->image.id,
11461    windows->image.attributes.event_mask);
11462  if ((state & EscapeState) != 0)
11463    {
11464      /*
11465        User want to exit without region of interest.
11466      */
11467      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11468      (void) XFreeCursor(display,cursor);
11469      return(MagickTrue);
11470    }
11471  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11472  do
11473  {
11474    /*
11475      Size rectangle as pointer moves until the mouse button is released.
11476    */
11477    x=(int) roi_info.x;
11478    y=(int) roi_info.y;
11479    roi_info.width=0;
11480    roi_info.height=0;
11481    state=DefaultState;
11482    do
11483    {
11484      highlight_info=roi_info;
11485      highlight_info.x=roi_info.x-windows->image.x;
11486      highlight_info.y=roi_info.y-windows->image.y;
11487      if ((highlight_info.width > 3) && (highlight_info.height > 3))
11488        {
11489          /*
11490            Display info and draw region of interest rectangle.
11491          */
11492          if (IfMagickFalse(windows->info.mapped) )
11493            (void) XMapWindow(display,windows->info.id);
11494          (void) FormatLocaleString(text,MagickPathExtent,
11495            " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11496            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11497          XInfoWidget(display,windows,text);
11498          XHighlightRectangle(display,windows->image.id,
11499            windows->image.highlight_context,&highlight_info);
11500        }
11501      else
11502        if (IfMagickTrue(windows->info.mapped) )
11503          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11504      /*
11505        Wait for next event.
11506      */
11507      XScreenEvent(display,windows,&event,exception);
11508      if ((highlight_info.width > 3) && (highlight_info.height > 3))
11509        XHighlightRectangle(display,windows->image.id,
11510          windows->image.highlight_context,&highlight_info);
11511      switch (event.type)
11512      {
11513        case ButtonPress:
11514        {
11515          roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11516          roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11517          break;
11518        }
11519        case ButtonRelease:
11520        {
11521          /*
11522            User has committed to region of interest rectangle.
11523          */
11524          roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11525          roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11526          XSetCursorState(display,windows,MagickFalse);
11527          state|=ExitState;
11528          if (LocaleCompare(windows->command.name,"Apply") == 0)
11529            break;
11530          (void) CloneString(&windows->command.name,"Apply");
11531          windows->command.data=ApplyMenus;
11532          (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11533          break;
11534        }
11535        case Expose:
11536          break;
11537        case MotionNotify:
11538        {
11539          roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11540          roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11541        }
11542        default:
11543          break;
11544      }
11545      if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11546          ((state & ExitState) != 0))
11547        {
11548          /*
11549            Check boundary conditions.
11550          */
11551          if (roi_info.x < 0)
11552            roi_info.x=0;
11553          else
11554            if (roi_info.x > (ssize_t) windows->image.ximage->width)
11555              roi_info.x=(ssize_t) windows->image.ximage->width;
11556          if ((int) roi_info.x < x)
11557            roi_info.width=(unsigned int) (x-roi_info.x);
11558          else
11559            {
11560              roi_info.width=(unsigned int) (roi_info.x-x);
11561              roi_info.x=(ssize_t) x;
11562            }
11563          if (roi_info.y < 0)
11564            roi_info.y=0;
11565          else
11566            if (roi_info.y > (ssize_t) windows->image.ximage->height)
11567              roi_info.y=(ssize_t) windows->image.ximage->height;
11568          if ((int) roi_info.y < y)
11569            roi_info.height=(unsigned int) (y-roi_info.y);
11570          else
11571            {
11572              roi_info.height=(unsigned int) (roi_info.y-y);
11573              roi_info.y=(ssize_t) y;
11574            }
11575        }
11576    } while ((state & ExitState) == 0);
11577    /*
11578      Wait for user to grab a corner of the rectangle or press return.
11579    */
11580    state=DefaultState;
11581    command_type=NullCommand;
11582    crop_info.x=0;
11583    crop_info.y=0;
11584    (void) XMapWindow(display,windows->info.id);
11585    do
11586    {
11587      if (IfMagickTrue(windows->info.mapped) )
11588        {
11589          /*
11590            Display pointer position.
11591          */
11592          (void) FormatLocaleString(text,MagickPathExtent,
11593            " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11594            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11595          XInfoWidget(display,windows,text);
11596        }
11597      highlight_info=roi_info;
11598      highlight_info.x=roi_info.x-windows->image.x;
11599      highlight_info.y=roi_info.y-windows->image.y;
11600      if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11601        {
11602          state|=EscapeState;
11603          state|=ExitState;
11604          break;
11605        }
11606      if ((state & UpdateRegionState) != 0)
11607        {
11608          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11609          switch (command_type)
11610          {
11611            case UndoCommand:
11612            case RedoCommand:
11613            {
11614              (void) XMagickCommand(display,resource_info,windows,command_type,
11615                image,exception);
11616              break;
11617            }
11618            default:
11619            {
11620              /*
11621                Region of interest is relative to image configuration.
11622              */
11623              progress_monitor=SetImageProgressMonitor(*image,
11624                (MagickProgressMonitor) NULL,(*image)->client_data);
11625              crop_info=roi_info;
11626              width=(unsigned int) (*image)->columns;
11627              height=(unsigned int) (*image)->rows;
11628              x=0;
11629              y=0;
11630              if (windows->image.crop_geometry != (char *) NULL)
11631                (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11632                  &width,&height);
11633              scale_factor=(double) width/windows->image.ximage->width;
11634              crop_info.x+=x;
11635              crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11636              crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11637              scale_factor=(double)
11638                height/windows->image.ximage->height;
11639              crop_info.y+=y;
11640              crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11641              crop_info.height=(unsigned int)
11642                (scale_factor*crop_info.height+0.5);
11643              roi_image=CropImage(*image,&crop_info,exception);
11644              (void) SetImageProgressMonitor(*image,progress_monitor,
11645                (*image)->client_data);
11646              if (roi_image == (Image *) NULL)
11647                continue;
11648              /*
11649                Apply image processing technique to the region of interest.
11650              */
11651              windows->image.orphan=MagickTrue;
11652              (void) XMagickCommand(display,resource_info,windows,command_type,
11653                &roi_image,exception);
11654              progress_monitor=SetImageProgressMonitor(*image,
11655                (MagickProgressMonitor) NULL,(*image)->client_data);
11656              (void) XMagickCommand(display,resource_info,windows,
11657                SaveToUndoBufferCommand,image,exception);
11658              windows->image.orphan=MagickFalse;
11659              (void) CompositeImage(*image,roi_image,CopyCompositeOp,
11660                MagickTrue,crop_info.x,crop_info.y,exception);
11661              roi_image=DestroyImage(roi_image);
11662              (void) SetImageProgressMonitor(*image,progress_monitor,
11663                (*image)->client_data);
11664              break;
11665            }
11666          }
11667          if (command_type != InfoCommand)
11668            {
11669              XConfigureImageColormap(display,resource_info,windows,*image,
11670                exception);
11671              (void) XConfigureImage(display,resource_info,windows,*image,
11672                exception);
11673            }
11674          XCheckRefreshWindows(display,windows);
11675          XInfoWidget(display,windows,text);
11676          (void) XSetFunction(display,windows->image.highlight_context,
11677            GXinvert);
11678          state&=(~UpdateRegionState);
11679        }
11680      XHighlightRectangle(display,windows->image.id,
11681        windows->image.highlight_context,&highlight_info);
11682      XScreenEvent(display,windows,&event,exception);
11683      if (event.xany.window == windows->command.id)
11684        {
11685          /*
11686            Select a command from the Command widget.
11687          */
11688          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11689          command_type=NullCommand;
11690          id=XCommandWidget(display,windows,ApplyMenu,&event);
11691          if (id >= 0)
11692            {
11693              (void) CopyMagickString(command,ApplyMenu[id],MagickPathExtent);
11694              command_type=ApplyCommands[id];
11695              if (id < ApplyMenus)
11696                {
11697                  /*
11698                    Select a command from a pop-up menu.
11699                  */
11700                  entry=XMenuWidget(display,windows,ApplyMenu[id],
11701                    (const char **) Menus[id],command);
11702                  if (entry >= 0)
11703                    {
11704                      (void) CopyMagickString(command,Menus[id][entry],
11705                        MagickPathExtent);
11706                      command_type=Commands[id][entry];
11707                    }
11708                }
11709            }
11710          (void) XSetFunction(display,windows->image.highlight_context,
11711            GXinvert);
11712          XHighlightRectangle(display,windows->image.id,
11713            windows->image.highlight_context,&highlight_info);
11714          if (command_type == HelpCommand)
11715            {
11716              (void) XSetFunction(display,windows->image.highlight_context,
11717                GXcopy);
11718              XTextViewWidget(display,resource_info,windows,MagickFalse,
11719                "Help Viewer - Region of Interest",ImageROIHelp);
11720              (void) XSetFunction(display,windows->image.highlight_context,
11721                GXinvert);
11722              continue;
11723            }
11724          if (command_type == QuitCommand)
11725            {
11726              /*
11727                exit.
11728              */
11729              state|=EscapeState;
11730              state|=ExitState;
11731              continue;
11732            }
11733          if (command_type != NullCommand)
11734            state|=UpdateRegionState;
11735          continue;
11736        }
11737      XHighlightRectangle(display,windows->image.id,
11738        windows->image.highlight_context,&highlight_info);
11739      switch (event.type)
11740      {
11741        case ButtonPress:
11742        {
11743          x=windows->image.x;
11744          y=windows->image.y;
11745          if (event.xbutton.button != Button1)
11746            break;
11747          if (event.xbutton.window != windows->image.id)
11748            break;
11749          x=windows->image.x+event.xbutton.x;
11750          y=windows->image.y+event.xbutton.y;
11751          if ((x < (int) (roi_info.x+RoiDelta)) &&
11752              (x > (int) (roi_info.x-RoiDelta)) &&
11753              (y < (int) (roi_info.y+RoiDelta)) &&
11754              (y > (int) (roi_info.y-RoiDelta)))
11755            {
11756              roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11757              roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11758              state|=UpdateConfigurationState;
11759              break;
11760            }
11761          if ((x < (int) (roi_info.x+RoiDelta)) &&
11762              (x > (int) (roi_info.x-RoiDelta)) &&
11763              (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11764              (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11765            {
11766              roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11767              state|=UpdateConfigurationState;
11768              break;
11769            }
11770          if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11771              (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11772              (y < (int) (roi_info.y+RoiDelta)) &&
11773              (y > (int) (roi_info.y-RoiDelta)))
11774            {
11775              roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11776              state|=UpdateConfigurationState;
11777              break;
11778            }
11779          if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11780              (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11781              (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11782              (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11783            {
11784              state|=UpdateConfigurationState;
11785              break;
11786            }
11787        }
11788        case ButtonRelease:
11789        {
11790          if (event.xbutton.window == windows->pan.id)
11791            if ((highlight_info.x != crop_info.x-windows->image.x) ||
11792                (highlight_info.y != crop_info.y-windows->image.y))
11793              XHighlightRectangle(display,windows->image.id,
11794                windows->image.highlight_context,&highlight_info);
11795          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11796            event.xbutton.time);
11797          break;
11798        }
11799        case Expose:
11800        {
11801          if (event.xexpose.window == windows->image.id)
11802            if (event.xexpose.count == 0)
11803              {
11804                event.xexpose.x=(int) highlight_info.x;
11805                event.xexpose.y=(int) highlight_info.y;
11806                event.xexpose.width=(int) highlight_info.width;
11807                event.xexpose.height=(int) highlight_info.height;
11808                XRefreshWindow(display,&windows->image,&event);
11809              }
11810          if (event.xexpose.window == windows->info.id)
11811            if (event.xexpose.count == 0)
11812              XInfoWidget(display,windows,text);
11813          break;
11814        }
11815        case KeyPress:
11816        {
11817          KeySym
11818            key_symbol;
11819
11820          if (event.xkey.window != windows->image.id)
11821            break;
11822          /*
11823            Respond to a user key press.
11824          */
11825          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11826            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11827          switch ((int) key_symbol)
11828          {
11829            case XK_Shift_L:
11830            case XK_Shift_R:
11831              break;
11832            case XK_Escape:
11833            case XK_F20:
11834              state|=EscapeState;
11835            case XK_Return:
11836            {
11837              state|=ExitState;
11838              break;
11839            }
11840            case XK_Home:
11841            case XK_KP_Home:
11842            {
11843              roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11844              roi_info.y=(ssize_t) (windows->image.height/2L-
11845                roi_info.height/2L);
11846              break;
11847            }
11848            case XK_Left:
11849            case XK_KP_Left:
11850            {
11851              roi_info.x--;
11852              break;
11853            }
11854            case XK_Up:
11855            case XK_KP_Up:
11856            case XK_Next:
11857            {
11858              roi_info.y--;
11859              break;
11860            }
11861            case XK_Right:
11862            case XK_KP_Right:
11863            {
11864              roi_info.x++;
11865              break;
11866            }
11867            case XK_Prior:
11868            case XK_Down:
11869            case XK_KP_Down:
11870            {
11871              roi_info.y++;
11872              break;
11873            }
11874            case XK_F1:
11875            case XK_Help:
11876            {
11877              (void) XSetFunction(display,windows->image.highlight_context,
11878                GXcopy);
11879              XTextViewWidget(display,resource_info,windows,MagickFalse,
11880                "Help Viewer - Region of Interest",ImageROIHelp);
11881              (void) XSetFunction(display,windows->image.highlight_context,
11882                GXinvert);
11883              break;
11884            }
11885            default:
11886            {
11887              command_type=XImageWindowCommand(display,resource_info,windows,
11888                event.xkey.state,key_symbol,image,exception);
11889              if (command_type != NullCommand)
11890                state|=UpdateRegionState;
11891              break;
11892            }
11893          }
11894          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11895            event.xkey.time);
11896          break;
11897        }
11898        case KeyRelease:
11899          break;
11900        case MotionNotify:
11901        {
11902          if (event.xbutton.window != windows->image.id)
11903            break;
11904          /*
11905            Map and unmap Info widget as text cursor crosses its boundaries.
11906          */
11907          x=event.xmotion.x;
11908          y=event.xmotion.y;
11909          if (IfMagickTrue(windows->info.mapped) )
11910            {
11911              if ((x < (int) (windows->info.x+windows->info.width)) &&
11912                  (y < (int) (windows->info.y+windows->info.height)))
11913                (void) XWithdrawWindow(display,windows->info.id,
11914                  windows->info.screen);
11915            }
11916          else
11917            if ((x > (int) (windows->info.x+windows->info.width)) ||
11918                (y > (int) (windows->info.y+windows->info.height)))
11919              (void) XMapWindow(display,windows->info.id);
11920          roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11921          roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11922          break;
11923        }
11924        case SelectionRequest:
11925        {
11926          XSelectionEvent
11927            notify;
11928
11929          XSelectionRequestEvent
11930            *request;
11931
11932          /*
11933            Set primary selection.
11934          */
11935          (void) FormatLocaleString(text,MagickPathExtent,
11936            "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11937            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11938          request=(&(event.xselectionrequest));
11939          (void) XChangeProperty(request->display,request->requestor,
11940            request->property,request->target,8,PropModeReplace,
11941            (unsigned char *) text,(int) strlen(text));
11942          notify.type=SelectionNotify;
11943          notify.display=request->display;
11944          notify.requestor=request->requestor;
11945          notify.selection=request->selection;
11946          notify.target=request->target;
11947          notify.time=request->time;
11948          if (request->property == None)
11949            notify.property=request->target;
11950          else
11951            notify.property=request->property;
11952          (void) XSendEvent(request->display,request->requestor,False,0,
11953            (XEvent *) &notify);
11954        }
11955        default:
11956          break;
11957      }
11958      if ((state & UpdateConfigurationState) != 0)
11959        {
11960          (void) XPutBackEvent(display,&event);
11961          (void) XCheckDefineCursor(display,windows->image.id,cursor);
11962          break;
11963        }
11964    } while ((state & ExitState) == 0);
11965  } while ((state & ExitState) == 0);
11966  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11967  XSetCursorState(display,windows,MagickFalse);
11968  if ((state & EscapeState) != 0)
11969    return(MagickTrue);
11970  return(MagickTrue);
11971}
11972
11973/*
11974%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11975%                                                                             %
11976%                                                                             %
11977%                                                                             %
11978+   X R o t a t e I m a g e                                                   %
11979%                                                                             %
11980%                                                                             %
11981%                                                                             %
11982%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11983%
11984%  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
11985%  rotation angle is computed from the slope of a line drawn by the user.
11986%
11987%  The format of the XRotateImage method is:
11988%
11989%      MagickBooleanType XRotateImage(Display *display,
11990%        XResourceInfo *resource_info,XWindows *windows,double degrees,
11991%        Image **image,ExceptionInfo *exception)
11992%
11993%  A description of each parameter follows:
11994%
11995%    o display: Specifies a connection to an X server; returned from
11996%      XOpenDisplay.
11997%
11998%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11999%
12000%    o windows: Specifies a pointer to a XWindows structure.
12001%
12002%    o degrees: Specifies the number of degrees to rotate the image.
12003%
12004%    o image: the image.
12005%
12006%    o exception: return any errors or warnings in this structure.
12007%
12008*/
12009static MagickBooleanType XRotateImage(Display *display,
12010  XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
12011  ExceptionInfo *exception)
12012{
12013  static const char
12014    *RotateMenu[] =
12015    {
12016      "Pixel Color",
12017      "Direction",
12018      "Help",
12019      "Dismiss",
12020      (char *) NULL
12021    };
12022
12023  static ModeType
12024    direction = HorizontalRotateCommand;
12025
12026  static const ModeType
12027    DirectionCommands[] =
12028    {
12029      HorizontalRotateCommand,
12030      VerticalRotateCommand
12031    },
12032    RotateCommands[] =
12033    {
12034      RotateColorCommand,
12035      RotateDirectionCommand,
12036      RotateHelpCommand,
12037      RotateDismissCommand
12038    };
12039
12040  static unsigned int
12041    pen_id = 0;
12042
12043  char
12044    command[MagickPathExtent],
12045    text[MagickPathExtent];
12046
12047  Image
12048    *rotate_image;
12049
12050  int
12051    id,
12052    x,
12053    y;
12054
12055  double
12056    normalized_degrees;
12057
12058  register int
12059    i;
12060
12061  unsigned int
12062    height,
12063    rotations,
12064    width;
12065
12066  if (degrees == 0.0)
12067    {
12068      unsigned int
12069        distance;
12070
12071      size_t
12072        state;
12073
12074      XEvent
12075        event;
12076
12077      XSegment
12078        rotate_info;
12079
12080      /*
12081        Map Command widget.
12082      */
12083      (void) CloneString(&windows->command.name,"Rotate");
12084      windows->command.data=2;
12085      (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12086      (void) XMapRaised(display,windows->command.id);
12087      XClientMessage(display,windows->image.id,windows->im_protocols,
12088        windows->im_update_widget,CurrentTime);
12089      /*
12090        Wait for first button press.
12091      */
12092      (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12093      XQueryPosition(display,windows->image.id,&x,&y);
12094      rotate_info.x1=x;
12095      rotate_info.y1=y;
12096      rotate_info.x2=x;
12097      rotate_info.y2=y;
12098      state=DefaultState;
12099      do
12100      {
12101        XHighlightLine(display,windows->image.id,
12102          windows->image.highlight_context,&rotate_info);
12103        /*
12104          Wait for next event.
12105        */
12106        XScreenEvent(display,windows,&event,exception);
12107        XHighlightLine(display,windows->image.id,
12108          windows->image.highlight_context,&rotate_info);
12109        if (event.xany.window == windows->command.id)
12110          {
12111            /*
12112              Select a command from the Command widget.
12113            */
12114            id=XCommandWidget(display,windows,RotateMenu,&event);
12115            if (id < 0)
12116              continue;
12117            (void) XSetFunction(display,windows->image.highlight_context,
12118              GXcopy);
12119            switch (RotateCommands[id])
12120            {
12121              case RotateColorCommand:
12122              {
12123                const char
12124                  *ColorMenu[MaxNumberPens];
12125
12126                int
12127                  pen_number;
12128
12129                XColor
12130                  color;
12131
12132                /*
12133                  Initialize menu selections.
12134                */
12135                for (i=0; i < (int) (MaxNumberPens-2); i++)
12136                  ColorMenu[i]=resource_info->pen_colors[i];
12137                ColorMenu[MaxNumberPens-2]="Browser...";
12138                ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12139                /*
12140                  Select a pen color from the pop-up menu.
12141                */
12142                pen_number=XMenuWidget(display,windows,RotateMenu[id],
12143                  (const char **) ColorMenu,command);
12144                if (pen_number < 0)
12145                  break;
12146                if (pen_number == (MaxNumberPens-2))
12147                  {
12148                    static char
12149                      color_name[MagickPathExtent] = "gray";
12150
12151                    /*
12152                      Select a pen color from a dialog.
12153                    */
12154                    resource_info->pen_colors[pen_number]=color_name;
12155                    XColorBrowserWidget(display,windows,"Select",color_name);
12156                    if (*color_name == '\0')
12157                      break;
12158                  }
12159                /*
12160                  Set pen color.
12161                */
12162                (void) XParseColor(display,windows->map_info->colormap,
12163                  resource_info->pen_colors[pen_number],&color);
12164                XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12165                  (unsigned int) MaxColors,&color);
12166                windows->pixel_info->pen_colors[pen_number]=color;
12167                pen_id=(unsigned int) pen_number;
12168                break;
12169              }
12170              case RotateDirectionCommand:
12171              {
12172                static const char
12173                  *Directions[] =
12174                  {
12175                    "horizontal",
12176                    "vertical",
12177                    (char *) NULL,
12178                  };
12179
12180                /*
12181                  Select a command from the pop-up menu.
12182                */
12183                id=XMenuWidget(display,windows,RotateMenu[id],
12184                  Directions,command);
12185                if (id >= 0)
12186                  direction=DirectionCommands[id];
12187                break;
12188              }
12189              case RotateHelpCommand:
12190              {
12191                XTextViewWidget(display,resource_info,windows,MagickFalse,
12192                  "Help Viewer - Image Rotation",ImageRotateHelp);
12193                break;
12194              }
12195              case RotateDismissCommand:
12196              {
12197                /*
12198                  Prematurely exit.
12199                */
12200                state|=EscapeState;
12201                state|=ExitState;
12202                break;
12203              }
12204              default:
12205                break;
12206            }
12207            (void) XSetFunction(display,windows->image.highlight_context,
12208              GXinvert);
12209            continue;
12210          }
12211        switch (event.type)
12212        {
12213          case ButtonPress:
12214          {
12215            if (event.xbutton.button != Button1)
12216              break;
12217            if (event.xbutton.window != windows->image.id)
12218              break;
12219            /*
12220              exit loop.
12221            */
12222            (void) XSetFunction(display,windows->image.highlight_context,
12223              GXcopy);
12224            rotate_info.x1=event.xbutton.x;
12225            rotate_info.y1=event.xbutton.y;
12226            state|=ExitState;
12227            break;
12228          }
12229          case ButtonRelease:
12230            break;
12231          case Expose:
12232            break;
12233          case KeyPress:
12234          {
12235            char
12236              command[MagickPathExtent];
12237
12238            KeySym
12239              key_symbol;
12240
12241            if (event.xkey.window != windows->image.id)
12242              break;
12243            /*
12244              Respond to a user key press.
12245            */
12246            (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12247              sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12248            switch ((int) key_symbol)
12249            {
12250              case XK_Escape:
12251              case XK_F20:
12252              {
12253                /*
12254                  Prematurely exit.
12255                */
12256                state|=EscapeState;
12257                state|=ExitState;
12258                break;
12259              }
12260              case XK_F1:
12261              case XK_Help:
12262              {
12263                (void) XSetFunction(display,windows->image.highlight_context,
12264                  GXcopy);
12265                XTextViewWidget(display,resource_info,windows,MagickFalse,
12266                  "Help Viewer - Image Rotation",ImageRotateHelp);
12267                (void) XSetFunction(display,windows->image.highlight_context,
12268                  GXinvert);
12269                break;
12270              }
12271              default:
12272              {
12273                (void) XBell(display,0);
12274                break;
12275              }
12276            }
12277            break;
12278          }
12279          case MotionNotify:
12280          {
12281            rotate_info.x1=event.xmotion.x;
12282            rotate_info.y1=event.xmotion.y;
12283          }
12284        }
12285        rotate_info.x2=rotate_info.x1;
12286        rotate_info.y2=rotate_info.y1;
12287        if (direction == HorizontalRotateCommand)
12288          rotate_info.x2+=32;
12289        else
12290          rotate_info.y2-=32;
12291      } while ((state & ExitState) == 0);
12292      (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12293      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12294      if ((state & EscapeState) != 0)
12295        return(MagickTrue);
12296      /*
12297        Draw line as pointer moves until the mouse button is released.
12298      */
12299      distance=0;
12300      (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12301      state=DefaultState;
12302      do
12303      {
12304        if (distance > 9)
12305          {
12306            /*
12307              Display info and draw rotation line.
12308            */
12309            if (IfMagickFalse(windows->info.mapped) )
12310              (void) XMapWindow(display,windows->info.id);
12311            (void) FormatLocaleString(text,MagickPathExtent," %g",
12312              direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12313            XInfoWidget(display,windows,text);
12314            XHighlightLine(display,windows->image.id,
12315              windows->image.highlight_context,&rotate_info);
12316          }
12317        else
12318          if (IfMagickTrue(windows->info.mapped) )
12319            (void) XWithdrawWindow(display,windows->info.id,
12320              windows->info.screen);
12321        /*
12322          Wait for next event.
12323        */
12324        XScreenEvent(display,windows,&event,exception);
12325        if (distance > 9)
12326          XHighlightLine(display,windows->image.id,
12327            windows->image.highlight_context,&rotate_info);
12328        switch (event.type)
12329        {
12330          case ButtonPress:
12331            break;
12332          case ButtonRelease:
12333          {
12334            /*
12335              User has committed to rotation line.
12336            */
12337            rotate_info.x2=event.xbutton.x;
12338            rotate_info.y2=event.xbutton.y;
12339            state|=ExitState;
12340            break;
12341          }
12342          case Expose:
12343            break;
12344          case MotionNotify:
12345          {
12346            rotate_info.x2=event.xmotion.x;
12347            rotate_info.y2=event.xmotion.y;
12348          }
12349          default:
12350            break;
12351        }
12352        /*
12353          Check boundary conditions.
12354        */
12355        if (rotate_info.x2 < 0)
12356          rotate_info.x2=0;
12357        else
12358          if (rotate_info.x2 > (int) windows->image.width)
12359            rotate_info.x2=(short) windows->image.width;
12360        if (rotate_info.y2 < 0)
12361          rotate_info.y2=0;
12362        else
12363          if (rotate_info.y2 > (int) windows->image.height)
12364            rotate_info.y2=(short) windows->image.height;
12365        /*
12366          Compute rotation angle from the slope of the line.
12367        */
12368        degrees=0.0;
12369        distance=(unsigned int)
12370          ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12371          ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12372        if (distance > 9)
12373          degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12374            rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12375      } while ((state & ExitState) == 0);
12376      (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12377      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12378      if (distance <= 9)
12379        return(MagickTrue);
12380    }
12381  if (direction == VerticalRotateCommand)
12382    degrees-=90.0;
12383  if (degrees == 0.0)
12384    return(MagickTrue);
12385  /*
12386    Rotate image.
12387  */
12388  normalized_degrees=degrees;
12389  while (normalized_degrees < -45.0)
12390    normalized_degrees+=360.0;
12391  for (rotations=0; normalized_degrees > 45.0; rotations++)
12392    normalized_degrees-=90.0;
12393  if (normalized_degrees != 0.0)
12394    (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12395      exception);
12396  XSetCursorState(display,windows,MagickTrue);
12397  XCheckRefreshWindows(display,windows);
12398  (*image)->background_color.red=(double) ScaleShortToQuantum(
12399    windows->pixel_info->pen_colors[pen_id].red);
12400  (*image)->background_color.green=(double) ScaleShortToQuantum(
12401    windows->pixel_info->pen_colors[pen_id].green);
12402  (*image)->background_color.blue=(double) ScaleShortToQuantum(
12403    windows->pixel_info->pen_colors[pen_id].blue);
12404  rotate_image=RotateImage(*image,degrees,exception);
12405  XSetCursorState(display,windows,MagickFalse);
12406  if (rotate_image == (Image *) NULL)
12407    return(MagickFalse);
12408  *image=DestroyImage(*image);
12409  *image=rotate_image;
12410  if (windows->image.crop_geometry != (char *) NULL)
12411    {
12412      /*
12413        Rotate crop geometry.
12414      */
12415      width=(unsigned int) (*image)->columns;
12416      height=(unsigned int) (*image)->rows;
12417      (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12418      switch (rotations % 4)
12419      {
12420        default:
12421        case 0:
12422          break;
12423        case 1:
12424        {
12425          /*
12426            Rotate 90 degrees.
12427          */
12428          (void) FormatLocaleString(windows->image.crop_geometry,MagickPathExtent,
12429            "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12430            (int) height-y,x);
12431          break;
12432        }
12433        case 2:
12434        {
12435          /*
12436            Rotate 180 degrees.
12437          */
12438          (void) FormatLocaleString(windows->image.crop_geometry,MagickPathExtent,
12439            "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12440          break;
12441        }
12442        case 3:
12443        {
12444          /*
12445            Rotate 270 degrees.
12446          */
12447          (void) FormatLocaleString(windows->image.crop_geometry,MagickPathExtent,
12448            "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12449          break;
12450        }
12451      }
12452    }
12453  if (IfMagickTrue(windows->image.orphan) )
12454    return(MagickTrue);
12455  if (normalized_degrees != 0.0)
12456    {
12457      /*
12458        Update image colormap.
12459      */
12460      windows->image.window_changes.width=(int) (*image)->columns;
12461      windows->image.window_changes.height=(int) (*image)->rows;
12462      if (windows->image.crop_geometry != (char *) NULL)
12463        {
12464          /*
12465            Obtain dimensions of image from crop geometry.
12466          */
12467          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12468            &width,&height);
12469          windows->image.window_changes.width=(int) width;
12470          windows->image.window_changes.height=(int) height;
12471        }
12472      XConfigureImageColormap(display,resource_info,windows,*image,exception);
12473    }
12474  else
12475    if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12476      {
12477        windows->image.window_changes.width=windows->image.ximage->height;
12478        windows->image.window_changes.height=windows->image.ximage->width;
12479      }
12480  /*
12481    Update image configuration.
12482  */
12483  (void) XConfigureImage(display,resource_info,windows,*image,exception);
12484  return(MagickTrue);
12485}
12486
12487/*
12488%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12489%                                                                             %
12490%                                                                             %
12491%                                                                             %
12492+   X S a v e I m a g e                                                       %
12493%                                                                             %
12494%                                                                             %
12495%                                                                             %
12496%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12497%
12498%  XSaveImage() saves an image to a file.
12499%
12500%  The format of the XSaveImage method is:
12501%
12502%      MagickBooleanType XSaveImage(Display *display,
12503%        XResourceInfo *resource_info,XWindows *windows,Image *image,
12504%        ExceptionInfo *exception)
12505%
12506%  A description of each parameter follows:
12507%
12508%    o display: Specifies a connection to an X server; returned from
12509%      XOpenDisplay.
12510%
12511%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12512%
12513%    o windows: Specifies a pointer to a XWindows structure.
12514%
12515%    o image: the image.
12516%
12517%    o exception: return any errors or warnings in this structure.
12518%
12519*/
12520static MagickBooleanType XSaveImage(Display *display,
12521  XResourceInfo *resource_info,XWindows *windows,Image *image,
12522  ExceptionInfo *exception)
12523{
12524  char
12525    filename[MagickPathExtent],
12526    geometry[MagickPathExtent];
12527
12528  Image
12529    *save_image;
12530
12531  ImageInfo
12532    *image_info;
12533
12534  MagickStatusType
12535    status;
12536
12537  /*
12538    Request file name from user.
12539  */
12540  if (resource_info->write_filename != (char *) NULL)
12541    (void) CopyMagickString(filename,resource_info->write_filename,
12542      MagickPathExtent);
12543  else
12544    {
12545      char
12546        path[MagickPathExtent];
12547
12548      int
12549        status;
12550
12551      GetPathComponent(image->filename,HeadPath,path);
12552      GetPathComponent(image->filename,TailPath,filename);
12553      if (*path != '\0')
12554        {
12555          status=chdir(path);
12556          if (status == -1)
12557            (void) ThrowMagickException(exception,GetMagickModule(),
12558              FileOpenError,"UnableToOpenFile","%s",path);
12559        }
12560    }
12561  XFileBrowserWidget(display,windows,"Save",filename);
12562  if (*filename == '\0')
12563    return(MagickTrue);
12564  if (IfMagickTrue(IsPathAccessible(filename)) )
12565    {
12566      int
12567        status;
12568
12569      /*
12570        File exists-- seek user's permission before overwriting.
12571      */
12572      status=XConfirmWidget(display,windows,"Overwrite",filename);
12573      if (status <= 0)
12574        return(MagickTrue);
12575    }
12576  image_info=CloneImageInfo(resource_info->image_info);
12577  (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
12578  (void) SetImageInfo(image_info,1,exception);
12579  if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12580      (LocaleCompare(image_info->magick,"JPG") == 0))
12581    {
12582      char
12583        quality[MagickPathExtent];
12584
12585      int
12586        status;
12587
12588      /*
12589        Request JPEG quality from user.
12590      */
12591      (void) FormatLocaleString(quality,MagickPathExtent,"%.20g",(double)
12592        image->quality);
12593      status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12594        quality);
12595      if (*quality == '\0')
12596        return(MagickTrue);
12597      image->quality=StringToUnsignedLong(quality);
12598      image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12599    }
12600  if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12601      (LocaleCompare(image_info->magick,"PDF") == 0) ||
12602      (LocaleCompare(image_info->magick,"PS") == 0) ||
12603      (LocaleCompare(image_info->magick,"PS2") == 0))
12604    {
12605      char
12606        geometry[MagickPathExtent];
12607
12608      /*
12609        Request page geometry from user.
12610      */
12611      (void) CopyMagickString(geometry,PSPageGeometry,MagickPathExtent);
12612      if (LocaleCompare(image_info->magick,"PDF") == 0)
12613        (void) CopyMagickString(geometry,PSPageGeometry,MagickPathExtent);
12614      if (image_info->page != (char *) NULL)
12615        (void) CopyMagickString(geometry,image_info->page,MagickPathExtent);
12616      XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12617        "Select page geometry:",geometry);
12618      if (*geometry != '\0')
12619        image_info->page=GetPageGeometry(geometry);
12620    }
12621  /*
12622    Apply image transforms.
12623  */
12624  XSetCursorState(display,windows,MagickTrue);
12625  XCheckRefreshWindows(display,windows);
12626  save_image=CloneImage(image,0,0,MagickTrue,exception);
12627  if (save_image == (Image *) NULL)
12628    return(MagickFalse);
12629  (void) FormatLocaleString(geometry,MagickPathExtent,"%dx%d!",
12630    windows->image.ximage->width,windows->image.ximage->height);
12631  (void) TransformImage(&save_image,windows->image.crop_geometry,geometry,
12632    exception);
12633  /*
12634    Write image.
12635  */
12636  (void) CopyMagickString(save_image->filename,filename,MagickPathExtent);
12637  status=WriteImage(image_info,save_image,exception);
12638  if (IfMagickTrue(status) )
12639    image->taint=MagickFalse;
12640  save_image=DestroyImage(save_image);
12641  image_info=DestroyImageInfo(image_info);
12642  XSetCursorState(display,windows,MagickFalse);
12643  return(IsMagickTrue(status));
12644}
12645
12646/*
12647%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12648%                                                                             %
12649%                                                                             %
12650%                                                                             %
12651+   X S c r e e n E v e n t                                                   %
12652%                                                                             %
12653%                                                                             %
12654%                                                                             %
12655%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12656%
12657%  XScreenEvent() handles global events associated with the Pan and Magnify
12658%  windows.
12659%
12660%  The format of the XScreenEvent function is:
12661%
12662%      void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12663%        ExceptionInfo *exception)
12664%
12665%  A description of each parameter follows:
12666%
12667%    o display: Specifies a pointer to the Display structure;  returned from
12668%      XOpenDisplay.
12669%
12670%    o windows: Specifies a pointer to a XWindows structure.
12671%
12672%    o event: Specifies a pointer to a X11 XEvent structure.
12673%
12674%    o exception: return any errors or warnings in this structure.
12675%
12676*/
12677
12678#if defined(__cplusplus) || defined(c_plusplus)
12679extern "C" {
12680#endif
12681
12682static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12683{
12684  register XWindows
12685    *windows;
12686
12687  windows=(XWindows *) data;
12688  if ((event->type == ClientMessage) &&
12689      (event->xclient.window == windows->image.id))
12690    return(MagickFalse);
12691  return(MagickTrue);
12692}
12693
12694#if defined(__cplusplus) || defined(c_plusplus)
12695}
12696#endif
12697
12698static void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12699  ExceptionInfo *exception)
12700{
12701  register int
12702    x,
12703    y;
12704
12705  (void) XIfEvent(display,event,XPredicate,(char *) windows);
12706  if (event->xany.window == windows->command.id)
12707    return;
12708  switch (event->type)
12709  {
12710    case ButtonPress:
12711    case ButtonRelease:
12712    {
12713      if ((event->xbutton.button == Button3) &&
12714          (event->xbutton.state & Mod1Mask))
12715        {
12716          /*
12717            Convert Alt-Button3 to Button2.
12718          */
12719          event->xbutton.button=Button2;
12720          event->xbutton.state&=(~Mod1Mask);
12721        }
12722      if (event->xbutton.window == windows->backdrop.id)
12723        {
12724          (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12725            event->xbutton.time);
12726          break;
12727        }
12728      if (event->xbutton.window == windows->pan.id)
12729        {
12730          XPanImage(display,windows,event,exception);
12731          break;
12732        }
12733      if (event->xbutton.window == windows->image.id)
12734        if (event->xbutton.button == Button2)
12735          {
12736            /*
12737              Update magnified image.
12738            */
12739            x=event->xbutton.x;
12740            y=event->xbutton.y;
12741            if (x < 0)
12742              x=0;
12743            else
12744              if (x >= (int) windows->image.width)
12745                x=(int) (windows->image.width-1);
12746            windows->magnify.x=(int) windows->image.x+x;
12747            if (y < 0)
12748              y=0;
12749            else
12750             if (y >= (int) windows->image.height)
12751               y=(int) (windows->image.height-1);
12752            windows->magnify.y=windows->image.y+y;
12753            if (IfMagickFalse(windows->magnify.mapped) )
12754              (void) XMapRaised(display,windows->magnify.id);
12755            XMakeMagnifyImage(display,windows,exception);
12756            if (event->type == ButtonRelease)
12757              (void) XWithdrawWindow(display,windows->info.id,
12758                windows->info.screen);
12759            break;
12760          }
12761      break;
12762    }
12763    case ClientMessage:
12764    {
12765      /*
12766        If client window delete message, exit.
12767      */
12768      if (event->xclient.message_type != windows->wm_protocols)
12769        break;
12770      if (*event->xclient.data.l != (long) windows->wm_delete_window)
12771        break;
12772      if (event->xclient.window == windows->magnify.id)
12773        {
12774          (void) XWithdrawWindow(display,windows->magnify.id,
12775            windows->magnify.screen);
12776          break;
12777        }
12778      break;
12779    }
12780    case ConfigureNotify:
12781    {
12782      if (event->xconfigure.window == windows->magnify.id)
12783        {
12784          unsigned int
12785            magnify;
12786
12787          /*
12788            Magnify window has a new configuration.
12789          */
12790          windows->magnify.width=(unsigned int) event->xconfigure.width;
12791          windows->magnify.height=(unsigned int) event->xconfigure.height;
12792          if (IfMagickFalse(windows->magnify.mapped) )
12793            break;
12794          magnify=1;
12795          while ((int) magnify <= event->xconfigure.width)
12796            magnify<<=1;
12797          while ((int) magnify <= event->xconfigure.height)
12798            magnify<<=1;
12799          magnify>>=1;
12800          if (((int) magnify != event->xconfigure.width) ||
12801              ((int) magnify != event->xconfigure.height))
12802            {
12803              XWindowChanges
12804                window_changes;
12805
12806              window_changes.width=(int) magnify;
12807              window_changes.height=(int) magnify;
12808              (void) XReconfigureWMWindow(display,windows->magnify.id,
12809                windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12810                &window_changes);
12811              break;
12812            }
12813          XMakeMagnifyImage(display,windows,exception);
12814          break;
12815        }
12816      break;
12817    }
12818    case Expose:
12819    {
12820      if (event->xexpose.window == windows->image.id)
12821        {
12822          XRefreshWindow(display,&windows->image,event);
12823          break;
12824        }
12825      if (event->xexpose.window == windows->pan.id)
12826        if (event->xexpose.count == 0)
12827          {
12828            XDrawPanRectangle(display,windows);
12829            break;
12830          }
12831      if (event->xexpose.window == windows->magnify.id)
12832        if (event->xexpose.count == 0)
12833          {
12834            XMakeMagnifyImage(display,windows,exception);
12835            break;
12836          }
12837      break;
12838    }
12839    case KeyPress:
12840    {
12841      char
12842        command[MagickPathExtent];
12843
12844      KeySym
12845        key_symbol;
12846
12847      if (event->xkey.window != windows->magnify.id)
12848        break;
12849      /*
12850        Respond to a user key press.
12851      */
12852      (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12853        sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12854      XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol,
12855        exception);
12856      break;
12857    }
12858    case MapNotify:
12859    {
12860      if (event->xmap.window == windows->magnify.id)
12861        {
12862          windows->magnify.mapped=MagickTrue;
12863          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12864          break;
12865        }
12866      if (event->xmap.window == windows->info.id)
12867        {
12868          windows->info.mapped=MagickTrue;
12869          break;
12870        }
12871      break;
12872    }
12873    case MotionNotify:
12874    {
12875      while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12876      if (event->xmotion.window == windows->image.id)
12877        if (IfMagickTrue(windows->magnify.mapped) )
12878          {
12879            /*
12880              Update magnified image.
12881            */
12882            x=event->xmotion.x;
12883            y=event->xmotion.y;
12884            if (x < 0)
12885              x=0;
12886            else
12887              if (x >= (int) windows->image.width)
12888                x=(int) (windows->image.width-1);
12889            windows->magnify.x=(int) windows->image.x+x;
12890            if (y < 0)
12891              y=0;
12892            else
12893             if (y >= (int) windows->image.height)
12894               y=(int) (windows->image.height-1);
12895            windows->magnify.y=windows->image.y+y;
12896            XMakeMagnifyImage(display,windows,exception);
12897          }
12898      break;
12899    }
12900    case UnmapNotify:
12901    {
12902      if (event->xunmap.window == windows->magnify.id)
12903        {
12904          windows->magnify.mapped=MagickFalse;
12905          break;
12906        }
12907      if (event->xunmap.window == windows->info.id)
12908        {
12909          windows->info.mapped=MagickFalse;
12910          break;
12911        }
12912      break;
12913    }
12914    default:
12915      break;
12916  }
12917}
12918
12919/*
12920%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12921%                                                                             %
12922%                                                                             %
12923%                                                                             %
12924+   X S e t C r o p G e o m e t r y                                           %
12925%                                                                             %
12926%                                                                             %
12927%                                                                             %
12928%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12929%
12930%  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12931%  and translates it to a cropping geometry relative to the image.
12932%
12933%  The format of the XSetCropGeometry method is:
12934%
12935%      void XSetCropGeometry(Display *display,XWindows *windows,
12936%        RectangleInfo *crop_info,Image *image)
12937%
12938%  A description of each parameter follows:
12939%
12940%    o display: Specifies a connection to an X server; returned from
12941%      XOpenDisplay.
12942%
12943%    o windows: Specifies a pointer to a XWindows structure.
12944%
12945%    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12946%      Image window to crop.
12947%
12948%    o image: the image.
12949%
12950*/
12951static void XSetCropGeometry(Display *display,XWindows *windows,
12952  RectangleInfo *crop_info,Image *image)
12953{
12954  char
12955    text[MagickPathExtent];
12956
12957  int
12958    x,
12959    y;
12960
12961  double
12962    scale_factor;
12963
12964  unsigned int
12965    height,
12966    width;
12967
12968  if (IfMagickTrue(windows->info.mapped) )
12969    {
12970      /*
12971        Display info on cropping rectangle.
12972      */
12973      (void) FormatLocaleString(text,MagickPathExtent," %.20gx%.20g%+.20g%+.20g",
12974        (double) crop_info->width,(double) crop_info->height,(double)
12975        crop_info->x,(double) crop_info->y);
12976      XInfoWidget(display,windows,text);
12977    }
12978  /*
12979    Cropping geometry is relative to any previous crop geometry.
12980  */
12981  x=0;
12982  y=0;
12983  width=(unsigned int) image->columns;
12984  height=(unsigned int) image->rows;
12985  if (windows->image.crop_geometry != (char *) NULL)
12986    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12987  else
12988    windows->image.crop_geometry=AcquireString((char *) NULL);
12989  /*
12990    Define the crop geometry string from the cropping rectangle.
12991  */
12992  scale_factor=(double) width/windows->image.ximage->width;
12993  if (crop_info->x > 0)
12994    x+=(int) (scale_factor*crop_info->x+0.5);
12995  width=(unsigned int) (scale_factor*crop_info->width+0.5);
12996  if (width == 0)
12997    width=1;
12998  scale_factor=(double) height/windows->image.ximage->height;
12999  if (crop_info->y > 0)
13000    y+=(int) (scale_factor*crop_info->y+0.5);
13001  height=(unsigned int) (scale_factor*crop_info->height+0.5);
13002  if (height == 0)
13003    height=1;
13004  (void) FormatLocaleString(windows->image.crop_geometry,MagickPathExtent,
13005    "%ux%u%+d%+d",width,height,x,y);
13006}
13007
13008/*
13009%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13010%                                                                             %
13011%                                                                             %
13012%                                                                             %
13013+   X T i l e I m a g e                                                       %
13014%                                                                             %
13015%                                                                             %
13016%                                                                             %
13017%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13018%
13019%  XTileImage() loads or deletes a selected tile from a visual image directory.
13020%  The load or delete command is chosen from a menu.
13021%
13022%  The format of the XTileImage method is:
13023%
13024%      Image *XTileImage(Display *display,XResourceInfo *resource_info,
13025%        XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13026%
13027%  A description of each parameter follows:
13028%
13029%    o tile_image:  XTileImage reads or deletes the tile image
13030%      and returns it.  A null image is returned if an error occurs.
13031%
13032%    o display: Specifies a connection to an X server;  returned from
13033%      XOpenDisplay.
13034%
13035%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13036%
13037%    o windows: Specifies a pointer to a XWindows structure.
13038%
13039%    o image: the image; returned from ReadImage.
13040%
13041%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
13042%      the entire image is refreshed.
13043%
13044%    o exception: return any errors or warnings in this structure.
13045%
13046*/
13047static Image *XTileImage(Display *display,XResourceInfo *resource_info,
13048  XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13049{
13050  static const char
13051    *VerbMenu[] =
13052    {
13053      "Load",
13054      "Next",
13055      "Former",
13056      "Delete",
13057      "Update",
13058      (char *) NULL,
13059    };
13060
13061  static const ModeType
13062    TileCommands[] =
13063    {
13064      TileLoadCommand,
13065      TileNextCommand,
13066      TileFormerCommand,
13067      TileDeleteCommand,
13068      TileUpdateCommand
13069    };
13070
13071  char
13072    command[MagickPathExtent],
13073    filename[MagickPathExtent];
13074
13075  Image
13076    *tile_image;
13077
13078  int
13079    id,
13080    status,
13081    tile,
13082    x,
13083    y;
13084
13085  double
13086    scale_factor;
13087
13088  register char
13089    *p,
13090    *q;
13091
13092  register int
13093    i;
13094
13095  unsigned int
13096    height,
13097    width;
13098
13099  /*
13100    Tile image is relative to montage image configuration.
13101  */
13102  x=0;
13103  y=0;
13104  width=(unsigned int) image->columns;
13105  height=(unsigned int) image->rows;
13106  if (windows->image.crop_geometry != (char *) NULL)
13107    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13108  scale_factor=(double) width/windows->image.ximage->width;
13109  event->xbutton.x+=windows->image.x;
13110  event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13111  scale_factor=(double) height/windows->image.ximage->height;
13112  event->xbutton.y+=windows->image.y;
13113  event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13114  /*
13115    Determine size and location of each tile in the visual image directory.
13116  */
13117  width=(unsigned int) image->columns;
13118  height=(unsigned int) image->rows;
13119  x=0;
13120  y=0;
13121  (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13122  tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13123    (event->xbutton.x-x)/width;
13124  if (tile < 0)
13125    {
13126      /*
13127        Button press is outside any tile.
13128      */
13129      (void) XBell(display,0);
13130      return((Image *) NULL);
13131    }
13132  /*
13133    Determine file name from the tile directory.
13134  */
13135  p=image->directory;
13136  for (i=tile; (i != 0) && (*p != '\0'); )
13137  {
13138    if (*p == '\n')
13139      i--;
13140    p++;
13141  }
13142  if (*p == '\0')
13143    {
13144      /*
13145        Button press is outside any tile.
13146      */
13147      (void) XBell(display,0);
13148      return((Image *) NULL);
13149    }
13150  /*
13151    Select a command from the pop-up menu.
13152  */
13153  id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13154  if (id < 0)
13155    return((Image *) NULL);
13156  q=p;
13157  while ((*q != '\n') && (*q != '\0'))
13158    q++;
13159  (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13160  /*
13161    Perform command for the selected tile.
13162  */
13163  XSetCursorState(display,windows,MagickTrue);
13164  XCheckRefreshWindows(display,windows);
13165  tile_image=NewImageList();
13166  switch (TileCommands[id])
13167  {
13168    case TileLoadCommand:
13169    {
13170      /*
13171        Load tile image.
13172      */
13173      XCheckRefreshWindows(display,windows);
13174      (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13175        MagickPathExtent);
13176      (void) CopyMagickString(resource_info->image_info->filename,filename,
13177        MagickPathExtent);
13178      tile_image=ReadImage(resource_info->image_info,exception);
13179      CatchException(exception);
13180      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13181      break;
13182    }
13183    case TileNextCommand:
13184    {
13185      /*
13186        Display next image.
13187      */
13188      XClientMessage(display,windows->image.id,windows->im_protocols,
13189        windows->im_next_image,CurrentTime);
13190      break;
13191    }
13192    case TileFormerCommand:
13193    {
13194      /*
13195        Display former image.
13196      */
13197      XClientMessage(display,windows->image.id,windows->im_protocols,
13198        windows->im_former_image,CurrentTime);
13199      break;
13200    }
13201    case TileDeleteCommand:
13202    {
13203      /*
13204        Delete tile image.
13205      */
13206      if (IfMagickFalse(IsPathAccessible(filename)) )
13207        {
13208          XNoticeWidget(display,windows,"Image file does not exist:",filename);
13209          break;
13210        }
13211      status=XConfirmWidget(display,windows,"Really delete tile",filename);
13212      if (status <= 0)
13213        break;
13214      status=ShredFile(filename);
13215      if (IfMagickTrue(status) )
13216        {
13217          XNoticeWidget(display,windows,"Unable to delete image file:",
13218            filename);
13219          break;
13220        }
13221    }
13222    case TileUpdateCommand:
13223    {
13224      int
13225        x_offset,
13226        y_offset;
13227
13228      PixelInfo
13229        pixel;
13230
13231      register int
13232        j;
13233
13234      register Quantum
13235        *s;
13236
13237      /*
13238        Ensure all the images exist.
13239      */
13240      tile=0;
13241      GetPixelInfo(image,&pixel);
13242      for (p=image->directory; *p != '\0'; p++)
13243      {
13244        CacheView
13245          *image_view;
13246
13247        q=p;
13248        while ((*q != '\n') && (*q != '\0'))
13249          q++;
13250        (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13251        p=q;
13252        if (IfMagickTrue(IsPathAccessible(filename)) )
13253          {
13254            tile++;
13255            continue;
13256          }
13257        /*
13258          Overwrite tile with background color.
13259        */
13260        x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13261        y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13262        image_view=AcquireAuthenticCacheView(image,exception);
13263        (void) GetOneCacheViewVirtualPixelInfo(image_view,0,0,&pixel,exception);
13264        for (i=0; i < (int) height; i++)
13265        {
13266          s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13267            y_offset+i,width,1,exception);
13268          if (s == (Quantum *) NULL)
13269            break;
13270          for (j=0; j < (int) width; j++)
13271          {
13272            SetPixelViaPixelInfo(image,&pixel,s);
13273            s+=GetPixelChannels(image);
13274          }
13275          if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
13276            break;
13277        }
13278        image_view=DestroyCacheView(image_view);
13279        tile++;
13280      }
13281      windows->image.window_changes.width=(int) image->columns;
13282      windows->image.window_changes.height=(int) image->rows;
13283      XConfigureImageColormap(display,resource_info,windows,image,exception);
13284      (void) XConfigureImage(display,resource_info,windows,image,exception);
13285      break;
13286    }
13287    default:
13288      break;
13289  }
13290  XSetCursorState(display,windows,MagickFalse);
13291  return(tile_image);
13292}
13293
13294/*
13295%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13296%                                                                             %
13297%                                                                             %
13298%                                                                             %
13299+   X T r a n s l a t e I m a g e                                             %
13300%                                                                             %
13301%                                                                             %
13302%                                                                             %
13303%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13304%
13305%  XTranslateImage() translates the image within an Image window by one pixel
13306%  as specified by the key symbol.  If the image has a montage string the
13307%  translation is respect to the width and height contained within the string.
13308%
13309%  The format of the XTranslateImage method is:
13310%
13311%      void XTranslateImage(Display *display,XWindows *windows,
13312%        Image *image,const KeySym key_symbol)
13313%
13314%  A description of each parameter follows:
13315%
13316%    o display: Specifies a connection to an X server; returned from
13317%      XOpenDisplay.
13318%
13319%    o windows: Specifies a pointer to a XWindows structure.
13320%
13321%    o image: the image.
13322%
13323%    o key_symbol: Specifies a KeySym which indicates which side of the image
13324%      to trim.
13325%
13326*/
13327static void XTranslateImage(Display *display,XWindows *windows,
13328  Image *image,const KeySym key_symbol)
13329{
13330  char
13331    text[MagickPathExtent];
13332
13333  int
13334    x,
13335    y;
13336
13337  unsigned int
13338    x_offset,
13339    y_offset;
13340
13341  /*
13342    User specified a pan position offset.
13343  */
13344  x_offset=windows->image.width;
13345  y_offset=windows->image.height;
13346  if (image->montage != (char *) NULL)
13347    (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13348  switch ((int) key_symbol)
13349  {
13350    case XK_Home:
13351    case XK_KP_Home:
13352    {
13353      windows->image.x=(int) windows->image.width/2;
13354      windows->image.y=(int) windows->image.height/2;
13355      break;
13356    }
13357    case XK_Left:
13358    case XK_KP_Left:
13359    {
13360      windows->image.x-=x_offset;
13361      break;
13362    }
13363    case XK_Next:
13364    case XK_Up:
13365    case XK_KP_Up:
13366    {
13367      windows->image.y-=y_offset;
13368      break;
13369    }
13370    case XK_Right:
13371    case XK_KP_Right:
13372    {
13373      windows->image.x+=x_offset;
13374      break;
13375    }
13376    case XK_Prior:
13377    case XK_Down:
13378    case XK_KP_Down:
13379    {
13380      windows->image.y+=y_offset;
13381      break;
13382    }
13383    default:
13384      return;
13385  }
13386  /*
13387    Check boundary conditions.
13388  */
13389  if (windows->image.x < 0)
13390    windows->image.x=0;
13391  else
13392    if ((int) (windows->image.x+windows->image.width) >
13393        windows->image.ximage->width)
13394      windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13395  if (windows->image.y < 0)
13396    windows->image.y=0;
13397  else
13398    if ((int) (windows->image.y+windows->image.height) >
13399        windows->image.ximage->height)
13400      windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13401  /*
13402    Refresh Image window.
13403  */
13404  (void) FormatLocaleString(text,MagickPathExtent," %ux%u%+d%+d ",
13405    windows->image.width,windows->image.height,windows->image.x,
13406    windows->image.y);
13407  XInfoWidget(display,windows,text);
13408  XCheckRefreshWindows(display,windows);
13409  XDrawPanRectangle(display,windows);
13410  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13411  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13412}
13413
13414/*
13415%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13416%                                                                             %
13417%                                                                             %
13418%                                                                             %
13419+   X T r i m I m a g e                                                       %
13420%                                                                             %
13421%                                                                             %
13422%                                                                             %
13423%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13424%
13425%  XTrimImage() trims the edges from the Image window.
13426%
13427%  The format of the XTrimImage method is:
13428%
13429%      MagickBooleanType XTrimImage(Display *display,
13430%        XResourceInfo *resource_info,XWindows *windows,Image *image,
13431%        ExceptionInfo *exception)
13432%
13433%  A description of each parameter follows:
13434%
13435%    o display: Specifies a connection to an X server; returned from
13436%      XOpenDisplay.
13437%
13438%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13439%
13440%    o windows: Specifies a pointer to a XWindows structure.
13441%
13442%    o image: the image.
13443%
13444%    o exception: return any errors or warnings in this structure.
13445%
13446*/
13447static MagickBooleanType XTrimImage(Display *display,
13448  XResourceInfo *resource_info,XWindows *windows,Image *image,
13449  ExceptionInfo *exception)
13450{
13451  RectangleInfo
13452    trim_info;
13453
13454  register int
13455    x,
13456    y;
13457
13458  size_t
13459    background,
13460    pixel;
13461
13462  /*
13463    Trim edges from image.
13464  */
13465  XSetCursorState(display,windows,MagickTrue);
13466  XCheckRefreshWindows(display,windows);
13467  /*
13468    Crop the left edge.
13469  */
13470  background=XGetPixel(windows->image.ximage,0,0);
13471  trim_info.width=(size_t) windows->image.ximage->width;
13472  for (x=0; x < windows->image.ximage->width; x++)
13473  {
13474    for (y=0; y < windows->image.ximage->height; y++)
13475    {
13476      pixel=XGetPixel(windows->image.ximage,x,y);
13477      if (pixel != background)
13478        break;
13479    }
13480    if (y < windows->image.ximage->height)
13481      break;
13482  }
13483  trim_info.x=(ssize_t) x;
13484  if (trim_info.x == (ssize_t) windows->image.ximage->width)
13485    {
13486      XSetCursorState(display,windows,MagickFalse);
13487      return(MagickFalse);
13488    }
13489  /*
13490    Crop the right edge.
13491  */
13492  background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13493  for (x=windows->image.ximage->width-1; x != 0; x--)
13494  {
13495    for (y=0; y < windows->image.ximage->height; y++)
13496    {
13497      pixel=XGetPixel(windows->image.ximage,x,y);
13498      if (pixel != background)
13499        break;
13500    }
13501    if (y < windows->image.ximage->height)
13502      break;
13503  }
13504  trim_info.width=(size_t) (x-trim_info.x+1);
13505  /*
13506    Crop the top edge.
13507  */
13508  background=XGetPixel(windows->image.ximage,0,0);
13509  trim_info.height=(size_t) windows->image.ximage->height;
13510  for (y=0; y < windows->image.ximage->height; y++)
13511  {
13512    for (x=0; x < windows->image.ximage->width; x++)
13513    {
13514      pixel=XGetPixel(windows->image.ximage,x,y);
13515      if (pixel != background)
13516        break;
13517    }
13518    if (x < windows->image.ximage->width)
13519      break;
13520  }
13521  trim_info.y=(ssize_t) y;
13522  /*
13523    Crop the bottom edge.
13524  */
13525  background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13526  for (y=windows->image.ximage->height-1; y != 0; y--)
13527  {
13528    for (x=0; x < windows->image.ximage->width; x++)
13529    {
13530      pixel=XGetPixel(windows->image.ximage,x,y);
13531      if (pixel != background)
13532        break;
13533    }
13534    if (x < windows->image.ximage->width)
13535      break;
13536  }
13537  trim_info.height=(size_t) y-trim_info.y+1;
13538  if (((unsigned int) trim_info.width != windows->image.width) ||
13539      ((unsigned int) trim_info.height != windows->image.height))
13540    {
13541      /*
13542        Reconfigure Image window as defined by the trimming rectangle.
13543      */
13544      XSetCropGeometry(display,windows,&trim_info,image);
13545      windows->image.window_changes.width=(int) trim_info.width;
13546      windows->image.window_changes.height=(int) trim_info.height;
13547      (void) XConfigureImage(display,resource_info,windows,image,exception);
13548    }
13549  XSetCursorState(display,windows,MagickFalse);
13550  return(MagickTrue);
13551}
13552
13553/*
13554%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13555%                                                                             %
13556%                                                                             %
13557%                                                                             %
13558+   X V i s u a l D i r e c t o r y I m a g e                                 %
13559%                                                                             %
13560%                                                                             %
13561%                                                                             %
13562%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13563%
13564%  XVisualDirectoryImage() creates a Visual Image Directory.
13565%
13566%  The format of the XVisualDirectoryImage method is:
13567%
13568%      Image *XVisualDirectoryImage(Display *display,
13569%        XResourceInfo *resource_info,XWindows *windows,
13570%        ExceptionInfo *exception)
13571%
13572%  A description of each parameter follows:
13573%
13574%    o display: Specifies a connection to an X server; returned from
13575%      XOpenDisplay.
13576%
13577%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13578%
13579%    o windows: Specifies a pointer to a XWindows structure.
13580%
13581%    o exception: return any errors or warnings in this structure.
13582%
13583*/
13584static Image *XVisualDirectoryImage(Display *display,
13585  XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
13586{
13587#define TileImageTag  "Scale/Image"
13588#define XClientName  "montage"
13589
13590  char
13591    **filelist;
13592
13593  Image
13594    *images,
13595    *montage_image,
13596    *next_image,
13597    *thumbnail_image;
13598
13599  ImageInfo
13600    *read_info;
13601
13602  int
13603    number_files;
13604
13605  MagickBooleanType
13606    backdrop;
13607
13608  MagickStatusType
13609    status;
13610
13611  MontageInfo
13612    *montage_info;
13613
13614  RectangleInfo
13615    geometry;
13616
13617  register int
13618    i;
13619
13620  static char
13621    filename[MagickPathExtent] = "\0",
13622    filenames[MagickPathExtent] = "*";
13623
13624  XResourceInfo
13625    background_resources;
13626
13627  /*
13628    Request file name from user.
13629  */
13630  XFileBrowserWidget(display,windows,"Directory",filenames);
13631  if (*filenames == '\0')
13632    return((Image *) NULL);
13633  /*
13634    Expand the filenames.
13635  */
13636  filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13637  if (filelist == (char **) NULL)
13638    {
13639      ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13640        filenames);
13641      return((Image *) NULL);
13642    }
13643  number_files=1;
13644  filelist[0]=filenames;
13645  status=ExpandFilenames(&number_files,&filelist);
13646  if (IfMagickFalse(status) || (number_files == 0))
13647    {
13648      if (number_files == 0)
13649        ThrowXWindowException(ImageError,"NoImagesWereFound",filenames)
13650      else
13651        ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13652          filenames);
13653      return((Image *) NULL);
13654    }
13655  /*
13656    Set image background resources.
13657  */
13658  background_resources=(*resource_info);
13659  background_resources.window_id=AcquireString("");
13660  (void) FormatLocaleString(background_resources.window_id,MagickPathExtent,
13661    "0x%lx",windows->image.id);
13662  background_resources.backdrop=MagickTrue;
13663  /*
13664    Read each image and convert them to a tile.
13665  */
13666  backdrop=IsMagickTrue( (windows->visual_info->klass == TrueColor) ||
13667    (windows->visual_info->klass == DirectColor) );
13668  read_info=CloneImageInfo(resource_info->image_info);
13669  (void) SetImageOption(read_info,"jpeg:size","120x120");
13670  (void) CloneString(&read_info->size,DefaultTileGeometry);
13671  (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13672    (void *) NULL);
13673  images=NewImageList();
13674  XSetCursorState(display,windows,MagickTrue);
13675  XCheckRefreshWindows(display,windows);
13676  for (i=0; i < (int) number_files; i++)
13677  {
13678    (void) CopyMagickString(read_info->filename,filelist[i],MagickPathExtent);
13679    filelist[i]=DestroyString(filelist[i]);
13680    *read_info->magick='\0';
13681    next_image=ReadImage(read_info,exception);
13682    CatchException(exception);
13683    if (next_image != (Image *) NULL)
13684      {
13685        (void) DeleteImageProperty(next_image,"label");
13686        (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13687          read_info,next_image,DefaultTileLabel,exception),exception);
13688        (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13689          exception);
13690        thumbnail_image=ThumbnailImage(next_image,geometry.width,
13691          geometry.height,exception);
13692        if (thumbnail_image != (Image *) NULL)
13693          {
13694            next_image=DestroyImage(next_image);
13695            next_image=thumbnail_image;
13696          }
13697        if (backdrop)
13698          {
13699            (void) XDisplayBackgroundImage(display,&background_resources,
13700              next_image,exception);
13701            XSetCursorState(display,windows,MagickTrue);
13702          }
13703        AppendImageToList(&images,next_image);
13704        if (images->progress_monitor != (MagickProgressMonitor) NULL)
13705          {
13706            MagickBooleanType
13707              proceed;
13708
13709            proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13710              (MagickSizeType) number_files);
13711            if (IfMagickFalse(proceed) )
13712              break;
13713          }
13714      }
13715  }
13716  filelist=(char **) RelinquishMagickMemory(filelist);
13717  if (images == (Image *) NULL)
13718    {
13719      read_info=DestroyImageInfo(read_info);
13720      XSetCursorState(display,windows,MagickFalse);
13721      ThrowXWindowException(ImageError,"NoImagesWereLoaded",filenames);
13722      return((Image *) NULL);
13723    }
13724  /*
13725    Create the Visual Image Directory.
13726  */
13727  montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13728  montage_info->pointsize=10;
13729  if (resource_info->font != (char *) NULL)
13730    (void) CloneString(&montage_info->font,resource_info->font);
13731  (void) CopyMagickString(montage_info->filename,filename,MagickPathExtent);
13732  montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13733    images),exception);
13734  images=DestroyImageList(images);
13735  montage_info=DestroyMontageInfo(montage_info);
13736  read_info=DestroyImageInfo(read_info);
13737  XSetCursorState(display,windows,MagickFalse);
13738  if (montage_image == (Image *) NULL)
13739    return(montage_image);
13740  XClientMessage(display,windows->image.id,windows->im_protocols,
13741    windows->im_next_image,CurrentTime);
13742  return(montage_image);
13743}
13744
13745/*
13746%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13747%                                                                             %
13748%                                                                             %
13749%                                                                             %
13750%   X D i s p l a y B a c k g r o u n d I m a g e                             %
13751%                                                                             %
13752%                                                                             %
13753%                                                                             %
13754%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13755%
13756%  XDisplayBackgroundImage() displays an image in the background of a window.
13757%
13758%  The format of the XDisplayBackgroundImage method is:
13759%
13760%      MagickBooleanType XDisplayBackgroundImage(Display *display,
13761%        XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13762%
13763%  A description of each parameter follows:
13764%
13765%    o display: Specifies a connection to an X server;  returned from
13766%      XOpenDisplay.
13767%
13768%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13769%
13770%    o image: the image.
13771%
13772%    o exception: return any errors or warnings in this structure.
13773%
13774*/
13775MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13776  XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13777{
13778  char
13779    geometry[MagickPathExtent],
13780    visual_type[MagickPathExtent];
13781
13782  int
13783    height,
13784    status,
13785    width;
13786
13787  RectangleInfo
13788    geometry_info;
13789
13790  static XPixelInfo
13791    pixel;
13792
13793  static XStandardColormap
13794    *map_info;
13795
13796  static XVisualInfo
13797    *visual_info = (XVisualInfo *) NULL;
13798
13799  static XWindowInfo
13800    window_info;
13801
13802  size_t
13803    delay;
13804
13805  Window
13806    root_window;
13807
13808  XGCValues
13809    context_values;
13810
13811  XResourceInfo
13812    resources;
13813
13814  XWindowAttributes
13815    window_attributes;
13816
13817  /*
13818    Determine target window.
13819  */
13820  assert(image != (Image *) NULL);
13821  assert(image->signature == MagickCoreSignature);
13822  if (IfMagickTrue(image->debug) )
13823    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13824  resources=(*resource_info);
13825  window_info.id=(Window) NULL;
13826  root_window=XRootWindow(display,XDefaultScreen(display));
13827  if (LocaleCompare(resources.window_id,"root") == 0)
13828    window_info.id=root_window;
13829  else
13830    {
13831      if (isdigit((int) ((unsigned char) *resources.window_id)) != 0)
13832        window_info.id=XWindowByID(display,root_window,
13833          (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13834      if (window_info.id == (Window) NULL)
13835        window_info.id=XWindowByName(display,root_window,resources.window_id);
13836    }
13837  if (window_info.id == (Window) NULL)
13838    {
13839      ThrowXWindowException(XServerError,"NoWindowWithSpecifiedIDExists",
13840        resources.window_id);
13841      return(MagickFalse);
13842    }
13843  /*
13844    Determine window visual id.
13845  */
13846  window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13847  window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13848  (void) CopyMagickString(visual_type,"default",MagickPathExtent);
13849  status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13850  if (status != 0)
13851    (void) FormatLocaleString(visual_type,MagickPathExtent,"0x%lx",
13852      XVisualIDFromVisual(window_attributes.visual));
13853  if (visual_info == (XVisualInfo *) NULL)
13854    {
13855      /*
13856        Allocate standard colormap.
13857      */
13858      map_info=XAllocStandardColormap();
13859      if (map_info == (XStandardColormap *) NULL)
13860        ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13861          image->filename);
13862      map_info->colormap=(Colormap) NULL;
13863      pixel.pixels=(unsigned long *) NULL;
13864      /*
13865        Initialize visual info.
13866      */
13867      resources.map_type=(char *) NULL;
13868      resources.visual_type=visual_type;
13869      visual_info=XBestVisualInfo(display,map_info,&resources);
13870      if (visual_info == (XVisualInfo *) NULL)
13871        ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13872          resources.visual_type);
13873      /*
13874        Initialize window info.
13875      */
13876      window_info.ximage=(XImage *) NULL;
13877      window_info.matte_image=(XImage *) NULL;
13878      window_info.pixmap=(Pixmap) NULL;
13879      window_info.matte_pixmap=(Pixmap) NULL;
13880    }
13881  /*
13882    Free previous root colors.
13883  */
13884  if (window_info.id == root_window)
13885    (void) XDestroyWindowColors(display,root_window);
13886  /*
13887    Initialize Standard Colormap.
13888  */
13889  resources.colormap=SharedColormap;
13890  XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel,
13891    exception);
13892  /*
13893    Graphic context superclass.
13894  */
13895  context_values.background=pixel.background_color.pixel;
13896  context_values.foreground=pixel.foreground_color.pixel;
13897  pixel.annotate_context=XCreateGC(display,window_info.id,
13898    (size_t) (GCBackground | GCForeground),&context_values);
13899  if (pixel.annotate_context == (GC) NULL)
13900    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13901      image->filename);
13902  /*
13903    Initialize Image window attributes.
13904  */
13905  window_info.name=AcquireString("\0");
13906  window_info.icon_name=AcquireString("\0");
13907  XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13908    &resources,&window_info);
13909  /*
13910    Create the X image.
13911  */
13912  window_info.width=(unsigned int) image->columns;
13913  window_info.height=(unsigned int) image->rows;
13914  if ((image->columns != window_info.width) ||
13915      (image->rows != window_info.height))
13916    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13917      image->filename);
13918  (void) FormatLocaleString(geometry,MagickPathExtent,"%ux%u+0+0>",
13919    window_attributes.width,window_attributes.height);
13920  geometry_info.width=window_info.width;
13921  geometry_info.height=window_info.height;
13922  geometry_info.x=(ssize_t) window_info.x;
13923  geometry_info.y=(ssize_t) window_info.y;
13924  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13925    &geometry_info.width,&geometry_info.height);
13926  window_info.width=(unsigned int) geometry_info.width;
13927  window_info.height=(unsigned int) geometry_info.height;
13928  window_info.x=(int) geometry_info.x;
13929  window_info.y=(int) geometry_info.y;
13930  status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13931    window_info.height,exception);
13932  if (IfMagickFalse(status) )
13933    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13934      image->filename);
13935  window_info.x=0;
13936  window_info.y=0;
13937  if (IfMagickTrue(image->debug) )
13938    {
13939      (void) LogMagickEvent(X11Event,GetMagickModule(),
13940        "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13941        (double) image->columns,(double) image->rows);
13942      if (image->colors != 0)
13943        (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13944          image->colors);
13945      (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13946    }
13947  /*
13948    Adjust image dimensions as specified by backdrop or geometry options.
13949  */
13950  width=(int) window_info.width;
13951  height=(int) window_info.height;
13952  if (IfMagickTrue(resources.backdrop) )
13953    {
13954      /*
13955        Center image on window.
13956      */
13957      window_info.x=(window_attributes.width/2)-
13958        (window_info.ximage->width/2);
13959      window_info.y=(window_attributes.height/2)-
13960        (window_info.ximage->height/2);
13961      width=window_attributes.width;
13962      height=window_attributes.height;
13963    }
13964  if ((resources.image_geometry != (char *) NULL) &&
13965      (*resources.image_geometry != '\0'))
13966    {
13967      char
13968        default_geometry[MagickPathExtent];
13969
13970      int
13971        flags,
13972        gravity;
13973
13974      XSizeHints
13975        *size_hints;
13976
13977      /*
13978        User specified geometry.
13979      */
13980      size_hints=XAllocSizeHints();
13981      if (size_hints == (XSizeHints *) NULL)
13982        ThrowXWindowFatalException(ResourceLimitFatalError,
13983          "MemoryAllocationFailed",image->filename);
13984      size_hints->flags=0L;
13985      (void) FormatLocaleString(default_geometry,MagickPathExtent,"%dx%d",
13986        width,height);
13987      flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
13988        default_geometry,window_info.border_width,size_hints,&window_info.x,
13989        &window_info.y,&width,&height,&gravity);
13990      if (flags & (XValue | YValue))
13991        {
13992          width=window_attributes.width;
13993          height=window_attributes.height;
13994        }
13995      (void) XFree((void *) size_hints);
13996    }
13997  /*
13998    Create the X pixmap.
13999  */
14000  window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
14001    (unsigned int) height,window_info.depth);
14002  if (window_info.pixmap == (Pixmap) NULL)
14003    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
14004      image->filename);
14005  /*
14006    Display pixmap on the window.
14007  */
14008  if (((unsigned int) width > window_info.width) ||
14009      ((unsigned int) height > window_info.height))
14010    (void) XFillRectangle(display,window_info.pixmap,
14011      window_info.annotate_context,0,0,(unsigned int) width,
14012      (unsigned int) height);
14013  (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
14014    window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
14015    window_info.width,(unsigned int) window_info.height);
14016  (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
14017  (void) XClearWindow(display,window_info.id);
14018  delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
14019  XDelay(display,delay == 0UL ? 10UL : delay);
14020  (void) XSync(display,MagickFalse);
14021  return(IsMagickTrue(window_info.id == root_window));
14022}
14023
14024/*
14025%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14026%                                                                             %
14027%                                                                             %
14028%                                                                             %
14029+   X D i s p l a y I m a g e                                                 %
14030%                                                                             %
14031%                                                                             %
14032%                                                                             %
14033%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14034%
14035%  XDisplayImage() displays an image via X11.  A new image is created and
14036%  returned if the user interactively transforms the displayed image.
14037%
14038%  The format of the XDisplayImage method is:
14039%
14040%      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14041%        char **argv,int argc,Image **image,size_t *state,
14042%        ExceptionInfo *exception)
14043%
14044%  A description of each parameter follows:
14045%
14046%    o nexus:  Method XDisplayImage returns an image when the
14047%      user chooses 'Open Image' from the command menu or picks a tile
14048%      from the image directory.  Otherwise a null image is returned.
14049%
14050%    o display: Specifies a connection to an X server;  returned from
14051%      XOpenDisplay.
14052%
14053%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14054%
14055%    o argv: Specifies the application's argument list.
14056%
14057%    o argc: Specifies the number of arguments.
14058%
14059%    o image: Specifies an address to an address of an Image structure;
14060%
14061%    o exception: return any errors or warnings in this structure.
14062%
14063*/
14064MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14065  char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
14066{
14067#define MagnifySize  256  /* must be a power of 2 */
14068#define MagickMenus  10
14069#define MagickTitle  "Commands"
14070
14071  static const char
14072    *CommandMenu[] =
14073    {
14074      "File",
14075      "Edit",
14076      "View",
14077      "Transform",
14078      "Enhance",
14079      "Effects",
14080      "F/X",
14081      "Image Edit",
14082      "Miscellany",
14083      "Help",
14084      (char *) NULL
14085    },
14086    *FileMenu[] =
14087    {
14088      "Open...",
14089      "Next",
14090      "Former",
14091      "Select...",
14092      "Save...",
14093      "Print...",
14094      "Delete...",
14095      "New...",
14096      "Visual Directory...",
14097      "Quit",
14098      (char *) NULL
14099    },
14100    *EditMenu[] =
14101    {
14102      "Undo",
14103      "Redo",
14104      "Cut",
14105      "Copy",
14106      "Paste",
14107      (char *) NULL
14108    },
14109    *ViewMenu[] =
14110    {
14111      "Half Size",
14112      "Original Size",
14113      "Double Size",
14114      "Resize...",
14115      "Apply",
14116      "Refresh",
14117      "Restore",
14118      (char *) NULL
14119    },
14120    *TransformMenu[] =
14121    {
14122      "Crop",
14123      "Chop",
14124      "Flop",
14125      "Flip",
14126      "Rotate Right",
14127      "Rotate Left",
14128      "Rotate...",
14129      "Shear...",
14130      "Roll...",
14131      "Trim Edges",
14132      (char *) NULL
14133    },
14134    *EnhanceMenu[] =
14135    {
14136      "Hue...",
14137      "Saturation...",
14138      "Brightness...",
14139      "Gamma...",
14140      "Spiff",
14141      "Dull",
14142      "Contrast Stretch...",
14143      "Sigmoidal Contrast...",
14144      "Normalize",
14145      "Equalize",
14146      "Negate",
14147      "Grayscale",
14148      "Map...",
14149      "Quantize...",
14150      (char *) NULL
14151    },
14152    *EffectsMenu[] =
14153    {
14154      "Despeckle",
14155      "Emboss",
14156      "Reduce Noise",
14157      "Add Noise...",
14158      "Sharpen...",
14159      "Blur...",
14160      "Threshold...",
14161      "Edge Detect...",
14162      "Spread...",
14163      "Shade...",
14164      "Raise...",
14165      "Segment...",
14166      (char *) NULL
14167    },
14168    *FXMenu[] =
14169    {
14170      "Solarize...",
14171      "Sepia Tone...",
14172      "Swirl...",
14173      "Implode...",
14174      "Vignette...",
14175      "Wave...",
14176      "Oil Paint...",
14177      "Charcoal Draw...",
14178      (char *) NULL
14179    },
14180    *ImageEditMenu[] =
14181    {
14182      "Annotate...",
14183      "Draw...",
14184      "Color...",
14185      "Matte...",
14186      "Composite...",
14187      "Add Border...",
14188      "Add Frame...",
14189      "Comment...",
14190      "Launch...",
14191      "Region of Interest...",
14192      (char *) NULL
14193    },
14194    *MiscellanyMenu[] =
14195    {
14196      "Image Info",
14197      "Zoom Image",
14198      "Show Preview...",
14199      "Show Histogram",
14200      "Show Matte",
14201      "Background...",
14202      "Slide Show...",
14203      "Preferences...",
14204      (char *) NULL
14205    },
14206    *HelpMenu[] =
14207    {
14208      "Overview",
14209      "Browse Documentation",
14210      "About Display",
14211      (char *) NULL
14212    },
14213    *ShortCutsMenu[] =
14214    {
14215      "Next",
14216      "Former",
14217      "Open...",
14218      "Save...",
14219      "Print...",
14220      "Undo",
14221      "Restore",
14222      "Image Info",
14223      "Quit",
14224      (char *) NULL
14225    },
14226    *VirtualMenu[] =
14227    {
14228      "Image Info",
14229      "Print",
14230      "Next",
14231      "Quit",
14232      (char *) NULL
14233    };
14234
14235  static const char
14236    **Menus[MagickMenus] =
14237    {
14238      FileMenu,
14239      EditMenu,
14240      ViewMenu,
14241      TransformMenu,
14242      EnhanceMenu,
14243      EffectsMenu,
14244      FXMenu,
14245      ImageEditMenu,
14246      MiscellanyMenu,
14247      HelpMenu
14248    };
14249
14250  static CommandType
14251    CommandMenus[] =
14252    {
14253      NullCommand,
14254      NullCommand,
14255      NullCommand,
14256      NullCommand,
14257      NullCommand,
14258      NullCommand,
14259      NullCommand,
14260      NullCommand,
14261      NullCommand,
14262      NullCommand,
14263    },
14264    FileCommands[] =
14265    {
14266      OpenCommand,
14267      NextCommand,
14268      FormerCommand,
14269      SelectCommand,
14270      SaveCommand,
14271      PrintCommand,
14272      DeleteCommand,
14273      NewCommand,
14274      VisualDirectoryCommand,
14275      QuitCommand
14276    },
14277    EditCommands[] =
14278    {
14279      UndoCommand,
14280      RedoCommand,
14281      CutCommand,
14282      CopyCommand,
14283      PasteCommand
14284    },
14285    ViewCommands[] =
14286    {
14287      HalfSizeCommand,
14288      OriginalSizeCommand,
14289      DoubleSizeCommand,
14290      ResizeCommand,
14291      ApplyCommand,
14292      RefreshCommand,
14293      RestoreCommand
14294    },
14295    TransformCommands[] =
14296    {
14297      CropCommand,
14298      ChopCommand,
14299      FlopCommand,
14300      FlipCommand,
14301      RotateRightCommand,
14302      RotateLeftCommand,
14303      RotateCommand,
14304      ShearCommand,
14305      RollCommand,
14306      TrimCommand
14307    },
14308    EnhanceCommands[] =
14309    {
14310      HueCommand,
14311      SaturationCommand,
14312      BrightnessCommand,
14313      GammaCommand,
14314      SpiffCommand,
14315      DullCommand,
14316      ContrastStretchCommand,
14317      SigmoidalContrastCommand,
14318      NormalizeCommand,
14319      EqualizeCommand,
14320      NegateCommand,
14321      GrayscaleCommand,
14322      MapCommand,
14323      QuantizeCommand
14324    },
14325    EffectsCommands[] =
14326    {
14327      DespeckleCommand,
14328      EmbossCommand,
14329      ReduceNoiseCommand,
14330      AddNoiseCommand,
14331      SharpenCommand,
14332      BlurCommand,
14333      ThresholdCommand,
14334      EdgeDetectCommand,
14335      SpreadCommand,
14336      ShadeCommand,
14337      RaiseCommand,
14338      SegmentCommand
14339    },
14340    FXCommands[] =
14341    {
14342      SolarizeCommand,
14343      SepiaToneCommand,
14344      SwirlCommand,
14345      ImplodeCommand,
14346      VignetteCommand,
14347      WaveCommand,
14348      OilPaintCommand,
14349      CharcoalDrawCommand
14350    },
14351    ImageEditCommands[] =
14352    {
14353      AnnotateCommand,
14354      DrawCommand,
14355      ColorCommand,
14356      MatteCommand,
14357      CompositeCommand,
14358      AddBorderCommand,
14359      AddFrameCommand,
14360      CommentCommand,
14361      LaunchCommand,
14362      RegionofInterestCommand
14363    },
14364    MiscellanyCommands[] =
14365    {
14366      InfoCommand,
14367      ZoomCommand,
14368      ShowPreviewCommand,
14369      ShowHistogramCommand,
14370      ShowMatteCommand,
14371      BackgroundCommand,
14372      SlideShowCommand,
14373      PreferencesCommand
14374    },
14375    HelpCommands[] =
14376    {
14377      HelpCommand,
14378      BrowseDocumentationCommand,
14379      VersionCommand
14380    },
14381    ShortCutsCommands[] =
14382    {
14383      NextCommand,
14384      FormerCommand,
14385      OpenCommand,
14386      SaveCommand,
14387      PrintCommand,
14388      UndoCommand,
14389      RestoreCommand,
14390      InfoCommand,
14391      QuitCommand
14392    },
14393    VirtualCommands[] =
14394    {
14395      InfoCommand,
14396      PrintCommand,
14397      NextCommand,
14398      QuitCommand
14399    };
14400
14401  static CommandType
14402    *Commands[MagickMenus] =
14403    {
14404      FileCommands,
14405      EditCommands,
14406      ViewCommands,
14407      TransformCommands,
14408      EnhanceCommands,
14409      EffectsCommands,
14410      FXCommands,
14411      ImageEditCommands,
14412      MiscellanyCommands,
14413      HelpCommands
14414    };
14415
14416  char
14417    command[MagickPathExtent],
14418    *directory,
14419    geometry[MagickPathExtent],
14420    resource_name[MagickPathExtent];
14421
14422  CommandType
14423    command_type;
14424
14425  Image
14426    *display_image,
14427    *nexus;
14428
14429  int
14430    entry,
14431    id;
14432
14433  KeySym
14434    key_symbol;
14435
14436  MagickStatusType
14437    context_mask,
14438    status;
14439
14440  RectangleInfo
14441    geometry_info;
14442
14443  register int
14444    i;
14445
14446  static char
14447    working_directory[MagickPathExtent];
14448
14449  static XPoint
14450    vid_info;
14451
14452  static XWindowInfo
14453    *magick_windows[MaxXWindows];
14454
14455  static unsigned int
14456    number_windows;
14457
14458  struct stat
14459    attributes;
14460
14461  time_t
14462    timer,
14463    timestamp,
14464    update_time;
14465
14466  unsigned int
14467    height,
14468    width;
14469
14470  size_t
14471    delay;
14472
14473  WarningHandler
14474    warning_handler;
14475
14476  Window
14477    root_window;
14478
14479  XClassHint
14480    *class_hints;
14481
14482  XEvent
14483    event;
14484
14485  XFontStruct
14486    *font_info;
14487
14488  XGCValues
14489    context_values;
14490
14491  XPixelInfo
14492    *icon_pixel,
14493    *pixel;
14494
14495  XResourceInfo
14496    *icon_resources;
14497
14498  XStandardColormap
14499    *icon_map,
14500    *map_info;
14501
14502  XVisualInfo
14503    *icon_visual,
14504    *visual_info;
14505
14506  XWindowChanges
14507    window_changes;
14508
14509  XWindows
14510    *windows;
14511
14512  XWMHints
14513    *manager_hints;
14514
14515  assert(image != (Image **) NULL);
14516  assert((*image)->signature == MagickCoreSignature);
14517  if (IfMagickTrue((*image)->debug) )
14518    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14519  display_image=(*image);
14520  warning_handler=(WarningHandler) NULL;
14521  windows=XSetWindows((XWindows *) ~0);
14522  if (windows != (XWindows *) NULL)
14523    {
14524      int
14525        status;
14526
14527      if (*working_directory == '\0')
14528        (void) CopyMagickString(working_directory,".",MagickPathExtent);
14529      status=chdir(working_directory);
14530      if (status == -1)
14531        (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14532          "UnableToOpenFile","%s",working_directory);
14533      warning_handler=resource_info->display_warnings ?
14534        SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14535      warning_handler=resource_info->display_warnings ?
14536        SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14537    }
14538  else
14539    {
14540      /*
14541        Allocate windows structure.
14542      */
14543      resource_info->colors=display_image->colors;
14544      windows=XSetWindows(XInitializeWindows(display,resource_info));
14545      if (windows == (XWindows *) NULL)
14546        ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14547          (*image)->filename);
14548      /*
14549        Initialize window id's.
14550      */
14551      number_windows=0;
14552      magick_windows[number_windows++]=(&windows->icon);
14553      magick_windows[number_windows++]=(&windows->backdrop);
14554      magick_windows[number_windows++]=(&windows->image);
14555      magick_windows[number_windows++]=(&windows->info);
14556      magick_windows[number_windows++]=(&windows->command);
14557      magick_windows[number_windows++]=(&windows->widget);
14558      magick_windows[number_windows++]=(&windows->popup);
14559      magick_windows[number_windows++]=(&windows->magnify);
14560      magick_windows[number_windows++]=(&windows->pan);
14561      for (i=0; i < (int) number_windows; i++)
14562        magick_windows[i]->id=(Window) NULL;
14563      vid_info.x=0;
14564      vid_info.y=0;
14565    }
14566  /*
14567    Initialize font info.
14568  */
14569  if (windows->font_info != (XFontStruct *) NULL)
14570    (void) XFreeFont(display,windows->font_info);
14571  windows->font_info=XBestFont(display,resource_info,MagickFalse);
14572  if (windows->font_info == (XFontStruct *) NULL)
14573    ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14574      resource_info->font);
14575  /*
14576    Initialize Standard Colormap.
14577  */
14578  map_info=windows->map_info;
14579  icon_map=windows->icon_map;
14580  visual_info=windows->visual_info;
14581  icon_visual=windows->icon_visual;
14582  pixel=windows->pixel_info;
14583  icon_pixel=windows->icon_pixel;
14584  font_info=windows->font_info;
14585  icon_resources=windows->icon_resources;
14586  class_hints=windows->class_hints;
14587  manager_hints=windows->manager_hints;
14588  root_window=XRootWindow(display,visual_info->screen);
14589  nexus=NewImageList();
14590  if (IfMagickTrue(display_image->debug) )
14591    {
14592      (void) LogMagickEvent(X11Event,GetMagickModule(),
14593        "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14594        (double) display_image->scene,(double) display_image->columns,
14595        (double) display_image->rows);
14596      if (display_image->colors != 0)
14597        (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14598          display_image->colors);
14599      (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14600        display_image->magick);
14601    }
14602  XMakeStandardColormap(display,visual_info,resource_info,display_image,
14603    map_info,pixel,exception);
14604  display_image->taint=MagickFalse;
14605  /*
14606    Initialize graphic context.
14607  */
14608  windows->context.id=(Window) NULL;
14609  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14610    resource_info,&windows->context);
14611  (void) CloneString(&class_hints->res_name,resource_info->client_name);
14612  (void) CloneString(&class_hints->res_class,resource_info->client_name);
14613  class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14614  manager_hints->flags=InputHint | StateHint;
14615  manager_hints->input=MagickFalse;
14616  manager_hints->initial_state=WithdrawnState;
14617  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14618    &windows->context);
14619  if (IfMagickTrue(display_image->debug) )
14620    (void) LogMagickEvent(X11Event,GetMagickModule(),
14621      "Window id: 0x%lx (context)",windows->context.id);
14622  context_values.background=pixel->background_color.pixel;
14623  context_values.font=font_info->fid;
14624  context_values.foreground=pixel->foreground_color.pixel;
14625  context_values.graphics_exposures=MagickFalse;
14626  context_mask=(MagickStatusType)
14627    (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14628  if (pixel->annotate_context != (GC) NULL)
14629    (void) XFreeGC(display,pixel->annotate_context);
14630  pixel->annotate_context=XCreateGC(display,windows->context.id,
14631    context_mask,&context_values);
14632  if (pixel->annotate_context == (GC) NULL)
14633    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14634      display_image->filename);
14635  context_values.background=pixel->depth_color.pixel;
14636  if (pixel->widget_context != (GC) NULL)
14637    (void) XFreeGC(display,pixel->widget_context);
14638  pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14639    &context_values);
14640  if (pixel->widget_context == (GC) NULL)
14641    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14642      display_image->filename);
14643  context_values.background=pixel->foreground_color.pixel;
14644  context_values.foreground=pixel->background_color.pixel;
14645  context_values.plane_mask=context_values.background ^
14646    context_values.foreground;
14647  if (pixel->highlight_context != (GC) NULL)
14648    (void) XFreeGC(display,pixel->highlight_context);
14649  pixel->highlight_context=XCreateGC(display,windows->context.id,
14650    (size_t) (context_mask | GCPlaneMask),&context_values);
14651  if (pixel->highlight_context == (GC) NULL)
14652    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14653      display_image->filename);
14654  (void) XDestroyWindow(display,windows->context.id);
14655  /*
14656    Initialize icon window.
14657  */
14658  XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14659    icon_resources,&windows->icon);
14660  windows->icon.geometry=resource_info->icon_geometry;
14661  XBestIconSize(display,&windows->icon,display_image);
14662  windows->icon.attributes.colormap=XDefaultColormap(display,
14663    icon_visual->screen);
14664  windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14665  manager_hints->flags=InputHint | StateHint;
14666  manager_hints->input=MagickFalse;
14667  manager_hints->initial_state=IconicState;
14668  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14669    &windows->icon);
14670  if (IfMagickTrue(display_image->debug) )
14671    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14672      windows->icon.id);
14673  /*
14674    Initialize graphic context for icon window.
14675  */
14676  if (icon_pixel->annotate_context != (GC) NULL)
14677    (void) XFreeGC(display,icon_pixel->annotate_context);
14678  context_values.background=icon_pixel->background_color.pixel;
14679  context_values.foreground=icon_pixel->foreground_color.pixel;
14680  icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14681    (size_t) (GCBackground | GCForeground),&context_values);
14682  if (icon_pixel->annotate_context == (GC) NULL)
14683    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14684      display_image->filename);
14685  windows->icon.annotate_context=icon_pixel->annotate_context;
14686  /*
14687    Initialize Image window.
14688  */
14689  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14690    &windows->image);
14691  windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14692  if (IfMagickFalse(resource_info->use_shared_memory) )
14693    windows->image.shared_memory=MagickFalse;
14694  if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14695    {
14696      char
14697        *title;
14698
14699      title=InterpretImageProperties(resource_info->image_info,display_image,
14700        resource_info->title,exception);
14701      (void) CopyMagickString(windows->image.name,title,MagickPathExtent);
14702      (void) CopyMagickString(windows->image.icon_name,title,MagickPathExtent);
14703      title=DestroyString(title);
14704    }
14705  else
14706    {
14707      char
14708        filename[MagickPathExtent];
14709
14710      /*
14711        Window name is the base of the filename.
14712      */
14713      GetPathComponent(display_image->magick_filename,TailPath,filename);
14714      if (display_image->scene == 0)
14715        (void) FormatLocaleString(windows->image.name,MagickPathExtent,
14716          "%s: %s",MagickPackageName,filename);
14717      else
14718        (void) FormatLocaleString(windows->image.name,MagickPathExtent,
14719          "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14720          (double) display_image->scene,(double) GetImageListLength(
14721          display_image));
14722      (void) CopyMagickString(windows->image.icon_name,filename,MagickPathExtent);
14723    }
14724  if (resource_info->immutable)
14725    windows->image.immutable=MagickTrue;
14726  windows->image.use_pixmap=resource_info->use_pixmap;
14727  windows->image.geometry=resource_info->image_geometry;
14728  (void) FormatLocaleString(geometry,MagickPathExtent,"%ux%u+0+0>!",
14729    XDisplayWidth(display,visual_info->screen),
14730    XDisplayHeight(display,visual_info->screen));
14731  geometry_info.width=display_image->columns;
14732  geometry_info.height=display_image->rows;
14733  geometry_info.x=0;
14734  geometry_info.y=0;
14735  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14736    &geometry_info.width,&geometry_info.height);
14737  windows->image.width=(unsigned int) geometry_info.width;
14738  windows->image.height=(unsigned int) geometry_info.height;
14739  windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14740    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14741    KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14742    PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14743  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14744    resource_info,&windows->backdrop);
14745  if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14746    {
14747      /*
14748        Initialize backdrop window.
14749      */
14750      windows->backdrop.x=0;
14751      windows->backdrop.y=0;
14752      (void) CloneString(&windows->backdrop.name,"Backdrop");
14753      windows->backdrop.flags=(size_t) (USSize | USPosition);
14754      windows->backdrop.width=(unsigned int)
14755        XDisplayWidth(display,visual_info->screen);
14756      windows->backdrop.height=(unsigned int)
14757        XDisplayHeight(display,visual_info->screen);
14758      windows->backdrop.border_width=0;
14759      windows->backdrop.immutable=MagickTrue;
14760      windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14761        ButtonReleaseMask;
14762      windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14763        StructureNotifyMask;
14764      manager_hints->flags=IconWindowHint | InputHint | StateHint;
14765      manager_hints->icon_window=windows->icon.id;
14766      manager_hints->input=MagickTrue;
14767      manager_hints->initial_state=resource_info->iconic ? IconicState :
14768        NormalState;
14769      XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14770        &windows->backdrop);
14771      if (IfMagickTrue(display_image->debug) )
14772        (void) LogMagickEvent(X11Event,GetMagickModule(),
14773          "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14774      (void) XMapWindow(display,windows->backdrop.id);
14775      (void) XClearWindow(display,windows->backdrop.id);
14776      if (windows->image.id != (Window) NULL)
14777        {
14778          (void) XDestroyWindow(display,windows->image.id);
14779          windows->image.id=(Window) NULL;
14780        }
14781      /*
14782        Position image in the center the backdrop.
14783      */
14784      windows->image.flags|=USPosition;
14785      windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14786        (windows->image.width/2);
14787      windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14788        (windows->image.height/2);
14789    }
14790  manager_hints->flags=IconWindowHint | InputHint | StateHint;
14791  manager_hints->icon_window=windows->icon.id;
14792  manager_hints->input=MagickTrue;
14793  manager_hints->initial_state=resource_info->iconic ? IconicState :
14794    NormalState;
14795  if (windows->group_leader.id != (Window) NULL)
14796    {
14797      /*
14798        Follow the leader.
14799      */
14800      manager_hints->flags|=WindowGroupHint;
14801      manager_hints->window_group=windows->group_leader.id;
14802      (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14803      if (IfMagickTrue(display_image->debug) )
14804        (void) LogMagickEvent(X11Event,GetMagickModule(),
14805          "Window id: 0x%lx (group leader)",windows->group_leader.id);
14806    }
14807  XMakeWindow(display,
14808    (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14809    argv,argc,class_hints,manager_hints,&windows->image);
14810  (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14811    XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14812  if (windows->group_leader.id != (Window) NULL)
14813    (void) XSetTransientForHint(display,windows->image.id,
14814      windows->group_leader.id);
14815  if (IfMagickTrue(display_image->debug) )
14816    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14817      windows->image.id);
14818  /*
14819    Initialize Info widget.
14820  */
14821  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14822    &windows->info);
14823  (void) CloneString(&windows->info.name,"Info");
14824  (void) CloneString(&windows->info.icon_name,"Info");
14825  windows->info.border_width=1;
14826  windows->info.x=2;
14827  windows->info.y=2;
14828  windows->info.flags|=PPosition;
14829  windows->info.attributes.win_gravity=UnmapGravity;
14830  windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14831    StructureNotifyMask;
14832  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14833  manager_hints->input=MagickFalse;
14834  manager_hints->initial_state=NormalState;
14835  manager_hints->window_group=windows->image.id;
14836  XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14837    &windows->info);
14838  windows->info.highlight_stipple=XCreateBitmapFromData(display,
14839    windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14840  windows->info.shadow_stipple=XCreateBitmapFromData(display,
14841    windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14842  (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14843  if (IfMagickTrue(windows->image.mapped) )
14844    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14845  if (IfMagickTrue(display_image->debug) )
14846    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14847      windows->info.id);
14848  /*
14849    Initialize Command widget.
14850  */
14851  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14852    resource_info,&windows->command);
14853  windows->command.data=MagickMenus;
14854  (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14855  (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.command",
14856    resource_info->client_name);
14857  windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14858    resource_name,"geometry",(char *) NULL);
14859  (void) CloneString(&windows->command.name,MagickTitle);
14860  windows->command.border_width=0;
14861  windows->command.flags|=PPosition;
14862  windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14863    ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14864    OwnerGrabButtonMask | StructureNotifyMask;
14865  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14866  manager_hints->input=MagickTrue;
14867  manager_hints->initial_state=NormalState;
14868  manager_hints->window_group=windows->image.id;
14869  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14870    &windows->command);
14871  windows->command.highlight_stipple=XCreateBitmapFromData(display,
14872    windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14873    HighlightHeight);
14874  windows->command.shadow_stipple=XCreateBitmapFromData(display,
14875    windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14876  (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14877  if (IfMagickTrue(windows->command.mapped) )
14878    (void) XMapRaised(display,windows->command.id);
14879  if (IfMagickTrue(display_image->debug) )
14880    (void) LogMagickEvent(X11Event,GetMagickModule(),
14881      "Window id: 0x%lx (command)",windows->command.id);
14882  /*
14883    Initialize Widget window.
14884  */
14885  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14886    resource_info,&windows->widget);
14887  (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.widget",
14888    resource_info->client_name);
14889  windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14890    resource_name,"geometry",(char *) NULL);
14891  windows->widget.border_width=0;
14892  windows->widget.flags|=PPosition;
14893  windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14894    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14895    KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14896    StructureNotifyMask;
14897  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14898  manager_hints->input=MagickTrue;
14899  manager_hints->initial_state=NormalState;
14900  manager_hints->window_group=windows->image.id;
14901  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14902    &windows->widget);
14903  windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14904    windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14905  windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14906    windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14907  (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14908  if (IfMagickTrue(display_image->debug) )
14909    (void) LogMagickEvent(X11Event,GetMagickModule(),
14910      "Window id: 0x%lx (widget)",windows->widget.id);
14911  /*
14912    Initialize popup window.
14913  */
14914  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14915    resource_info,&windows->popup);
14916  windows->popup.border_width=0;
14917  windows->popup.flags|=PPosition;
14918  windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14919    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14920    KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14921  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14922  manager_hints->input=MagickTrue;
14923  manager_hints->initial_state=NormalState;
14924  manager_hints->window_group=windows->image.id;
14925  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14926    &windows->popup);
14927  windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14928    windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14929  windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14930    windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14931  (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14932  if (IfMagickTrue(display_image->debug) )
14933    (void) LogMagickEvent(X11Event,GetMagickModule(),
14934      "Window id: 0x%lx (pop up)",windows->popup.id);
14935  /*
14936    Initialize Magnify window and cursor.
14937  */
14938  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14939    resource_info,&windows->magnify);
14940  if (IfMagickFalse(resource_info->use_shared_memory) )
14941    windows->magnify.shared_memory=MagickFalse;
14942  (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.magnify",
14943    resource_info->client_name);
14944  windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14945    resource_name,"geometry",(char *) NULL);
14946  (void) FormatLocaleString(windows->magnify.name,MagickPathExtent,"Magnify %uX",
14947    resource_info->magnify);
14948  if (windows->magnify.cursor != (Cursor) NULL)
14949    (void) XFreeCursor(display,windows->magnify.cursor);
14950  windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14951    map_info->colormap,resource_info->background_color,
14952    resource_info->foreground_color);
14953  if (windows->magnify.cursor == (Cursor) NULL)
14954    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14955      display_image->filename);
14956  windows->magnify.width=MagnifySize;
14957  windows->magnify.height=MagnifySize;
14958  windows->magnify.flags|=PPosition;
14959  windows->magnify.min_width=MagnifySize;
14960  windows->magnify.min_height=MagnifySize;
14961  windows->magnify.width_inc=MagnifySize;
14962  windows->magnify.height_inc=MagnifySize;
14963  windows->magnify.data=resource_info->magnify;
14964  windows->magnify.attributes.cursor=windows->magnify.cursor;
14965  windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14966    ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14967    StructureNotifyMask;
14968  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14969  manager_hints->input=MagickTrue;
14970  manager_hints->initial_state=NormalState;
14971  manager_hints->window_group=windows->image.id;
14972  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14973    &windows->magnify);
14974  if (IfMagickTrue(display_image->debug) )
14975    (void) LogMagickEvent(X11Event,GetMagickModule(),
14976      "Window id: 0x%lx (magnify)",windows->magnify.id);
14977  (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14978  /*
14979    Initialize panning window.
14980  */
14981  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14982    resource_info,&windows->pan);
14983  (void) CloneString(&windows->pan.name,"Pan Icon");
14984  windows->pan.width=windows->icon.width;
14985  windows->pan.height=windows->icon.height;
14986  (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.pan",
14987    resource_info->client_name);
14988  windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
14989    resource_name,"geometry",(char *) NULL);
14990  (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
14991    &windows->pan.width,&windows->pan.height);
14992  windows->pan.flags|=PPosition;
14993  windows->pan.immutable=MagickTrue;
14994  windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14995    ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
14996    StructureNotifyMask;
14997  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14998  manager_hints->input=MagickFalse;
14999  manager_hints->initial_state=NormalState;
15000  manager_hints->window_group=windows->image.id;
15001  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
15002    &windows->pan);
15003  if (IfMagickTrue(display_image->debug) )
15004    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
15005      windows->pan.id);
15006  (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
15007  if (IfMagickTrue(windows->info.mapped) )
15008    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15009  if (IfMagickFalse(windows->image.mapped) ||
15010      (windows->backdrop.id != (Window) NULL))
15011    (void) XMapWindow(display,windows->image.id);
15012  /*
15013    Set our progress monitor and warning handlers.
15014  */
15015  if (warning_handler == (WarningHandler) NULL)
15016    {
15017      warning_handler=resource_info->display_warnings ?
15018        SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
15019      warning_handler=resource_info->display_warnings ?
15020        SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
15021    }
15022  /*
15023    Initialize Image and Magnify X images.
15024  */
15025  windows->image.x=0;
15026  windows->image.y=0;
15027  windows->magnify.shape=MagickFalse;
15028  width=(unsigned int) display_image->columns;
15029  height=(unsigned int) display_image->rows;
15030  if ((display_image->columns != width) || (display_image->rows != height))
15031    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15032      display_image->filename);
15033  status=XMakeImage(display,resource_info,&windows->image,display_image,
15034    width,height,exception);
15035  if (IfMagickFalse(status) )
15036    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15037      display_image->filename);
15038  status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
15039    windows->magnify.width,windows->magnify.height,exception);
15040  if (IfMagickFalse(status))
15041    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15042      display_image->filename);
15043  if (IfMagickTrue(windows->magnify.mapped) )
15044    (void) XMapRaised(display,windows->magnify.id);
15045  if (IfMagickTrue(windows->pan.mapped) )
15046    (void) XMapRaised(display,windows->pan.id);
15047  windows->image.window_changes.width=(int) display_image->columns;
15048  windows->image.window_changes.height=(int) display_image->rows;
15049  (void) XConfigureImage(display,resource_info,windows,display_image,exception);
15050  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15051  (void) XSync(display,MagickFalse);
15052  /*
15053    Respond to events.
15054  */
15055  delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
15056  timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15057  update_time=0;
15058  if (IfMagickTrue(resource_info->update) )
15059    {
15060      MagickBooleanType
15061        status;
15062
15063      /*
15064        Determine when file data was last modified.
15065      */
15066      status=GetPathAttributes(display_image->filename,&attributes);
15067      if (IfMagickTrue(status) )
15068        update_time=attributes.st_mtime;
15069    }
15070  *state&=(~FormerImageState);
15071  *state&=(~MontageImageState);
15072  *state&=(~NextImageState);
15073  do
15074  {
15075    /*
15076      Handle a window event.
15077    */
15078    if (IfMagickTrue(windows->image.mapped) )
15079      if ((display_image->delay != 0) || (resource_info->update != 0))
15080        {
15081          if (timer < time((time_t *) NULL))
15082            {
15083              if (IfMagickFalse(resource_info->update) )
15084                *state|=NextImageState | ExitState;
15085              else
15086                {
15087                  MagickBooleanType
15088                    status;
15089
15090                  /*
15091                    Determine if image file was modified.
15092                  */
15093                  status=GetPathAttributes(display_image->filename,&attributes);
15094                  if (IfMagickTrue(status) )
15095                    if (update_time != attributes.st_mtime)
15096                      {
15097                        /*
15098                          Redisplay image.
15099                        */
15100                        (void) FormatLocaleString(
15101                          resource_info->image_info->filename,MagickPathExtent,
15102                          "%s:%s",display_image->magick,
15103                          display_image->filename);
15104                        nexus=ReadImage(resource_info->image_info,exception);
15105                        if (nexus != (Image *) NULL)
15106                          {
15107                            nexus=DestroyImage(nexus);
15108                            *state|=NextImageState | ExitState;
15109                          }
15110                      }
15111                  delay=display_image->delay/MagickMax(
15112                    display_image->ticks_per_second,1L);
15113                  timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15114                }
15115            }
15116          if (XEventsQueued(display,QueuedAfterFlush) == 0)
15117            {
15118              /*
15119                Do not block if delay > 0.
15120              */
15121              XDelay(display,SuspendTime << 2);
15122              continue;
15123            }
15124        }
15125    timestamp=time((time_t *) NULL);
15126    (void) XNextEvent(display,&event);
15127    if (IfMagickFalse(windows->image.stasis) )
15128      windows->image.stasis=IsMagickTrue((time((time_t *) NULL)-timestamp) > 0);
15129    if (IfMagickFalse(windows->magnify.stasis) )
15130      windows->magnify.stasis=IsMagickTrue((time((time_t *) NULL)-timestamp) > 0);
15131    if (event.xany.window == windows->command.id)
15132      {
15133        /*
15134          Select a command from the Command widget.
15135        */
15136        id=XCommandWidget(display,windows,CommandMenu,&event);
15137        if (id < 0)
15138          continue;
15139        (void) CopyMagickString(command,CommandMenu[id],MagickPathExtent);
15140        command_type=CommandMenus[id];
15141        if (id < MagickMenus)
15142          {
15143            /*
15144              Select a command from a pop-up menu.
15145            */
15146            entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15147              command);
15148            if (entry < 0)
15149              continue;
15150            (void) CopyMagickString(command,Menus[id][entry],MagickPathExtent);
15151            command_type=Commands[id][entry];
15152          }
15153        if (command_type != NullCommand)
15154          nexus=XMagickCommand(display,resource_info,windows,command_type,
15155            &display_image,exception);
15156        continue;
15157      }
15158    switch (event.type)
15159    {
15160      case ButtonPress:
15161      {
15162        if (IfMagickTrue(display_image->debug) )
15163          (void) LogMagickEvent(X11Event,GetMagickModule(),
15164            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15165            event.xbutton.button,event.xbutton.x,event.xbutton.y);
15166        if ((event.xbutton.button == Button3) &&
15167            (event.xbutton.state & Mod1Mask))
15168          {
15169            /*
15170              Convert Alt-Button3 to Button2.
15171            */
15172            event.xbutton.button=Button2;
15173            event.xbutton.state&=(~Mod1Mask);
15174          }
15175        if (event.xbutton.window == windows->backdrop.id)
15176          {
15177            (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15178              event.xbutton.time);
15179            break;
15180          }
15181        if (event.xbutton.window == windows->image.id)
15182          {
15183            switch (event.xbutton.button)
15184            {
15185              case Button1:
15186              {
15187                if (resource_info->immutable)
15188                  {
15189                    /*
15190                      Select a command from the Virtual menu.
15191                    */
15192                    entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15193                      command);
15194                    if (entry >= 0)
15195                      nexus=XMagickCommand(display,resource_info,windows,
15196                        VirtualCommands[entry],&display_image,exception);
15197                    break;
15198                  }
15199                /*
15200                  Map/unmap Command widget.
15201                */
15202                if (IfMagickTrue(windows->command.mapped) )
15203                  (void) XWithdrawWindow(display,windows->command.id,
15204                    windows->command.screen);
15205                else
15206                  {
15207                    (void) XCommandWidget(display,windows,CommandMenu,
15208                      (XEvent *) NULL);
15209                    (void) XMapRaised(display,windows->command.id);
15210                  }
15211                break;
15212              }
15213              case Button2:
15214              {
15215                /*
15216                  User pressed the image magnify button.
15217                */
15218                (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15219                  &display_image,exception);
15220                XMagnifyImage(display,windows,&event,exception);
15221                break;
15222              }
15223              case Button3:
15224              {
15225                if (resource_info->immutable)
15226                  {
15227                    /*
15228                      Select a command from the Virtual menu.
15229                    */
15230                    entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15231                      command);
15232                    if (entry >= 0)
15233                      nexus=XMagickCommand(display,resource_info,windows,
15234                        VirtualCommands[entry],&display_image,exception);
15235                    break;
15236                  }
15237                if (display_image->montage != (char *) NULL)
15238                  {
15239                    /*
15240                      Open or delete a tile from a visual image directory.
15241                    */
15242                    nexus=XTileImage(display,resource_info,windows,
15243                      display_image,&event,exception);
15244                    if (nexus != (Image *) NULL)
15245                      *state|=MontageImageState | NextImageState | ExitState;
15246                    vid_info.x=(short int) windows->image.x;
15247                    vid_info.y=(short int) windows->image.y;
15248                    break;
15249                  }
15250                /*
15251                  Select a command from the Short Cuts menu.
15252                */
15253                entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15254                  command);
15255                if (entry >= 0)
15256                  nexus=XMagickCommand(display,resource_info,windows,
15257                    ShortCutsCommands[entry],&display_image,exception);
15258                break;
15259              }
15260              case Button4:
15261              {
15262                /*
15263                  Wheel up.
15264                */
15265                XTranslateImage(display,windows,*image,XK_Up);
15266                break;
15267              }
15268              case Button5:
15269              {
15270                /*
15271                  Wheel down.
15272                */
15273                XTranslateImage(display,windows,*image,XK_Down);
15274                break;
15275              }
15276              default:
15277                break;
15278            }
15279            break;
15280          }
15281        if (event.xbutton.window == windows->magnify.id)
15282          {
15283            int
15284              factor;
15285
15286            static const char
15287              *MagnifyMenu[] =
15288              {
15289                "2",
15290                "4",
15291                "5",
15292                "6",
15293                "7",
15294                "8",
15295                "9",
15296                "3",
15297                (char *) NULL,
15298              };
15299
15300            static KeySym
15301              MagnifyCommands[] =
15302              {
15303                XK_2,
15304                XK_4,
15305                XK_5,
15306                XK_6,
15307                XK_7,
15308                XK_8,
15309                XK_9,
15310                XK_3
15311              };
15312
15313            /*
15314              Select a magnify factor from the pop-up menu.
15315            */
15316            factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15317            if (factor >= 0)
15318              XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor],
15319                exception);
15320            break;
15321          }
15322        if (event.xbutton.window == windows->pan.id)
15323          {
15324            switch (event.xbutton.button)
15325            {
15326              case Button4:
15327              {
15328                /*
15329                  Wheel up.
15330                */
15331                XTranslateImage(display,windows,*image,XK_Up);
15332                break;
15333              }
15334              case Button5:
15335              {
15336                /*
15337                  Wheel down.
15338                */
15339                XTranslateImage(display,windows,*image,XK_Down);
15340                break;
15341              }
15342              default:
15343              {
15344                XPanImage(display,windows,&event,exception);
15345                break;
15346              }
15347            }
15348            break;
15349          }
15350        delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15351          1L);
15352        timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15353        break;
15354      }
15355      case ButtonRelease:
15356      {
15357        if (IfMagickTrue(display_image->debug) )
15358          (void) LogMagickEvent(X11Event,GetMagickModule(),
15359            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15360            event.xbutton.button,event.xbutton.x,event.xbutton.y);
15361        break;
15362      }
15363      case ClientMessage:
15364      {
15365        if (IfMagickTrue(display_image->debug) )
15366          (void) LogMagickEvent(X11Event,GetMagickModule(),
15367            "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15368            event.xclient.message_type,event.xclient.format,(unsigned long)
15369            event.xclient.data.l[0]);
15370        if (event.xclient.message_type == windows->im_protocols)
15371          {
15372            if (*event.xclient.data.l == (long) windows->im_update_widget)
15373              {
15374                (void) CloneString(&windows->command.name,MagickTitle);
15375                windows->command.data=MagickMenus;
15376                (void) XCommandWidget(display,windows,CommandMenu,
15377                  (XEvent *) NULL);
15378                break;
15379              }
15380            if (*event.xclient.data.l == (long) windows->im_update_colormap)
15381              {
15382                /*
15383                  Update graphic context and window colormap.
15384                */
15385                for (i=0; i < (int) number_windows; i++)
15386                {
15387                  if (magick_windows[i]->id == windows->icon.id)
15388                    continue;
15389                  context_values.background=pixel->background_color.pixel;
15390                  context_values.foreground=pixel->foreground_color.pixel;
15391                  (void) XChangeGC(display,magick_windows[i]->annotate_context,
15392                    context_mask,&context_values);
15393                  (void) XChangeGC(display,magick_windows[i]->widget_context,
15394                    context_mask,&context_values);
15395                  context_values.background=pixel->foreground_color.pixel;
15396                  context_values.foreground=pixel->background_color.pixel;
15397                  context_values.plane_mask=context_values.background ^
15398                    context_values.foreground;
15399                  (void) XChangeGC(display,magick_windows[i]->highlight_context,
15400                    (size_t) (context_mask | GCPlaneMask),
15401                    &context_values);
15402                  magick_windows[i]->attributes.background_pixel=
15403                    pixel->background_color.pixel;
15404                  magick_windows[i]->attributes.border_pixel=
15405                    pixel->border_color.pixel;
15406                  magick_windows[i]->attributes.colormap=map_info->colormap;
15407                  (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15408                    (unsigned long) magick_windows[i]->mask,
15409                    &magick_windows[i]->attributes);
15410                }
15411                if (IfMagickTrue(windows->pan.mapped) )
15412                  {
15413                    (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15414                      windows->pan.pixmap);
15415                    (void) XClearWindow(display,windows->pan.id);
15416                    XDrawPanRectangle(display,windows);
15417                  }
15418                if (windows->backdrop.id != (Window) NULL)
15419                  (void) XInstallColormap(display,map_info->colormap);
15420                break;
15421              }
15422            if (*event.xclient.data.l == (long) windows->im_former_image)
15423              {
15424                *state|=FormerImageState | ExitState;
15425                break;
15426              }
15427            if (*event.xclient.data.l == (long) windows->im_next_image)
15428              {
15429                *state|=NextImageState | ExitState;
15430                break;
15431              }
15432            if (*event.xclient.data.l == (long) windows->im_retain_colors)
15433              {
15434                *state|=RetainColorsState;
15435                break;
15436              }
15437            if (*event.xclient.data.l == (long) windows->im_exit)
15438              {
15439                *state|=ExitState;
15440                break;
15441              }
15442            break;
15443          }
15444        if (event.xclient.message_type == windows->dnd_protocols)
15445          {
15446            Atom
15447              selection,
15448              type;
15449
15450            int
15451              format,
15452              status;
15453
15454            unsigned char
15455              *data;
15456
15457            unsigned long
15458              after,
15459              length;
15460
15461            /*
15462              Display image named by the Drag-and-Drop selection.
15463            */
15464            if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15465              break;
15466            selection=XInternAtom(display,"DndSelection",MagickFalse);
15467            status=XGetWindowProperty(display,root_window,selection,0L,(long)
15468              MagickPathExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15469              &length,&after,&data);
15470            if ((status != Success) || (length == 0))
15471              break;
15472            if (*event.xclient.data.l == 2)
15473              {
15474                /*
15475                  Offix DND.
15476                */
15477                (void) CopyMagickString(resource_info->image_info->filename,
15478                  (char *) data,MagickPathExtent);
15479              }
15480            else
15481              {
15482                /*
15483                  XDND.
15484                */
15485                if (strncmp((char *) data, "file:", 5) != 0)
15486                  {
15487                    (void) XFree((void *) data);
15488                    break;
15489                  }
15490                (void) CopyMagickString(resource_info->image_info->filename,
15491                  ((char *) data)+5,MagickPathExtent);
15492              }
15493            nexus=ReadImage(resource_info->image_info,exception);
15494            CatchException(exception);
15495            if (nexus != (Image *) NULL)
15496              *state|=NextImageState | ExitState;
15497            (void) XFree((void *) data);
15498            break;
15499          }
15500        /*
15501          If client window delete message, exit.
15502        */
15503        if (event.xclient.message_type != windows->wm_protocols)
15504          break;
15505        if (*event.xclient.data.l != (long) windows->wm_delete_window)
15506          break;
15507        (void) XWithdrawWindow(display,event.xclient.window,
15508          visual_info->screen);
15509        if (event.xclient.window == windows->image.id)
15510          {
15511            *state|=ExitState;
15512            break;
15513          }
15514        if (event.xclient.window == windows->pan.id)
15515          {
15516            /*
15517              Restore original image size when pan window is deleted.
15518            */
15519            windows->image.window_changes.width=windows->image.ximage->width;
15520            windows->image.window_changes.height=windows->image.ximage->height;
15521            (void) XConfigureImage(display,resource_info,windows,
15522              display_image,exception);
15523          }
15524        break;
15525      }
15526      case ConfigureNotify:
15527      {
15528        if (IfMagickTrue(display_image->debug) )
15529          (void) LogMagickEvent(X11Event,GetMagickModule(),
15530            "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15531            event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15532            event.xconfigure.y,event.xconfigure.send_event);
15533        if (event.xconfigure.window == windows->image.id)
15534          {
15535            /*
15536              Image window has a new configuration.
15537            */
15538            if (event.xconfigure.send_event != 0)
15539              {
15540                XWindowChanges
15541                  window_changes;
15542
15543                /*
15544                  Position the transient windows relative of the Image window.
15545                */
15546                if (windows->command.geometry == (char *) NULL)
15547                  if (IfMagickFalse(windows->command.mapped) )
15548                    {
15549                      windows->command.x=event.xconfigure.x-
15550                        windows->command.width-25;
15551                      windows->command.y=event.xconfigure.y;
15552                      XConstrainWindowPosition(display,&windows->command);
15553                      window_changes.x=windows->command.x;
15554                      window_changes.y=windows->command.y;
15555                      (void) XReconfigureWMWindow(display,windows->command.id,
15556                        windows->command.screen,(unsigned int) (CWX | CWY),
15557                        &window_changes);
15558                    }
15559                if (windows->widget.geometry == (char *) NULL)
15560                  if (IfMagickFalse(windows->widget.mapped) )
15561                    {
15562                      windows->widget.x=event.xconfigure.x+
15563                        event.xconfigure.width/10;
15564                      windows->widget.y=event.xconfigure.y+
15565                        event.xconfigure.height/10;
15566                      XConstrainWindowPosition(display,&windows->widget);
15567                      window_changes.x=windows->widget.x;
15568                      window_changes.y=windows->widget.y;
15569                      (void) XReconfigureWMWindow(display,windows->widget.id,
15570                        windows->widget.screen,(unsigned int) (CWX | CWY),
15571                        &window_changes);
15572                    }
15573                if (windows->magnify.geometry == (char *) NULL)
15574                  if (IfMagickFalse(windows->magnify.mapped) )
15575                    {
15576                      windows->magnify.x=event.xconfigure.x+
15577                        event.xconfigure.width+25;
15578                      windows->magnify.y=event.xconfigure.y;
15579                      XConstrainWindowPosition(display,&windows->magnify);
15580                      window_changes.x=windows->magnify.x;
15581                      window_changes.y=windows->magnify.y;
15582                      (void) XReconfigureWMWindow(display,windows->magnify.id,
15583                        windows->magnify.screen,(unsigned int) (CWX | CWY),
15584                        &window_changes);
15585                    }
15586                if (windows->pan.geometry == (char *) NULL)
15587                  if (IfMagickFalse(windows->pan.mapped) )
15588                    {
15589                      windows->pan.x=event.xconfigure.x+
15590                        event.xconfigure.width+25;
15591                      windows->pan.y=event.xconfigure.y+
15592                        windows->magnify.height+50;
15593                      XConstrainWindowPosition(display,&windows->pan);
15594                      window_changes.x=windows->pan.x;
15595                      window_changes.y=windows->pan.y;
15596                      (void) XReconfigureWMWindow(display,windows->pan.id,
15597                        windows->pan.screen,(unsigned int) (CWX | CWY),
15598                        &window_changes);
15599                    }
15600              }
15601            if ((event.xconfigure.width == (int) windows->image.width) &&
15602                (event.xconfigure.height == (int) windows->image.height))
15603              break;
15604            windows->image.width=(unsigned int) event.xconfigure.width;
15605            windows->image.height=(unsigned int) event.xconfigure.height;
15606            windows->image.x=0;
15607            windows->image.y=0;
15608            if (display_image->montage != (char *) NULL)
15609              {
15610                windows->image.x=vid_info.x;
15611                windows->image.y=vid_info.y;
15612              }
15613            if (IfMagickTrue(windows->image.mapped) &&
15614                IfMagickTrue(windows->image.stasis) )
15615              {
15616                /*
15617                  Update image window configuration.
15618                */
15619                windows->image.window_changes.width=event.xconfigure.width;
15620                windows->image.window_changes.height=event.xconfigure.height;
15621                (void) XConfigureImage(display,resource_info,windows,
15622                  display_image,exception);
15623              }
15624            /*
15625              Update pan window configuration.
15626            */
15627            if ((event.xconfigure.width < windows->image.ximage->width) ||
15628                (event.xconfigure.height < windows->image.ximage->height))
15629              {
15630                (void) XMapRaised(display,windows->pan.id);
15631                XDrawPanRectangle(display,windows);
15632              }
15633            else
15634              if (IfMagickTrue(windows->pan.mapped) )
15635                (void) XWithdrawWindow(display,windows->pan.id,
15636                  windows->pan.screen);
15637            break;
15638          }
15639        if (event.xconfigure.window == windows->magnify.id)
15640          {
15641            unsigned int
15642              magnify;
15643
15644            /*
15645              Magnify window has a new configuration.
15646            */
15647            windows->magnify.width=(unsigned int) event.xconfigure.width;
15648            windows->magnify.height=(unsigned int) event.xconfigure.height;
15649            if (IfMagickFalse(windows->magnify.mapped) )
15650              break;
15651            magnify=1;
15652            while ((int) magnify <= event.xconfigure.width)
15653              magnify<<=1;
15654            while ((int) magnify <= event.xconfigure.height)
15655              magnify<<=1;
15656            magnify>>=1;
15657            if (((int) magnify != event.xconfigure.width) ||
15658                ((int) magnify != event.xconfigure.height))
15659              {
15660                window_changes.width=(int) magnify;
15661                window_changes.height=(int) magnify;
15662                (void) XReconfigureWMWindow(display,windows->magnify.id,
15663                  windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15664                  &window_changes);
15665                break;
15666              }
15667            if (IfMagickTrue(windows->magnify.mapped) &&
15668                IfMagickTrue(windows->magnify.stasis) )
15669              {
15670                status=XMakeImage(display,resource_info,&windows->magnify,
15671                  display_image,windows->magnify.width,windows->magnify.height,
15672                  exception);
15673                XMakeMagnifyImage(display,windows,exception);
15674              }
15675            break;
15676          }
15677        if (IfMagickTrue(windows->magnify.mapped) &&
15678            (event.xconfigure.window == windows->pan.id))
15679          {
15680            /*
15681              Pan icon window has a new configuration.
15682            */
15683            if (event.xconfigure.send_event != 0)
15684              {
15685                windows->pan.x=event.xconfigure.x;
15686                windows->pan.y=event.xconfigure.y;
15687              }
15688            windows->pan.width=(unsigned int) event.xconfigure.width;
15689            windows->pan.height=(unsigned int) event.xconfigure.height;
15690            break;
15691          }
15692        if (event.xconfigure.window == windows->icon.id)
15693          {
15694            /*
15695              Icon window has a new configuration.
15696            */
15697            windows->icon.width=(unsigned int) event.xconfigure.width;
15698            windows->icon.height=(unsigned int) event.xconfigure.height;
15699            break;
15700          }
15701        break;
15702      }
15703      case DestroyNotify:
15704      {
15705        /*
15706          Group leader has exited.
15707        */
15708        if (IfMagickTrue(display_image->debug) )
15709          (void) LogMagickEvent(X11Event,GetMagickModule(),
15710            "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15711        if (event.xdestroywindow.window == windows->group_leader.id)
15712          {
15713            *state|=ExitState;
15714            break;
15715          }
15716        break;
15717      }
15718      case EnterNotify:
15719      {
15720        /*
15721          Selectively install colormap.
15722        */
15723        if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15724          if (event.xcrossing.mode != NotifyUngrab)
15725            XInstallColormap(display,map_info->colormap);
15726        break;
15727      }
15728      case Expose:
15729      {
15730        if (IfMagickTrue(display_image->debug) )
15731          (void) LogMagickEvent(X11Event,GetMagickModule(),
15732            "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15733            event.xexpose.width,event.xexpose.height,event.xexpose.x,
15734            event.xexpose.y);
15735        /*
15736          Refresh windows that are now exposed.
15737        */
15738        if ((event.xexpose.window == windows->image.id) &&
15739            IfMagickTrue(windows->image.mapped) )
15740          {
15741            XRefreshWindow(display,&windows->image,&event);
15742            delay=display_image->delay/MagickMax(
15743              display_image->ticks_per_second,1L);
15744            timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15745            break;
15746          }
15747        if ((event.xexpose.window == windows->magnify.id) &&
15748            IfMagickTrue(windows->magnify.mapped))
15749          {
15750            XMakeMagnifyImage(display,windows,exception);
15751            break;
15752          }
15753        if (event.xexpose.window == windows->pan.id)
15754          {
15755            XDrawPanRectangle(display,windows);
15756            break;
15757          }
15758        if (event.xexpose.window == windows->icon.id)
15759          {
15760            XRefreshWindow(display,&windows->icon,&event);
15761            break;
15762          }
15763        break;
15764      }
15765      case KeyPress:
15766      {
15767        int
15768          length;
15769
15770        /*
15771          Respond to a user key press.
15772        */
15773        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15774          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15775        *(command+length)='\0';
15776        if (IfMagickTrue(display_image->debug) )
15777          (void) LogMagickEvent(X11Event,GetMagickModule(),
15778            "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15779            key_symbol,command);
15780        if (event.xkey.window == windows->image.id)
15781          {
15782            command_type=XImageWindowCommand(display,resource_info,windows,
15783              event.xkey.state,key_symbol,&display_image,exception);
15784            if (command_type != NullCommand)
15785              nexus=XMagickCommand(display,resource_info,windows,command_type,
15786                &display_image,exception);
15787          }
15788        if (event.xkey.window == windows->magnify.id)
15789          XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol,
15790            exception);
15791        if (event.xkey.window == windows->pan.id)
15792          {
15793            if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15794              (void) XWithdrawWindow(display,windows->pan.id,
15795                windows->pan.screen);
15796            else
15797              if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15798                XTextViewWidget(display,resource_info,windows,MagickFalse,
15799                  "Help Viewer - Image Pan",ImagePanHelp);
15800              else
15801                XTranslateImage(display,windows,*image,key_symbol);
15802          }
15803        delay=display_image->delay/MagickMax(
15804          display_image->ticks_per_second,1L);
15805        timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15806        break;
15807      }
15808      case KeyRelease:
15809      {
15810        /*
15811          Respond to a user key release.
15812        */
15813        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15814          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15815        if (IfMagickTrue(display_image->debug) )
15816          (void) LogMagickEvent(X11Event,GetMagickModule(),
15817            "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15818        break;
15819      }
15820      case LeaveNotify:
15821      {
15822        /*
15823          Selectively uninstall colormap.
15824        */
15825        if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15826          if (event.xcrossing.mode != NotifyUngrab)
15827            XUninstallColormap(display,map_info->colormap);
15828        break;
15829      }
15830      case MapNotify:
15831      {
15832        if (IfMagickTrue(display_image->debug) )
15833          (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15834            event.xmap.window);
15835        if (event.xmap.window == windows->backdrop.id)
15836          {
15837            (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15838              CurrentTime);
15839            windows->backdrop.mapped=MagickTrue;
15840            break;
15841          }
15842        if (event.xmap.window == windows->image.id)
15843          {
15844            if (windows->backdrop.id != (Window) NULL)
15845              (void) XInstallColormap(display,map_info->colormap);
15846            if (LocaleCompare(display_image->magick,"LOGO") == 0)
15847              {
15848                if (LocaleCompare(display_image->filename,"LOGO") == 0)
15849                  nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15850              }
15851            if (((int) windows->image.width < windows->image.ximage->width) ||
15852                ((int) windows->image.height < windows->image.ximage->height))
15853              (void) XMapRaised(display,windows->pan.id);
15854            windows->image.mapped=MagickTrue;
15855            break;
15856          }
15857        if (event.xmap.window == windows->magnify.id)
15858          {
15859            XMakeMagnifyImage(display,windows,exception);
15860            windows->magnify.mapped=MagickTrue;
15861            (void) XWithdrawWindow(display,windows->info.id,
15862              windows->info.screen);
15863            break;
15864          }
15865        if (event.xmap.window == windows->pan.id)
15866          {
15867            XMakePanImage(display,resource_info,windows,display_image,
15868              exception);
15869            windows->pan.mapped=MagickTrue;
15870            break;
15871          }
15872        if (event.xmap.window == windows->info.id)
15873          {
15874            windows->info.mapped=MagickTrue;
15875            break;
15876          }
15877        if (event.xmap.window == windows->icon.id)
15878          {
15879            MagickBooleanType
15880              taint;
15881
15882            /*
15883              Create an icon image.
15884            */
15885            taint=display_image->taint;
15886            XMakeStandardColormap(display,icon_visual,icon_resources,
15887              display_image,icon_map,icon_pixel,exception);
15888            (void) XMakeImage(display,icon_resources,&windows->icon,
15889              display_image,windows->icon.width,windows->icon.height,
15890              exception);
15891            display_image->taint=taint;
15892            (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15893              windows->icon.pixmap);
15894            (void) XClearWindow(display,windows->icon.id);
15895            (void) XWithdrawWindow(display,windows->info.id,
15896              windows->info.screen);
15897            windows->icon.mapped=MagickTrue;
15898            break;
15899          }
15900        if (event.xmap.window == windows->command.id)
15901          {
15902            windows->command.mapped=MagickTrue;
15903            break;
15904          }
15905        if (event.xmap.window == windows->popup.id)
15906          {
15907            windows->popup.mapped=MagickTrue;
15908            break;
15909          }
15910        if (event.xmap.window == windows->widget.id)
15911          {
15912            windows->widget.mapped=MagickTrue;
15913            break;
15914          }
15915        break;
15916      }
15917      case MappingNotify:
15918      {
15919        (void) XRefreshKeyboardMapping(&event.xmapping);
15920        break;
15921      }
15922      case NoExpose:
15923        break;
15924      case PropertyNotify:
15925      {
15926        Atom
15927          type;
15928
15929        int
15930          format,
15931          status;
15932
15933        unsigned char
15934          *data;
15935
15936        unsigned long
15937          after,
15938          length;
15939
15940        if (IfMagickTrue(display_image->debug) )
15941          (void) LogMagickEvent(X11Event,GetMagickModule(),
15942            "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15943            event.xproperty.atom,event.xproperty.state);
15944        if (event.xproperty.atom != windows->im_remote_command)
15945          break;
15946        /*
15947          Display image named by the remote command protocol.
15948        */
15949        status=XGetWindowProperty(display,event.xproperty.window,
15950          event.xproperty.atom,0L,(long) MagickPathExtent,MagickFalse,(Atom)
15951          AnyPropertyType,&type,&format,&length,&after,&data);
15952        if ((status != Success) || (length == 0))
15953          break;
15954        if (LocaleCompare((char *) data,"-quit") == 0)
15955          {
15956            XClientMessage(display,windows->image.id,windows->im_protocols,
15957              windows->im_exit,CurrentTime);
15958            (void) XFree((void *) data);
15959            break;
15960          }
15961        (void) CopyMagickString(resource_info->image_info->filename,
15962          (char *) data,MagickPathExtent);
15963        (void) XFree((void *) data);
15964        nexus=ReadImage(resource_info->image_info,exception);
15965        CatchException(exception);
15966        if (nexus != (Image *) NULL)
15967          *state|=NextImageState | ExitState;
15968        break;
15969      }
15970      case ReparentNotify:
15971      {
15972        if (IfMagickTrue(display_image->debug) )
15973          (void) LogMagickEvent(X11Event,GetMagickModule(),
15974            "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15975            event.xreparent.window);
15976        break;
15977      }
15978      case UnmapNotify:
15979      {
15980        if (IfMagickTrue(display_image->debug) )
15981          (void) LogMagickEvent(X11Event,GetMagickModule(),
15982            "Unmap Notify: 0x%lx",event.xunmap.window);
15983        if (event.xunmap.window == windows->backdrop.id)
15984          {
15985            windows->backdrop.mapped=MagickFalse;
15986            break;
15987          }
15988        if (event.xunmap.window == windows->image.id)
15989          {
15990            windows->image.mapped=MagickFalse;
15991            break;
15992          }
15993        if (event.xunmap.window == windows->magnify.id)
15994          {
15995            windows->magnify.mapped=MagickFalse;
15996            break;
15997          }
15998        if (event.xunmap.window == windows->pan.id)
15999          {
16000            windows->pan.mapped=MagickFalse;
16001            break;
16002          }
16003        if (event.xunmap.window == windows->info.id)
16004          {
16005            windows->info.mapped=MagickFalse;
16006            break;
16007          }
16008        if (event.xunmap.window == windows->icon.id)
16009          {
16010            if (map_info->colormap == icon_map->colormap)
16011              XConfigureImageColormap(display,resource_info,windows,
16012                display_image,exception);
16013            (void) XFreeStandardColormap(display,icon_visual,icon_map,
16014              icon_pixel);
16015            windows->icon.mapped=MagickFalse;
16016            break;
16017          }
16018        if (event.xunmap.window == windows->command.id)
16019          {
16020            windows->command.mapped=MagickFalse;
16021            break;
16022          }
16023        if (event.xunmap.window == windows->popup.id)
16024          {
16025            if (windows->backdrop.id != (Window) NULL)
16026              (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16027                CurrentTime);
16028            windows->popup.mapped=MagickFalse;
16029            break;
16030          }
16031        if (event.xunmap.window == windows->widget.id)
16032          {
16033            if (windows->backdrop.id != (Window) NULL)
16034              (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16035                CurrentTime);
16036            windows->widget.mapped=MagickFalse;
16037            break;
16038          }
16039        break;
16040      }
16041      default:
16042      {
16043        if (IfMagickTrue(display_image->debug) )
16044          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
16045            event.type);
16046        break;
16047      }
16048    }
16049  } while (!(*state & ExitState));
16050  if ((*state & ExitState) == 0)
16051    (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
16052      &display_image,exception);
16053  else
16054    if (IfMagickTrue(resource_info->confirm_edit) )
16055      {
16056        /*
16057          Query user if image has changed.
16058        */
16059        if (IfMagickFalse(resource_info->immutable) &&
16060            IfMagickTrue(display_image->taint))
16061          {
16062            int
16063              status;
16064
16065            status=XConfirmWidget(display,windows,"Your image changed.",
16066              "Do you want to save it");
16067            if (status == 0)
16068              *state&=(~ExitState);
16069            else
16070              if (status > 0)
16071                (void) XMagickCommand(display,resource_info,windows,SaveCommand,
16072                  &display_image,exception);
16073          }
16074      }
16075  if ((windows->visual_info->klass == GrayScale) ||
16076      (windows->visual_info->klass == PseudoColor) ||
16077      (windows->visual_info->klass == DirectColor))
16078    {
16079      /*
16080        Withdraw pan and Magnify window.
16081      */
16082      if (IfMagickTrue(windows->info.mapped) )
16083        (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16084      if (IfMagickTrue(windows->magnify.mapped) )
16085        (void) XWithdrawWindow(display,windows->magnify.id,
16086          windows->magnify.screen);
16087      if (IfMagickTrue(windows->command.mapped) )
16088        (void) XWithdrawWindow(display,windows->command.id,
16089          windows->command.screen);
16090    }
16091  if (IfMagickTrue(windows->pan.mapped) )
16092    (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16093  if (IfMagickFalse(resource_info->backdrop) )
16094    if (windows->backdrop.mapped)
16095      {
16096        (void) XWithdrawWindow(display,windows->backdrop.id,
16097          windows->backdrop.screen);
16098        (void) XDestroyWindow(display,windows->backdrop.id);
16099        windows->backdrop.id=(Window) NULL;
16100        (void) XWithdrawWindow(display,windows->image.id,
16101          windows->image.screen);
16102        (void) XDestroyWindow(display,windows->image.id);
16103        windows->image.id=(Window) NULL;
16104      }
16105  XSetCursorState(display,windows,MagickTrue);
16106  XCheckRefreshWindows(display,windows);
16107  if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16108    *state&=(~ExitState);
16109  if (*state & ExitState)
16110    {
16111      /*
16112        Free Standard Colormap.
16113      */
16114      (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16115      if (resource_info->map_type == (char *) NULL)
16116        (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16117      /*
16118        Free X resources.
16119      */
16120      if (resource_info->copy_image != (Image *) NULL)
16121        {
16122          resource_info->copy_image=DestroyImage(resource_info->copy_image);
16123          resource_info->copy_image=NewImageList();
16124        }
16125      DestroyXResources();
16126    }
16127  (void) XSync(display,MagickFalse);
16128  /*
16129    Restore our progress monitor and warning handlers.
16130  */
16131  (void) SetErrorHandler(warning_handler);
16132  (void) SetWarningHandler(warning_handler);
16133  /*
16134    Change to home directory.
16135  */
16136  directory=getcwd(working_directory,MagickPathExtent);
16137  (void) directory;
16138  {
16139    int
16140      status;
16141
16142    if (*resource_info->home_directory == '\0')
16143      (void) CopyMagickString(resource_info->home_directory,".",MagickPathExtent);
16144    status=chdir(resource_info->home_directory);
16145    if (status == -1)
16146      (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
16147        "UnableToOpenFile","%s",resource_info->home_directory);
16148  }
16149  *image=display_image;
16150  return(nexus);
16151}
16152#else
16153
16154/*
16155%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16156%                                                                             %
16157%                                                                             %
16158%                                                                             %
16159+   D i s p l a y I m a g e s                                                 %
16160%                                                                             %
16161%                                                                             %
16162%                                                                             %
16163%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16164%
16165%  DisplayImages() displays an image sequence to any X window screen.  It
16166%  returns a value other than 0 if successful.  Check the exception member
16167%  of image to determine the reason for any failure.
16168%
16169%  The format of the DisplayImages method is:
16170%
16171%      MagickBooleanType DisplayImages(const ImageInfo *image_info,
16172%        Image *images,ExceptionInfo *exception)
16173%
16174%  A description of each parameter follows:
16175%
16176%    o image_info: the image info.
16177%
16178%    o image: the image.
16179%
16180%    o exception: return any errors or warnings in this structure.
16181%
16182*/
16183MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16184  Image *image,ExceptionInfo *exception)
16185{
16186  assert(image_info != (const ImageInfo *) NULL);
16187  assert(image_info->signature == MagickCoreSignature);
16188  assert(image != (Image *) NULL);
16189  assert(image->signature == MagickCoreSignature);
16190  (void) image_info;
16191  if (IfMagickTrue(image->debug) )
16192    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16193  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16194    "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image->filename);
16195  return(MagickFalse);
16196}
16197
16198/*
16199%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16200%                                                                             %
16201%                                                                             %
16202%                                                                             %
16203+   R e m o t e D i s p l a y C o m m a n d                                   %
16204%                                                                             %
16205%                                                                             %
16206%                                                                             %
16207%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16208%
16209%  RemoteDisplayCommand() encourages a remote display program to display the
16210%  specified image filename.
16211%
16212%  The format of the RemoteDisplayCommand method is:
16213%
16214%      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16215%        const char *window,const char *filename,ExceptionInfo *exception)
16216%
16217%  A description of each parameter follows:
16218%
16219%    o image_info: the image info.
16220%
16221%    o window: Specifies the name or id of an X window.
16222%
16223%    o filename: the name of the image filename to display.
16224%
16225%    o exception: return any errors or warnings in this structure.
16226%
16227*/
16228MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16229  const char *window,const char *filename,ExceptionInfo *exception)
16230{
16231  assert(image_info != (const ImageInfo *) NULL);
16232  assert(image_info->signature == MagickCoreSignature);
16233  assert(filename != (char *) NULL);
16234  (void) window;
16235  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16236  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16237    "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image_info->filename);
16238  return(MagickFalse);
16239}
16240#endif
16241