display.c revision 803640d20a6a664315eddfff6f8531d0c5e0871d
1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3%                                                                             %
4%                                                                             %
5%                                                                             %
6%               DDDD   IIIII  SSSSS  PPPP   L       AAA   Y   Y               %
7%               D   D    I    SS     P   P  L      A   A   Y Y                %
8%               D   D    I     SSS   PPPP   L      AAAAA    Y                 %
9%               D   D    I       SS  P      L      A   A    Y                 %
10%               DDDD   IIIII  SSSSS  P      LLLLL  A   A    Y                 %
11%                                                                             %
12%                                                                             %
13%        MagickCore Methods to Interactively Display and Edit an Image        %
14%                                                                             %
15%                             Software Design                                 %
16%                               John Cristy                                   %
17%                                July 1992                                    %
18%                                                                             %
19%                                                                             %
20%  Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization      %
21%  dedicated to making software imaging solutions freely available.           %
22%                                                                             %
23%  You may not use this file except in compliance with the License.  You may  %
24%  obtain a copy of the License at                                            %
25%                                                                             %
26%    http://www.imagemagick.org/script/license.php                            %
27%                                                                             %
28%  Unless required by applicable law or agreed to in writing, software        %
29%  distributed under the License is distributed on an "AS IS" BASIS,          %
30%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31%  See the License for the specific language governing permissions and        %
32%  limitations under the License.                                             %
33%                                                                             %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37*/
38
39/*
40  Include declarations.
41*/
42#include "MagickCore/studio.h"
43#include "MagickCore/artifact.h"
44#include "MagickCore/blob.h"
45#include "MagickCore/cache.h"
46#include "MagickCore/cache-private.h"
47#include "MagickCore/client.h"
48#include "MagickCore/color.h"
49#include "MagickCore/colorspace.h"
50#include "MagickCore/composite.h"
51#include "MagickCore/constitute.h"
52#include "MagickCore/decorate.h"
53#include "MagickCore/delegate.h"
54#include "MagickCore/display.h"
55#include "MagickCore/display-private.h"
56#include "MagickCore/draw.h"
57#include "MagickCore/effect.h"
58#include "MagickCore/enhance.h"
59#include "MagickCore/exception.h"
60#include "MagickCore/exception-private.h"
61#include "MagickCore/fx.h"
62#include "MagickCore/geometry.h"
63#include "MagickCore/image.h"
64#include "MagickCore/image-private.h"
65#include "MagickCore/list.h"
66#include "MagickCore/log.h"
67#include "MagickCore/magick.h"
68#include "MagickCore/memory_.h"
69#include "MagickCore/monitor.h"
70#include "MagickCore/monitor-private.h"
71#include "MagickCore/montage.h"
72#include "MagickCore/option.h"
73#include "MagickCore/paint.h"
74#include "MagickCore/pixel.h"
75#include "MagickCore/pixel-accessor.h"
76#include "MagickCore/PreRvIcccm.h"
77#include "MagickCore/property.h"
78#include "MagickCore/quantum.h"
79#include "MagickCore/quantum-private.h"
80#include "MagickCore/resize.h"
81#include "MagickCore/resource_.h"
82#include "MagickCore/shear.h"
83#include "MagickCore/segment.h"
84#include "MagickCore/string_.h"
85#include "MagickCore/string-private.h"
86#include "MagickCore/transform.h"
87#include "MagickCore/threshold.h"
88#include "MagickCore/utility.h"
89#include "MagickCore/utility-private.h"
90#include "MagickCore/version.h"
91#include "MagickCore/widget.h"
92#include "MagickCore/widget-private.h"
93#include "MagickCore/xwindow.h"
94#include "MagickCore/xwindow-private.h"
95
96#if defined(MAGICKCORE_X11_DELEGATE)
97/*
98  Define declarations.
99*/
100#define MaxColors  MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
101
102/*
103  Constant declarations.
104*/
105static const unsigned char
106  HighlightBitmap[8] =
107  {
108    0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
109  },
110  OpaqueBitmap[8] =
111  {
112    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
113  },
114  ShadowBitmap[8] =
115  {
116    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
117  };
118
119static const char
120  *PageSizes[] =
121  {
122    "Letter",
123    "Tabloid",
124    "Ledger",
125    "Legal",
126    "Statement",
127    "Executive",
128    "A3",
129    "A4",
130    "A5",
131    "B4",
132    "B5",
133    "Folio",
134    "Quarto",
135    "10x14",
136    (char *) NULL
137  };
138
139/*
140  Help widget declarations.
141*/
142static const char
143  *ImageAnnotateHelp[] =
144  {
145    "In annotate mode, the Command widget has these options:",
146    "",
147    "    Font Name",
148    "      fixed",
149    "      variable",
150    "      5x8",
151    "      6x10",
152    "      7x13bold",
153    "      8x13bold",
154    "      9x15bold",
155    "      10x20",
156    "      12x24",
157    "      Browser...",
158    "    Font Color",
159    "      black",
160    "      blue",
161    "      cyan",
162    "      green",
163    "      gray",
164    "      red",
165    "      magenta",
166    "      yellow",
167    "      white",
168    "      transparent",
169    "      Browser...",
170    "    Font Color",
171    "      black",
172    "      blue",
173    "      cyan",
174    "      green",
175    "      gray",
176    "      red",
177    "      magenta",
178    "      yellow",
179    "      white",
180    "      transparent",
181    "      Browser...",
182    "    Rotate Text",
183    "      -90",
184    "      -45",
185    "      -30",
186    "      0",
187    "      30",
188    "      45",
189    "      90",
190    "      180",
191    "      Dialog...",
192    "    Help",
193    "    Dismiss",
194    "",
195    "Choose a font name from the Font Name sub-menu.  Additional",
196    "font names can be specified with the font browser.  You can",
197    "change the menu names by setting the X resources font1",
198    "through font9.",
199    "",
200    "Choose a font color from the Font Color sub-menu.",
201    "Additional font colors can be specified with the color",
202    "browser.  You can change the menu colors by setting the X",
203    "resources pen1 through pen9.",
204    "",
205    "If you select the color browser and press Grab, you can",
206    "choose the font color by moving the pointer to the desired",
207    "color on the screen and press any button.",
208    "",
209    "If you choose to rotate the text, choose Rotate Text from the",
210    "menu and select an angle.  Typically you will only want to",
211    "rotate one line of text at a time.  Depending on the angle you",
212    "choose, subsequent lines may end up overwriting each other.",
213    "",
214    "Choosing a font and its color is optional.  The default font",
215    "is fixed and the default color is black.  However, you must",
216    "choose a location to begin entering text and press button 1.",
217    "An underscore character will appear at the location of the",
218    "pointer.  The cursor changes to a pencil to indicate you are",
219    "in text mode.  To exit immediately, press Dismiss.",
220    "",
221    "In text mode, any key presses will display the character at",
222    "the location of the underscore and advance the underscore",
223    "cursor.  Enter your text and once completed press Apply to",
224    "finish your image annotation.  To correct errors press BACK",
225    "SPACE.  To delete an entire line of text, press DELETE.  Any",
226    "text that exceeds the boundaries of the image window is",
227    "automagically continued onto the next line.",
228    "",
229    "The actual color you request for the font is saved in the",
230    "image.  However, the color that appears in your image window",
231    "may be different.  For example, on a monochrome screen the",
232    "text will appear black or white even if you choose the color",
233    "red as the font color.  However, the image saved to a file",
234    "with -write is written with red lettering.  To assure the",
235    "correct color text in the final image, any PseudoClass image",
236    "is promoted to DirectClass (see miff(5)).  To force a",
237    "PseudoClass image to remain PseudoClass, use -colors.",
238    (char *) NULL,
239  },
240  *ImageChopHelp[] =
241  {
242    "In chop mode, the Command widget has these options:",
243    "",
244    "    Direction",
245    "      horizontal",
246    "      vertical",
247    "    Help",
248    "    Dismiss",
249    "",
250    "If the you choose the horizontal direction (this the",
251    "default), the area of the image between the two horizontal",
252    "endpoints of the chop line is removed.  Otherwise, the area",
253    "of the image between the two vertical endpoints of the chop",
254    "line is removed.",
255    "",
256    "Select a location within the image window to begin your chop,",
257    "press and hold any button.  Next, move the pointer to",
258    "another location in the image.  As you move a line will",
259    "connect the initial location and the pointer.  When you",
260    "release the button, the area within the image to chop is",
261    "determined by which direction you choose from the Command",
262    "widget.",
263    "",
264    "To cancel the image chopping, move the pointer back to the",
265    "starting point of the line and release the button.",
266    (char *) NULL,
267  },
268  *ImageColorEditHelp[] =
269  {
270    "In color edit mode, the Command widget has these options:",
271    "",
272    "    Method",
273    "      point",
274    "      replace",
275    "      floodfill",
276    "      filltoborder",
277    "      reset",
278    "    Pixel Color",
279    "      black",
280    "      blue",
281    "      cyan",
282    "      green",
283    "      gray",
284    "      red",
285    "      magenta",
286    "      yellow",
287    "      white",
288    "      Browser...",
289    "    Border Color",
290    "      black",
291    "      blue",
292    "      cyan",
293    "      green",
294    "      gray",
295    "      red",
296    "      magenta",
297    "      yellow",
298    "      white",
299    "      Browser...",
300    "    Fuzz",
301    "      0%",
302    "      2%",
303    "      5%",
304    "      10%",
305    "      15%",
306    "      Dialog...",
307    "    Undo",
308    "    Help",
309    "    Dismiss",
310    "",
311    "Choose a color editing method from the Method sub-menu",
312    "of the Command widget.  The point method recolors any pixel",
313    "selected with the pointer until the button is released.  The",
314    "replace method recolors any pixel that matches the color of",
315    "the pixel you select with a button press.  Floodfill recolors",
316    "any pixel that matches the color of the pixel you select with",
317    "a button press and is a neighbor.  Whereas filltoborder recolors",
318    "any neighbor pixel that is not the border color.  Finally reset",
319    "changes the entire image to the designated color.",
320    "",
321    "Next, choose a pixel color from the Pixel Color sub-menu.",
322    "Additional pixel colors can be specified with the color",
323    "browser.  You can change the menu colors by setting the X",
324    "resources pen1 through pen9.",
325    "",
326    "Now press button 1 to select a pixel within the image window",
327    "to change its color.  Additional pixels may be recolored as",
328    "prescribed by the method you choose.",
329    "",
330    "If the Magnify widget is mapped, it can be helpful in positioning",
331    "your pointer within the image (refer to button 2).",
332    "",
333    "The actual color you request for the pixels is saved in the",
334    "image.  However, the color that appears in your image window",
335    "may be different.  For example, on a monochrome screen the",
336    "pixel will appear black or white even if you choose the",
337    "color red as the pixel color.  However, the image saved to a",
338    "file with -write is written with red pixels.  To assure the",
339    "correct color text in the final image, any PseudoClass image",
340    "is promoted to DirectClass (see miff(5)).  To force a",
341    "PseudoClass image to remain PseudoClass, use -colors.",
342    (char *) NULL,
343  },
344  *ImageCompositeHelp[] =
345  {
346    "First a widget window is displayed requesting you to enter an",
347    "image name. Press Composite, Grab or type a file name.",
348    "Press Cancel if you choose not to create a composite image.",
349    "When you choose Grab, move the pointer to the desired window",
350    "and press any button.",
351    "",
352    "If the Composite image does not have any matte information,",
353    "you are informed and the file browser is displayed again.",
354    "Enter the name of a mask image.  The image is typically",
355    "grayscale and the same size as the composite image.  If the",
356    "image is not grayscale, it is converted to grayscale and the",
357    "resulting intensities are used as matte information.",
358    "",
359    "A small window appears showing the location of the cursor in",
360    "the image window. You are now in composite mode.  To exit",
361    "immediately, press Dismiss.  In composite mode, the Command",
362    "widget has these options:",
363    "",
364    "    Operators",
365    "      Over",
366    "      In",
367    "      Out",
368    "      Atop",
369    "      Xor",
370    "      Plus",
371    "      Minus",
372    "      Add",
373    "      Subtract",
374    "      Difference",
375    "      Multiply",
376    "      Bumpmap",
377    "      Copy",
378    "      CopyRed",
379    "      CopyGreen",
380    "      CopyBlue",
381    "      CopyOpacity",
382    "      Clear",
383    "    Dissolve",
384    "    Displace",
385    "    Help",
386    "    Dismiss",
387    "",
388    "Choose a composite operation from the Operators sub-menu of",
389    "the Command widget.  How each operator behaves is described",
390    "below.  Image window is the image currently displayed on",
391    "your X server and image is the image obtained with the File",
392    "Browser widget.",
393    "",
394    "Over     The result is the union of the two image shapes,",
395    "         with image obscuring image window in the region of",
396    "         overlap.",
397    "",
398    "In       The result is simply image cut by the shape of",
399    "         image window.  None of the image data of image",
400    "         window is in the result.",
401    "",
402    "Out      The resulting image is image with the shape of",
403    "         image window cut out.",
404    "",
405    "Atop     The result is the same shape as image image window,",
406    "         with image obscuring image window where the image",
407    "         shapes overlap.  Note this differs from over",
408    "         because the portion of image outside image window's",
409    "         shape does not appear in the result.",
410    "",
411    "Xor      The result is the image data from both image and",
412    "         image window that is outside the overlap region.",
413    "         The overlap region is blank.",
414    "",
415    "Plus     The result is just the sum of the image data.",
416    "         Output values are cropped to QuantumRange (no overflow).",
417    "",
418    "Minus    The result of image - image window, with underflow",
419    "         cropped to zero.",
420    "",
421    "Add      The result of image + image window, with overflow",
422    "         wrapping around (mod 256).",
423    "",
424    "Subtract The result of image - image window, with underflow",
425    "         wrapping around (mod 256).  The add and subtract",
426    "         operators can be used to perform reversible",
427    "         transformations.",
428    "",
429    "Difference",
430    "         The result of abs(image - image window).  This",
431    "         useful for comparing two very similar images.",
432    "",
433    "Multiply",
434    "         The result of image * image window.  This",
435    "         useful for the creation of drop-shadows.",
436    "",
437    "Bumpmap  The result of surface normals from image * image",
438    "         window.",
439    "",
440    "Copy     The resulting image is image window replaced with",
441    "         image.  Here the matte information is ignored.",
442    "",
443    "CopyRed  The red layer of the image window is replace with",
444    "         the red layer of the image.  The other layers are",
445    "         untouched.",
446    "",
447    "CopyGreen",
448    "         The green layer of the image window is replace with",
449    "         the green layer of the image.  The other layers are",
450    "         untouched.",
451    "",
452    "CopyBlue The blue layer of the image window is replace with",
453    "         the blue layer of the image.  The other layers are",
454    "         untouched.",
455    "",
456    "CopyOpacity",
457    "         The matte layer of the image window is replace with",
458    "         the matte layer of the image.  The other layers are",
459    "         untouched.",
460    "",
461    "The image compositor requires a matte, or alpha channel in",
462    "the image for some operations.  This extra channel usually",
463    "defines a mask which represents a sort of a cookie-cutter",
464    "for the image.  This the case when matte is opaque (full",
465    "coverage) for pixels inside the shape, zero outside, and",
466    "between 0 and QuantumRange on the boundary.  If image does not",
467    "have a matte channel, it is initialized with 0 for any pixel",
468    "matching in color to pixel location (0,0), otherwise QuantumRange.",
469    "",
470    "If you choose Dissolve, the composite operator becomes Over.  The",
471    "image matte channel percent transparency is initialized to factor.",
472    "The image window is initialized to (100-factor). Where factor is the",
473    "value you specify in the Dialog widget.",
474    "",
475    "Displace shifts the image pixels as defined by a displacement",
476    "map.  With this option, image is used as a displacement map.",
477    "Black, within the displacement map, is a maximum positive",
478    "displacement.  White is a maximum negative displacement and",
479    "middle gray is neutral.  The displacement is scaled to determine",
480    "the pixel shift.  By default, the displacement applies in both the",
481    "horizontal and vertical directions.  However, if you specify a mask,",
482    "image is the horizontal X displacement and mask the vertical Y",
483    "displacement.",
484    "",
485    "Note that matte information for image window is not retained",
486    "for colormapped X server visuals (e.g. StaticColor,",
487    "StaticColor, GrayScale, PseudoColor).  Correct compositing",
488    "behavior may require a TrueColor or DirectColor visual or a",
489    "Standard Colormap.",
490    "",
491    "Choosing a composite operator is optional.  The default",
492    "operator is replace.  However, you must choose a location to",
493    "composite your image and press button 1.  Press and hold the",
494    "button before releasing and an outline of the image will",
495    "appear to help you identify your location.",
496    "",
497    "The actual colors of the composite image is saved.  However,",
498    "the color that appears in image window may be different.",
499    "For example, on a monochrome screen image window will appear",
500    "black or white even though your composited image may have",
501    "many colors.  If the image is saved to a file it is written",
502    "with the correct colors.  To assure the correct colors are",
503    "saved in the final image, any PseudoClass image is promoted",
504    "to DirectClass (see miff(5)).  To force a PseudoClass image",
505    "to remain PseudoClass, use -colors.",
506    (char *) NULL,
507  },
508  *ImageCutHelp[] =
509  {
510    "In cut mode, the Command widget has these options:",
511    "",
512    "    Help",
513    "    Dismiss",
514    "",
515    "To define a cut region, press button 1 and drag.  The",
516    "cut region is defined by a highlighted rectangle that",
517    "expands or contracts as it follows the pointer.  Once you",
518    "are satisfied with the cut region, release the button.",
519    "You are now in rectify mode.  In rectify mode, the Command",
520    "widget has these options:",
521    "",
522    "    Cut",
523    "    Help",
524    "    Dismiss",
525    "",
526    "You can make adjustments by moving the pointer to one of the",
527    "cut rectangle corners, pressing a button, and dragging.",
528    "Finally, press Cut to commit your copy region.  To",
529    "exit without cutting the image, press Dismiss.",
530    (char *) NULL,
531  },
532  *ImageCopyHelp[] =
533  {
534    "In copy mode, the Command widget has these options:",
535    "",
536    "    Help",
537    "    Dismiss",
538    "",
539    "To define a copy region, press button 1 and drag.  The",
540    "copy region is defined by a highlighted rectangle that",
541    "expands or contracts as it follows the pointer.  Once you",
542    "are satisfied with the copy region, release the button.",
543    "You are now in rectify mode.  In rectify mode, the Command",
544    "widget has these options:",
545    "",
546    "    Copy",
547    "    Help",
548    "    Dismiss",
549    "",
550    "You can make adjustments by moving the pointer to one of the",
551    "copy rectangle corners, pressing a button, and dragging.",
552    "Finally, press Copy to commit your copy region.  To",
553    "exit without copying the image, press Dismiss.",
554    (char *) NULL,
555  },
556  *ImageCropHelp[] =
557  {
558    "In crop mode, the Command widget has these options:",
559    "",
560    "    Help",
561    "    Dismiss",
562    "",
563    "To define a cropping region, press button 1 and drag.  The",
564    "cropping region is defined by a highlighted rectangle that",
565    "expands or contracts as it follows the pointer.  Once you",
566    "are satisfied with the cropping region, release the button.",
567    "You are now in rectify mode.  In rectify mode, the Command",
568    "widget has these options:",
569    "",
570    "    Crop",
571    "    Help",
572    "    Dismiss",
573    "",
574    "You can make adjustments by moving the pointer to one of the",
575    "cropping rectangle corners, pressing a button, and dragging.",
576    "Finally, press Crop to commit your cropping region.  To",
577    "exit without cropping the image, press Dismiss.",
578    (char *) NULL,
579  },
580  *ImageDrawHelp[] =
581  {
582    "The cursor changes to a crosshair to indicate you are in",
583    "draw mode.  To exit immediately, press Dismiss.  In draw mode,",
584    "the Command widget has these options:",
585    "",
586    "    Element",
587    "      point",
588    "      line",
589    "      rectangle",
590    "      fill rectangle",
591    "      circle",
592    "      fill circle",
593    "      ellipse",
594    "      fill ellipse",
595    "      polygon",
596    "      fill polygon",
597    "    Color",
598    "      black",
599    "      blue",
600    "      cyan",
601    "      green",
602    "      gray",
603    "      red",
604    "      magenta",
605    "      yellow",
606    "      white",
607    "      transparent",
608    "      Browser...",
609    "    Stipple",
610    "      Brick",
611    "      Diagonal",
612    "      Scales",
613    "      Vertical",
614    "      Wavy",
615    "      Translucent",
616    "      Opaque",
617    "      Open...",
618    "    Width",
619    "      1",
620    "      2",
621    "      4",
622    "      8",
623    "      16",
624    "      Dialog...",
625    "    Undo",
626    "    Help",
627    "    Dismiss",
628    "",
629    "Choose a drawing primitive from the Element sub-menu.",
630    "",
631    "Choose a color from the Color sub-menu.  Additional",
632    "colors can be specified with the color browser.",
633    "",
634    "If you choose the color browser and press Grab, you can",
635    "select the color by moving the pointer to the desired",
636    "color on the screen and press any button.  The transparent",
637    "color updates the image matte channel and is useful for",
638    "image compositing.",
639    "",
640    "Choose a stipple, if appropriate, from the Stipple sub-menu.",
641    "Additional stipples can be specified with the file browser.",
642    "Stipples obtained from the file browser must be on disk in the",
643    "X11 bitmap format.",
644    "",
645    "Choose a width, if appropriate, from the Width sub-menu.  To",
646    "choose a specific width select the Dialog widget.",
647    "",
648    "Choose a point in the Image window and press button 1 and",
649    "hold.  Next, move the pointer to another location in the",
650    "image.  As you move, a line connects the initial location and",
651    "the pointer.  When you release the button, the image is",
652    "updated with the primitive you just drew.  For polygons, the",
653    "image is updated when you press and release the button without",
654    "moving the pointer.",
655    "",
656    "To cancel image drawing, move the pointer back to the",
657    "starting point of the line and release the button.",
658    (char *) NULL,
659  },
660  *DisplayHelp[] =
661  {
662    "BUTTONS",
663    "  The effects of each button press is described below.  Three",
664    "  buttons are required.  If you have a two button mouse,",
665    "  button 1 and 3 are returned.  Press ALT and button 3 to",
666    "  simulate button 2.",
667    "",
668    "  1    Press this button to map or unmap the Command widget.",
669    "",
670    "  2    Press and drag to define a region of the image to",
671    "       magnify.",
672    "",
673    "  3    Press and drag to choose from a select set of commands.",
674    "       This button behaves differently if the image being",
675    "       displayed is a visual image directory.  Here, choose a",
676    "       particular tile of the directory and press this button and",
677    "       drag to select a command from a pop-up menu.  Choose from",
678    "       these menu items:",
679    "",
680    "           Open",
681    "           Next",
682    "           Former",
683    "           Delete",
684    "           Update",
685    "",
686    "       If you choose Open, the image represented by the tile is",
687    "       displayed.  To return to the visual image directory, choose",
688    "       Next from the Command widget.  Next and Former moves to the",
689    "       next or former image respectively.  Choose Delete to delete",
690    "       a particular image tile.  Finally, choose Update to",
691    "       synchronize all the image tiles with their respective",
692    "       images.",
693    "",
694    "COMMAND WIDGET",
695    "  The Command widget lists a number of sub-menus and commands.",
696    "  They are",
697    "",
698    "      File",
699    "        Open...",
700    "        Next",
701    "        Former",
702    "        Select...",
703    "        Save...",
704    "        Print...",
705    "        Delete...",
706    "        New...",
707    "        Visual Directory...",
708    "        Quit",
709    "      Edit",
710    "        Undo",
711    "        Redo",
712    "        Cut",
713    "        Copy",
714    "        Paste",
715    "      View",
716    "        Half Size",
717    "        Original Size",
718    "        Double Size",
719    "        Resize...",
720    "        Apply",
721    "        Refresh",
722    "        Restore",
723    "      Transform",
724    "        Crop",
725    "        Chop",
726    "        Flop",
727    "        Flip",
728    "        Rotate Right",
729    "        Rotate Left",
730    "        Rotate...",
731    "        Shear...",
732    "        Roll...",
733    "        Trim Edges",
734    "      Enhance",
735    "        Brightness...",
736    "        Saturation...",
737    "        Hue...",
738    "        Gamma...",
739    "        Sharpen...",
740    "        Dull",
741    "        Contrast Stretch...",
742    "        Sigmoidal Contrast...",
743    "        Normalize",
744    "        Equalize",
745    "        Negate",
746    "        Grayscale",
747    "        Map...",
748    "        Quantize...",
749    "      Effects",
750    "        Despeckle",
751    "        Emboss",
752    "        Reduce Noise",
753    "        Add Noise",
754    "        Sharpen...",
755    "        Blur...",
756    "        Threshold...",
757    "        Edge Detect...",
758    "        Spread...",
759    "        Shade...",
760    "        Painting...",
761    "        Segment...",
762    "      F/X",
763    "        Solarize...",
764    "        Sepia Tone...",
765    "        Swirl...",
766    "        Implode...",
767    "        Vignette...",
768    "        Wave...",
769    "        Oil Painting...",
770    "        Charcoal Drawing...",
771    "      Image Edit",
772    "        Annotate...",
773    "        Draw...",
774    "        Color...",
775    "        Matte...",
776    "        Composite...",
777    "        Add Border...",
778    "        Add Frame...",
779    "        Comment...",
780    "        Launch...",
781    "        Region of Interest...",
782    "      Miscellany",
783    "        Image Info",
784    "        Zoom Image",
785    "        Show Preview...",
786    "        Show Histogram",
787    "        Show Matte",
788    "        Background...",
789    "        Slide Show",
790    "        Preferences...",
791    "      Help",
792    "        Overview",
793    "        Browse Documentation",
794    "        About Display",
795    "",
796    "  Menu items with a indented triangle have a sub-menu.  They",
797    "  are represented above as the indented items.  To access a",
798    "  sub-menu item, move the pointer to the appropriate menu and",
799    "  press a button and drag.  When you find the desired sub-menu",
800    "  item, release the button and the command is executed.  Move",
801    "  the pointer away from the sub-menu if you decide not to",
802    "  execute a particular command.",
803    "",
804    "KEYBOARD ACCELERATORS",
805    "  Accelerators are one or two key presses that effect a",
806    "  particular command.  The keyboard accelerators that",
807    "  display(1) understands is:",
808    "",
809    "  Ctl+O     Press to open an image from a file.",
810    "",
811    "  space     Press to display the next image.",
812    "",
813    "            If the image is a multi-paged document such as a Postscript",
814    "            document, you can skip ahead several pages by preceding",
815    "            this command with a number.  For example to display the",
816    "            third page beyond the current page, press 3<space>.",
817    "",
818    "  backspace Press to display the former image.",
819    "",
820    "            If the image is a multi-paged document such as a Postscript",
821    "            document, you can skip behind several pages by preceding",
822    "            this command with a number.  For example to display the",
823    "            third page preceding the current page, press 3<backspace>.",
824    "",
825    "  Ctl+S     Press to write the image to a file.",
826    "",
827    "  Ctl+P     Press to print the image to a Postscript printer.",
828    "",
829    "  Ctl+D     Press to delete an image file.",
830    "",
831    "  Ctl+N     Press to create a blank canvas.",
832    "",
833    "  Ctl+Q     Press to discard all images and exit program.",
834    "",
835    "  Ctl+Z     Press to undo last image transformation.",
836    "",
837    "  Ctl+R     Press to redo last image transformation.",
838    "",
839    "  Ctl+X     Press to cut a region of the image.",
840    "",
841    "  Ctl+C     Press to copy a region of the image.",
842    "",
843    "  Ctl+V     Press to paste a region to the image.",
844    "",
845    "  <         Press to half the image size.",
846    "",
847    "  -         Press to return to the original image size.",
848    "",
849    "  >         Press to double the image size.",
850    "",
851    "  %         Press to resize the image to a width and height you",
852    "            specify.",
853    "",
854    "Cmd-A       Press to make any image transformations permanent."
855    "",
856    "            By default, any image size transformations are applied",
857    "            to the original image to create the image displayed on",
858    "            the X server.  However, the transformations are not",
859    "            permanent (i.e. the original image does not change",
860    "            size only the X image does).  For example, if you",
861    "            press > the X image will appear to double in size,",
862    "            but the original image will in fact remain the same size.",
863    "            To force the original image to double in size, press >",
864    "            followed by Cmd-A.",
865    "",
866    "  @         Press to refresh the image window.",
867    "",
868    "  C         Press to cut out a rectangular region of the image.",
869    "",
870    "  [         Press to chop the image.",
871    "",
872    "  H         Press to flop image in the horizontal direction.",
873    "",
874    "  V         Press to flip image in the vertical direction.",
875    "",
876    "  /         Press to rotate the image 90 degrees clockwise.",
877    "",
878    " \\         Press to rotate the image 90 degrees counter-clockwise.",
879    "",
880    "  *         Press to rotate the image the number of degrees you",
881    "            specify.",
882    "",
883    "  S         Press to shear the image the number of degrees you",
884    "            specify.",
885    "",
886    "  R         Press to roll the image.",
887    "",
888    "  T         Press to trim the image edges.",
889    "",
890    "  Shft-H    Press to vary the image hue.",
891    "",
892    "  Shft-S    Press to vary the color saturation.",
893    "",
894    "  Shft-L    Press to vary the color brightness.",
895    "",
896    "  Shft-G    Press to gamma correct the image.",
897    "",
898    "  Shft-C    Press to sharpen the image contrast.",
899    "",
900    "  Shft-Z    Press to dull the image contrast.",
901    "",
902    "  =         Press to perform histogram equalization on the image.",
903    "",
904    "  Shft-N    Press to perform histogram normalization on the image.",
905    "",
906    "  Shft-~    Press to negate the colors of the image.",
907    "",
908    "  .         Press to convert the image colors to gray.",
909    "",
910    "  Shft-#    Press to set the maximum number of unique colors in the",
911    "            image.",
912    "",
913    "  F2        Press to reduce the speckles in an image.",
914    "",
915    "  F3        Press to eliminate peak noise from an image.",
916    "",
917    "  F4        Press to add noise to an image.",
918    "",
919    "  F5        Press to sharpen an image.",
920    "",
921    "  F6        Press to delete an image file.",
922    "",
923    "  F7        Press to threshold the image.",
924    "",
925    "  F8        Press to detect edges within an image.",
926    "",
927    "  F9        Press to emboss an image.",
928    "",
929    "  F10       Press to displace pixels by a random amount.",
930    "",
931    "  F11       Press to negate all pixels above the threshold level.",
932    "",
933    "  F12       Press to shade the image using a distant light source.",
934    "",
935    "  F13       Press to lighten or darken image edges to create a 3-D effect.",
936    "",
937    "  F14       Press to segment the image by color.",
938    "",
939    "  Meta-S    Press to swirl image pixels about the center.",
940    "",
941    "  Meta-I    Press to implode image pixels about the center.",
942    "",
943    "  Meta-W    Press to alter an image along a sine wave.",
944    "",
945    "  Meta-P    Press to simulate an oil painting.",
946    "",
947    "  Meta-C    Press to simulate a charcoal drawing.",
948    "",
949    "  Alt-A     Press to annotate the image with text.",
950    "",
951    "  Alt-D     Press to draw on an image.",
952    "",
953    "  Alt-P     Press to edit an image pixel color.",
954    "",
955    "  Alt-M     Press to edit the image matte information.",
956    "",
957    "  Alt-V     Press to composite the image with another.",
958    "",
959    "  Alt-B     Press to add a border to the image.",
960    "",
961    "  Alt-F     Press to add an ornamental border to the image.",
962    "",
963    "  Alt-Shft-!",
964    "            Press to add an image comment.",
965    "",
966    "  Ctl-A     Press to apply image processing techniques to a region",
967    "            of interest.",
968    "",
969    "  Shft-?    Press to display information about the image.",
970    "",
971    "  Shft-+    Press to map the zoom image window.",
972    "",
973    "  Shft-P    Press to preview an image enhancement, effect, or f/x.",
974    "",
975    "  F1        Press to display helpful information about display(1).",
976    "",
977    "  Find      Press to browse documentation about ImageMagick.",
978    "",
979    "  1-9       Press to change the level of magnification.",
980    "",
981    "  Use the arrow keys to move the image one pixel up, down,",
982    "  left, or right within the magnify window.  Be sure to first",
983    "  map the magnify window by pressing button 2.",
984    "",
985    "  Press ALT and one of the arrow keys to trim off one pixel",
986    "  from any side of the image.",
987    (char *) NULL,
988  },
989  *ImageMatteEditHelp[] =
990  {
991    "Matte information within an image is useful for some",
992    "operations such as image compositing (See IMAGE",
993    "COMPOSITING).  This extra channel usually defines a mask",
994    "which represents a sort of a cookie-cutter for the image.",
995    "This the case when matte is opaque (full coverage) for",
996    "pixels inside the shape, zero outside, and between 0 and",
997    "QuantumRange on the boundary.",
998    "",
999    "A small window appears showing the location of the cursor in",
1000    "the image window. You are now in matte edit mode.  To exit",
1001    "immediately, press Dismiss.  In matte edit mode, the Command",
1002    "widget has these options:",
1003    "",
1004    "    Method",
1005    "      point",
1006    "      replace",
1007    "      floodfill",
1008    "      filltoborder",
1009    "      reset",
1010    "    Border Color",
1011    "      black",
1012    "      blue",
1013    "      cyan",
1014    "      green",
1015    "      gray",
1016    "      red",
1017    "      magenta",
1018    "      yellow",
1019    "      white",
1020    "      Browser...",
1021    "    Fuzz",
1022    "      0%",
1023    "      2%",
1024    "      5%",
1025    "      10%",
1026    "      15%",
1027    "      Dialog...",
1028    "    Matte",
1029    "      Opaque",
1030    "      Transparent",
1031    "      Dialog...",
1032    "    Undo",
1033    "    Help",
1034    "    Dismiss",
1035    "",
1036    "Choose a matte editing method from the Method sub-menu of",
1037    "the Command widget.  The point method changes the matte value",
1038    "of any pixel selected with the pointer until the button is",
1039    "is released.  The replace method changes the matte value of",
1040    "any pixel that matches the color of the pixel you select with",
1041    "a button press.  Floodfill changes the matte value of any pixel",
1042    "that matches the color of the pixel you select with a button",
1043    "press and is a neighbor.  Whereas filltoborder changes the matte",
1044    "value any neighbor pixel that is not the border color.  Finally",
1045    "reset changes the entire image to the designated matte value.",
1046    "",
1047    "Choose Matte Value and pick Opaque or Transarent.  For other values",
1048    "select the Dialog entry.  Here a dialog appears requesting a matte",
1049    "value.  The value you select is assigned as the opacity value of the",
1050    "selected pixel or pixels.",
1051    "",
1052    "Now, press any button to select a pixel within the image",
1053    "window to change its matte value.",
1054    "",
1055    "If the Magnify widget is mapped, it can be helpful in positioning",
1056    "your pointer within the image (refer to button 2).",
1057    "",
1058    "Matte information is only valid in a DirectClass image.",
1059    "Therefore, any PseudoClass image is promoted to DirectClass",
1060    "(see miff(5)).  Note that matte information for PseudoClass",
1061    "is not retained for colormapped X server visuals (e.g.",
1062    "StaticColor, StaticColor, GrayScale, PseudoColor) unless you",
1063    "immediately save your image to a file (refer to Write).",
1064    "Correct matte editing behavior may require a TrueColor or",
1065    "DirectColor visual or a Standard Colormap.",
1066    (char *) NULL,
1067  },
1068  *ImagePanHelp[] =
1069  {
1070    "When an image exceeds the width or height of the X server",
1071    "screen, display maps a small panning icon.  The rectangle",
1072    "within the panning icon shows the area that is currently",
1073    "displayed in the image window.  To pan about the image,",
1074    "press any button and drag the pointer within the panning",
1075    "icon.  The pan rectangle moves with the pointer and the",
1076    "image window is updated to reflect the location of the",
1077    "rectangle within the panning icon.  When you have selected",
1078    "the area of the image you wish to view, release the button.",
1079    "",
1080    "Use the arrow keys to pan the image one pixel up, down,",
1081    "left, or right within the image window.",
1082    "",
1083    "The panning icon is withdrawn if the image becomes smaller",
1084    "than the dimensions of the X server screen.",
1085    (char *) NULL,
1086  },
1087  *ImagePasteHelp[] =
1088  {
1089    "A small window appears showing the location of the cursor in",
1090    "the image window. You are now in paste mode.  To exit",
1091    "immediately, press Dismiss.  In paste mode, the Command",
1092    "widget has these options:",
1093    "",
1094    "    Operators",
1095    "      over",
1096    "      in",
1097    "      out",
1098    "      atop",
1099    "      xor",
1100    "      plus",
1101    "      minus",
1102    "      add",
1103    "      subtract",
1104    "      difference",
1105    "      replace",
1106    "    Help",
1107    "    Dismiss",
1108    "",
1109    "Choose a composite operation from the Operators sub-menu of",
1110    "the Command widget.  How each operator behaves is described",
1111    "below.  Image window is the image currently displayed on",
1112    "your X server and image is the image obtained with the File",
1113    "Browser widget.",
1114    "",
1115    "Over     The result is the union of the two image shapes,",
1116    "         with image obscuring image window in the region of",
1117    "         overlap.",
1118    "",
1119    "In       The result is simply image cut by the shape of",
1120    "         image window.  None of the image data of image",
1121    "         window is in the result.",
1122    "",
1123    "Out      The resulting image is image with the shape of",
1124    "         image window cut out.",
1125    "",
1126    "Atop     The result is the same shape as image image window,",
1127    "         with image obscuring image window where the image",
1128    "         shapes overlap.  Note this differs from over",
1129    "         because the portion of image outside image window's",
1130    "         shape does not appear in the result.",
1131    "",
1132    "Xor      The result is the image data from both image and",
1133    "         image window that is outside the overlap region.",
1134    "         The overlap region is blank.",
1135    "",
1136    "Plus     The result is just the sum of the image data.",
1137    "         Output values are cropped to QuantumRange (no overflow).",
1138    "         This operation is independent of the matte",
1139    "         channels.",
1140    "",
1141    "Minus    The result of image - image window, with underflow",
1142    "         cropped to zero.",
1143    "",
1144    "Add      The result of image + image window, with overflow",
1145    "         wrapping around (mod 256).",
1146    "",
1147    "Subtract The result of image - image window, with underflow",
1148    "         wrapping around (mod 256).  The add and subtract",
1149    "         operators can be used to perform reversible",
1150    "         transformations.",
1151    "",
1152    "Difference",
1153    "         The result of abs(image - image window).  This",
1154    "         useful for comparing two very similar images.",
1155    "",
1156    "Copy     The resulting image is image window replaced with",
1157    "         image.  Here the matte information is ignored.",
1158    "",
1159    "CopyRed  The red layer of the image window is replace with",
1160    "         the red layer of the image.  The other layers are",
1161    "         untouched.",
1162    "",
1163    "CopyGreen",
1164    "         The green layer of the image window is replace with",
1165    "         the green layer of the image.  The other layers are",
1166    "         untouched.",
1167    "",
1168    "CopyBlue The blue layer of the image window is replace with",
1169    "         the blue layer of the image.  The other layers are",
1170    "         untouched.",
1171    "",
1172    "CopyOpacity",
1173    "         The matte layer of the image window is replace with",
1174    "         the matte layer of the image.  The other layers are",
1175    "         untouched.",
1176    "",
1177    "The image compositor requires a matte, or alpha channel in",
1178    "the image for some operations.  This extra channel usually",
1179    "defines a mask which represents a sort of a cookie-cutter",
1180    "for the image.  This the case when matte is opaque (full",
1181    "coverage) for pixels inside the shape, zero outside, and",
1182    "between 0 and QuantumRange on the boundary.  If image does not",
1183    "have a matte channel, it is initialized with 0 for any pixel",
1184    "matching in color to pixel location (0,0), otherwise QuantumRange.",
1185    "",
1186    "Note that matte information for image window is not retained",
1187    "for colormapped X server visuals (e.g. StaticColor,",
1188    "StaticColor, GrayScale, PseudoColor).  Correct compositing",
1189    "behavior may require a TrueColor or DirectColor visual or a",
1190    "Standard Colormap.",
1191    "",
1192    "Choosing a composite operator is optional.  The default",
1193    "operator is replace.  However, you must choose a location to",
1194    "paste your image and press button 1.  Press and hold the",
1195    "button before releasing and an outline of the image will",
1196    "appear to help you identify your location.",
1197    "",
1198    "The actual colors of the pasted image is saved.  However,",
1199    "the color that appears in image window may be different.",
1200    "For example, on a monochrome screen image window will appear",
1201    "black or white even though your pasted image may have",
1202    "many colors.  If the image is saved to a file it is written",
1203    "with the correct colors.  To assure the correct colors are",
1204    "saved in the final image, any PseudoClass image is promoted",
1205    "to DirectClass (see miff(5)).  To force a PseudoClass image",
1206    "to remain PseudoClass, use -colors.",
1207    (char *) NULL,
1208  },
1209  *ImageROIHelp[] =
1210  {
1211    "In region of interest mode, the Command widget has these",
1212    "options:",
1213    "",
1214    "    Help",
1215    "    Dismiss",
1216    "",
1217    "To define a region of interest, press button 1 and drag.",
1218    "The region of interest is defined by a highlighted rectangle",
1219    "that expands or contracts as it follows the pointer.  Once",
1220    "you are satisfied with the region of interest, release the",
1221    "button.  You are now in apply mode.  In apply mode the",
1222    "Command widget has these options:",
1223    "",
1224    "      File",
1225    "        Save...",
1226    "        Print...",
1227    "      Edit",
1228    "        Undo",
1229    "        Redo",
1230    "      Transform",
1231    "        Flop",
1232    "        Flip",
1233    "        Rotate Right",
1234    "        Rotate Left",
1235    "      Enhance",
1236    "        Hue...",
1237    "        Saturation...",
1238    "        Brightness...",
1239    "        Gamma...",
1240    "        Spiff",
1241    "        Dull",
1242    "        Contrast Stretch",
1243    "        Sigmoidal Contrast...",
1244    "        Normalize",
1245    "        Equalize",
1246    "        Negate",
1247    "        Grayscale",
1248    "        Map...",
1249    "        Quantize...",
1250    "      Effects",
1251    "        Despeckle",
1252    "        Emboss",
1253    "        Reduce Noise",
1254    "        Sharpen...",
1255    "        Blur...",
1256    "        Threshold...",
1257    "        Edge Detect...",
1258    "        Spread...",
1259    "        Shade...",
1260    "        Raise...",
1261    "        Segment...",
1262    "      F/X",
1263    "        Solarize...",
1264    "        Sepia Tone...",
1265    "        Swirl...",
1266    "        Implode...",
1267    "        Vignette...",
1268    "        Wave...",
1269    "        Oil Painting...",
1270    "        Charcoal Drawing...",
1271    "      Miscellany",
1272    "        Image Info",
1273    "        Zoom Image",
1274    "        Show Preview...",
1275    "        Show Histogram",
1276    "        Show Matte",
1277    "      Help",
1278    "      Dismiss",
1279    "",
1280    "You can make adjustments to the region of interest by moving",
1281    "the pointer to one of the rectangle corners, pressing a",
1282    "button, and dragging.  Finally, choose an image processing",
1283    "technique from the Command widget.  You can choose more than",
1284    "one image processing technique to apply to an area.",
1285    "Alternatively, you can move the region of interest before",
1286    "applying another image processing technique.  To exit, press",
1287    "Dismiss.",
1288    (char *) NULL,
1289  },
1290  *ImageRotateHelp[] =
1291  {
1292    "In rotate mode, the Command widget has these options:",
1293    "",
1294    "    Pixel Color",
1295    "      black",
1296    "      blue",
1297    "      cyan",
1298    "      green",
1299    "      gray",
1300    "      red",
1301    "      magenta",
1302    "      yellow",
1303    "      white",
1304    "      Browser...",
1305    "    Direction",
1306    "      horizontal",
1307    "      vertical",
1308    "    Help",
1309    "    Dismiss",
1310    "",
1311    "Choose a background color from the Pixel Color sub-menu.",
1312    "Additional background colors can be specified with the color",
1313    "browser.  You can change the menu colors by setting the X",
1314    "resources pen1 through pen9.",
1315    "",
1316    "If you choose the color browser and press Grab, you can",
1317    "select the background color by moving the pointer to the",
1318    "desired color on the screen and press any button.",
1319    "",
1320    "Choose a point in the image window and press this button and",
1321    "hold.  Next, move the pointer to another location in the",
1322    "image.  As you move a line connects the initial location and",
1323    "the pointer.  When you release the button, the degree of",
1324    "image rotation is determined by the slope of the line you",
1325    "just drew.  The slope is relative to the direction you",
1326    "choose from the Direction sub-menu of the Command widget.",
1327    "",
1328    "To cancel the image rotation, move the pointer back to the",
1329    "starting point of the line and release the button.",
1330    (char *) NULL,
1331  };
1332
1333/*
1334  Enumeration declarations.
1335*/
1336typedef enum
1337{
1338  CopyMode,
1339  CropMode,
1340  CutMode
1341} ClipboardMode;
1342
1343typedef enum
1344{
1345  OpenCommand,
1346  NextCommand,
1347  FormerCommand,
1348  SelectCommand,
1349  SaveCommand,
1350  PrintCommand,
1351  DeleteCommand,
1352  NewCommand,
1353  VisualDirectoryCommand,
1354  QuitCommand,
1355  UndoCommand,
1356  RedoCommand,
1357  CutCommand,
1358  CopyCommand,
1359  PasteCommand,
1360  HalfSizeCommand,
1361  OriginalSizeCommand,
1362  DoubleSizeCommand,
1363  ResizeCommand,
1364  ApplyCommand,
1365  RefreshCommand,
1366  RestoreCommand,
1367  CropCommand,
1368  ChopCommand,
1369  FlopCommand,
1370  FlipCommand,
1371  RotateRightCommand,
1372  RotateLeftCommand,
1373  RotateCommand,
1374  ShearCommand,
1375  RollCommand,
1376  TrimCommand,
1377  HueCommand,
1378  SaturationCommand,
1379  BrightnessCommand,
1380  GammaCommand,
1381  SpiffCommand,
1382  DullCommand,
1383  ContrastStretchCommand,
1384  SigmoidalContrastCommand,
1385  NormalizeCommand,
1386  EqualizeCommand,
1387  NegateCommand,
1388  GrayscaleCommand,
1389  MapCommand,
1390  QuantizeCommand,
1391  DespeckleCommand,
1392  EmbossCommand,
1393  ReduceNoiseCommand,
1394  AddNoiseCommand,
1395  SharpenCommand,
1396  BlurCommand,
1397  ThresholdCommand,
1398  EdgeDetectCommand,
1399  SpreadCommand,
1400  ShadeCommand,
1401  RaiseCommand,
1402  SegmentCommand,
1403  SolarizeCommand,
1404  SepiaToneCommand,
1405  SwirlCommand,
1406  ImplodeCommand,
1407  VignetteCommand,
1408  WaveCommand,
1409  OilPaintCommand,
1410  CharcoalDrawCommand,
1411  AnnotateCommand,
1412  DrawCommand,
1413  ColorCommand,
1414  MatteCommand,
1415  CompositeCommand,
1416  AddBorderCommand,
1417  AddFrameCommand,
1418  CommentCommand,
1419  LaunchCommand,
1420  RegionofInterestCommand,
1421  ROIHelpCommand,
1422  ROIDismissCommand,
1423  InfoCommand,
1424  ZoomCommand,
1425  ShowPreviewCommand,
1426  ShowHistogramCommand,
1427  ShowMatteCommand,
1428  BackgroundCommand,
1429  SlideShowCommand,
1430  PreferencesCommand,
1431  HelpCommand,
1432  BrowseDocumentationCommand,
1433  VersionCommand,
1434  SaveToUndoBufferCommand,
1435  FreeBuffersCommand,
1436  NullCommand
1437} CommandType;
1438
1439typedef enum
1440{
1441  AnnotateNameCommand,
1442  AnnotateFontColorCommand,
1443  AnnotateBackgroundColorCommand,
1444  AnnotateRotateCommand,
1445  AnnotateHelpCommand,
1446  AnnotateDismissCommand,
1447  TextHelpCommand,
1448  TextApplyCommand,
1449  ChopDirectionCommand,
1450  ChopHelpCommand,
1451  ChopDismissCommand,
1452  HorizontalChopCommand,
1453  VerticalChopCommand,
1454  ColorEditMethodCommand,
1455  ColorEditColorCommand,
1456  ColorEditBorderCommand,
1457  ColorEditFuzzCommand,
1458  ColorEditUndoCommand,
1459  ColorEditHelpCommand,
1460  ColorEditDismissCommand,
1461  CompositeOperatorsCommand,
1462  CompositeDissolveCommand,
1463  CompositeDisplaceCommand,
1464  CompositeHelpCommand,
1465  CompositeDismissCommand,
1466  CropHelpCommand,
1467  CropDismissCommand,
1468  RectifyCopyCommand,
1469  RectifyHelpCommand,
1470  RectifyDismissCommand,
1471  DrawElementCommand,
1472  DrawColorCommand,
1473  DrawStippleCommand,
1474  DrawWidthCommand,
1475  DrawUndoCommand,
1476  DrawHelpCommand,
1477  DrawDismissCommand,
1478  MatteEditMethod,
1479  MatteEditBorderCommand,
1480  MatteEditFuzzCommand,
1481  MatteEditValueCommand,
1482  MatteEditUndoCommand,
1483  MatteEditHelpCommand,
1484  MatteEditDismissCommand,
1485  PasteOperatorsCommand,
1486  PasteHelpCommand,
1487  PasteDismissCommand,
1488  RotateColorCommand,
1489  RotateDirectionCommand,
1490  RotateCropCommand,
1491  RotateSharpenCommand,
1492  RotateHelpCommand,
1493  RotateDismissCommand,
1494  HorizontalRotateCommand,
1495  VerticalRotateCommand,
1496  TileLoadCommand,
1497  TileNextCommand,
1498  TileFormerCommand,
1499  TileDeleteCommand,
1500  TileUpdateCommand
1501} ModeType;
1502
1503/*
1504  Stipples.
1505*/
1506#define BricksWidth  20
1507#define BricksHeight  20
1508#define DiagonalWidth  16
1509#define DiagonalHeight  16
1510#define HighlightWidth  8
1511#define HighlightHeight  8
1512#define OpaqueWidth  8
1513#define OpaqueHeight  8
1514#define ScalesWidth  16
1515#define ScalesHeight  16
1516#define ShadowWidth  8
1517#define ShadowHeight  8
1518#define VerticalWidth  16
1519#define VerticalHeight  16
1520#define WavyWidth  16
1521#define WavyHeight  16
1522
1523/*
1524  Constant declaration.
1525*/
1526static const int
1527  RoiDelta = 8;
1528
1529static const unsigned char
1530  BricksBitmap[] =
1531  {
1532    0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1533    0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1534    0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1535    0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1536    0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1537  },
1538  DiagonalBitmap[] =
1539  {
1540    0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1541    0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1542    0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1543  },
1544  ScalesBitmap[] =
1545  {
1546    0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1547    0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1548    0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1549  },
1550  VerticalBitmap[] =
1551  {
1552    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1553    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1554    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1555  },
1556  WavyBitmap[] =
1557  {
1558    0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1559    0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1560    0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1561  };
1562
1563/*
1564  Function prototypes.
1565*/
1566static CommandType
1567  XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1568    const MagickStatusType,KeySym,Image **,ExceptionInfo *);
1569
1570static Image
1571  *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
1572    Image **,ExceptionInfo *),
1573  *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1574  *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *,
1575    ExceptionInfo *),
1576  *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *,
1577    ExceptionInfo *);
1578
1579static MagickBooleanType
1580  XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *,
1581    ExceptionInfo *),
1582  XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **,
1583    ExceptionInfo *),
1584  XChopImage(Display *,XResourceInfo *,XWindows *,Image **,
1585    ExceptionInfo *),
1586  XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode,
1587    ExceptionInfo *),
1588  XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1589    ExceptionInfo *),
1590  XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *,
1591    ExceptionInfo *),
1592  XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1593  XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1594    ExceptionInfo *),
1595  XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1596    ExceptionInfo *),
1597  XPasteImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1598  XPrintImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1599  XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **,
1600    ExceptionInfo *),
1601  XROIImage(Display *,XResourceInfo *,XWindows *,Image **,ExceptionInfo *),
1602  XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1603  XTrimImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
1604
1605static void
1606  XDrawPanRectangle(Display *,XWindows *),
1607  XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **,
1608    ExceptionInfo *),
1609  XMagnifyImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1610  XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1611  XPanImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1612  XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1613    const KeySym,ExceptionInfo *),
1614  XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1615  XScreenEvent(Display *,XWindows *,XEvent *,ExceptionInfo *),
1616  XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1617
1618/*
1619%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1620%                                                                             %
1621%                                                                             %
1622%                                                                             %
1623%   D i s p l a y I m a g e s                                                 %
1624%                                                                             %
1625%                                                                             %
1626%                                                                             %
1627%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1628%
1629%  DisplayImages() displays an image sequence to any X window screen.  It
1630%  returns a value other than 0 if successful.  Check the exception member
1631%  of image to determine the reason for any failure.
1632%
1633%  The format of the DisplayImages method is:
1634%
1635%      MagickBooleanType DisplayImages(const ImageInfo *image_info,
1636%        Image *images,ExceptionInfo *exception)
1637%
1638%  A description of each parameter follows:
1639%
1640%    o image_info: the image info.
1641%
1642%    o image: the image.
1643%
1644%    o exception: return any errors or warnings in this structure.
1645%
1646*/
1647MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1648  Image *images,ExceptionInfo *exception)
1649{
1650  char
1651    *argv[1];
1652
1653  Display
1654    *display;
1655
1656  Image
1657    *image;
1658
1659  register ssize_t
1660    i;
1661
1662  size_t
1663    state;
1664
1665  XrmDatabase
1666    resource_database;
1667
1668  XResourceInfo
1669    resource_info;
1670
1671  assert(image_info != (const ImageInfo *) NULL);
1672  assert(image_info->signature == MagickSignature);
1673  assert(images != (Image *) NULL);
1674  assert(images->signature == MagickSignature);
1675  if (images->debug != MagickFalse)
1676    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1677  display=XOpenDisplay(image_info->server_name);
1678  if (display == (Display *) NULL)
1679    {
1680      (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1681        "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1682      return(MagickFalse);
1683    }
1684  if (exception->severity != UndefinedException)
1685    CatchException(exception);
1686  (void) XSetErrorHandler(XError);
1687  resource_database=XGetResourceDatabase(display,GetClientName());
1688  (void) ResetMagickMemory(&resource_info,0,sizeof(resource_info));
1689  XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1690  if (image_info->page != (char *) NULL)
1691    resource_info.image_geometry=AcquireString(image_info->page);
1692  resource_info.immutable=MagickTrue;
1693  argv[0]=AcquireString(GetClientName());
1694  state=DefaultState;
1695  for (i=0; (state & ExitState) == 0; i++)
1696  {
1697    if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1698      break;
1699    image=GetImageFromList(images,i % GetImageListLength(images));
1700    (void) XDisplayImage(display,&resource_info,argv,1,&image,&state,exception);
1701  }
1702  SetErrorHandler((ErrorHandler) NULL);
1703  SetWarningHandler((WarningHandler) NULL);
1704  argv[0]=DestroyString(argv[0]);
1705  (void) XCloseDisplay(display);
1706  XDestroyResourceInfo(&resource_info);
1707  if (exception->severity != UndefinedException)
1708    return(MagickFalse);
1709  return(MagickTrue);
1710}
1711
1712/*
1713%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1714%                                                                             %
1715%                                                                             %
1716%                                                                             %
1717%   R e m o t e D i s p l a y C o m m a n d                                   %
1718%                                                                             %
1719%                                                                             %
1720%                                                                             %
1721%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1722%
1723%  RemoteDisplayCommand() encourages a remote display program to display the
1724%  specified image filename.
1725%
1726%  The format of the RemoteDisplayCommand method is:
1727%
1728%      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1729%        const char *window,const char *filename,ExceptionInfo *exception)
1730%
1731%  A description of each parameter follows:
1732%
1733%    o image_info: the image info.
1734%
1735%    o window: Specifies the name or id of an X window.
1736%
1737%    o filename: the name of the image filename to display.
1738%
1739%    o exception: return any errors or warnings in this structure.
1740%
1741*/
1742MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1743  const char *window,const char *filename,ExceptionInfo *exception)
1744{
1745  Display
1746    *display;
1747
1748  MagickStatusType
1749    status;
1750
1751  assert(image_info != (const ImageInfo *) NULL);
1752  assert(image_info->signature == MagickSignature);
1753  assert(filename != (char *) NULL);
1754  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1755  display=XOpenDisplay(image_info->server_name);
1756  if (display == (Display *) NULL)
1757    {
1758      (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1759        "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1760      return(MagickFalse);
1761    }
1762  (void) XSetErrorHandler(XError);
1763  status=XRemoteCommand(display,window,filename);
1764  (void) XCloseDisplay(display);
1765  return(status != 0 ? MagickTrue : MagickFalse);
1766}
1767
1768/*
1769%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1770%                                                                             %
1771%                                                                             %
1772%                                                                             %
1773+   X A n n o t a t e E d i t I m a g e                                       %
1774%                                                                             %
1775%                                                                             %
1776%                                                                             %
1777%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1778%
1779%  XAnnotateEditImage() annotates the image with text.
1780%
1781%  The format of the XAnnotateEditImage method is:
1782%
1783%      MagickBooleanType XAnnotateEditImage(Display *display,
1784%        XResourceInfo *resource_info,XWindows *windows,Image *image,
1785%        ExceptionInfo *exception)
1786%
1787%  A description of each parameter follows:
1788%
1789%    o display: Specifies a connection to an X server;  returned from
1790%      XOpenDisplay.
1791%
1792%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1793%
1794%    o windows: Specifies a pointer to a XWindows structure.
1795%
1796%    o image: the image; returned from ReadImage.
1797%
1798*/
1799
1800static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
1801{
1802  if (x > y)
1803    return(x);
1804  return(y);
1805}
1806
1807static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
1808{
1809  if (x < y)
1810    return(x);
1811  return(y);
1812}
1813
1814static MagickBooleanType XAnnotateEditImage(Display *display,
1815  XResourceInfo *resource_info,XWindows *windows,Image *image,
1816  ExceptionInfo *exception)
1817{
1818  static const char
1819    *AnnotateMenu[] =
1820    {
1821      "Font Name",
1822      "Font Color",
1823      "Box Color",
1824      "Rotate Text",
1825      "Help",
1826      "Dismiss",
1827      (char *) NULL
1828    },
1829    *TextMenu[] =
1830    {
1831      "Help",
1832      "Apply",
1833      (char *) NULL
1834    };
1835
1836  static const ModeType
1837    AnnotateCommands[] =
1838    {
1839      AnnotateNameCommand,
1840      AnnotateFontColorCommand,
1841      AnnotateBackgroundColorCommand,
1842      AnnotateRotateCommand,
1843      AnnotateHelpCommand,
1844      AnnotateDismissCommand
1845    },
1846    TextCommands[] =
1847    {
1848      TextHelpCommand,
1849      TextApplyCommand
1850    };
1851
1852  static MagickBooleanType
1853    transparent_box = MagickTrue,
1854    transparent_pen = MagickFalse;
1855
1856  static MagickRealType
1857    degrees = 0.0;
1858
1859  static unsigned int
1860    box_id = MaxNumberPens-2,
1861    font_id = 0,
1862    pen_id = 0;
1863
1864  char
1865    command[MaxTextExtent],
1866    text[MaxTextExtent];
1867
1868  const char
1869    *ColorMenu[MaxNumberPens+1];
1870
1871  Cursor
1872    cursor;
1873
1874  GC
1875    annotate_context;
1876
1877  int
1878    id,
1879    pen_number,
1880    status,
1881    x,
1882    y;
1883
1884  KeySym
1885    key_symbol;
1886
1887  register char
1888    *p;
1889
1890  register ssize_t
1891    i;
1892
1893  unsigned int
1894    height,
1895    width;
1896
1897  size_t
1898    state;
1899
1900  XAnnotateInfo
1901    *annotate_info,
1902    *previous_info;
1903
1904  XColor
1905    color;
1906
1907  XFontStruct
1908    *font_info;
1909
1910  XEvent
1911    event,
1912    text_event;
1913
1914  /*
1915    Map Command widget.
1916  */
1917  (void) CloneString(&windows->command.name,"Annotate");
1918  windows->command.data=4;
1919  (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1920  (void) XMapRaised(display,windows->command.id);
1921  XClientMessage(display,windows->image.id,windows->im_protocols,
1922    windows->im_update_widget,CurrentTime);
1923  /*
1924    Track pointer until button 1 is pressed.
1925  */
1926  XQueryPosition(display,windows->image.id,&x,&y);
1927  (void) XSelectInput(display,windows->image.id,
1928    windows->image.attributes.event_mask | PointerMotionMask);
1929  cursor=XCreateFontCursor(display,XC_left_side);
1930  (void) XCheckDefineCursor(display,windows->image.id,cursor);
1931  state=DefaultState;
1932  do
1933  {
1934    if (windows->info.mapped != MagickFalse)
1935      {
1936        /*
1937          Display pointer position.
1938        */
1939        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
1940          x+windows->image.x,y+windows->image.y);
1941        XInfoWidget(display,windows,text);
1942      }
1943    /*
1944      Wait for next event.
1945    */
1946    XScreenEvent(display,windows,&event,exception);
1947    if (event.xany.window == windows->command.id)
1948      {
1949        /*
1950          Select a command from the Command widget.
1951        */
1952        id=XCommandWidget(display,windows,AnnotateMenu,&event);
1953        (void) XCheckDefineCursor(display,windows->image.id,cursor);
1954        if (id < 0)
1955          continue;
1956        switch (AnnotateCommands[id])
1957        {
1958          case AnnotateNameCommand:
1959          {
1960            const char
1961              *FontMenu[MaxNumberFonts];
1962
1963            int
1964              font_number;
1965
1966            /*
1967              Initialize menu selections.
1968            */
1969            for (i=0; i < MaxNumberFonts; i++)
1970              FontMenu[i]=resource_info->font_name[i];
1971            FontMenu[MaxNumberFonts-2]="Browser...";
1972            FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1973            /*
1974              Select a font name from the pop-up menu.
1975            */
1976            font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1977              (const char **) FontMenu,command);
1978            if (font_number < 0)
1979              break;
1980            if (font_number == (MaxNumberFonts-2))
1981              {
1982                static char
1983                  font_name[MaxTextExtent] = "fixed";
1984
1985                /*
1986                  Select a font name from a browser.
1987                */
1988                resource_info->font_name[font_number]=font_name;
1989                XFontBrowserWidget(display,windows,"Select",font_name);
1990                if (*font_name == '\0')
1991                  break;
1992              }
1993            /*
1994              Initialize font info.
1995            */
1996            font_info=XLoadQueryFont(display,resource_info->font_name[
1997              font_number]);
1998            if (font_info == (XFontStruct *) NULL)
1999              {
2000                XNoticeWidget(display,windows,"Unable to load font:",
2001                  resource_info->font_name[font_number]);
2002                break;
2003              }
2004            font_id=(unsigned int) font_number;
2005            (void) XFreeFont(display,font_info);
2006            break;
2007          }
2008          case AnnotateFontColorCommand:
2009          {
2010            /*
2011              Initialize menu selections.
2012            */
2013            for (i=0; i < (int) (MaxNumberPens-2); i++)
2014              ColorMenu[i]=resource_info->pen_colors[i];
2015            ColorMenu[MaxNumberPens-2]="transparent";
2016            ColorMenu[MaxNumberPens-1]="Browser...";
2017            ColorMenu[MaxNumberPens]=(const char *) NULL;
2018            /*
2019              Select a pen color from the pop-up menu.
2020            */
2021            pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2022              (const char **) ColorMenu,command);
2023            if (pen_number < 0)
2024              break;
2025            transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
2026              MagickFalse;
2027            if (transparent_pen != MagickFalse)
2028              break;
2029            if (pen_number == (MaxNumberPens-1))
2030              {
2031                static char
2032                  color_name[MaxTextExtent] = "gray";
2033
2034                /*
2035                  Select a pen color from a dialog.
2036                */
2037                resource_info->pen_colors[pen_number]=color_name;
2038                XColorBrowserWidget(display,windows,"Select",color_name);
2039                if (*color_name == '\0')
2040                  break;
2041              }
2042            /*
2043              Set pen color.
2044            */
2045            (void) XParseColor(display,windows->map_info->colormap,
2046              resource_info->pen_colors[pen_number],&color);
2047            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2048              (unsigned int) MaxColors,&color);
2049            windows->pixel_info->pen_colors[pen_number]=color;
2050            pen_id=(unsigned int) pen_number;
2051            break;
2052          }
2053          case AnnotateBackgroundColorCommand:
2054          {
2055            /*
2056              Initialize menu selections.
2057            */
2058            for (i=0; i < (int) (MaxNumberPens-2); i++)
2059              ColorMenu[i]=resource_info->pen_colors[i];
2060            ColorMenu[MaxNumberPens-2]="transparent";
2061            ColorMenu[MaxNumberPens-1]="Browser...";
2062            ColorMenu[MaxNumberPens]=(const char *) NULL;
2063            /*
2064              Select a pen color from the pop-up menu.
2065            */
2066            pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2067              (const char **) ColorMenu,command);
2068            if (pen_number < 0)
2069              break;
2070            transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2071              MagickFalse;
2072            if (transparent_box != MagickFalse)
2073              break;
2074            if (pen_number == (MaxNumberPens-1))
2075              {
2076                static char
2077                  color_name[MaxTextExtent] = "gray";
2078
2079                /*
2080                  Select a pen color from a dialog.
2081                */
2082                resource_info->pen_colors[pen_number]=color_name;
2083                XColorBrowserWidget(display,windows,"Select",color_name);
2084                if (*color_name == '\0')
2085                  break;
2086              }
2087            /*
2088              Set pen color.
2089            */
2090            (void) XParseColor(display,windows->map_info->colormap,
2091              resource_info->pen_colors[pen_number],&color);
2092            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2093              (unsigned int) MaxColors,&color);
2094            windows->pixel_info->pen_colors[pen_number]=color;
2095            box_id=(unsigned int) pen_number;
2096            break;
2097          }
2098          case AnnotateRotateCommand:
2099          {
2100            int
2101              entry;
2102
2103            static char
2104              angle[MaxTextExtent] = "30.0";
2105
2106            static const char
2107              *RotateMenu[] =
2108              {
2109                "-90",
2110                "-45",
2111                "-30",
2112                "0",
2113                "30",
2114                "45",
2115                "90",
2116                "180",
2117                "Dialog...",
2118                (char *) NULL,
2119              };
2120
2121            /*
2122              Select a command from the pop-up menu.
2123            */
2124            entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2125              command);
2126            if (entry < 0)
2127              break;
2128            if (entry != 8)
2129              {
2130                degrees=StringToDouble(RotateMenu[entry],(char **) NULL);
2131                break;
2132              }
2133            (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2134              angle);
2135            if (*angle == '\0')
2136              break;
2137            degrees=StringToDouble(angle,(char **) NULL);
2138            break;
2139          }
2140          case AnnotateHelpCommand:
2141          {
2142            XTextViewWidget(display,resource_info,windows,MagickFalse,
2143              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2144            break;
2145          }
2146          case AnnotateDismissCommand:
2147          {
2148            /*
2149              Prematurely exit.
2150            */
2151            state|=EscapeState;
2152            state|=ExitState;
2153            break;
2154          }
2155          default:
2156            break;
2157        }
2158        continue;
2159      }
2160    switch (event.type)
2161    {
2162      case ButtonPress:
2163      {
2164        if (event.xbutton.button != Button1)
2165          break;
2166        if (event.xbutton.window != windows->image.id)
2167          break;
2168        /*
2169          Change to text entering mode.
2170        */
2171        x=event.xbutton.x;
2172        y=event.xbutton.y;
2173        state|=ExitState;
2174        break;
2175      }
2176      case ButtonRelease:
2177        break;
2178      case Expose:
2179        break;
2180      case KeyPress:
2181      {
2182        if (event.xkey.window != windows->image.id)
2183          break;
2184        /*
2185          Respond to a user key press.
2186        */
2187        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2188          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2189        switch ((int) key_symbol)
2190        {
2191          case XK_Escape:
2192          case XK_F20:
2193          {
2194            /*
2195              Prematurely exit.
2196            */
2197            state|=EscapeState;
2198            state|=ExitState;
2199            break;
2200          }
2201          case XK_F1:
2202          case XK_Help:
2203          {
2204            XTextViewWidget(display,resource_info,windows,MagickFalse,
2205              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2206            break;
2207          }
2208          default:
2209          {
2210            (void) XBell(display,0);
2211            break;
2212          }
2213        }
2214        break;
2215      }
2216      case MotionNotify:
2217      {
2218        /*
2219          Map and unmap Info widget as cursor crosses its boundaries.
2220        */
2221        x=event.xmotion.x;
2222        y=event.xmotion.y;
2223        if (windows->info.mapped != MagickFalse)
2224          {
2225            if ((x < (int) (windows->info.x+windows->info.width)) &&
2226                (y < (int) (windows->info.y+windows->info.height)))
2227              (void) XWithdrawWindow(display,windows->info.id,
2228                windows->info.screen);
2229          }
2230        else
2231          if ((x > (int) (windows->info.x+windows->info.width)) ||
2232              (y > (int) (windows->info.y+windows->info.height)))
2233            (void) XMapWindow(display,windows->info.id);
2234        break;
2235      }
2236      default:
2237        break;
2238    }
2239  } while ((state & ExitState) == 0);
2240  (void) XSelectInput(display,windows->image.id,
2241    windows->image.attributes.event_mask);
2242  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2243  if ((state & EscapeState) != 0)
2244    return(MagickTrue);
2245  /*
2246    Set font info and check boundary conditions.
2247  */
2248  font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2249  if (font_info == (XFontStruct *) NULL)
2250    {
2251      XNoticeWidget(display,windows,"Unable to load font:",
2252        resource_info->font_name[font_id]);
2253      font_info=windows->font_info;
2254    }
2255  if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2256    x=(int) windows->image.width-font_info->max_bounds.width;
2257  if (y < (int) (font_info->ascent+font_info->descent))
2258    y=(int) font_info->ascent+font_info->descent;
2259  if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2260      ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2261    return(MagickFalse);
2262  /*
2263    Initialize annotate structure.
2264  */
2265  annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2266  if (annotate_info == (XAnnotateInfo *) NULL)
2267    return(MagickFalse);
2268  XGetAnnotateInfo(annotate_info);
2269  annotate_info->x=x;
2270  annotate_info->y=y;
2271  if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
2272    annotate_info->stencil=OpaqueStencil;
2273  else
2274    if (transparent_box == MagickFalse)
2275      annotate_info->stencil=BackgroundStencil;
2276    else
2277      annotate_info->stencil=ForegroundStencil;
2278  annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2279  annotate_info->degrees=degrees;
2280  annotate_info->font_info=font_info;
2281  annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2282    windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
2283    sizeof(*annotate_info->text));
2284  if (annotate_info->text == (char *) NULL)
2285    return(MagickFalse);
2286  /*
2287    Create cursor and set graphic context.
2288  */
2289  cursor=XCreateFontCursor(display,XC_pencil);
2290  (void) XCheckDefineCursor(display,windows->image.id,cursor);
2291  annotate_context=windows->image.annotate_context;
2292  (void) XSetFont(display,annotate_context,font_info->fid);
2293  (void) XSetBackground(display,annotate_context,
2294    windows->pixel_info->pen_colors[box_id].pixel);
2295  (void) XSetForeground(display,annotate_context,
2296    windows->pixel_info->pen_colors[pen_id].pixel);
2297  /*
2298    Begin annotating the image with text.
2299  */
2300  (void) CloneString(&windows->command.name,"Text");
2301  windows->command.data=0;
2302  (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2303  state=DefaultState;
2304  (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2305  text_event.xexpose.width=(int) font_info->max_bounds.width;
2306  text_event.xexpose.height=font_info->max_bounds.ascent+
2307    font_info->max_bounds.descent;
2308  p=annotate_info->text;
2309  do
2310  {
2311    /*
2312      Display text cursor.
2313    */
2314    *p='\0';
2315    (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2316    /*
2317      Wait for next event.
2318    */
2319    XScreenEvent(display,windows,&event,exception);
2320    if (event.xany.window == windows->command.id)
2321      {
2322        /*
2323          Select a command from the Command widget.
2324        */
2325        (void) XSetBackground(display,annotate_context,
2326          windows->pixel_info->background_color.pixel);
2327        (void) XSetForeground(display,annotate_context,
2328          windows->pixel_info->foreground_color.pixel);
2329        id=XCommandWidget(display,windows,AnnotateMenu,&event);
2330        (void) XSetBackground(display,annotate_context,
2331          windows->pixel_info->pen_colors[box_id].pixel);
2332        (void) XSetForeground(display,annotate_context,
2333          windows->pixel_info->pen_colors[pen_id].pixel);
2334        if (id < 0)
2335          continue;
2336        switch (TextCommands[id])
2337        {
2338          case TextHelpCommand:
2339          {
2340            XTextViewWidget(display,resource_info,windows,MagickFalse,
2341              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2342            (void) XCheckDefineCursor(display,windows->image.id,cursor);
2343            break;
2344          }
2345          case TextApplyCommand:
2346          {
2347            /*
2348              Finished annotating.
2349            */
2350            annotate_info->width=(unsigned int) XTextWidth(font_info,
2351              annotate_info->text,(int) strlen(annotate_info->text));
2352            XRefreshWindow(display,&windows->image,&text_event);
2353            state|=ExitState;
2354            break;
2355          }
2356          default:
2357            break;
2358        }
2359        continue;
2360      }
2361    /*
2362      Erase text cursor.
2363    */
2364    text_event.xexpose.x=x;
2365    text_event.xexpose.y=y-font_info->max_bounds.ascent;
2366    (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2367      (unsigned int) text_event.xexpose.width,(unsigned int)
2368      text_event.xexpose.height,MagickFalse);
2369    XRefreshWindow(display,&windows->image,&text_event);
2370    switch (event.type)
2371    {
2372      case ButtonPress:
2373      {
2374        if (event.xbutton.window != windows->image.id)
2375          break;
2376        if (event.xbutton.button == Button2)
2377          {
2378            /*
2379              Request primary selection.
2380            */
2381            (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2382              windows->image.id,CurrentTime);
2383            break;
2384          }
2385        break;
2386      }
2387      case Expose:
2388      {
2389        if (event.xexpose.count == 0)
2390          {
2391            XAnnotateInfo
2392              *text_info;
2393
2394            /*
2395              Refresh Image window.
2396            */
2397            XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2398            text_info=annotate_info;
2399            while (text_info != (XAnnotateInfo *) NULL)
2400            {
2401              if (annotate_info->stencil == ForegroundStencil)
2402                (void) XDrawString(display,windows->image.id,annotate_context,
2403                  text_info->x,text_info->y,text_info->text,
2404                  (int) strlen(text_info->text));
2405              else
2406                (void) XDrawImageString(display,windows->image.id,
2407                  annotate_context,text_info->x,text_info->y,text_info->text,
2408                  (int) strlen(text_info->text));
2409              text_info=text_info->previous;
2410            }
2411            (void) XDrawString(display,windows->image.id,annotate_context,
2412              x,y,"_",1);
2413          }
2414        break;
2415      }
2416      case KeyPress:
2417      {
2418        int
2419          length;
2420
2421        if (event.xkey.window != windows->image.id)
2422          break;
2423        /*
2424          Respond to a user key press.
2425        */
2426        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2427          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2428        *(command+length)='\0';
2429        if (((event.xkey.state & ControlMask) != 0) ||
2430            ((event.xkey.state & Mod1Mask) != 0))
2431          state|=ModifierState;
2432        if ((state & ModifierState) != 0)
2433          switch ((int) key_symbol)
2434          {
2435            case XK_u:
2436            case XK_U:
2437            {
2438              key_symbol=DeleteCommand;
2439              break;
2440            }
2441            default:
2442              break;
2443          }
2444        switch ((int) key_symbol)
2445        {
2446          case XK_BackSpace:
2447          {
2448            /*
2449              Erase one character.
2450            */
2451            if (p == annotate_info->text)
2452              {
2453                if (annotate_info->previous == (XAnnotateInfo *) NULL)
2454                  break;
2455                else
2456                  {
2457                    /*
2458                      Go to end of the previous line of text.
2459                    */
2460                    annotate_info=annotate_info->previous;
2461                    p=annotate_info->text;
2462                    x=annotate_info->x+annotate_info->width;
2463                    y=annotate_info->y;
2464                    if (annotate_info->width != 0)
2465                      p+=strlen(annotate_info->text);
2466                    break;
2467                  }
2468              }
2469            p--;
2470            x-=XTextWidth(font_info,p,1);
2471            text_event.xexpose.x=x;
2472            text_event.xexpose.y=y-font_info->max_bounds.ascent;
2473            XRefreshWindow(display,&windows->image,&text_event);
2474            break;
2475          }
2476          case XK_bracketleft:
2477          {
2478            key_symbol=XK_Escape;
2479            break;
2480          }
2481          case DeleteCommand:
2482          {
2483            /*
2484              Erase the entire line of text.
2485            */
2486            while (p != annotate_info->text)
2487            {
2488              p--;
2489              x-=XTextWidth(font_info,p,1);
2490              text_event.xexpose.x=x;
2491              XRefreshWindow(display,&windows->image,&text_event);
2492            }
2493            break;
2494          }
2495          case XK_Escape:
2496          case XK_F20:
2497          {
2498            /*
2499              Finished annotating.
2500            */
2501            annotate_info->width=(unsigned int) XTextWidth(font_info,
2502              annotate_info->text,(int) strlen(annotate_info->text));
2503            XRefreshWindow(display,&windows->image,&text_event);
2504            state|=ExitState;
2505            break;
2506          }
2507          default:
2508          {
2509            /*
2510              Draw a single character on the Image window.
2511            */
2512            if ((state & ModifierState) != 0)
2513              break;
2514            if (*command == '\0')
2515              break;
2516            *p=(*command);
2517            if (annotate_info->stencil == ForegroundStencil)
2518              (void) XDrawString(display,windows->image.id,annotate_context,
2519                x,y,p,1);
2520            else
2521              (void) XDrawImageString(display,windows->image.id,
2522                annotate_context,x,y,p,1);
2523            x+=XTextWidth(font_info,p,1);
2524            p++;
2525            if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2526              break;
2527          }
2528          case XK_Return:
2529          case XK_KP_Enter:
2530          {
2531            /*
2532              Advance to the next line of text.
2533            */
2534            *p='\0';
2535            annotate_info->width=(unsigned int) XTextWidth(font_info,
2536              annotate_info->text,(int) strlen(annotate_info->text));
2537            if (annotate_info->next != (XAnnotateInfo *) NULL)
2538              {
2539                /*
2540                  Line of text already exists.
2541                */
2542                annotate_info=annotate_info->next;
2543                x=annotate_info->x;
2544                y=annotate_info->y;
2545                p=annotate_info->text;
2546                break;
2547              }
2548            annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2549              sizeof(*annotate_info->next));
2550            if (annotate_info->next == (XAnnotateInfo *) NULL)
2551              return(MagickFalse);
2552            *annotate_info->next=(*annotate_info);
2553            annotate_info->next->previous=annotate_info;
2554            annotate_info=annotate_info->next;
2555            annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2556              windows->image.width/MagickMax((ssize_t)
2557              font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2558            if (annotate_info->text == (char *) NULL)
2559              return(MagickFalse);
2560            annotate_info->y+=annotate_info->height;
2561            if (annotate_info->y > (int) windows->image.height)
2562              annotate_info->y=(int) annotate_info->height;
2563            annotate_info->next=(XAnnotateInfo *) NULL;
2564            x=annotate_info->x;
2565            y=annotate_info->y;
2566            p=annotate_info->text;
2567            break;
2568          }
2569        }
2570        break;
2571      }
2572      case KeyRelease:
2573      {
2574        /*
2575          Respond to a user key release.
2576        */
2577        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2578          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2579        state&=(~ModifierState);
2580        break;
2581      }
2582      case SelectionNotify:
2583      {
2584        Atom
2585          type;
2586
2587        int
2588          format;
2589
2590        unsigned char
2591          *data;
2592
2593        unsigned long
2594          after,
2595          length;
2596
2597        /*
2598          Obtain response from primary selection.
2599        */
2600        if (event.xselection.property == (Atom) None)
2601          break;
2602        status=XGetWindowProperty(display,event.xselection.requestor,
2603          event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
2604          &type,&format,&length,&after,&data);
2605        if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2606            (length == 0))
2607          break;
2608        /*
2609          Annotate Image window with primary selection.
2610        */
2611        for (i=0; i < (ssize_t) length; i++)
2612        {
2613          if ((char) data[i] != '\n')
2614            {
2615              /*
2616                Draw a single character on the Image window.
2617              */
2618              *p=(char) data[i];
2619              (void) XDrawString(display,windows->image.id,annotate_context,
2620                x,y,p,1);
2621              x+=XTextWidth(font_info,p,1);
2622              p++;
2623              if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2624                continue;
2625            }
2626          /*
2627            Advance to the next line of text.
2628          */
2629          *p='\0';
2630          annotate_info->width=(unsigned int) XTextWidth(font_info,
2631            annotate_info->text,(int) strlen(annotate_info->text));
2632          if (annotate_info->next != (XAnnotateInfo *) NULL)
2633            {
2634              /*
2635                Line of text already exists.
2636              */
2637              annotate_info=annotate_info->next;
2638              x=annotate_info->x;
2639              y=annotate_info->y;
2640              p=annotate_info->text;
2641              continue;
2642            }
2643          annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2644            sizeof(*annotate_info->next));
2645          if (annotate_info->next == (XAnnotateInfo *) NULL)
2646            return(MagickFalse);
2647          *annotate_info->next=(*annotate_info);
2648          annotate_info->next->previous=annotate_info;
2649          annotate_info=annotate_info->next;
2650          annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2651            windows->image.width/MagickMax((ssize_t)
2652            font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2653          if (annotate_info->text == (char *) NULL)
2654            return(MagickFalse);
2655          annotate_info->y+=annotate_info->height;
2656          if (annotate_info->y > (int) windows->image.height)
2657            annotate_info->y=(int) annotate_info->height;
2658          annotate_info->next=(XAnnotateInfo *) NULL;
2659          x=annotate_info->x;
2660          y=annotate_info->y;
2661          p=annotate_info->text;
2662        }
2663        (void) XFree((void *) data);
2664        break;
2665      }
2666      default:
2667        break;
2668    }
2669  } while ((state & ExitState) == 0);
2670  (void) XFreeCursor(display,cursor);
2671  /*
2672    Annotation is relative to image configuration.
2673  */
2674  width=(unsigned int) image->columns;
2675  height=(unsigned int) image->rows;
2676  x=0;
2677  y=0;
2678  if (windows->image.crop_geometry != (char *) NULL)
2679    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2680  /*
2681    Initialize annotated image.
2682  */
2683  XSetCursorState(display,windows,MagickTrue);
2684  XCheckRefreshWindows(display,windows);
2685  while (annotate_info != (XAnnotateInfo *) NULL)
2686  {
2687    if (annotate_info->width == 0)
2688      {
2689        /*
2690          No text on this line--  go to the next line of text.
2691        */
2692        previous_info=annotate_info->previous;
2693        annotate_info->text=(char *)
2694          RelinquishMagickMemory(annotate_info->text);
2695        annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2696        annotate_info=previous_info;
2697        continue;
2698      }
2699    /*
2700      Determine pixel index for box and pen color.
2701    */
2702    windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2703    if (windows->pixel_info->colors != 0)
2704      for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2705        if (windows->pixel_info->pixels[i] ==
2706            windows->pixel_info->pen_colors[box_id].pixel)
2707          {
2708            windows->pixel_info->box_index=(unsigned short) i;
2709            break;
2710          }
2711    windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2712    if (windows->pixel_info->colors != 0)
2713      for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2714        if (windows->pixel_info->pixels[i] ==
2715            windows->pixel_info->pen_colors[pen_id].pixel)
2716          {
2717            windows->pixel_info->pen_index=(unsigned short) i;
2718            break;
2719          }
2720    /*
2721      Define the annotate geometry string.
2722    */
2723    annotate_info->x=(int)
2724      width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2725    annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2726      windows->image.y)/windows->image.ximage->height;
2727    (void) FormatLocaleString(annotate_info->geometry,MaxTextExtent,
2728      "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2729      height*annotate_info->height/windows->image.ximage->height,
2730      annotate_info->x+x,annotate_info->y+y);
2731    /*
2732      Annotate image with text.
2733    */
2734    status=XAnnotateImage(display,windows->pixel_info,annotate_info,image,
2735      exception);
2736    if (status == 0)
2737      return(MagickFalse);
2738    /*
2739      Free up memory.
2740    */
2741    previous_info=annotate_info->previous;
2742    annotate_info->text=DestroyString(annotate_info->text);
2743    annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2744    annotate_info=previous_info;
2745  }
2746  (void) XSetForeground(display,annotate_context,
2747    windows->pixel_info->foreground_color.pixel);
2748  (void) XSetBackground(display,annotate_context,
2749    windows->pixel_info->background_color.pixel);
2750  (void) XSetFont(display,annotate_context,windows->font_info->fid);
2751  XSetCursorState(display,windows,MagickFalse);
2752  (void) XFreeFont(display,font_info);
2753  /*
2754    Update image configuration.
2755  */
2756  XConfigureImageColormap(display,resource_info,windows,image,exception);
2757  (void) XConfigureImage(display,resource_info,windows,image,exception);
2758  return(MagickTrue);
2759}
2760
2761/*
2762%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2763%                                                                             %
2764%                                                                             %
2765%                                                                             %
2766+   X B a c k g r o u n d I m a g e                                           %
2767%                                                                             %
2768%                                                                             %
2769%                                                                             %
2770%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2771%
2772%  XBackgroundImage() displays the image in the background of a window.
2773%
2774%  The format of the XBackgroundImage method is:
2775%
2776%      MagickBooleanType XBackgroundImage(Display *display,
2777%        XResourceInfo *resource_info,XWindows *windows,Image **image,
2778%        ExceptionInfo *exception)
2779%
2780%  A description of each parameter follows:
2781%
2782%    o display: Specifies a connection to an X server; returned from
2783%      XOpenDisplay.
2784%
2785%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2786%
2787%    o windows: Specifies a pointer to a XWindows structure.
2788%
2789%    o image: the image.
2790%
2791%    o exception: return any errors or warnings in this structure.
2792%
2793*/
2794static MagickBooleanType XBackgroundImage(Display *display,
2795  XResourceInfo *resource_info,XWindows *windows,Image **image,
2796  ExceptionInfo *exception)
2797{
2798#define BackgroundImageTag  "Background/Image"
2799
2800  int
2801    status;
2802
2803  static char
2804    window_id[MaxTextExtent] = "root";
2805
2806  XResourceInfo
2807    background_resources;
2808
2809  /*
2810    Put image in background.
2811  */
2812  status=XDialogWidget(display,windows,"Background",
2813    "Enter window id (id 0x00 selects window with pointer):",window_id);
2814  if (*window_id == '\0')
2815    return(MagickFalse);
2816  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
2817    exception);
2818  XInfoWidget(display,windows,BackgroundImageTag);
2819  XSetCursorState(display,windows,MagickTrue);
2820  XCheckRefreshWindows(display,windows);
2821  background_resources=(*resource_info);
2822  background_resources.window_id=window_id;
2823  background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2824  status=XDisplayBackgroundImage(display,&background_resources,*image,
2825    exception);
2826  if (status != MagickFalse)
2827    XClientMessage(display,windows->image.id,windows->im_protocols,
2828      windows->im_retain_colors,CurrentTime);
2829  XSetCursorState(display,windows,MagickFalse);
2830  (void) XMagickCommand(display,resource_info,windows,UndoCommand,image,
2831    exception);
2832  return(MagickTrue);
2833}
2834
2835/*
2836%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2837%                                                                             %
2838%                                                                             %
2839%                                                                             %
2840+   X C h o p I m a g e                                                       %
2841%                                                                             %
2842%                                                                             %
2843%                                                                             %
2844%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2845%
2846%  XChopImage() chops the X image.
2847%
2848%  The format of the XChopImage method is:
2849%
2850%    MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2851%      XWindows *windows,Image **image,ExceptionInfo *exception)
2852%
2853%  A description of each parameter follows:
2854%
2855%    o display: Specifies a connection to an X server; returned from
2856%      XOpenDisplay.
2857%
2858%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2859%
2860%    o windows: Specifies a pointer to a XWindows structure.
2861%
2862%    o image: the image.
2863%
2864%    o exception: return any errors or warnings in this structure.
2865%
2866*/
2867static MagickBooleanType XChopImage(Display *display,
2868  XResourceInfo *resource_info,XWindows *windows,Image **image,
2869  ExceptionInfo *exception)
2870{
2871  static const char
2872    *ChopMenu[] =
2873    {
2874      "Direction",
2875      "Help",
2876      "Dismiss",
2877      (char *) NULL
2878    };
2879
2880  static ModeType
2881    direction = HorizontalChopCommand;
2882
2883  static const ModeType
2884    ChopCommands[] =
2885    {
2886      ChopDirectionCommand,
2887      ChopHelpCommand,
2888      ChopDismissCommand
2889    },
2890    DirectionCommands[] =
2891    {
2892      HorizontalChopCommand,
2893      VerticalChopCommand
2894    };
2895
2896  char
2897    text[MaxTextExtent];
2898
2899  Image
2900    *chop_image;
2901
2902  int
2903    id,
2904    x,
2905    y;
2906
2907  MagickRealType
2908    scale_factor;
2909
2910  RectangleInfo
2911    chop_info;
2912
2913  unsigned int
2914    distance,
2915    height,
2916    width;
2917
2918  size_t
2919    state;
2920
2921  XEvent
2922    event;
2923
2924  XSegment
2925    segment_info;
2926
2927  /*
2928    Map Command widget.
2929  */
2930  (void) CloneString(&windows->command.name,"Chop");
2931  windows->command.data=1;
2932  (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2933  (void) XMapRaised(display,windows->command.id);
2934  XClientMessage(display,windows->image.id,windows->im_protocols,
2935    windows->im_update_widget,CurrentTime);
2936  /*
2937    Track pointer until button 1 is pressed.
2938  */
2939  XQueryPosition(display,windows->image.id,&x,&y);
2940  (void) XSelectInput(display,windows->image.id,
2941    windows->image.attributes.event_mask | PointerMotionMask);
2942  state=DefaultState;
2943  do
2944  {
2945    if (windows->info.mapped != MagickFalse)
2946      {
2947        /*
2948          Display pointer position.
2949        */
2950        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
2951          x+windows->image.x,y+windows->image.y);
2952        XInfoWidget(display,windows,text);
2953      }
2954    /*
2955      Wait for next event.
2956    */
2957    XScreenEvent(display,windows,&event,exception);
2958    if (event.xany.window == windows->command.id)
2959      {
2960        /*
2961          Select a command from the Command widget.
2962        */
2963        id=XCommandWidget(display,windows,ChopMenu,&event);
2964        if (id < 0)
2965          continue;
2966        switch (ChopCommands[id])
2967        {
2968          case ChopDirectionCommand:
2969          {
2970            char
2971              command[MaxTextExtent];
2972
2973            static const char
2974              *Directions[] =
2975              {
2976                "horizontal",
2977                "vertical",
2978                (char *) NULL,
2979              };
2980
2981            /*
2982              Select a command from the pop-up menu.
2983            */
2984            id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2985            if (id >= 0)
2986              direction=DirectionCommands[id];
2987            break;
2988          }
2989          case ChopHelpCommand:
2990          {
2991            XTextViewWidget(display,resource_info,windows,MagickFalse,
2992              "Help Viewer - Image Chop",ImageChopHelp);
2993            break;
2994          }
2995          case ChopDismissCommand:
2996          {
2997            /*
2998              Prematurely exit.
2999            */
3000            state|=EscapeState;
3001            state|=ExitState;
3002            break;
3003          }
3004          default:
3005            break;
3006        }
3007        continue;
3008      }
3009    switch (event.type)
3010    {
3011      case ButtonPress:
3012      {
3013        if (event.xbutton.button != Button1)
3014          break;
3015        if (event.xbutton.window != windows->image.id)
3016          break;
3017        /*
3018          User has committed to start point of chopping line.
3019        */
3020        segment_info.x1=(short int) event.xbutton.x;
3021        segment_info.x2=(short int) event.xbutton.x;
3022        segment_info.y1=(short int) event.xbutton.y;
3023        segment_info.y2=(short int) event.xbutton.y;
3024        state|=ExitState;
3025        break;
3026      }
3027      case ButtonRelease:
3028        break;
3029      case Expose:
3030        break;
3031      case KeyPress:
3032      {
3033        char
3034          command[MaxTextExtent];
3035
3036        KeySym
3037          key_symbol;
3038
3039        if (event.xkey.window != windows->image.id)
3040          break;
3041        /*
3042          Respond to a user key press.
3043        */
3044        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3045          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3046        switch ((int) key_symbol)
3047        {
3048          case XK_Escape:
3049          case XK_F20:
3050          {
3051            /*
3052              Prematurely exit.
3053            */
3054            state|=EscapeState;
3055            state|=ExitState;
3056            break;
3057          }
3058          case XK_F1:
3059          case XK_Help:
3060          {
3061            (void) XSetFunction(display,windows->image.highlight_context,
3062              GXcopy);
3063            XTextViewWidget(display,resource_info,windows,MagickFalse,
3064              "Help Viewer - Image Chop",ImageChopHelp);
3065            (void) XSetFunction(display,windows->image.highlight_context,
3066              GXinvert);
3067            break;
3068          }
3069          default:
3070          {
3071            (void) XBell(display,0);
3072            break;
3073          }
3074        }
3075        break;
3076      }
3077      case MotionNotify:
3078      {
3079        /*
3080          Map and unmap Info widget as text cursor crosses its boundaries.
3081        */
3082        x=event.xmotion.x;
3083        y=event.xmotion.y;
3084        if (windows->info.mapped != MagickFalse)
3085          {
3086            if ((x < (int) (windows->info.x+windows->info.width)) &&
3087                (y < (int) (windows->info.y+windows->info.height)))
3088              (void) XWithdrawWindow(display,windows->info.id,
3089                windows->info.screen);
3090          }
3091        else
3092          if ((x > (int) (windows->info.x+windows->info.width)) ||
3093              (y > (int) (windows->info.y+windows->info.height)))
3094            (void) XMapWindow(display,windows->info.id);
3095      }
3096    }
3097  } while ((state & ExitState) == 0);
3098  (void) XSelectInput(display,windows->image.id,
3099    windows->image.attributes.event_mask);
3100  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3101  if ((state & EscapeState) != 0)
3102    return(MagickTrue);
3103  /*
3104    Draw line as pointer moves until the mouse button is released.
3105  */
3106  chop_info.width=0;
3107  chop_info.height=0;
3108  chop_info.x=0;
3109  chop_info.y=0;
3110  distance=0;
3111  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3112  state=DefaultState;
3113  do
3114  {
3115    if (distance > 9)
3116      {
3117        /*
3118          Display info and draw chopping line.
3119        */
3120        if (windows->info.mapped == MagickFalse)
3121          (void) XMapWindow(display,windows->info.id);
3122        (void) FormatLocaleString(text,MaxTextExtent,
3123          " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3124          chop_info.height,(double) chop_info.x,(double) chop_info.y);
3125        XInfoWidget(display,windows,text);
3126        XHighlightLine(display,windows->image.id,
3127          windows->image.highlight_context,&segment_info);
3128      }
3129    else
3130      if (windows->info.mapped != MagickFalse)
3131        (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3132    /*
3133      Wait for next event.
3134    */
3135    XScreenEvent(display,windows,&event,exception);
3136    if (distance > 9)
3137      XHighlightLine(display,windows->image.id,
3138        windows->image.highlight_context,&segment_info);
3139    switch (event.type)
3140    {
3141      case ButtonPress:
3142      {
3143        segment_info.x2=(short int) event.xmotion.x;
3144        segment_info.y2=(short int) event.xmotion.y;
3145        break;
3146      }
3147      case ButtonRelease:
3148      {
3149        /*
3150          User has committed to chopping line.
3151        */
3152        segment_info.x2=(short int) event.xbutton.x;
3153        segment_info.y2=(short int) event.xbutton.y;
3154        state|=ExitState;
3155        break;
3156      }
3157      case Expose:
3158        break;
3159      case MotionNotify:
3160      {
3161        segment_info.x2=(short int) event.xmotion.x;
3162        segment_info.y2=(short int) event.xmotion.y;
3163      }
3164      default:
3165        break;
3166    }
3167    /*
3168      Check boundary conditions.
3169    */
3170    if (segment_info.x2 < 0)
3171      segment_info.x2=0;
3172    else
3173      if (segment_info.x2 > windows->image.ximage->width)
3174        segment_info.x2=windows->image.ximage->width;
3175    if (segment_info.y2 < 0)
3176      segment_info.y2=0;
3177    else
3178      if (segment_info.y2 > windows->image.ximage->height)
3179        segment_info.y2=windows->image.ximage->height;
3180    distance=(unsigned int)
3181      (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3182       ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3183    /*
3184      Compute chopping geometry.
3185    */
3186    if (direction == HorizontalChopCommand)
3187      {
3188        chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3189        chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3190        chop_info.height=0;
3191        chop_info.y=0;
3192        if (segment_info.x1 > (int) segment_info.x2)
3193          {
3194            chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3195            chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3196          }
3197      }
3198    else
3199      {
3200        chop_info.width=0;
3201        chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3202        chop_info.x=0;
3203        chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3204        if (segment_info.y1 > segment_info.y2)
3205          {
3206            chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3207            chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3208          }
3209      }
3210  } while ((state & ExitState) == 0);
3211  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3212  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3213  if (distance <= 9)
3214    return(MagickTrue);
3215  /*
3216    Image chopping is relative to image configuration.
3217  */
3218  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
3219    exception);
3220  XSetCursorState(display,windows,MagickTrue);
3221  XCheckRefreshWindows(display,windows);
3222  windows->image.window_changes.width=windows->image.ximage->width-
3223    (unsigned int) chop_info.width;
3224  windows->image.window_changes.height=windows->image.ximage->height-
3225    (unsigned int) chop_info.height;
3226  width=(unsigned int) (*image)->columns;
3227  height=(unsigned int) (*image)->rows;
3228  x=0;
3229  y=0;
3230  if (windows->image.crop_geometry != (char *) NULL)
3231    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3232  scale_factor=(MagickRealType) width/windows->image.ximage->width;
3233  chop_info.x+=x;
3234  chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3235  chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3236  scale_factor=(MagickRealType) height/windows->image.ximage->height;
3237  chop_info.y+=y;
3238  chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3239  chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3240  /*
3241    Chop image.
3242  */
3243  chop_image=ChopImage(*image,&chop_info,exception);
3244  XSetCursorState(display,windows,MagickFalse);
3245  if (chop_image == (Image *) NULL)
3246    return(MagickFalse);
3247  *image=DestroyImage(*image);
3248  *image=chop_image;
3249  /*
3250    Update image configuration.
3251  */
3252  XConfigureImageColormap(display,resource_info,windows,*image,exception);
3253  (void) XConfigureImage(display,resource_info,windows,*image,exception);
3254  return(MagickTrue);
3255}
3256
3257/*
3258%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3259%                                                                             %
3260%                                                                             %
3261%                                                                             %
3262+   X C o l o r E d i t I m a g e                                             %
3263%                                                                             %
3264%                                                                             %
3265%                                                                             %
3266%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3267%
3268%  XColorEditImage() allows the user to interactively change the color of one
3269%  pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3270%
3271%  The format of the XColorEditImage method is:
3272%
3273%      MagickBooleanType XColorEditImage(Display *display,
3274%        XResourceInfo *resource_info,XWindows *windows,Image **image,
3275%          ExceptionInfo *exception)
3276%
3277%  A description of each parameter follows:
3278%
3279%    o display: Specifies a connection to an X server;  returned from
3280%      XOpenDisplay.
3281%
3282%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3283%
3284%    o windows: Specifies a pointer to a XWindows structure.
3285%
3286%    o image: the image; returned from ReadImage.
3287%
3288%    o exception: return any errors or warnings in this structure.
3289%
3290*/
3291static MagickBooleanType XColorEditImage(Display *display,
3292  XResourceInfo *resource_info,XWindows *windows,Image **image,
3293  ExceptionInfo *exception)
3294{
3295  static const char
3296    *ColorEditMenu[] =
3297    {
3298      "Method",
3299      "Pixel Color",
3300      "Border Color",
3301      "Fuzz",
3302      "Undo",
3303      "Help",
3304      "Dismiss",
3305      (char *) NULL
3306    };
3307
3308  static const ModeType
3309    ColorEditCommands[] =
3310    {
3311      ColorEditMethodCommand,
3312      ColorEditColorCommand,
3313      ColorEditBorderCommand,
3314      ColorEditFuzzCommand,
3315      ColorEditUndoCommand,
3316      ColorEditHelpCommand,
3317      ColorEditDismissCommand
3318    };
3319
3320  static PaintMethod
3321    method = PointMethod;
3322
3323  static unsigned int
3324    pen_id = 0;
3325
3326  static XColor
3327    border_color = { 0, 0, 0, 0, 0, 0 };
3328
3329  char
3330    command[MaxTextExtent],
3331    text[MaxTextExtent];
3332
3333  Cursor
3334    cursor;
3335
3336  int
3337    entry,
3338    id,
3339    x,
3340    x_offset,
3341    y,
3342    y_offset;
3343
3344  register Quantum
3345    *q;
3346
3347  register ssize_t
3348    i;
3349
3350  unsigned int
3351    height,
3352    width;
3353
3354  size_t
3355    state;
3356
3357  XColor
3358    color;
3359
3360  XEvent
3361    event;
3362
3363  /*
3364    Map Command widget.
3365  */
3366  (void) CloneString(&windows->command.name,"Color Edit");
3367  windows->command.data=4;
3368  (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3369  (void) XMapRaised(display,windows->command.id);
3370  XClientMessage(display,windows->image.id,windows->im_protocols,
3371    windows->im_update_widget,CurrentTime);
3372  /*
3373    Make cursor.
3374  */
3375  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3376    resource_info->background_color,resource_info->foreground_color);
3377  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3378  /*
3379    Track pointer until button 1 is pressed.
3380  */
3381  XQueryPosition(display,windows->image.id,&x,&y);
3382  (void) XSelectInput(display,windows->image.id,
3383    windows->image.attributes.event_mask | PointerMotionMask);
3384  state=DefaultState;
3385  do
3386  {
3387    if (windows->info.mapped != MagickFalse)
3388      {
3389        /*
3390          Display pointer position.
3391        */
3392        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
3393          x+windows->image.x,y+windows->image.y);
3394        XInfoWidget(display,windows,text);
3395      }
3396    /*
3397      Wait for next event.
3398    */
3399    XScreenEvent(display,windows,&event,exception);
3400    if (event.xany.window == windows->command.id)
3401      {
3402        /*
3403          Select a command from the Command widget.
3404        */
3405        id=XCommandWidget(display,windows,ColorEditMenu,&event);
3406        if (id < 0)
3407          {
3408            (void) XCheckDefineCursor(display,windows->image.id,cursor);
3409            continue;
3410          }
3411        switch (ColorEditCommands[id])
3412        {
3413          case ColorEditMethodCommand:
3414          {
3415            char
3416              **methods;
3417
3418            /*
3419              Select a method from the pop-up menu.
3420            */
3421            methods=(char **) GetCommandOptions(MagickMethodOptions);
3422            if (methods == (char **) NULL)
3423              break;
3424            entry=XMenuWidget(display,windows,ColorEditMenu[id],
3425              (const char **) methods,command);
3426            if (entry >= 0)
3427              method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3428                MagickFalse,methods[entry]);
3429            methods=DestroyStringList(methods);
3430            break;
3431          }
3432          case ColorEditColorCommand:
3433          {
3434            const char
3435              *ColorMenu[MaxNumberPens];
3436
3437            int
3438              pen_number;
3439
3440            /*
3441              Initialize menu selections.
3442            */
3443            for (i=0; i < (int) (MaxNumberPens-2); i++)
3444              ColorMenu[i]=resource_info->pen_colors[i];
3445            ColorMenu[MaxNumberPens-2]="Browser...";
3446            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3447            /*
3448              Select a pen color from the pop-up menu.
3449            */
3450            pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3451              (const char **) ColorMenu,command);
3452            if (pen_number < 0)
3453              break;
3454            if (pen_number == (MaxNumberPens-2))
3455              {
3456                static char
3457                  color_name[MaxTextExtent] = "gray";
3458
3459                /*
3460                  Select a pen color from a dialog.
3461                */
3462                resource_info->pen_colors[pen_number]=color_name;
3463                XColorBrowserWidget(display,windows,"Select",color_name);
3464                if (*color_name == '\0')
3465                  break;
3466              }
3467            /*
3468              Set pen color.
3469            */
3470            (void) XParseColor(display,windows->map_info->colormap,
3471              resource_info->pen_colors[pen_number],&color);
3472            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3473              (unsigned int) MaxColors,&color);
3474            windows->pixel_info->pen_colors[pen_number]=color;
3475            pen_id=(unsigned int) pen_number;
3476            break;
3477          }
3478          case ColorEditBorderCommand:
3479          {
3480            const char
3481              *ColorMenu[MaxNumberPens];
3482
3483            int
3484              pen_number;
3485
3486            /*
3487              Initialize menu selections.
3488            */
3489            for (i=0; i < (int) (MaxNumberPens-2); i++)
3490              ColorMenu[i]=resource_info->pen_colors[i];
3491            ColorMenu[MaxNumberPens-2]="Browser...";
3492            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3493            /*
3494              Select a pen color from the pop-up menu.
3495            */
3496            pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3497              (const char **) ColorMenu,command);
3498            if (pen_number < 0)
3499              break;
3500            if (pen_number == (MaxNumberPens-2))
3501              {
3502                static char
3503                  color_name[MaxTextExtent] = "gray";
3504
3505                /*
3506                  Select a pen color from a dialog.
3507                */
3508                resource_info->pen_colors[pen_number]=color_name;
3509                XColorBrowserWidget(display,windows,"Select",color_name);
3510                if (*color_name == '\0')
3511                  break;
3512              }
3513            /*
3514              Set border color.
3515            */
3516            (void) XParseColor(display,windows->map_info->colormap,
3517              resource_info->pen_colors[pen_number],&border_color);
3518            break;
3519          }
3520          case ColorEditFuzzCommand:
3521          {
3522            static char
3523              fuzz[MaxTextExtent];
3524
3525            static const char
3526              *FuzzMenu[] =
3527              {
3528                "0%",
3529                "2%",
3530                "5%",
3531                "10%",
3532                "15%",
3533                "Dialog...",
3534                (char *) NULL,
3535              };
3536
3537            /*
3538              Select a command from the pop-up menu.
3539            */
3540            entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3541              command);
3542            if (entry < 0)
3543              break;
3544            if (entry != 5)
3545              {
3546                (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
3547                  QuantumRange+1.0);
3548                break;
3549              }
3550            (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
3551            (void) XDialogWidget(display,windows,"Ok",
3552              "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3553            if (*fuzz == '\0')
3554              break;
3555            (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
3556            (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
3557              1.0);
3558            break;
3559          }
3560          case ColorEditUndoCommand:
3561          {
3562            (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3563              image,exception);
3564            break;
3565          }
3566          case ColorEditHelpCommand:
3567          default:
3568          {
3569            XTextViewWidget(display,resource_info,windows,MagickFalse,
3570              "Help Viewer - Image Annotation",ImageColorEditHelp);
3571            break;
3572          }
3573          case ColorEditDismissCommand:
3574          {
3575            /*
3576              Prematurely exit.
3577            */
3578            state|=EscapeState;
3579            state|=ExitState;
3580            break;
3581          }
3582        }
3583        (void) XCheckDefineCursor(display,windows->image.id,cursor);
3584        continue;
3585      }
3586    switch (event.type)
3587    {
3588      case ButtonPress:
3589      {
3590        if (event.xbutton.button != Button1)
3591          break;
3592        if ((event.xbutton.window != windows->image.id) &&
3593            (event.xbutton.window != windows->magnify.id))
3594          break;
3595        /*
3596          exit loop.
3597        */
3598        x=event.xbutton.x;
3599        y=event.xbutton.y;
3600        (void) XMagickCommand(display,resource_info,windows,
3601          SaveToUndoBufferCommand,image,exception);
3602        state|=UpdateConfigurationState;
3603        break;
3604      }
3605      case ButtonRelease:
3606      {
3607        if (event.xbutton.button != Button1)
3608          break;
3609        if ((event.xbutton.window != windows->image.id) &&
3610            (event.xbutton.window != windows->magnify.id))
3611          break;
3612        /*
3613          Update colormap information.
3614        */
3615        x=event.xbutton.x;
3616        y=event.xbutton.y;
3617        XConfigureImageColormap(display,resource_info,windows,*image,exception);
3618        (void) XConfigureImage(display,resource_info,windows,*image,exception);
3619        XInfoWidget(display,windows,text);
3620        (void) XCheckDefineCursor(display,windows->image.id,cursor);
3621        state&=(~UpdateConfigurationState);
3622        break;
3623      }
3624      case Expose:
3625        break;
3626      case KeyPress:
3627      {
3628        KeySym
3629          key_symbol;
3630
3631        if (event.xkey.window == windows->magnify.id)
3632          {
3633            Window
3634              window;
3635
3636            window=windows->magnify.id;
3637            while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3638          }
3639        if (event.xkey.window != windows->image.id)
3640          break;
3641        /*
3642          Respond to a user key press.
3643        */
3644        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3645          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3646        switch ((int) key_symbol)
3647        {
3648          case XK_Escape:
3649          case XK_F20:
3650          {
3651            /*
3652              Prematurely exit.
3653            */
3654            state|=ExitState;
3655            break;
3656          }
3657          case XK_F1:
3658          case XK_Help:
3659          {
3660            XTextViewWidget(display,resource_info,windows,MagickFalse,
3661              "Help Viewer - Image Annotation",ImageColorEditHelp);
3662            break;
3663          }
3664          default:
3665          {
3666            (void) XBell(display,0);
3667            break;
3668          }
3669        }
3670        break;
3671      }
3672      case MotionNotify:
3673      {
3674        /*
3675          Map and unmap Info widget as cursor crosses its boundaries.
3676        */
3677        x=event.xmotion.x;
3678        y=event.xmotion.y;
3679        if (windows->info.mapped != MagickFalse)
3680          {
3681            if ((x < (int) (windows->info.x+windows->info.width)) &&
3682                (y < (int) (windows->info.y+windows->info.height)))
3683              (void) XWithdrawWindow(display,windows->info.id,
3684                windows->info.screen);
3685          }
3686        else
3687          if ((x > (int) (windows->info.x+windows->info.width)) ||
3688              (y > (int) (windows->info.y+windows->info.height)))
3689            (void) XMapWindow(display,windows->info.id);
3690        break;
3691      }
3692      default:
3693        break;
3694    }
3695    if (event.xany.window == windows->magnify.id)
3696      {
3697        x=windows->magnify.x-windows->image.x;
3698        y=windows->magnify.y-windows->image.y;
3699      }
3700    x_offset=x;
3701    y_offset=y;
3702    if ((state & UpdateConfigurationState) != 0)
3703      {
3704        CacheView
3705          *image_view;
3706
3707        int
3708          x,
3709          y;
3710
3711        /*
3712          Pixel edit is relative to image configuration.
3713        */
3714        (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3715          MagickTrue);
3716        color=windows->pixel_info->pen_colors[pen_id];
3717        XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3718        width=(unsigned int) (*image)->columns;
3719        height=(unsigned int) (*image)->rows;
3720        x=0;
3721        y=0;
3722        if (windows->image.crop_geometry != (char *) NULL)
3723          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3724            &width,&height);
3725        x_offset=(int)
3726          (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3727        y_offset=(int)
3728          (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3729        if ((x_offset < 0) || (y_offset < 0))
3730          continue;
3731        if ((x_offset >= (int) (*image)->columns) ||
3732            (y_offset >= (int) (*image)->rows))
3733          continue;
3734        image_view=AcquireCacheView(*image);
3735        switch (method)
3736        {
3737          case PointMethod:
3738          default:
3739          {
3740            /*
3741              Update color information using point algorithm.
3742            */
3743            if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3744              return(MagickFalse);
3745            q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3746              (ssize_t) y_offset,1,1,exception);
3747            if (q == (Quantum *) NULL)
3748              break;
3749            SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3750            SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3751            SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3752            (void) SyncCacheViewAuthenticPixels(image_view,exception);
3753            break;
3754          }
3755          case ReplaceMethod:
3756          {
3757            PixelInfo
3758              pixel,
3759              target;
3760
3761            Quantum
3762              virtual_pixel[CompositePixelChannel];
3763
3764            /*
3765              Update color information using replace algorithm.
3766            */
3767            (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
3768              (ssize_t) y_offset,virtual_pixel,exception);
3769            target.red=virtual_pixel[RedPixelChannel];
3770            target.green=virtual_pixel[GreenPixelChannel];
3771            target.blue=virtual_pixel[BluePixelChannel];
3772            target.alpha=virtual_pixel[AlphaPixelChannel];
3773            if ((*image)->storage_class == DirectClass)
3774              {
3775                for (y=0; y < (int) (*image)->rows; y++)
3776                {
3777                  q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3778                    (*image)->columns,1,exception);
3779                  if (q == (Quantum *) NULL)
3780                    break;
3781                  for (x=0; x < (int) (*image)->columns; x++)
3782                  {
3783                    GetPixelInfoPixel(*image,q,&pixel);
3784                    if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
3785                      {
3786                        SetPixelRed(*image,ScaleShortToQuantum(
3787                          color.red),q);
3788                        SetPixelGreen(*image,ScaleShortToQuantum(
3789                          color.green),q);
3790                        SetPixelBlue(*image,ScaleShortToQuantum(
3791                          color.blue),q);
3792                      }
3793                    q+=GetPixelChannels(*image);
3794                  }
3795                  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3796                    break;
3797                }
3798              }
3799            else
3800              {
3801                for (i=0; i < (ssize_t) (*image)->colors; i++)
3802                  if (IsFuzzyEquivalencePixelInfo((*image)->colormap+i,&target))
3803                    {
3804                      (*image)->colormap[i].red=ScaleShortToQuantum(
3805                        color.red);
3806                      (*image)->colormap[i].green=ScaleShortToQuantum(
3807                        color.green);
3808                      (*image)->colormap[i].blue=ScaleShortToQuantum(
3809                        color.blue);
3810                    }
3811                (void) SyncImage(*image,exception);
3812              }
3813            break;
3814          }
3815          case FloodfillMethod:
3816          case FillToBorderMethod:
3817          {
3818            DrawInfo
3819              *draw_info;
3820
3821            PixelInfo
3822              target;
3823
3824            /*
3825              Update color information using floodfill algorithm.
3826            */
3827            (void) GetOneVirtualMagickPixel(*image,
3828              GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
3829              y_offset,&target,exception);
3830            if (method == FillToBorderMethod)
3831              {
3832                target.red=(MagickRealType)
3833                  ScaleShortToQuantum(border_color.red);
3834                target.green=(MagickRealType)
3835                  ScaleShortToQuantum(border_color.green);
3836                target.blue=(MagickRealType)
3837                  ScaleShortToQuantum(border_color.blue);
3838              }
3839            draw_info=CloneDrawInfo(resource_info->image_info,
3840              (DrawInfo *) NULL);
3841            (void) QueryColorCompliance(resource_info->pen_colors[pen_id],
3842              AllCompliance,&draw_info->fill,exception);
3843            (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
3844              x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
3845              MagickFalse : MagickTrue,exception);
3846            draw_info=DestroyDrawInfo(draw_info);
3847            break;
3848          }
3849          case ResetMethod:
3850          {
3851            /*
3852              Update color information using reset algorithm.
3853            */
3854            if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3855              return(MagickFalse);
3856            for (y=0; y < (int) (*image)->rows; y++)
3857            {
3858              q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3859                (*image)->columns,1,exception);
3860              if (q == (Quantum *) NULL)
3861                break;
3862              for (x=0; x < (int) (*image)->columns; x++)
3863              {
3864                SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3865                SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3866                SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3867                q+=GetPixelChannels(*image);
3868              }
3869              if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3870                break;
3871            }
3872            break;
3873          }
3874        }
3875        image_view=DestroyCacheView(image_view);
3876        state&=(~UpdateConfigurationState);
3877      }
3878  } while ((state & ExitState) == 0);
3879  (void) XSelectInput(display,windows->image.id,
3880    windows->image.attributes.event_mask);
3881  XSetCursorState(display,windows,MagickFalse);
3882  (void) XFreeCursor(display,cursor);
3883  return(MagickTrue);
3884}
3885
3886/*
3887%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3888%                                                                             %
3889%                                                                             %
3890%                                                                             %
3891+   X C o m p o s i t e I m a g e                                             %
3892%                                                                             %
3893%                                                                             %
3894%                                                                             %
3895%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3896%
3897%  XCompositeImage() requests an image name from the user, reads the image and
3898%  composites it with the X window image at a location the user chooses with
3899%  the pointer.
3900%
3901%  The format of the XCompositeImage method is:
3902%
3903%      MagickBooleanType XCompositeImage(Display *display,
3904%        XResourceInfo *resource_info,XWindows *windows,Image *image,
3905%        ExceptionInfo *exception)
3906%
3907%  A description of each parameter follows:
3908%
3909%    o display: Specifies a connection to an X server;  returned from
3910%      XOpenDisplay.
3911%
3912%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3913%
3914%    o windows: Specifies a pointer to a XWindows structure.
3915%
3916%    o image: the image; returned from ReadImage.
3917%
3918%    o exception: return any errors or warnings in this structure.
3919%
3920*/
3921static MagickBooleanType XCompositeImage(Display *display,
3922  XResourceInfo *resource_info,XWindows *windows,Image *image,
3923  ExceptionInfo *exception)
3924{
3925  static char
3926    displacement_geometry[MaxTextExtent] = "30x30",
3927    filename[MaxTextExtent] = "\0";
3928
3929  static const char
3930    *CompositeMenu[] =
3931    {
3932      "Operators",
3933      "Dissolve",
3934      "Displace",
3935      "Help",
3936      "Dismiss",
3937      (char *) NULL
3938    };
3939
3940  static CompositeOperator
3941    compose = CopyCompositeOp;
3942
3943  static const ModeType
3944    CompositeCommands[] =
3945    {
3946      CompositeOperatorsCommand,
3947      CompositeDissolveCommand,
3948      CompositeDisplaceCommand,
3949      CompositeHelpCommand,
3950      CompositeDismissCommand
3951    };
3952
3953  char
3954    text[MaxTextExtent];
3955
3956  Cursor
3957    cursor;
3958
3959  Image
3960    *composite_image;
3961
3962  int
3963    entry,
3964    id,
3965    x,
3966    y;
3967
3968  MagickRealType
3969    blend,
3970    scale_factor;
3971
3972  RectangleInfo
3973    highlight_info,
3974    composite_info;
3975
3976  unsigned int
3977    height,
3978    width;
3979
3980  size_t
3981    state;
3982
3983  XEvent
3984    event;
3985
3986  /*
3987    Request image file name from user.
3988  */
3989  XFileBrowserWidget(display,windows,"Composite",filename);
3990  if (*filename == '\0')
3991    return(MagickTrue);
3992  /*
3993    Read image.
3994  */
3995  XSetCursorState(display,windows,MagickTrue);
3996  XCheckRefreshWindows(display,windows);
3997  (void) CopyMagickString(resource_info->image_info->filename,filename,
3998    MaxTextExtent);
3999  composite_image=ReadImage(resource_info->image_info,exception);
4000  CatchException(exception);
4001  XSetCursorState(display,windows,MagickFalse);
4002  if (composite_image == (Image *) NULL)
4003    return(MagickFalse);
4004  /*
4005    Map Command widget.
4006  */
4007  (void) CloneString(&windows->command.name,"Composite");
4008  windows->command.data=1;
4009  (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
4010  (void) XMapRaised(display,windows->command.id);
4011  XClientMessage(display,windows->image.id,windows->im_protocols,
4012    windows->im_update_widget,CurrentTime);
4013  /*
4014    Track pointer until button 1 is pressed.
4015  */
4016  XQueryPosition(display,windows->image.id,&x,&y);
4017  (void) XSelectInput(display,windows->image.id,
4018    windows->image.attributes.event_mask | PointerMotionMask);
4019  composite_info.x=(ssize_t) windows->image.x+x;
4020  composite_info.y=(ssize_t) windows->image.y+y;
4021  composite_info.width=0;
4022  composite_info.height=0;
4023  cursor=XCreateFontCursor(display,XC_ul_angle);
4024  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4025  blend=0.0;
4026  state=DefaultState;
4027  do
4028  {
4029    if (windows->info.mapped != MagickFalse)
4030      {
4031        /*
4032          Display pointer position.
4033        */
4034        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4035          (long) composite_info.x,(long) composite_info.y);
4036        XInfoWidget(display,windows,text);
4037      }
4038    highlight_info=composite_info;
4039    highlight_info.x=composite_info.x-windows->image.x;
4040    highlight_info.y=composite_info.y-windows->image.y;
4041    XHighlightRectangle(display,windows->image.id,
4042      windows->image.highlight_context,&highlight_info);
4043    /*
4044      Wait for next event.
4045    */
4046    XScreenEvent(display,windows,&event,exception);
4047    XHighlightRectangle(display,windows->image.id,
4048      windows->image.highlight_context,&highlight_info);
4049    if (event.xany.window == windows->command.id)
4050      {
4051        /*
4052          Select a command from the Command widget.
4053        */
4054        id=XCommandWidget(display,windows,CompositeMenu,&event);
4055        if (id < 0)
4056          continue;
4057        switch (CompositeCommands[id])
4058        {
4059          case CompositeOperatorsCommand:
4060          {
4061            char
4062              command[MaxTextExtent],
4063              **operators;
4064
4065            /*
4066              Select a command from the pop-up menu.
4067            */
4068            operators=GetCommandOptions(MagickComposeOptions);
4069            if (operators == (char **) NULL)
4070              break;
4071            entry=XMenuWidget(display,windows,CompositeMenu[id],
4072              (const char **) operators,command);
4073            if (entry >= 0)
4074              compose=(CompositeOperator) ParseCommandOption(
4075                MagickComposeOptions,MagickFalse,operators[entry]);
4076            operators=DestroyStringList(operators);
4077            break;
4078          }
4079          case CompositeDissolveCommand:
4080          {
4081            static char
4082              factor[MaxTextExtent] = "20.0";
4083
4084            /*
4085              Dissolve the two images a given percent.
4086            */
4087            (void) XSetFunction(display,windows->image.highlight_context,
4088              GXcopy);
4089            (void) XDialogWidget(display,windows,"Dissolve",
4090              "Enter the blend factor (0.0 - 99.9%):",factor);
4091            (void) XSetFunction(display,windows->image.highlight_context,
4092              GXinvert);
4093            if (*factor == '\0')
4094              break;
4095            blend=StringToDouble(factor,(char **) NULL);
4096            compose=DissolveCompositeOp;
4097            break;
4098          }
4099          case CompositeDisplaceCommand:
4100          {
4101            /*
4102              Get horizontal and vertical scale displacement geometry.
4103            */
4104            (void) XSetFunction(display,windows->image.highlight_context,
4105              GXcopy);
4106            (void) XDialogWidget(display,windows,"Displace",
4107              "Enter the horizontal and vertical scale:",displacement_geometry);
4108            (void) XSetFunction(display,windows->image.highlight_context,
4109              GXinvert);
4110            if (*displacement_geometry == '\0')
4111              break;
4112            compose=DisplaceCompositeOp;
4113            break;
4114          }
4115          case CompositeHelpCommand:
4116          {
4117            (void) XSetFunction(display,windows->image.highlight_context,
4118              GXcopy);
4119            XTextViewWidget(display,resource_info,windows,MagickFalse,
4120              "Help Viewer - Image Composite",ImageCompositeHelp);
4121            (void) XSetFunction(display,windows->image.highlight_context,
4122              GXinvert);
4123            break;
4124          }
4125          case CompositeDismissCommand:
4126          {
4127            /*
4128              Prematurely exit.
4129            */
4130            state|=EscapeState;
4131            state|=ExitState;
4132            break;
4133          }
4134          default:
4135            break;
4136        }
4137        continue;
4138      }
4139    switch (event.type)
4140    {
4141      case ButtonPress:
4142      {
4143        if (image->debug != MagickFalse)
4144          (void) LogMagickEvent(X11Event,GetMagickModule(),
4145            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4146            event.xbutton.button,event.xbutton.x,event.xbutton.y);
4147        if (event.xbutton.button != Button1)
4148          break;
4149        if (event.xbutton.window != windows->image.id)
4150          break;
4151        /*
4152          Change cursor.
4153        */
4154        composite_info.width=composite_image->columns;
4155        composite_info.height=composite_image->rows;
4156        (void) XCheckDefineCursor(display,windows->image.id,cursor);
4157        composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4158        composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4159        break;
4160      }
4161      case ButtonRelease:
4162      {
4163        if (image->debug != MagickFalse)
4164          (void) LogMagickEvent(X11Event,GetMagickModule(),
4165            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4166            event.xbutton.button,event.xbutton.x,event.xbutton.y);
4167        if (event.xbutton.button != Button1)
4168          break;
4169        if (event.xbutton.window != windows->image.id)
4170          break;
4171        if ((composite_info.width != 0) && (composite_info.height != 0))
4172          {
4173            /*
4174              User has selected the location of the composite image.
4175            */
4176            composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4177            composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4178            state|=ExitState;
4179          }
4180        break;
4181      }
4182      case Expose:
4183        break;
4184      case KeyPress:
4185      {
4186        char
4187          command[MaxTextExtent];
4188
4189        KeySym
4190          key_symbol;
4191
4192        int
4193          length;
4194
4195        if (event.xkey.window != windows->image.id)
4196          break;
4197        /*
4198          Respond to a user key press.
4199        */
4200        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4201          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4202        *(command+length)='\0';
4203        if (image->debug != MagickFalse)
4204          (void) LogMagickEvent(X11Event,GetMagickModule(),
4205            "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4206        switch ((int) key_symbol)
4207        {
4208          case XK_Escape:
4209          case XK_F20:
4210          {
4211            /*
4212              Prematurely exit.
4213            */
4214            composite_image=DestroyImage(composite_image);
4215            state|=EscapeState;
4216            state|=ExitState;
4217            break;
4218          }
4219          case XK_F1:
4220          case XK_Help:
4221          {
4222            (void) XSetFunction(display,windows->image.highlight_context,
4223              GXcopy);
4224            XTextViewWidget(display,resource_info,windows,MagickFalse,
4225              "Help Viewer - Image Composite",ImageCompositeHelp);
4226            (void) XSetFunction(display,windows->image.highlight_context,
4227              GXinvert);
4228            break;
4229          }
4230          default:
4231          {
4232            (void) XBell(display,0);
4233            break;
4234          }
4235        }
4236        break;
4237      }
4238      case MotionNotify:
4239      {
4240        /*
4241          Map and unmap Info widget as text cursor crosses its boundaries.
4242        */
4243        x=event.xmotion.x;
4244        y=event.xmotion.y;
4245        if (windows->info.mapped != MagickFalse)
4246          {
4247            if ((x < (int) (windows->info.x+windows->info.width)) &&
4248                (y < (int) (windows->info.y+windows->info.height)))
4249              (void) XWithdrawWindow(display,windows->info.id,
4250                windows->info.screen);
4251          }
4252        else
4253          if ((x > (int) (windows->info.x+windows->info.width)) ||
4254              (y > (int) (windows->info.y+windows->info.height)))
4255            (void) XMapWindow(display,windows->info.id);
4256        composite_info.x=(ssize_t) windows->image.x+x;
4257        composite_info.y=(ssize_t) windows->image.y+y;
4258        break;
4259      }
4260      default:
4261      {
4262        if (image->debug != MagickFalse)
4263          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4264            event.type);
4265        break;
4266      }
4267    }
4268  } while ((state & ExitState) == 0);
4269  (void) XSelectInput(display,windows->image.id,
4270    windows->image.attributes.event_mask);
4271  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4272  XSetCursorState(display,windows,MagickFalse);
4273  (void) XFreeCursor(display,cursor);
4274  if ((state & EscapeState) != 0)
4275    return(MagickTrue);
4276  /*
4277    Image compositing is relative to image configuration.
4278  */
4279  XSetCursorState(display,windows,MagickTrue);
4280  XCheckRefreshWindows(display,windows);
4281  width=(unsigned int) image->columns;
4282  height=(unsigned int) image->rows;
4283  x=0;
4284  y=0;
4285  if (windows->image.crop_geometry != (char *) NULL)
4286    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4287  scale_factor=(MagickRealType) width/windows->image.ximage->width;
4288  composite_info.x+=x;
4289  composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4290  composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4291  scale_factor=(MagickRealType) height/windows->image.ximage->height;
4292  composite_info.y+=y;
4293  composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4294  composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4295  if ((composite_info.width != composite_image->columns) ||
4296      (composite_info.height != composite_image->rows))
4297    {
4298      Image
4299        *resize_image;
4300
4301      /*
4302        Scale composite image.
4303      */
4304      resize_image=ResizeImage(composite_image,composite_info.width,
4305        composite_info.height,composite_image->filter,composite_image->blur,
4306        exception);
4307      composite_image=DestroyImage(composite_image);
4308      if (resize_image == (Image *) NULL)
4309        {
4310          XSetCursorState(display,windows,MagickFalse);
4311          return(MagickFalse);
4312        }
4313      composite_image=resize_image;
4314    }
4315  if (compose == DisplaceCompositeOp)
4316    (void) SetImageArtifact(composite_image,"compose:args",
4317      displacement_geometry);
4318  if (blend != 0.0)
4319    {
4320      CacheView
4321        *image_view;
4322
4323      int
4324        y;
4325
4326      Quantum
4327        opacity;
4328
4329      register int
4330        x;
4331
4332      register Quantum
4333        *q;
4334
4335      /*
4336        Create mattes for blending.
4337      */
4338      (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,exception);
4339      opacity=(Quantum) (ScaleQuantumToChar((Quantum) QuantumRange)-
4340        ((ssize_t) ScaleQuantumToChar((Quantum) QuantumRange)*blend)/100);
4341      if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
4342        return(MagickFalse);
4343      image->matte=MagickTrue;
4344      image_view=AcquireCacheView(image);
4345      for (y=0; y < (int) image->rows; y++)
4346      {
4347        q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4348          exception);
4349        if (q == (Quantum *) NULL)
4350          break;
4351        for (x=0; x < (int) image->columns; x++)
4352        {
4353          SetPixelAlpha(image,opacity,q);
4354          q+=GetPixelChannels(image);
4355        }
4356        if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4357          break;
4358      }
4359      image_view=DestroyCacheView(image_view);
4360    }
4361  /*
4362    Composite image with X Image window.
4363  */
4364  (void) CompositeImage(image,compose,composite_image,composite_info.x,
4365    composite_info.y,exception);
4366  composite_image=DestroyImage(composite_image);
4367  XSetCursorState(display,windows,MagickFalse);
4368  /*
4369    Update image configuration.
4370  */
4371  XConfigureImageColormap(display,resource_info,windows,image,exception);
4372  (void) XConfigureImage(display,resource_info,windows,image,exception);
4373  return(MagickTrue);
4374}
4375
4376/*
4377%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4378%                                                                             %
4379%                                                                             %
4380%                                                                             %
4381+   X C o n f i g u r e I m a g e                                             %
4382%                                                                             %
4383%                                                                             %
4384%                                                                             %
4385%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4386%
4387%  XConfigureImage() creates a new X image.  It also notifies the window
4388%  manager of the new image size and configures the transient widows.
4389%
4390%  The format of the XConfigureImage method is:
4391%
4392%      MagickBooleanType XConfigureImage(Display *display,
4393%        XResourceInfo *resource_info,XWindows *windows,Image *image,
4394%        ExceptionInfo *exception)
4395%
4396%  A description of each parameter follows:
4397%
4398%    o display: Specifies a connection to an X server; returned from
4399%      XOpenDisplay.
4400%
4401%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4402%
4403%    o windows: Specifies a pointer to a XWindows structure.
4404%
4405%    o image: the image.
4406%
4407%    o exception: return any errors or warnings in this structure.
4408%
4409%    o exception: return any errors or warnings in this structure.
4410%
4411*/
4412static MagickBooleanType XConfigureImage(Display *display,
4413  XResourceInfo *resource_info,XWindows *windows,Image *image,
4414  ExceptionInfo *exception)
4415{
4416  char
4417    geometry[MaxTextExtent];
4418
4419  MagickStatusType
4420    status;
4421
4422  size_t
4423    mask,
4424    height,
4425    width;
4426
4427  ssize_t
4428    x,
4429    y;
4430
4431  XSizeHints
4432    *size_hints;
4433
4434  XWindowChanges
4435    window_changes;
4436
4437  /*
4438    Dismiss if window dimensions are zero.
4439  */
4440  width=(unsigned int) windows->image.window_changes.width;
4441  height=(unsigned int) windows->image.window_changes.height;
4442  if (image->debug != MagickFalse)
4443    (void) LogMagickEvent(X11Event,GetMagickModule(),
4444      "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4445      windows->image.ximage->height,(double) width,(double) height);
4446  if ((width*height) == 0)
4447    return(MagickTrue);
4448  x=0;
4449  y=0;
4450  /*
4451    Resize image to fit Image window dimensions.
4452  */
4453  XSetCursorState(display,windows,MagickTrue);
4454  (void) XFlush(display);
4455  if (((int) width != windows->image.ximage->width) ||
4456      ((int) height != windows->image.ximage->height))
4457    image->taint=MagickTrue;
4458  windows->magnify.x=(int)
4459    width*windows->magnify.x/windows->image.ximage->width;
4460  windows->magnify.y=(int)
4461    height*windows->magnify.y/windows->image.ximage->height;
4462  windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4463  windows->image.y=(int)
4464    (height*windows->image.y/windows->image.ximage->height);
4465  status=XMakeImage(display,resource_info,&windows->image,image,
4466    (unsigned int) width,(unsigned int) height,exception);
4467  if (status == MagickFalse)
4468    XNoticeWidget(display,windows,"Unable to configure X image:",
4469      windows->image.name);
4470  /*
4471    Notify window manager of the new configuration.
4472  */
4473  if (resource_info->image_geometry != (char *) NULL)
4474    (void) FormatLocaleString(geometry,MaxTextExtent,"%s>!",
4475      resource_info->image_geometry);
4476  else
4477    (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
4478      XDisplayWidth(display,windows->image.screen),
4479      XDisplayHeight(display,windows->image.screen));
4480  (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4481  window_changes.width=(int) width;
4482  if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4483    window_changes.width=XDisplayWidth(display,windows->image.screen);
4484  window_changes.height=(int) height;
4485  if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4486    window_changes.height=XDisplayHeight(display,windows->image.screen);
4487  mask=(size_t) (CWWidth | CWHeight);
4488  if (resource_info->backdrop)
4489    {
4490      mask|=CWX | CWY;
4491      window_changes.x=(int)
4492        ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4493      window_changes.y=(int)
4494        ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4495    }
4496  (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4497    (unsigned int) mask,&window_changes);
4498  (void) XClearWindow(display,windows->image.id);
4499  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4500  /*
4501    Update Magnify window configuration.
4502  */
4503  if (windows->magnify.mapped != MagickFalse)
4504    XMakeMagnifyImage(display,windows,exception);
4505  windows->pan.crop_geometry=windows->image.crop_geometry;
4506  XBestIconSize(display,&windows->pan,image);
4507  while (((windows->pan.width << 1) < MaxIconSize) &&
4508         ((windows->pan.height << 1) < MaxIconSize))
4509  {
4510    windows->pan.width<<=1;
4511    windows->pan.height<<=1;
4512  }
4513  if (windows->pan.geometry != (char *) NULL)
4514    (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4515      &windows->pan.width,&windows->pan.height);
4516  window_changes.width=(int) windows->pan.width;
4517  window_changes.height=(int) windows->pan.height;
4518  size_hints=XAllocSizeHints();
4519  if (size_hints != (XSizeHints *) NULL)
4520    {
4521      /*
4522        Set new size hints.
4523      */
4524      size_hints->flags=PSize | PMinSize | PMaxSize;
4525      size_hints->width=window_changes.width;
4526      size_hints->height=window_changes.height;
4527      size_hints->min_width=size_hints->width;
4528      size_hints->min_height=size_hints->height;
4529      size_hints->max_width=size_hints->width;
4530      size_hints->max_height=size_hints->height;
4531      (void) XSetNormalHints(display,windows->pan.id,size_hints);
4532      (void) XFree((void *) size_hints);
4533    }
4534  (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4535    (unsigned int) (CWWidth | CWHeight),&window_changes);
4536  /*
4537    Update icon window configuration.
4538  */
4539  windows->icon.crop_geometry=windows->image.crop_geometry;
4540  XBestIconSize(display,&windows->icon,image);
4541  window_changes.width=(int) windows->icon.width;
4542  window_changes.height=(int) windows->icon.height;
4543  (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4544    (unsigned int) (CWWidth | CWHeight),&window_changes);
4545  XSetCursorState(display,windows,MagickFalse);
4546  return(status != 0 ? MagickTrue : MagickFalse);
4547}
4548
4549/*
4550%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4551%                                                                             %
4552%                                                                             %
4553%                                                                             %
4554+   X C r o p I m a g e                                                       %
4555%                                                                             %
4556%                                                                             %
4557%                                                                             %
4558%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4559%
4560%  XCropImage() allows the user to select a region of the image and crop, copy,
4561%  or cut it.  For copy or cut, the image can subsequently be composited onto
4562%  the image with XPasteImage.
4563%
4564%  The format of the XCropImage method is:
4565%
4566%      MagickBooleanType XCropImage(Display *display,
4567%        XResourceInfo *resource_info,XWindows *windows,Image *image,
4568%        const ClipboardMode mode,ExceptionInfo *exception)
4569%
4570%  A description of each parameter follows:
4571%
4572%    o display: Specifies a connection to an X server; returned from
4573%      XOpenDisplay.
4574%
4575%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4576%
4577%    o windows: Specifies a pointer to a XWindows structure.
4578%
4579%    o image: the image; returned from ReadImage.
4580%
4581%    o mode: This unsigned value specified whether the image should be
4582%      cropped, copied, or cut.
4583%
4584%    o exception: return any errors or warnings in this structure.
4585%
4586*/
4587static MagickBooleanType XCropImage(Display *display,
4588  XResourceInfo *resource_info,XWindows *windows,Image *image,
4589  const ClipboardMode mode,ExceptionInfo *exception)
4590{
4591  static const char
4592    *CropModeMenu[] =
4593    {
4594      "Help",
4595      "Dismiss",
4596      (char *) NULL
4597    },
4598    *RectifyModeMenu[] =
4599    {
4600      "Crop",
4601      "Help",
4602      "Dismiss",
4603      (char *) NULL
4604    };
4605
4606  static const ModeType
4607    CropCommands[] =
4608    {
4609      CropHelpCommand,
4610      CropDismissCommand
4611    },
4612    RectifyCommands[] =
4613    {
4614      RectifyCopyCommand,
4615      RectifyHelpCommand,
4616      RectifyDismissCommand
4617    };
4618
4619  CacheView
4620    *image_view;
4621
4622  char
4623    command[MaxTextExtent],
4624    text[MaxTextExtent];
4625
4626  Cursor
4627    cursor;
4628
4629  int
4630    id,
4631    x,
4632    y;
4633
4634  KeySym
4635    key_symbol;
4636
4637  Image
4638    *crop_image;
4639
4640  MagickRealType
4641    scale_factor;
4642
4643  RectangleInfo
4644    crop_info,
4645    highlight_info;
4646
4647  register Quantum
4648    *q;
4649
4650  unsigned int
4651    height,
4652    width;
4653
4654  size_t
4655    state;
4656
4657  XEvent
4658    event;
4659
4660  /*
4661    Map Command widget.
4662  */
4663  switch (mode)
4664  {
4665    case CopyMode:
4666    {
4667      (void) CloneString(&windows->command.name,"Copy");
4668      break;
4669    }
4670    case CropMode:
4671    {
4672      (void) CloneString(&windows->command.name,"Crop");
4673      break;
4674    }
4675    case CutMode:
4676    {
4677      (void) CloneString(&windows->command.name,"Cut");
4678      break;
4679    }
4680  }
4681  RectifyModeMenu[0]=windows->command.name;
4682  windows->command.data=0;
4683  (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4684  (void) XMapRaised(display,windows->command.id);
4685  XClientMessage(display,windows->image.id,windows->im_protocols,
4686    windows->im_update_widget,CurrentTime);
4687  /*
4688    Track pointer until button 1 is pressed.
4689  */
4690  XQueryPosition(display,windows->image.id,&x,&y);
4691  (void) XSelectInput(display,windows->image.id,
4692    windows->image.attributes.event_mask | PointerMotionMask);
4693  crop_info.x=(ssize_t) windows->image.x+x;
4694  crop_info.y=(ssize_t) windows->image.y+y;
4695  crop_info.width=0;
4696  crop_info.height=0;
4697  cursor=XCreateFontCursor(display,XC_fleur);
4698  state=DefaultState;
4699  do
4700  {
4701    if (windows->info.mapped != MagickFalse)
4702      {
4703        /*
4704          Display pointer position.
4705        */
4706        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4707          (long) crop_info.x,(long) crop_info.y);
4708        XInfoWidget(display,windows,text);
4709      }
4710    /*
4711      Wait for next event.
4712    */
4713    XScreenEvent(display,windows,&event,exception);
4714    if (event.xany.window == windows->command.id)
4715      {
4716        /*
4717          Select a command from the Command widget.
4718        */
4719        id=XCommandWidget(display,windows,CropModeMenu,&event);
4720        if (id < 0)
4721          continue;
4722        switch (CropCommands[id])
4723        {
4724          case CropHelpCommand:
4725          {
4726            switch (mode)
4727            {
4728              case CopyMode:
4729              {
4730                XTextViewWidget(display,resource_info,windows,MagickFalse,
4731                  "Help Viewer - Image Copy",ImageCopyHelp);
4732                break;
4733              }
4734              case CropMode:
4735              {
4736                XTextViewWidget(display,resource_info,windows,MagickFalse,
4737                  "Help Viewer - Image Crop",ImageCropHelp);
4738                break;
4739              }
4740              case CutMode:
4741              {
4742                XTextViewWidget(display,resource_info,windows,MagickFalse,
4743                  "Help Viewer - Image Cut",ImageCutHelp);
4744                break;
4745              }
4746            }
4747            break;
4748          }
4749          case CropDismissCommand:
4750          {
4751            /*
4752              Prematurely exit.
4753            */
4754            state|=EscapeState;
4755            state|=ExitState;
4756            break;
4757          }
4758          default:
4759            break;
4760        }
4761        continue;
4762      }
4763    switch (event.type)
4764    {
4765      case ButtonPress:
4766      {
4767        if (event.xbutton.button != Button1)
4768          break;
4769        if (event.xbutton.window != windows->image.id)
4770          break;
4771        /*
4772          Note first corner of cropping rectangle-- exit loop.
4773        */
4774        (void) XCheckDefineCursor(display,windows->image.id,cursor);
4775        crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4776        crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4777        state|=ExitState;
4778        break;
4779      }
4780      case ButtonRelease:
4781        break;
4782      case Expose:
4783        break;
4784      case KeyPress:
4785      {
4786        if (event.xkey.window != windows->image.id)
4787          break;
4788        /*
4789          Respond to a user key press.
4790        */
4791        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4792          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4793        switch ((int) key_symbol)
4794        {
4795          case XK_Escape:
4796          case XK_F20:
4797          {
4798            /*
4799              Prematurely exit.
4800            */
4801            state|=EscapeState;
4802            state|=ExitState;
4803            break;
4804          }
4805          case XK_F1:
4806          case XK_Help:
4807          {
4808            switch (mode)
4809            {
4810              case CopyMode:
4811              {
4812                XTextViewWidget(display,resource_info,windows,MagickFalse,
4813                  "Help Viewer - Image Copy",ImageCopyHelp);
4814                break;
4815              }
4816              case CropMode:
4817              {
4818                XTextViewWidget(display,resource_info,windows,MagickFalse,
4819                  "Help Viewer - Image Crop",ImageCropHelp);
4820                break;
4821              }
4822              case CutMode:
4823              {
4824                XTextViewWidget(display,resource_info,windows,MagickFalse,
4825                  "Help Viewer - Image Cut",ImageCutHelp);
4826                break;
4827              }
4828            }
4829            break;
4830          }
4831          default:
4832          {
4833            (void) XBell(display,0);
4834            break;
4835          }
4836        }
4837        break;
4838      }
4839      case MotionNotify:
4840      {
4841        if (event.xmotion.window != windows->image.id)
4842          break;
4843        /*
4844          Map and unmap Info widget as text cursor crosses its boundaries.
4845        */
4846        x=event.xmotion.x;
4847        y=event.xmotion.y;
4848        if (windows->info.mapped != MagickFalse)
4849          {
4850            if ((x < (int) (windows->info.x+windows->info.width)) &&
4851                (y < (int) (windows->info.y+windows->info.height)))
4852              (void) XWithdrawWindow(display,windows->info.id,
4853                windows->info.screen);
4854          }
4855        else
4856          if ((x > (int) (windows->info.x+windows->info.width)) ||
4857              (y > (int) (windows->info.y+windows->info.height)))
4858            (void) XMapWindow(display,windows->info.id);
4859        crop_info.x=(ssize_t) windows->image.x+x;
4860        crop_info.y=(ssize_t) windows->image.y+y;
4861        break;
4862      }
4863      default:
4864        break;
4865    }
4866  } while ((state & ExitState) == 0);
4867  (void) XSelectInput(display,windows->image.id,
4868    windows->image.attributes.event_mask);
4869  if ((state & EscapeState) != 0)
4870    {
4871      /*
4872        User want to exit without cropping.
4873      */
4874      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4875      (void) XFreeCursor(display,cursor);
4876      return(MagickTrue);
4877    }
4878  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4879  do
4880  {
4881    /*
4882      Size rectangle as pointer moves until the mouse button is released.
4883    */
4884    x=(int) crop_info.x;
4885    y=(int) crop_info.y;
4886    crop_info.width=0;
4887    crop_info.height=0;
4888    state=DefaultState;
4889    do
4890    {
4891      highlight_info=crop_info;
4892      highlight_info.x=crop_info.x-windows->image.x;
4893      highlight_info.y=crop_info.y-windows->image.y;
4894      if ((highlight_info.width > 3) && (highlight_info.height > 3))
4895        {
4896          /*
4897            Display info and draw cropping rectangle.
4898          */
4899          if (windows->info.mapped == MagickFalse)
4900            (void) XMapWindow(display,windows->info.id);
4901          (void) FormatLocaleString(text,MaxTextExtent,
4902            " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4903            crop_info.height,(double) crop_info.x,(double) crop_info.y);
4904          XInfoWidget(display,windows,text);
4905          XHighlightRectangle(display,windows->image.id,
4906            windows->image.highlight_context,&highlight_info);
4907        }
4908      else
4909        if (windows->info.mapped != MagickFalse)
4910          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4911      /*
4912        Wait for next event.
4913      */
4914      XScreenEvent(display,windows,&event,exception);
4915      if ((highlight_info.width > 3) && (highlight_info.height > 3))
4916        XHighlightRectangle(display,windows->image.id,
4917          windows->image.highlight_context,&highlight_info);
4918      switch (event.type)
4919      {
4920        case ButtonPress:
4921        {
4922          crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4923          crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4924          break;
4925        }
4926        case ButtonRelease:
4927        {
4928          /*
4929            User has committed to cropping rectangle.
4930          */
4931          crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4932          crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4933          XSetCursorState(display,windows,MagickFalse);
4934          state|=ExitState;
4935          windows->command.data=0;
4936          (void) XCommandWidget(display,windows,RectifyModeMenu,
4937            (XEvent *) NULL);
4938          break;
4939        }
4940        case Expose:
4941          break;
4942        case MotionNotify:
4943        {
4944          crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4945          crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4946        }
4947        default:
4948          break;
4949      }
4950      if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4951          ((state & ExitState) != 0))
4952        {
4953          /*
4954            Check boundary conditions.
4955          */
4956          if (crop_info.x < 0)
4957            crop_info.x=0;
4958          else
4959            if (crop_info.x > (ssize_t) windows->image.ximage->width)
4960              crop_info.x=(ssize_t) windows->image.ximage->width;
4961          if ((int) crop_info.x < x)
4962            crop_info.width=(unsigned int) (x-crop_info.x);
4963          else
4964            {
4965              crop_info.width=(unsigned int) (crop_info.x-x);
4966              crop_info.x=(ssize_t) x;
4967            }
4968          if (crop_info.y < 0)
4969            crop_info.y=0;
4970          else
4971            if (crop_info.y > (ssize_t) windows->image.ximage->height)
4972              crop_info.y=(ssize_t) windows->image.ximage->height;
4973          if ((int) crop_info.y < y)
4974            crop_info.height=(unsigned int) (y-crop_info.y);
4975          else
4976            {
4977              crop_info.height=(unsigned int) (crop_info.y-y);
4978              crop_info.y=(ssize_t) y;
4979            }
4980        }
4981    } while ((state & ExitState) == 0);
4982    /*
4983      Wait for user to grab a corner of the rectangle or press return.
4984    */
4985    state=DefaultState;
4986    (void) XMapWindow(display,windows->info.id);
4987    do
4988    {
4989      if (windows->info.mapped != MagickFalse)
4990        {
4991          /*
4992            Display pointer position.
4993          */
4994          (void) FormatLocaleString(text,MaxTextExtent,
4995            " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4996            crop_info.height,(double) crop_info.x,(double) crop_info.y);
4997          XInfoWidget(display,windows,text);
4998        }
4999      highlight_info=crop_info;
5000      highlight_info.x=crop_info.x-windows->image.x;
5001      highlight_info.y=crop_info.y-windows->image.y;
5002      if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
5003        {
5004          state|=EscapeState;
5005          state|=ExitState;
5006          break;
5007        }
5008      XHighlightRectangle(display,windows->image.id,
5009        windows->image.highlight_context,&highlight_info);
5010      XScreenEvent(display,windows,&event,exception);
5011      if (event.xany.window == windows->command.id)
5012        {
5013          /*
5014            Select a command from the Command widget.
5015          */
5016          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5017          id=XCommandWidget(display,windows,RectifyModeMenu,&event);
5018          (void) XSetFunction(display,windows->image.highlight_context,
5019            GXinvert);
5020          XHighlightRectangle(display,windows->image.id,
5021            windows->image.highlight_context,&highlight_info);
5022          if (id >= 0)
5023            switch (RectifyCommands[id])
5024            {
5025              case RectifyCopyCommand:
5026              {
5027                state|=ExitState;
5028                break;
5029              }
5030              case RectifyHelpCommand:
5031              {
5032                (void) XSetFunction(display,windows->image.highlight_context,
5033                  GXcopy);
5034                switch (mode)
5035                {
5036                  case CopyMode:
5037                  {
5038                    XTextViewWidget(display,resource_info,windows,MagickFalse,
5039                      "Help Viewer - Image Copy",ImageCopyHelp);
5040                    break;
5041                  }
5042                  case CropMode:
5043                  {
5044                    XTextViewWidget(display,resource_info,windows,MagickFalse,
5045                      "Help Viewer - Image Crop",ImageCropHelp);
5046                    break;
5047                  }
5048                  case CutMode:
5049                  {
5050                    XTextViewWidget(display,resource_info,windows,MagickFalse,
5051                      "Help Viewer - Image Cut",ImageCutHelp);
5052                    break;
5053                  }
5054                }
5055                (void) XSetFunction(display,windows->image.highlight_context,
5056                  GXinvert);
5057                break;
5058              }
5059              case RectifyDismissCommand:
5060              {
5061                /*
5062                  Prematurely exit.
5063                */
5064                state|=EscapeState;
5065                state|=ExitState;
5066                break;
5067              }
5068              default:
5069                break;
5070            }
5071          continue;
5072        }
5073      XHighlightRectangle(display,windows->image.id,
5074        windows->image.highlight_context,&highlight_info);
5075      switch (event.type)
5076      {
5077        case ButtonPress:
5078        {
5079          if (event.xbutton.button != Button1)
5080            break;
5081          if (event.xbutton.window != windows->image.id)
5082            break;
5083          x=windows->image.x+event.xbutton.x;
5084          y=windows->image.y+event.xbutton.y;
5085          if ((x < (int) (crop_info.x+RoiDelta)) &&
5086              (x > (int) (crop_info.x-RoiDelta)) &&
5087              (y < (int) (crop_info.y+RoiDelta)) &&
5088              (y > (int) (crop_info.y-RoiDelta)))
5089            {
5090              crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5091              crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5092              state|=UpdateConfigurationState;
5093              break;
5094            }
5095          if ((x < (int) (crop_info.x+RoiDelta)) &&
5096              (x > (int) (crop_info.x-RoiDelta)) &&
5097              (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5098              (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5099            {
5100              crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5101              state|=UpdateConfigurationState;
5102              break;
5103            }
5104          if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5105              (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5106              (y < (int) (crop_info.y+RoiDelta)) &&
5107              (y > (int) (crop_info.y-RoiDelta)))
5108            {
5109              crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5110              state|=UpdateConfigurationState;
5111              break;
5112            }
5113          if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5114              (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5115              (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5116              (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5117            {
5118              state|=UpdateConfigurationState;
5119              break;
5120            }
5121        }
5122        case ButtonRelease:
5123        {
5124          if (event.xbutton.window == windows->pan.id)
5125            if ((highlight_info.x != crop_info.x-windows->image.x) ||
5126                (highlight_info.y != crop_info.y-windows->image.y))
5127              XHighlightRectangle(display,windows->image.id,
5128                windows->image.highlight_context,&highlight_info);
5129          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5130            event.xbutton.time);
5131          break;
5132        }
5133        case Expose:
5134        {
5135          if (event.xexpose.window == windows->image.id)
5136            if (event.xexpose.count == 0)
5137              {
5138                event.xexpose.x=(int) highlight_info.x;
5139                event.xexpose.y=(int) highlight_info.y;
5140                event.xexpose.width=(int) highlight_info.width;
5141                event.xexpose.height=(int) highlight_info.height;
5142                XRefreshWindow(display,&windows->image,&event);
5143              }
5144          if (event.xexpose.window == windows->info.id)
5145            if (event.xexpose.count == 0)
5146              XInfoWidget(display,windows,text);
5147          break;
5148        }
5149        case KeyPress:
5150        {
5151          if (event.xkey.window != windows->image.id)
5152            break;
5153          /*
5154            Respond to a user key press.
5155          */
5156          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5157            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5158          switch ((int) key_symbol)
5159          {
5160            case XK_Escape:
5161            case XK_F20:
5162              state|=EscapeState;
5163            case XK_Return:
5164            {
5165              state|=ExitState;
5166              break;
5167            }
5168            case XK_Home:
5169            case XK_KP_Home:
5170            {
5171              crop_info.x=(ssize_t) (windows->image.width/2L-
5172                crop_info.width/2L);
5173              crop_info.y=(ssize_t) (windows->image.height/2L-
5174                crop_info.height/2L);
5175              break;
5176            }
5177            case XK_Left:
5178            case XK_KP_Left:
5179            {
5180              crop_info.x--;
5181              break;
5182            }
5183            case XK_Up:
5184            case XK_KP_Up:
5185            case XK_Next:
5186            {
5187              crop_info.y--;
5188              break;
5189            }
5190            case XK_Right:
5191            case XK_KP_Right:
5192            {
5193              crop_info.x++;
5194              break;
5195            }
5196            case XK_Prior:
5197            case XK_Down:
5198            case XK_KP_Down:
5199            {
5200              crop_info.y++;
5201              break;
5202            }
5203            case XK_F1:
5204            case XK_Help:
5205            {
5206              (void) XSetFunction(display,windows->image.highlight_context,
5207                GXcopy);
5208              switch (mode)
5209              {
5210                case CopyMode:
5211                {
5212                  XTextViewWidget(display,resource_info,windows,MagickFalse,
5213                    "Help Viewer - Image Copy",ImageCopyHelp);
5214                  break;
5215                }
5216                case CropMode:
5217                {
5218                  XTextViewWidget(display,resource_info,windows,MagickFalse,
5219                    "Help Viewer - Image Cropg",ImageCropHelp);
5220                  break;
5221                }
5222                case CutMode:
5223                {
5224                  XTextViewWidget(display,resource_info,windows,MagickFalse,
5225                    "Help Viewer - Image Cutg",ImageCutHelp);
5226                  break;
5227                }
5228              }
5229              (void) XSetFunction(display,windows->image.highlight_context,
5230                GXinvert);
5231              break;
5232            }
5233            default:
5234            {
5235              (void) XBell(display,0);
5236              break;
5237            }
5238          }
5239          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5240            event.xkey.time);
5241          break;
5242        }
5243        case KeyRelease:
5244          break;
5245        case MotionNotify:
5246        {
5247          if (event.xmotion.window != windows->image.id)
5248            break;
5249          /*
5250            Map and unmap Info widget as text cursor crosses its boundaries.
5251          */
5252          x=event.xmotion.x;
5253          y=event.xmotion.y;
5254          if (windows->info.mapped != MagickFalse)
5255            {
5256              if ((x < (int) (windows->info.x+windows->info.width)) &&
5257                  (y < (int) (windows->info.y+windows->info.height)))
5258                (void) XWithdrawWindow(display,windows->info.id,
5259                  windows->info.screen);
5260            }
5261          else
5262            if ((x > (int) (windows->info.x+windows->info.width)) ||
5263                (y > (int) (windows->info.y+windows->info.height)))
5264              (void) XMapWindow(display,windows->info.id);
5265          crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5266          crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5267          break;
5268        }
5269        case SelectionRequest:
5270        {
5271          XSelectionEvent
5272            notify;
5273
5274          XSelectionRequestEvent
5275            *request;
5276
5277          /*
5278            Set primary selection.
5279          */
5280          (void) FormatLocaleString(text,MaxTextExtent,
5281            "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5282            crop_info.height,(double) crop_info.x,(double) crop_info.y);
5283          request=(&(event.xselectionrequest));
5284          (void) XChangeProperty(request->display,request->requestor,
5285            request->property,request->target,8,PropModeReplace,
5286            (unsigned char *) text,(int) strlen(text));
5287          notify.type=SelectionNotify;
5288          notify.display=request->display;
5289          notify.requestor=request->requestor;
5290          notify.selection=request->selection;
5291          notify.target=request->target;
5292          notify.time=request->time;
5293          if (request->property == None)
5294            notify.property=request->target;
5295          else
5296            notify.property=request->property;
5297          (void) XSendEvent(request->display,request->requestor,False,0,
5298            (XEvent *) &notify);
5299        }
5300        default:
5301          break;
5302      }
5303      if ((state & UpdateConfigurationState) != 0)
5304        {
5305          (void) XPutBackEvent(display,&event);
5306          (void) XCheckDefineCursor(display,windows->image.id,cursor);
5307          break;
5308        }
5309    } while ((state & ExitState) == 0);
5310  } while ((state & ExitState) == 0);
5311  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5312  XSetCursorState(display,windows,MagickFalse);
5313  if ((state & EscapeState) != 0)
5314    return(MagickTrue);
5315  if (mode == CropMode)
5316    if (((int) crop_info.width != windows->image.ximage->width) ||
5317        ((int) crop_info.height != windows->image.ximage->height))
5318      {
5319        /*
5320          Reconfigure Image window as defined by cropping rectangle.
5321        */
5322        XSetCropGeometry(display,windows,&crop_info,image);
5323        windows->image.window_changes.width=(int) crop_info.width;
5324        windows->image.window_changes.height=(int) crop_info.height;
5325        (void) XConfigureImage(display,resource_info,windows,image,exception);
5326        return(MagickTrue);
5327      }
5328  /*
5329    Copy image before applying image transforms.
5330  */
5331  XSetCursorState(display,windows,MagickTrue);
5332  XCheckRefreshWindows(display,windows);
5333  width=(unsigned int) image->columns;
5334  height=(unsigned int) image->rows;
5335  x=0;
5336  y=0;
5337  if (windows->image.crop_geometry != (char *) NULL)
5338    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5339  scale_factor=(MagickRealType) width/windows->image.ximage->width;
5340  crop_info.x+=x;
5341  crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5342  crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5343  scale_factor=(MagickRealType) height/windows->image.ximage->height;
5344  crop_info.y+=y;
5345  crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5346  crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5347  crop_image=CropImage(image,&crop_info,exception);
5348  XSetCursorState(display,windows,MagickFalse);
5349  if (crop_image == (Image *) NULL)
5350    return(MagickFalse);
5351  if (resource_info->copy_image != (Image *) NULL)
5352    resource_info->copy_image=DestroyImage(resource_info->copy_image);
5353  resource_info->copy_image=crop_image;
5354  if (mode == CopyMode)
5355    {
5356      (void) XConfigureImage(display,resource_info,windows,image,exception);
5357      return(MagickTrue);
5358    }
5359  /*
5360    Cut image.
5361  */
5362  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
5363    return(MagickFalse);
5364  image->matte=MagickTrue;
5365  image_view=AcquireCacheView(image);
5366  for (y=0; y < (int) crop_info.height; y++)
5367  {
5368    q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5369      crop_info.width,1,exception);
5370    if (q == (Quantum *) NULL)
5371      break;
5372    for (x=0; x < (int) crop_info.width; x++)
5373    {
5374      SetPixelAlpha(image,TransparentAlpha,q);
5375      q+=GetPixelChannels(image);
5376    }
5377    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5378      break;
5379  }
5380  image_view=DestroyCacheView(image_view);
5381  /*
5382    Update image configuration.
5383  */
5384  XConfigureImageColormap(display,resource_info,windows,image,exception);
5385  (void) XConfigureImage(display,resource_info,windows,image,exception);
5386  return(MagickTrue);
5387}
5388
5389/*
5390%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5391%                                                                             %
5392%                                                                             %
5393%                                                                             %
5394+   X D r a w I m a g e                                                       %
5395%                                                                             %
5396%                                                                             %
5397%                                                                             %
5398%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5399%
5400%  XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5401%  the image.
5402%
5403%  The format of the XDrawEditImage method is:
5404%
5405%      MagickBooleanType XDrawEditImage(Display *display,
5406%        XResourceInfo *resource_info,XWindows *windows,Image **image,
5407%        ExceptionInfo *exception)
5408%
5409%  A description of each parameter follows:
5410%
5411%    o display: Specifies a connection to an X server; returned from
5412%      XOpenDisplay.
5413%
5414%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5415%
5416%    o windows: Specifies a pointer to a XWindows structure.
5417%
5418%    o image: the image.
5419%
5420%    o exception: return any errors or warnings in this structure.
5421%
5422*/
5423static MagickBooleanType XDrawEditImage(Display *display,
5424  XResourceInfo *resource_info,XWindows *windows,Image **image,
5425  ExceptionInfo *exception)
5426{
5427  static const char
5428    *DrawMenu[] =
5429    {
5430      "Element",
5431      "Color",
5432      "Stipple",
5433      "Width",
5434      "Undo",
5435      "Help",
5436      "Dismiss",
5437      (char *) NULL
5438    };
5439
5440  static ElementType
5441    element = PointElement;
5442
5443  static const ModeType
5444    DrawCommands[] =
5445    {
5446      DrawElementCommand,
5447      DrawColorCommand,
5448      DrawStippleCommand,
5449      DrawWidthCommand,
5450      DrawUndoCommand,
5451      DrawHelpCommand,
5452      DrawDismissCommand
5453    };
5454
5455  static Pixmap
5456    stipple = (Pixmap) NULL;
5457
5458  static unsigned int
5459    pen_id = 0,
5460    line_width = 1;
5461
5462  char
5463    command[MaxTextExtent],
5464    text[MaxTextExtent];
5465
5466  Cursor
5467    cursor;
5468
5469  int
5470    entry,
5471    id,
5472    number_coordinates,
5473    x,
5474    y;
5475
5476  MagickRealType
5477    degrees;
5478
5479  MagickStatusType
5480    status;
5481
5482  RectangleInfo
5483    rectangle_info;
5484
5485  register int
5486    i;
5487
5488  unsigned int
5489    distance,
5490    height,
5491    max_coordinates,
5492    width;
5493
5494  size_t
5495    state;
5496
5497  Window
5498    root_window;
5499
5500  XDrawInfo
5501    draw_info;
5502
5503  XEvent
5504    event;
5505
5506  XPoint
5507    *coordinate_info;
5508
5509  XSegment
5510    line_info;
5511
5512  /*
5513    Allocate polygon info.
5514  */
5515  max_coordinates=2048;
5516  coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5517    sizeof(*coordinate_info));
5518  if (coordinate_info == (XPoint *) NULL)
5519    {
5520      (void) ThrowMagickException(exception,GetMagickModule(),
5521        ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5522      return(MagickFalse);
5523    }
5524  /*
5525    Map Command widget.
5526  */
5527  (void) CloneString(&windows->command.name,"Draw");
5528  windows->command.data=4;
5529  (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5530  (void) XMapRaised(display,windows->command.id);
5531  XClientMessage(display,windows->image.id,windows->im_protocols,
5532    windows->im_update_widget,CurrentTime);
5533  /*
5534    Wait for first button press.
5535  */
5536  root_window=XRootWindow(display,XDefaultScreen(display));
5537  draw_info.stencil=OpaqueStencil;
5538  status=MagickTrue;
5539  cursor=XCreateFontCursor(display,XC_tcross);
5540  for ( ; ; )
5541  {
5542    XQueryPosition(display,windows->image.id,&x,&y);
5543    (void) XSelectInput(display,windows->image.id,
5544      windows->image.attributes.event_mask | PointerMotionMask);
5545    (void) XCheckDefineCursor(display,windows->image.id,cursor);
5546    state=DefaultState;
5547    do
5548    {
5549      if (windows->info.mapped != MagickFalse)
5550        {
5551          /*
5552            Display pointer position.
5553          */
5554          (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
5555            x+windows->image.x,y+windows->image.y);
5556          XInfoWidget(display,windows,text);
5557        }
5558      /*
5559        Wait for next event.
5560      */
5561      XScreenEvent(display,windows,&event,exception);
5562      if (event.xany.window == windows->command.id)
5563        {
5564          /*
5565            Select a command from the Command widget.
5566          */
5567          id=XCommandWidget(display,windows,DrawMenu,&event);
5568          if (id < 0)
5569            continue;
5570          switch (DrawCommands[id])
5571          {
5572            case DrawElementCommand:
5573            {
5574              static const char
5575                *Elements[] =
5576                {
5577                  "point",
5578                  "line",
5579                  "rectangle",
5580                  "fill rectangle",
5581                  "circle",
5582                  "fill circle",
5583                  "ellipse",
5584                  "fill ellipse",
5585                  "polygon",
5586                  "fill polygon",
5587                  (char *) NULL,
5588                };
5589
5590              /*
5591                Select a command from the pop-up menu.
5592              */
5593              element=(ElementType) (XMenuWidget(display,windows,
5594                DrawMenu[id],Elements,command)+1);
5595              break;
5596            }
5597            case DrawColorCommand:
5598            {
5599              const char
5600                *ColorMenu[MaxNumberPens+1];
5601
5602              int
5603                pen_number;
5604
5605              MagickBooleanType
5606                transparent;
5607
5608              XColor
5609                color;
5610
5611              /*
5612                Initialize menu selections.
5613              */
5614              for (i=0; i < (int) (MaxNumberPens-2); i++)
5615                ColorMenu[i]=resource_info->pen_colors[i];
5616              ColorMenu[MaxNumberPens-2]="transparent";
5617              ColorMenu[MaxNumberPens-1]="Browser...";
5618              ColorMenu[MaxNumberPens]=(char *) NULL;
5619              /*
5620                Select a pen color from the pop-up menu.
5621              */
5622              pen_number=XMenuWidget(display,windows,DrawMenu[id],
5623                (const char **) ColorMenu,command);
5624              if (pen_number < 0)
5625                break;
5626              transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5627                MagickFalse;
5628              if (transparent != MagickFalse)
5629                {
5630                  draw_info.stencil=TransparentStencil;
5631                  break;
5632                }
5633              if (pen_number == (MaxNumberPens-1))
5634                {
5635                  static char
5636                    color_name[MaxTextExtent] = "gray";
5637
5638                  /*
5639                    Select a pen color from a dialog.
5640                  */
5641                  resource_info->pen_colors[pen_number]=color_name;
5642                  XColorBrowserWidget(display,windows,"Select",color_name);
5643                  if (*color_name == '\0')
5644                    break;
5645                }
5646              /*
5647                Set pen color.
5648              */
5649              (void) XParseColor(display,windows->map_info->colormap,
5650                resource_info->pen_colors[pen_number],&color);
5651              XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5652                (unsigned int) MaxColors,&color);
5653              windows->pixel_info->pen_colors[pen_number]=color;
5654              pen_id=(unsigned int) pen_number;
5655              draw_info.stencil=OpaqueStencil;
5656              break;
5657            }
5658            case DrawStippleCommand:
5659            {
5660              Image
5661                *stipple_image;
5662
5663              ImageInfo
5664                *image_info;
5665
5666              int
5667                status;
5668
5669              static char
5670                filename[MaxTextExtent] = "\0";
5671
5672              static const char
5673                *StipplesMenu[] =
5674                {
5675                  "Brick",
5676                  "Diagonal",
5677                  "Scales",
5678                  "Vertical",
5679                  "Wavy",
5680                  "Translucent",
5681                  "Opaque",
5682                  (char *) NULL,
5683                  (char *) NULL,
5684                };
5685
5686              /*
5687                Select a command from the pop-up menu.
5688              */
5689              StipplesMenu[7]="Open...";
5690              entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5691                command);
5692              if (entry < 0)
5693                break;
5694              if (stipple != (Pixmap) NULL)
5695                (void) XFreePixmap(display,stipple);
5696              stipple=(Pixmap) NULL;
5697              if (entry != 7)
5698                {
5699                  switch (entry)
5700                  {
5701                    case 0:
5702                    {
5703                      stipple=XCreateBitmapFromData(display,root_window,
5704                        (char *) BricksBitmap,BricksWidth,BricksHeight);
5705                      break;
5706                    }
5707                    case 1:
5708                    {
5709                      stipple=XCreateBitmapFromData(display,root_window,
5710                        (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5711                      break;
5712                    }
5713                    case 2:
5714                    {
5715                      stipple=XCreateBitmapFromData(display,root_window,
5716                        (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5717                      break;
5718                    }
5719                    case 3:
5720                    {
5721                      stipple=XCreateBitmapFromData(display,root_window,
5722                        (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5723                      break;
5724                    }
5725                    case 4:
5726                    {
5727                      stipple=XCreateBitmapFromData(display,root_window,
5728                        (char *) WavyBitmap,WavyWidth,WavyHeight);
5729                      break;
5730                    }
5731                    case 5:
5732                    {
5733                      stipple=XCreateBitmapFromData(display,root_window,
5734                        (char *) HighlightBitmap,HighlightWidth,
5735                        HighlightHeight);
5736                      break;
5737                    }
5738                    case 6:
5739                    default:
5740                    {
5741                      stipple=XCreateBitmapFromData(display,root_window,
5742                        (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5743                      break;
5744                    }
5745                  }
5746                  break;
5747                }
5748              XFileBrowserWidget(display,windows,"Stipple",filename);
5749              if (*filename == '\0')
5750                break;
5751              /*
5752                Read image.
5753              */
5754              XSetCursorState(display,windows,MagickTrue);
5755              XCheckRefreshWindows(display,windows);
5756              image_info=AcquireImageInfo();
5757              (void) CopyMagickString(image_info->filename,filename,
5758                MaxTextExtent);
5759              stipple_image=ReadImage(image_info,exception);
5760              CatchException(exception);
5761              XSetCursorState(display,windows,MagickFalse);
5762              if (stipple_image == (Image *) NULL)
5763                break;
5764              (void) AcquireUniqueFileResource(filename);
5765              (void) FormatLocaleString(stipple_image->filename,MaxTextExtent,
5766                "xbm:%s",filename);
5767              (void) WriteImage(image_info,stipple_image,exception);
5768              stipple_image=DestroyImage(stipple_image);
5769              image_info=DestroyImageInfo(image_info);
5770              status=XReadBitmapFile(display,root_window,filename,&width,
5771                &height,&stipple,&x,&y);
5772              (void) RelinquishUniqueFileResource(filename);
5773              if ((status != BitmapSuccess) != 0)
5774                XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5775                  filename);
5776              break;
5777            }
5778            case DrawWidthCommand:
5779            {
5780              static char
5781                width[MaxTextExtent] = "0";
5782
5783              static const char
5784                *WidthsMenu[] =
5785                {
5786                  "1",
5787                  "2",
5788                  "4",
5789                  "8",
5790                  "16",
5791                  "Dialog...",
5792                  (char *) NULL,
5793                };
5794
5795              /*
5796                Select a command from the pop-up menu.
5797              */
5798              entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5799                command);
5800              if (entry < 0)
5801                break;
5802              if (entry != 5)
5803                {
5804                  line_width=(unsigned int) StringToUnsignedLong(
5805                    WidthsMenu[entry]);
5806                  break;
5807                }
5808              (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5809                width);
5810              if (*width == '\0')
5811                break;
5812              line_width=(unsigned int) StringToUnsignedLong(width);
5813              break;
5814            }
5815            case DrawUndoCommand:
5816            {
5817              (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5818                image,exception);
5819              break;
5820            }
5821            case DrawHelpCommand:
5822            {
5823              XTextViewWidget(display,resource_info,windows,MagickFalse,
5824                "Help Viewer - Image Rotation",ImageDrawHelp);
5825              (void) XCheckDefineCursor(display,windows->image.id,cursor);
5826              break;
5827            }
5828            case DrawDismissCommand:
5829            {
5830              /*
5831                Prematurely exit.
5832              */
5833              state|=EscapeState;
5834              state|=ExitState;
5835              break;
5836            }
5837            default:
5838              break;
5839          }
5840          (void) XCheckDefineCursor(display,windows->image.id,cursor);
5841          continue;
5842        }
5843      switch (event.type)
5844      {
5845        case ButtonPress:
5846        {
5847          if (event.xbutton.button != Button1)
5848            break;
5849          if (event.xbutton.window != windows->image.id)
5850            break;
5851          /*
5852            exit loop.
5853          */
5854          x=event.xbutton.x;
5855          y=event.xbutton.y;
5856          state|=ExitState;
5857          break;
5858        }
5859        case ButtonRelease:
5860          break;
5861        case Expose:
5862          break;
5863        case KeyPress:
5864        {
5865          KeySym
5866            key_symbol;
5867
5868          if (event.xkey.window != windows->image.id)
5869            break;
5870          /*
5871            Respond to a user key press.
5872          */
5873          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5874            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5875          switch ((int) key_symbol)
5876          {
5877            case XK_Escape:
5878            case XK_F20:
5879            {
5880              /*
5881                Prematurely exit.
5882              */
5883              state|=EscapeState;
5884              state|=ExitState;
5885              break;
5886            }
5887            case XK_F1:
5888            case XK_Help:
5889            {
5890              XTextViewWidget(display,resource_info,windows,MagickFalse,
5891                "Help Viewer - Image Rotation",ImageDrawHelp);
5892              break;
5893            }
5894            default:
5895            {
5896              (void) XBell(display,0);
5897              break;
5898            }
5899          }
5900          break;
5901        }
5902        case MotionNotify:
5903        {
5904          /*
5905            Map and unmap Info widget as text cursor crosses its boundaries.
5906          */
5907          x=event.xmotion.x;
5908          y=event.xmotion.y;
5909          if (windows->info.mapped != MagickFalse)
5910            {
5911              if ((x < (int) (windows->info.x+windows->info.width)) &&
5912                  (y < (int) (windows->info.y+windows->info.height)))
5913                (void) XWithdrawWindow(display,windows->info.id,
5914                  windows->info.screen);
5915            }
5916          else
5917            if ((x > (int) (windows->info.x+windows->info.width)) ||
5918                (y > (int) (windows->info.y+windows->info.height)))
5919              (void) XMapWindow(display,windows->info.id);
5920          break;
5921        }
5922      }
5923    } while ((state & ExitState) == 0);
5924    (void) XSelectInput(display,windows->image.id,
5925      windows->image.attributes.event_mask);
5926    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5927    if ((state & EscapeState) != 0)
5928      break;
5929    /*
5930      Draw element as pointer moves until the button is released.
5931    */
5932    distance=0;
5933    degrees=0.0;
5934    line_info.x1=x;
5935    line_info.y1=y;
5936    line_info.x2=x;
5937    line_info.y2=y;
5938    rectangle_info.x=(ssize_t) x;
5939    rectangle_info.y=(ssize_t) y;
5940    rectangle_info.width=0;
5941    rectangle_info.height=0;
5942    number_coordinates=1;
5943    coordinate_info->x=x;
5944    coordinate_info->y=y;
5945    (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5946    state=DefaultState;
5947    do
5948    {
5949      switch (element)
5950      {
5951        case PointElement:
5952        default:
5953        {
5954          if (number_coordinates > 1)
5955            {
5956              (void) XDrawLines(display,windows->image.id,
5957                windows->image.highlight_context,coordinate_info,
5958                number_coordinates,CoordModeOrigin);
5959              (void) FormatLocaleString(text,MaxTextExtent," %+d%+d",
5960                coordinate_info[number_coordinates-1].x,
5961                coordinate_info[number_coordinates-1].y);
5962              XInfoWidget(display,windows,text);
5963            }
5964          break;
5965        }
5966        case LineElement:
5967        {
5968          if (distance > 9)
5969            {
5970              /*
5971                Display angle of the line.
5972              */
5973              degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5974                line_info.y1),(double) (line_info.x2-line_info.x1)));
5975              (void) FormatLocaleString(text,MaxTextExtent," %g",
5976                (double) degrees);
5977              XInfoWidget(display,windows,text);
5978              XHighlightLine(display,windows->image.id,
5979                windows->image.highlight_context,&line_info);
5980            }
5981          else
5982            if (windows->info.mapped != MagickFalse)
5983              (void) XWithdrawWindow(display,windows->info.id,
5984                windows->info.screen);
5985          break;
5986        }
5987        case RectangleElement:
5988        case FillRectangleElement:
5989        {
5990          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5991            {
5992              /*
5993                Display info and draw drawing rectangle.
5994              */
5995              (void) FormatLocaleString(text,MaxTextExtent,
5996                " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5997                (double) rectangle_info.height,(double) rectangle_info.x,
5998                (double) rectangle_info.y);
5999              XInfoWidget(display,windows,text);
6000              XHighlightRectangle(display,windows->image.id,
6001                windows->image.highlight_context,&rectangle_info);
6002            }
6003          else
6004            if (windows->info.mapped != MagickFalse)
6005              (void) XWithdrawWindow(display,windows->info.id,
6006                windows->info.screen);
6007          break;
6008        }
6009        case CircleElement:
6010        case FillCircleElement:
6011        case EllipseElement:
6012        case FillEllipseElement:
6013        {
6014          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6015            {
6016              /*
6017                Display info and draw drawing rectangle.
6018              */
6019              (void) FormatLocaleString(text,MaxTextExtent,
6020                " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
6021                (double) rectangle_info.height,(double) rectangle_info.x,
6022                (double) rectangle_info.y);
6023              XInfoWidget(display,windows,text);
6024              XHighlightEllipse(display,windows->image.id,
6025                windows->image.highlight_context,&rectangle_info);
6026            }
6027          else
6028            if (windows->info.mapped != MagickFalse)
6029              (void) XWithdrawWindow(display,windows->info.id,
6030                windows->info.screen);
6031          break;
6032        }
6033        case PolygonElement:
6034        case FillPolygonElement:
6035        {
6036          if (number_coordinates > 1)
6037            (void) XDrawLines(display,windows->image.id,
6038              windows->image.highlight_context,coordinate_info,
6039              number_coordinates,CoordModeOrigin);
6040          if (distance > 9)
6041            {
6042              /*
6043                Display angle of the line.
6044              */
6045              degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
6046                line_info.y1),(double) (line_info.x2-line_info.x1)));
6047              (void) FormatLocaleString(text,MaxTextExtent," %g",
6048                (double) degrees);
6049              XInfoWidget(display,windows,text);
6050              XHighlightLine(display,windows->image.id,
6051                windows->image.highlight_context,&line_info);
6052            }
6053          else
6054            if (windows->info.mapped != MagickFalse)
6055              (void) XWithdrawWindow(display,windows->info.id,
6056                windows->info.screen);
6057          break;
6058        }
6059      }
6060      /*
6061        Wait for next event.
6062      */
6063      XScreenEvent(display,windows,&event,exception);
6064      switch (element)
6065      {
6066        case PointElement:
6067        default:
6068        {
6069          if (number_coordinates > 1)
6070            (void) XDrawLines(display,windows->image.id,
6071              windows->image.highlight_context,coordinate_info,
6072              number_coordinates,CoordModeOrigin);
6073          break;
6074        }
6075        case LineElement:
6076        {
6077          if (distance > 9)
6078            XHighlightLine(display,windows->image.id,
6079              windows->image.highlight_context,&line_info);
6080          break;
6081        }
6082        case RectangleElement:
6083        case FillRectangleElement:
6084        {
6085          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6086            XHighlightRectangle(display,windows->image.id,
6087              windows->image.highlight_context,&rectangle_info);
6088          break;
6089        }
6090        case CircleElement:
6091        case FillCircleElement:
6092        case EllipseElement:
6093        case FillEllipseElement:
6094        {
6095          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6096            XHighlightEllipse(display,windows->image.id,
6097              windows->image.highlight_context,&rectangle_info);
6098          break;
6099        }
6100        case PolygonElement:
6101        case FillPolygonElement:
6102        {
6103          if (number_coordinates > 1)
6104            (void) XDrawLines(display,windows->image.id,
6105              windows->image.highlight_context,coordinate_info,
6106              number_coordinates,CoordModeOrigin);
6107          if (distance > 9)
6108            XHighlightLine(display,windows->image.id,
6109              windows->image.highlight_context,&line_info);
6110          break;
6111        }
6112      }
6113      switch (event.type)
6114      {
6115        case ButtonPress:
6116          break;
6117        case ButtonRelease:
6118        {
6119          /*
6120            User has committed to element.
6121          */
6122          line_info.x2=event.xbutton.x;
6123          line_info.y2=event.xbutton.y;
6124          rectangle_info.x=(ssize_t) event.xbutton.x;
6125          rectangle_info.y=(ssize_t) event.xbutton.y;
6126          coordinate_info[number_coordinates].x=event.xbutton.x;
6127          coordinate_info[number_coordinates].y=event.xbutton.y;
6128          if (((element != PolygonElement) &&
6129               (element != FillPolygonElement)) || (distance <= 9))
6130            {
6131              state|=ExitState;
6132              break;
6133            }
6134          number_coordinates++;
6135          if (number_coordinates < (int) max_coordinates)
6136            {
6137              line_info.x1=event.xbutton.x;
6138              line_info.y1=event.xbutton.y;
6139              break;
6140            }
6141          max_coordinates<<=1;
6142          coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6143            max_coordinates,sizeof(*coordinate_info));
6144          if (coordinate_info == (XPoint *) NULL)
6145            (void) ThrowMagickException(exception,GetMagickModule(),
6146              ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6147          break;
6148        }
6149        case Expose:
6150          break;
6151        case MotionNotify:
6152        {
6153          if (event.xmotion.window != windows->image.id)
6154            break;
6155          if (element != PointElement)
6156            {
6157              line_info.x2=event.xmotion.x;
6158              line_info.y2=event.xmotion.y;
6159              rectangle_info.x=(ssize_t) event.xmotion.x;
6160              rectangle_info.y=(ssize_t) event.xmotion.y;
6161              break;
6162            }
6163          coordinate_info[number_coordinates].x=event.xbutton.x;
6164          coordinate_info[number_coordinates].y=event.xbutton.y;
6165          number_coordinates++;
6166          if (number_coordinates < (int) max_coordinates)
6167            break;
6168          max_coordinates<<=1;
6169          coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6170            max_coordinates,sizeof(*coordinate_info));
6171          if (coordinate_info == (XPoint *) NULL)
6172            (void) ThrowMagickException(exception,GetMagickModule(),
6173              ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6174          break;
6175        }
6176        default:
6177          break;
6178      }
6179      /*
6180        Check boundary conditions.
6181      */
6182      if (line_info.x2 < 0)
6183        line_info.x2=0;
6184      else
6185        if (line_info.x2 > (int) windows->image.width)
6186          line_info.x2=(short) windows->image.width;
6187      if (line_info.y2 < 0)
6188        line_info.y2=0;
6189      else
6190        if (line_info.y2 > (int) windows->image.height)
6191          line_info.y2=(short) windows->image.height;
6192      distance=(unsigned int)
6193        (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6194         ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6195      if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6196          ((state & ExitState) != 0))
6197        {
6198          if (rectangle_info.x < 0)
6199            rectangle_info.x=0;
6200          else
6201            if (rectangle_info.x > (ssize_t) windows->image.width)
6202              rectangle_info.x=(ssize_t) windows->image.width;
6203          if ((int) rectangle_info.x < x)
6204            rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6205          else
6206            {
6207              rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6208              rectangle_info.x=(ssize_t) x;
6209            }
6210          if (rectangle_info.y < 0)
6211            rectangle_info.y=0;
6212          else
6213            if (rectangle_info.y > (ssize_t) windows->image.height)
6214              rectangle_info.y=(ssize_t) windows->image.height;
6215          if ((int) rectangle_info.y < y)
6216            rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6217          else
6218            {
6219              rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6220              rectangle_info.y=(ssize_t) y;
6221            }
6222        }
6223    } while ((state & ExitState) == 0);
6224    (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6225    if ((element == PointElement) || (element == PolygonElement) ||
6226        (element == FillPolygonElement))
6227      {
6228        /*
6229          Determine polygon bounding box.
6230        */
6231        rectangle_info.x=(ssize_t) coordinate_info->x;
6232        rectangle_info.y=(ssize_t) coordinate_info->y;
6233        x=coordinate_info->x;
6234        y=coordinate_info->y;
6235        for (i=1; i < number_coordinates; i++)
6236        {
6237          if (coordinate_info[i].x > x)
6238            x=coordinate_info[i].x;
6239          if (coordinate_info[i].y > y)
6240            y=coordinate_info[i].y;
6241          if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6242            rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6243          if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6244            rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6245        }
6246        rectangle_info.width=(size_t) (x-rectangle_info.x);
6247        rectangle_info.height=(size_t) (y-rectangle_info.y);
6248        for (i=0; i < number_coordinates; i++)
6249        {
6250          coordinate_info[i].x-=rectangle_info.x;
6251          coordinate_info[i].y-=rectangle_info.y;
6252        }
6253      }
6254    else
6255      if (distance <= 9)
6256        continue;
6257      else
6258        if ((element == RectangleElement) ||
6259            (element == CircleElement) || (element == EllipseElement))
6260          {
6261            rectangle_info.width--;
6262            rectangle_info.height--;
6263          }
6264    /*
6265      Drawing is relative to image configuration.
6266    */
6267    draw_info.x=(int) rectangle_info.x;
6268    draw_info.y=(int) rectangle_info.y;
6269    (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6270      image,exception);
6271    width=(unsigned int) (*image)->columns;
6272    height=(unsigned int) (*image)->rows;
6273    x=0;
6274    y=0;
6275    if (windows->image.crop_geometry != (char *) NULL)
6276      (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6277    draw_info.x+=windows->image.x-(line_width/2);
6278    if (draw_info.x < 0)
6279      draw_info.x=0;
6280    draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6281    draw_info.y+=windows->image.y-(line_width/2);
6282    if (draw_info.y < 0)
6283      draw_info.y=0;
6284    draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6285    draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6286    if (draw_info.width > (unsigned int) (*image)->columns)
6287      draw_info.width=(unsigned int) (*image)->columns;
6288    draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6289    if (draw_info.height > (unsigned int) (*image)->rows)
6290      draw_info.height=(unsigned int) (*image)->rows;
6291    (void) FormatLocaleString(draw_info.geometry,MaxTextExtent,"%ux%u%+d%+d",
6292      width*draw_info.width/windows->image.ximage->width,
6293      height*draw_info.height/windows->image.ximage->height,
6294      draw_info.x+x,draw_info.y+y);
6295    /*
6296      Initialize drawing attributes.
6297    */
6298    draw_info.degrees=0.0;
6299    draw_info.element=element;
6300    draw_info.stipple=stipple;
6301    draw_info.line_width=line_width;
6302    draw_info.line_info=line_info;
6303    if (line_info.x1 > (int) (line_width/2))
6304      draw_info.line_info.x1=(short) line_width/2;
6305    if (line_info.y1 > (int) (line_width/2))
6306      draw_info.line_info.y1=(short) line_width/2;
6307    draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6308    draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6309    if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6310      {
6311        draw_info.line_info.x2=(-draw_info.line_info.x2);
6312        draw_info.line_info.y2=(-draw_info.line_info.y2);
6313      }
6314    if (draw_info.line_info.x2 < 0)
6315      {
6316        draw_info.line_info.x2=(-draw_info.line_info.x2);
6317        Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6318      }
6319    if (draw_info.line_info.y2 < 0)
6320      {
6321        draw_info.line_info.y2=(-draw_info.line_info.y2);
6322        Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6323      }
6324    draw_info.rectangle_info=rectangle_info;
6325    if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6326      draw_info.rectangle_info.x=(ssize_t) line_width/2;
6327    if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6328      draw_info.rectangle_info.y=(ssize_t) line_width/2;
6329    draw_info.number_coordinates=(unsigned int) number_coordinates;
6330    draw_info.coordinate_info=coordinate_info;
6331    windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6332    /*
6333      Draw element on image.
6334    */
6335    XSetCursorState(display,windows,MagickTrue);
6336    XCheckRefreshWindows(display,windows);
6337    status=XDrawImage(display,windows->pixel_info,&draw_info,*image,exception);
6338    XSetCursorState(display,windows,MagickFalse);
6339    /*
6340      Update image colormap and return to image drawing.
6341    */
6342    XConfigureImageColormap(display,resource_info,windows,*image,exception);
6343    (void) XConfigureImage(display,resource_info,windows,*image,exception);
6344  }
6345  XSetCursorState(display,windows,MagickFalse);
6346  coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6347  return(status != 0 ? MagickTrue : MagickFalse);
6348}
6349
6350/*
6351%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6352%                                                                             %
6353%                                                                             %
6354%                                                                             %
6355+   X D r a w P a n R e c t a n g l e                                         %
6356%                                                                             %
6357%                                                                             %
6358%                                                                             %
6359%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6360%
6361%  XDrawPanRectangle() draws a rectangle in the pan window.  The pan window
6362%  displays a zoom image and the rectangle shows which portion of the image is
6363%  displayed in the Image window.
6364%
6365%  The format of the XDrawPanRectangle method is:
6366%
6367%      XDrawPanRectangle(Display *display,XWindows *windows)
6368%
6369%  A description of each parameter follows:
6370%
6371%    o display: Specifies a connection to an X server;  returned from
6372%      XOpenDisplay.
6373%
6374%    o windows: Specifies a pointer to a XWindows structure.
6375%
6376*/
6377static void XDrawPanRectangle(Display *display,XWindows *windows)
6378{
6379  MagickRealType
6380    scale_factor;
6381
6382  RectangleInfo
6383    highlight_info;
6384
6385  /*
6386    Determine dimensions of the panning rectangle.
6387  */
6388  scale_factor=(MagickRealType) windows->pan.width/windows->image.ximage->width;
6389  highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6390  highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6391  scale_factor=(MagickRealType)
6392    windows->pan.height/windows->image.ximage->height;
6393  highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6394  highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6395  /*
6396    Display the panning rectangle.
6397  */
6398  (void) XClearWindow(display,windows->pan.id);
6399  XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6400    &highlight_info);
6401}
6402
6403/*
6404%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6405%                                                                             %
6406%                                                                             %
6407%                                                                             %
6408+   X I m a g e C a c h e                                                     %
6409%                                                                             %
6410%                                                                             %
6411%                                                                             %
6412%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6413%
6414%  XImageCache() handles the creation, manipulation, and destruction of the
6415%  image cache (undo and redo buffers).
6416%
6417%  The format of the XImageCache method is:
6418%
6419%      void XImageCache(Display *display,XResourceInfo *resource_info,
6420%        XWindows *windows,const CommandType command,Image **image,
6421%        ExceptionInfo *exception)
6422%
6423%  A description of each parameter follows:
6424%
6425%    o display: Specifies a connection to an X server; returned from
6426%      XOpenDisplay.
6427%
6428%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6429%
6430%    o windows: Specifies a pointer to a XWindows structure.
6431%
6432%    o command: Specifies a command to perform.
6433%
6434%    o image: the image;  XImageCache may transform the image and return a new
6435%      image pointer.
6436%
6437%    o exception: return any errors or warnings in this structure.
6438%
6439*/
6440static void XImageCache(Display *display,XResourceInfo *resource_info,
6441  XWindows *windows,const CommandType command,Image **image,
6442  ExceptionInfo *exception)
6443{
6444  Image
6445    *cache_image;
6446
6447  static Image
6448    *redo_image = (Image *) NULL,
6449    *undo_image = (Image *) NULL;
6450
6451  switch (command)
6452  {
6453    case FreeBuffersCommand:
6454    {
6455      /*
6456        Free memory from the undo and redo cache.
6457      */
6458      while (undo_image != (Image *) NULL)
6459      {
6460        cache_image=undo_image;
6461        undo_image=GetPreviousImageInList(undo_image);
6462        cache_image->list=DestroyImage(cache_image->list);
6463        cache_image=DestroyImage(cache_image);
6464      }
6465      undo_image=NewImageList();
6466      if (redo_image != (Image *) NULL)
6467        redo_image=DestroyImage(redo_image);
6468      redo_image=NewImageList();
6469      return;
6470    }
6471    case UndoCommand:
6472    {
6473      char
6474        image_geometry[MaxTextExtent];
6475
6476      /*
6477        Undo the last image transformation.
6478      */
6479      if (undo_image == (Image *) NULL)
6480        {
6481          (void) XBell(display,0);
6482          return;
6483        }
6484      cache_image=undo_image;
6485      undo_image=GetPreviousImageInList(undo_image);
6486      windows->image.window_changes.width=(int) cache_image->columns;
6487      windows->image.window_changes.height=(int) cache_image->rows;
6488      (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
6489        windows->image.ximage->width,windows->image.ximage->height);
6490      (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
6491        exception);
6492      if (windows->image.crop_geometry != (char *) NULL)
6493        windows->image.crop_geometry=(char *) RelinquishMagickMemory(
6494          windows->image.crop_geometry);
6495      windows->image.crop_geometry=cache_image->geometry;
6496      if (redo_image != (Image *) NULL)
6497        redo_image=DestroyImage(redo_image);
6498      redo_image=(*image);
6499      *image=cache_image->list;
6500      cache_image=DestroyImage(cache_image);
6501      if (windows->image.orphan != MagickFalse)
6502        return;
6503      XConfigureImageColormap(display,resource_info,windows,*image,exception);
6504      (void) XConfigureImage(display,resource_info,windows,*image,exception);
6505      return;
6506    }
6507    case CutCommand:
6508    case PasteCommand:
6509    case ApplyCommand:
6510    case HalfSizeCommand:
6511    case OriginalSizeCommand:
6512    case DoubleSizeCommand:
6513    case ResizeCommand:
6514    case TrimCommand:
6515    case CropCommand:
6516    case ChopCommand:
6517    case FlipCommand:
6518    case FlopCommand:
6519    case RotateRightCommand:
6520    case RotateLeftCommand:
6521    case RotateCommand:
6522    case ShearCommand:
6523    case RollCommand:
6524    case NegateCommand:
6525    case ContrastStretchCommand:
6526    case SigmoidalContrastCommand:
6527    case NormalizeCommand:
6528    case EqualizeCommand:
6529    case HueCommand:
6530    case SaturationCommand:
6531    case BrightnessCommand:
6532    case GammaCommand:
6533    case SpiffCommand:
6534    case DullCommand:
6535    case GrayscaleCommand:
6536    case MapCommand:
6537    case QuantizeCommand:
6538    case DespeckleCommand:
6539    case EmbossCommand:
6540    case ReduceNoiseCommand:
6541    case AddNoiseCommand:
6542    case SharpenCommand:
6543    case BlurCommand:
6544    case ThresholdCommand:
6545    case EdgeDetectCommand:
6546    case SpreadCommand:
6547    case ShadeCommand:
6548    case RaiseCommand:
6549    case SegmentCommand:
6550    case SolarizeCommand:
6551    case SepiaToneCommand:
6552    case SwirlCommand:
6553    case ImplodeCommand:
6554    case VignetteCommand:
6555    case WaveCommand:
6556    case OilPaintCommand:
6557    case CharcoalDrawCommand:
6558    case AnnotateCommand:
6559    case AddBorderCommand:
6560    case AddFrameCommand:
6561    case CompositeCommand:
6562    case CommentCommand:
6563    case LaunchCommand:
6564    case RegionofInterestCommand:
6565    case SaveToUndoBufferCommand:
6566    case RedoCommand:
6567    {
6568      Image
6569        *previous_image;
6570
6571      ssize_t
6572        bytes;
6573
6574      bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelInfo));
6575      if (undo_image != (Image *) NULL)
6576        {
6577          /*
6578            Ensure the undo cache has enough memory available.
6579          */
6580          previous_image=undo_image;
6581          while (previous_image != (Image *) NULL)
6582          {
6583            bytes+=previous_image->list->columns*previous_image->list->rows*
6584              sizeof(PixelInfo);
6585            if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6586              {
6587                previous_image=GetPreviousImageInList(previous_image);
6588                continue;
6589              }
6590            bytes-=previous_image->list->columns*previous_image->list->rows*
6591              sizeof(PixelInfo);
6592            if (previous_image == undo_image)
6593              undo_image=NewImageList();
6594            else
6595              previous_image->next->previous=NewImageList();
6596            break;
6597          }
6598          while (previous_image != (Image *) NULL)
6599          {
6600            /*
6601              Delete any excess memory from undo cache.
6602            */
6603            cache_image=previous_image;
6604            previous_image=GetPreviousImageInList(previous_image);
6605            cache_image->list=DestroyImage(cache_image->list);
6606            cache_image=DestroyImage(cache_image);
6607          }
6608        }
6609      if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6610        break;
6611      /*
6612        Save image before transformations are applied.
6613      */
6614      cache_image=AcquireImage((ImageInfo *) NULL,exception);
6615      if (cache_image == (Image *) NULL)
6616        break;
6617      XSetCursorState(display,windows,MagickTrue);
6618      XCheckRefreshWindows(display,windows);
6619      cache_image->list=CloneImage(*image,0,0,MagickTrue,exception);
6620      XSetCursorState(display,windows,MagickFalse);
6621      if (cache_image->list == (Image *) NULL)
6622        {
6623          cache_image=DestroyImage(cache_image);
6624          break;
6625        }
6626      cache_image->columns=(size_t) windows->image.ximage->width;
6627      cache_image->rows=(size_t) windows->image.ximage->height;
6628      cache_image->geometry=windows->image.crop_geometry;
6629      if (windows->image.crop_geometry != (char *) NULL)
6630        {
6631          cache_image->geometry=AcquireString((char *) NULL);
6632          (void) CopyMagickString(cache_image->geometry,
6633            windows->image.crop_geometry,MaxTextExtent);
6634        }
6635      if (undo_image == (Image *) NULL)
6636        {
6637          undo_image=cache_image;
6638          break;
6639        }
6640      undo_image->next=cache_image;
6641      undo_image->next->previous=undo_image;
6642      undo_image=undo_image->next;
6643      break;
6644    }
6645    default:
6646      break;
6647  }
6648  if (command == RedoCommand)
6649    {
6650      /*
6651        Redo the last image transformation.
6652      */
6653      if (redo_image == (Image *) NULL)
6654        {
6655          (void) XBell(display,0);
6656          return;
6657        }
6658      windows->image.window_changes.width=(int) redo_image->columns;
6659      windows->image.window_changes.height=(int) redo_image->rows;
6660      if (windows->image.crop_geometry != (char *) NULL)
6661        windows->image.crop_geometry=(char *)
6662          RelinquishMagickMemory(windows->image.crop_geometry);
6663      windows->image.crop_geometry=redo_image->geometry;
6664      *image=DestroyImage(*image);
6665      *image=redo_image;
6666      redo_image=NewImageList();
6667      if (windows->image.orphan != MagickFalse)
6668        return;
6669      XConfigureImageColormap(display,resource_info,windows,*image,exception);
6670      (void) XConfigureImage(display,resource_info,windows,*image,exception);
6671      return;
6672    }
6673  if (command != InfoCommand)
6674    return;
6675  /*
6676    Display image info.
6677  */
6678  XSetCursorState(display,windows,MagickTrue);
6679  XCheckRefreshWindows(display,windows);
6680  XDisplayImageInfo(display,resource_info,windows,undo_image,*image,exception);
6681  XSetCursorState(display,windows,MagickFalse);
6682}
6683
6684/*
6685%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6686%                                                                             %
6687%                                                                             %
6688%                                                                             %
6689+   X I m a g e W i n d o w C o m m a n d                                     %
6690%                                                                             %
6691%                                                                             %
6692%                                                                             %
6693%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6694%
6695%  XImageWindowCommand() makes a transform to the image or Image window as
6696%  specified by a user menu button or keyboard command.
6697%
6698%  The format of the XImageWindowCommand method is:
6699%
6700%      CommandType XImageWindowCommand(Display *display,
6701%        XResourceInfo *resource_info,XWindows *windows,
6702%        const MagickStatusType state,KeySym key_symbol,Image **image,
6703%        ExceptionInfo *exception)
6704%
6705%  A description of each parameter follows:
6706%
6707%    o nexus:  Method XImageWindowCommand returns an image when the
6708%      user chooses 'Open Image' from the command menu.  Otherwise a null
6709%      image is returned.
6710%
6711%    o display: Specifies a connection to an X server; returned from
6712%      XOpenDisplay.
6713%
6714%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6715%
6716%    o windows: Specifies a pointer to a XWindows structure.
6717%
6718%    o state: key mask.
6719%
6720%    o key_symbol: Specifies a command to perform.
6721%
6722%    o image: the image;  XImageWIndowCommand may transform the image and
6723%      return a new image pointer.
6724%
6725%    o exception: return any errors or warnings in this structure.
6726%
6727*/
6728static CommandType XImageWindowCommand(Display *display,
6729  XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6730  KeySym key_symbol,Image **image,ExceptionInfo *exception)
6731{
6732  static char
6733    delta[MaxTextExtent] = "";
6734
6735  static const char
6736    Digits[] = "01234567890";
6737
6738  static KeySym
6739    last_symbol = XK_0;
6740
6741  if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6742    {
6743      if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6744        {
6745          *delta='\0';
6746          resource_info->quantum=1;
6747        }
6748      last_symbol=key_symbol;
6749      delta[strlen(delta)+1]='\0';
6750      delta[strlen(delta)]=Digits[key_symbol-XK_0];
6751      resource_info->quantum=StringToLong(delta);
6752      return(NullCommand);
6753    }
6754  last_symbol=key_symbol;
6755  if (resource_info->immutable)
6756    {
6757      /*
6758        Virtual image window has a restricted command set.
6759      */
6760      switch (key_symbol)
6761      {
6762        case XK_question:
6763          return(InfoCommand);
6764        case XK_p:
6765        case XK_Print:
6766          return(PrintCommand);
6767        case XK_space:
6768          return(NextCommand);
6769        case XK_q:
6770        case XK_Escape:
6771          return(QuitCommand);
6772        default:
6773          break;
6774      }
6775      return(NullCommand);
6776    }
6777  switch ((int) key_symbol)
6778  {
6779    case XK_o:
6780    {
6781      if ((state & ControlMask) == 0)
6782        break;
6783      return(OpenCommand);
6784    }
6785    case XK_space:
6786      return(NextCommand);
6787    case XK_BackSpace:
6788      return(FormerCommand);
6789    case XK_s:
6790    {
6791      if ((state & Mod1Mask) != 0)
6792        return(SwirlCommand);
6793      if ((state & ControlMask) == 0)
6794        return(ShearCommand);
6795      return(SaveCommand);
6796    }
6797    case XK_p:
6798    case XK_Print:
6799    {
6800      if ((state & Mod1Mask) != 0)
6801        return(OilPaintCommand);
6802      if ((state & Mod4Mask) != 0)
6803        return(ColorCommand);
6804      if ((state & ControlMask) == 0)
6805        return(NullCommand);
6806      return(PrintCommand);
6807    }
6808    case XK_d:
6809    {
6810      if ((state & Mod4Mask) != 0)
6811        return(DrawCommand);
6812      if ((state & ControlMask) == 0)
6813        return(NullCommand);
6814      return(DeleteCommand);
6815    }
6816    case XK_Select:
6817    {
6818      if ((state & ControlMask) == 0)
6819        return(NullCommand);
6820      return(SelectCommand);
6821    }
6822    case XK_n:
6823    {
6824      if ((state & ControlMask) == 0)
6825        return(NullCommand);
6826      return(NewCommand);
6827    }
6828    case XK_q:
6829    case XK_Escape:
6830      return(QuitCommand);
6831    case XK_z:
6832    case XK_Undo:
6833    {
6834      if ((state & ControlMask) == 0)
6835        return(NullCommand);
6836      return(UndoCommand);
6837    }
6838    case XK_r:
6839    case XK_Redo:
6840    {
6841      if ((state & ControlMask) == 0)
6842        return(RollCommand);
6843      return(RedoCommand);
6844    }
6845    case XK_x:
6846    {
6847      if ((state & ControlMask) == 0)
6848        return(NullCommand);
6849      return(CutCommand);
6850    }
6851    case XK_c:
6852    {
6853      if ((state & Mod1Mask) != 0)
6854        return(CharcoalDrawCommand);
6855      if ((state & ControlMask) == 0)
6856        return(CropCommand);
6857      return(CopyCommand);
6858    }
6859    case XK_v:
6860    case XK_Insert:
6861    {
6862      if ((state & Mod4Mask) != 0)
6863        return(CompositeCommand);
6864      if ((state & ControlMask) == 0)
6865        return(FlipCommand);
6866      return(PasteCommand);
6867    }
6868    case XK_less:
6869      return(HalfSizeCommand);
6870    case XK_minus:
6871      return(OriginalSizeCommand);
6872    case XK_greater:
6873      return(DoubleSizeCommand);
6874    case XK_percent:
6875      return(ResizeCommand);
6876    case XK_at:
6877      return(RefreshCommand);
6878    case XK_bracketleft:
6879      return(ChopCommand);
6880    case XK_h:
6881      return(FlopCommand);
6882    case XK_slash:
6883      return(RotateRightCommand);
6884    case XK_backslash:
6885      return(RotateLeftCommand);
6886    case XK_asterisk:
6887      return(RotateCommand);
6888    case XK_t:
6889      return(TrimCommand);
6890    case XK_H:
6891      return(HueCommand);
6892    case XK_S:
6893      return(SaturationCommand);
6894    case XK_L:
6895      return(BrightnessCommand);
6896    case XK_G:
6897      return(GammaCommand);
6898    case XK_C:
6899      return(SpiffCommand);
6900    case XK_Z:
6901      return(DullCommand);
6902    case XK_N:
6903      return(NormalizeCommand);
6904    case XK_equal:
6905      return(EqualizeCommand);
6906    case XK_asciitilde:
6907      return(NegateCommand);
6908    case XK_period:
6909      return(GrayscaleCommand);
6910    case XK_numbersign:
6911      return(QuantizeCommand);
6912    case XK_F2:
6913      return(DespeckleCommand);
6914    case XK_F3:
6915      return(EmbossCommand);
6916    case XK_F4:
6917      return(ReduceNoiseCommand);
6918    case XK_F5:
6919      return(AddNoiseCommand);
6920    case XK_F6:
6921      return(SharpenCommand);
6922    case XK_F7:
6923      return(BlurCommand);
6924    case XK_F8:
6925      return(ThresholdCommand);
6926    case XK_F9:
6927      return(EdgeDetectCommand);
6928    case XK_F10:
6929      return(SpreadCommand);
6930    case XK_F11:
6931      return(ShadeCommand);
6932    case XK_F12:
6933      return(RaiseCommand);
6934    case XK_F13:
6935      return(SegmentCommand);
6936    case XK_i:
6937    {
6938      if ((state & Mod1Mask) == 0)
6939        return(NullCommand);
6940      return(ImplodeCommand);
6941    }
6942    case XK_w:
6943    {
6944      if ((state & Mod1Mask) == 0)
6945        return(NullCommand);
6946      return(WaveCommand);
6947    }
6948    case XK_m:
6949    {
6950      if ((state & Mod4Mask) == 0)
6951        return(NullCommand);
6952      return(MatteCommand);
6953    }
6954    case XK_b:
6955    {
6956      if ((state & Mod4Mask) == 0)
6957        return(NullCommand);
6958      return(AddBorderCommand);
6959    }
6960    case XK_f:
6961    {
6962      if ((state & Mod4Mask) == 0)
6963        return(NullCommand);
6964      return(AddFrameCommand);
6965    }
6966    case XK_exclam:
6967    {
6968      if ((state & Mod4Mask) == 0)
6969        return(NullCommand);
6970      return(CommentCommand);
6971    }
6972    case XK_a:
6973    {
6974      if ((state & Mod1Mask) != 0)
6975        return(ApplyCommand);
6976      if ((state & Mod4Mask) != 0)
6977        return(AnnotateCommand);
6978      if ((state & ControlMask) == 0)
6979        return(NullCommand);
6980      return(RegionofInterestCommand);
6981    }
6982    case XK_question:
6983      return(InfoCommand);
6984    case XK_plus:
6985      return(ZoomCommand);
6986    case XK_P:
6987    {
6988      if ((state & ShiftMask) == 0)
6989        return(NullCommand);
6990      return(ShowPreviewCommand);
6991    }
6992    case XK_Execute:
6993      return(LaunchCommand);
6994    case XK_F1:
6995      return(HelpCommand);
6996    case XK_Find:
6997      return(BrowseDocumentationCommand);
6998    case XK_Menu:
6999    {
7000      (void) XMapRaised(display,windows->command.id);
7001      return(NullCommand);
7002    }
7003    case XK_Next:
7004    case XK_Prior:
7005    case XK_Home:
7006    case XK_KP_Home:
7007    {
7008      XTranslateImage(display,windows,*image,key_symbol);
7009      return(NullCommand);
7010    }
7011    case XK_Up:
7012    case XK_KP_Up:
7013    case XK_Down:
7014    case XK_KP_Down:
7015    case XK_Left:
7016    case XK_KP_Left:
7017    case XK_Right:
7018    case XK_KP_Right:
7019    {
7020      if ((state & Mod1Mask) != 0)
7021        {
7022          RectangleInfo
7023            crop_info;
7024
7025          /*
7026            Trim one pixel from edge of image.
7027          */
7028          crop_info.x=0;
7029          crop_info.y=0;
7030          crop_info.width=(size_t) windows->image.ximage->width;
7031          crop_info.height=(size_t) windows->image.ximage->height;
7032          if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
7033            {
7034              if (resource_info->quantum >= (int) crop_info.height)
7035                resource_info->quantum=(int) crop_info.height-1;
7036              crop_info.height-=resource_info->quantum;
7037            }
7038          if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
7039            {
7040              if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
7041                resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
7042              crop_info.y+=resource_info->quantum;
7043              crop_info.height-=resource_info->quantum;
7044            }
7045          if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
7046            {
7047              if (resource_info->quantum >= (int) crop_info.width)
7048                resource_info->quantum=(int) crop_info.width-1;
7049              crop_info.width-=resource_info->quantum;
7050            }
7051          if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
7052            {
7053              if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
7054                resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
7055              crop_info.x+=resource_info->quantum;
7056              crop_info.width-=resource_info->quantum;
7057            }
7058          if ((int) (windows->image.x+windows->image.width) >
7059              (int) crop_info.width)
7060            windows->image.x=(int) (crop_info.width-windows->image.width);
7061          if ((int) (windows->image.y+windows->image.height) >
7062              (int) crop_info.height)
7063            windows->image.y=(int) (crop_info.height-windows->image.height);
7064          XSetCropGeometry(display,windows,&crop_info,*image);
7065          windows->image.window_changes.width=(int) crop_info.width;
7066          windows->image.window_changes.height=(int) crop_info.height;
7067          (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
7068          (void) XConfigureImage(display,resource_info,windows,*image,
7069            exception);
7070          return(NullCommand);
7071        }
7072      XTranslateImage(display,windows,*image,key_symbol);
7073      return(NullCommand);
7074    }
7075    default:
7076      return(NullCommand);
7077  }
7078  return(NullCommand);
7079}
7080
7081/*
7082%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7083%                                                                             %
7084%                                                                             %
7085%                                                                             %
7086+   X M a g i c k C o m m a n d                                               %
7087%                                                                             %
7088%                                                                             %
7089%                                                                             %
7090%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7091%
7092%  XMagickCommand() makes a transform to the image or Image window as
7093%  specified by a user menu button or keyboard command.
7094%
7095%  The format of the XMagickCommand method is:
7096%
7097%      Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7098%        XWindows *windows,const CommandType command,Image **image,
7099%        ExceptionInfo *exception)
7100%
7101%  A description of each parameter follows:
7102%
7103%    o display: Specifies a connection to an X server; returned from
7104%      XOpenDisplay.
7105%
7106%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7107%
7108%    o windows: Specifies a pointer to a XWindows structure.
7109%
7110%    o command: Specifies a command to perform.
7111%
7112%    o image: the image;  XMagickCommand may transform the image and return a
7113%      new image pointer.
7114%
7115%    o exception: return any errors or warnings in this structure.
7116%
7117*/
7118static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7119  XWindows *windows,const CommandType command,Image **image,
7120  ExceptionInfo *exception)
7121{
7122  char
7123    filename[MaxTextExtent],
7124    geometry[MaxTextExtent],
7125    modulate_factors[MaxTextExtent];
7126
7127  GeometryInfo
7128    geometry_info;
7129
7130  Image
7131    *nexus;
7132
7133  ImageInfo
7134    *image_info;
7135
7136  int
7137    x,
7138    y;
7139
7140  MagickStatusType
7141    flags,
7142    status;
7143
7144  QuantizeInfo
7145    quantize_info;
7146
7147  RectangleInfo
7148    page_geometry;
7149
7150  register int
7151    i;
7152
7153  static char
7154    color[MaxTextExtent] = "gray";
7155
7156  unsigned int
7157    height,
7158    width;
7159
7160  /*
7161    Process user command.
7162  */
7163  XCheckRefreshWindows(display,windows);
7164  XImageCache(display,resource_info,windows,command,image,exception);
7165  nexus=NewImageList();
7166  windows->image.window_changes.width=windows->image.ximage->width;
7167  windows->image.window_changes.height=windows->image.ximage->height;
7168  image_info=CloneImageInfo(resource_info->image_info);
7169  SetGeometryInfo(&geometry_info);
7170  GetQuantizeInfo(&quantize_info);
7171  switch (command)
7172  {
7173    case OpenCommand:
7174    {
7175      /*
7176        Load image.
7177      */
7178      nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7179      break;
7180    }
7181    case NextCommand:
7182    {
7183      /*
7184        Display next image.
7185      */
7186      for (i=0; i < resource_info->quantum; i++)
7187        XClientMessage(display,windows->image.id,windows->im_protocols,
7188          windows->im_next_image,CurrentTime);
7189      break;
7190    }
7191    case FormerCommand:
7192    {
7193      /*
7194        Display former image.
7195      */
7196      for (i=0; i < resource_info->quantum; i++)
7197        XClientMessage(display,windows->image.id,windows->im_protocols,
7198          windows->im_former_image,CurrentTime);
7199      break;
7200    }
7201    case SelectCommand:
7202    {
7203      int
7204        status;
7205
7206      /*
7207        Select image.
7208      */
7209      status=chdir(resource_info->home_directory);
7210      if (status == -1)
7211        (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
7212          "UnableToOpenFile","%s",resource_info->home_directory);
7213      nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7214      break;
7215    }
7216    case SaveCommand:
7217    {
7218      /*
7219        Save image.
7220      */
7221      status=XSaveImage(display,resource_info,windows,*image,exception);
7222      if (status == MagickFalse)
7223        {
7224          char
7225            message[MaxTextExtent];
7226
7227          (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7228            exception->reason != (char *) NULL ? exception->reason : "",
7229            exception->description != (char *) NULL ? exception->description :
7230            "");
7231          XNoticeWidget(display,windows,"Unable to save file:",message);
7232          break;
7233        }
7234      break;
7235    }
7236    case PrintCommand:
7237    {
7238      /*
7239        Print image.
7240      */
7241      status=XPrintImage(display,resource_info,windows,*image,exception);
7242      if (status == MagickFalse)
7243        {
7244          char
7245            message[MaxTextExtent];
7246
7247          (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7248            exception->reason != (char *) NULL ? exception->reason : "",
7249            exception->description != (char *) NULL ? exception->description :
7250            "");
7251          XNoticeWidget(display,windows,"Unable to print file:",message);
7252          break;
7253        }
7254      break;
7255    }
7256    case DeleteCommand:
7257    {
7258      static char
7259        filename[MaxTextExtent] = "\0";
7260
7261      /*
7262        Delete image file.
7263      */
7264      XFileBrowserWidget(display,windows,"Delete",filename);
7265      if (*filename == '\0')
7266        break;
7267      status=remove_utf8(filename) != 0 ? MagickTrue : MagickFalse;
7268      if (status != MagickFalse)
7269        XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7270      break;
7271    }
7272    case NewCommand:
7273    {
7274      int
7275        status;
7276
7277      static char
7278        color[MaxTextExtent] = "gray",
7279        geometry[MaxTextExtent] = "640x480";
7280
7281      static const char
7282        *format = "gradient";
7283
7284      /*
7285        Query user for canvas geometry.
7286      */
7287      status=XDialogWidget(display,windows,"New","Enter image geometry:",
7288        geometry);
7289      if (*geometry == '\0')
7290        break;
7291      if (status == 0)
7292        format="xc";
7293      XColorBrowserWidget(display,windows,"Select",color);
7294      if (*color == '\0')
7295        break;
7296      /*
7297        Create canvas.
7298      */
7299      (void) FormatLocaleString(image_info->filename,MaxTextExtent,
7300        "%s:%s",format,color);
7301      (void) CloneString(&image_info->size,geometry);
7302      nexus=ReadImage(image_info,exception);
7303      CatchException(exception);
7304      XClientMessage(display,windows->image.id,windows->im_protocols,
7305        windows->im_next_image,CurrentTime);
7306      break;
7307    }
7308    case VisualDirectoryCommand:
7309    {
7310      /*
7311        Visual Image directory.
7312      */
7313      nexus=XVisualDirectoryImage(display,resource_info,windows,exception);
7314      break;
7315    }
7316    case QuitCommand:
7317    {
7318      /*
7319        exit program.
7320      */
7321      if (resource_info->confirm_exit == MagickFalse)
7322        XClientMessage(display,windows->image.id,windows->im_protocols,
7323          windows->im_exit,CurrentTime);
7324      else
7325        {
7326          int
7327            status;
7328
7329          /*
7330            Confirm program exit.
7331          */
7332          status=XConfirmWidget(display,windows,"Do you really want to exit",
7333            resource_info->client_name);
7334          if (status > 0)
7335            XClientMessage(display,windows->image.id,windows->im_protocols,
7336              windows->im_exit,CurrentTime);
7337        }
7338      break;
7339    }
7340    case CutCommand:
7341    {
7342      /*
7343        Cut image.
7344      */
7345      (void) XCropImage(display,resource_info,windows,*image,CutMode,exception);
7346      break;
7347    }
7348    case CopyCommand:
7349    {
7350      /*
7351        Copy image.
7352      */
7353      (void) XCropImage(display,resource_info,windows,*image,CopyMode,
7354        exception);
7355      break;
7356    }
7357    case PasteCommand:
7358    {
7359      /*
7360        Paste image.
7361      */
7362      status=XPasteImage(display,resource_info,windows,*image,exception);
7363      if (status == MagickFalse)
7364        {
7365          XNoticeWidget(display,windows,"Unable to paste X image",
7366            (*image)->filename);
7367          break;
7368        }
7369      break;
7370    }
7371    case HalfSizeCommand:
7372    {
7373      /*
7374        Half image size.
7375      */
7376      windows->image.window_changes.width=windows->image.ximage->width/2;
7377      windows->image.window_changes.height=windows->image.ximage->height/2;
7378      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7379      break;
7380    }
7381    case OriginalSizeCommand:
7382    {
7383      /*
7384        Original image size.
7385      */
7386      windows->image.window_changes.width=(int) (*image)->columns;
7387      windows->image.window_changes.height=(int) (*image)->rows;
7388      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7389      break;
7390    }
7391    case DoubleSizeCommand:
7392    {
7393      /*
7394        Double the image size.
7395      */
7396      windows->image.window_changes.width=windows->image.ximage->width << 1;
7397      windows->image.window_changes.height=windows->image.ximage->height << 1;
7398      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7399      break;
7400    }
7401    case ResizeCommand:
7402    {
7403      int
7404        status;
7405
7406      size_t
7407        height,
7408        width;
7409
7410      ssize_t
7411        x,
7412        y;
7413
7414      /*
7415        Resize image.
7416      */
7417      width=(size_t) windows->image.ximage->width;
7418      height=(size_t) windows->image.ximage->height;
7419      x=0;
7420      y=0;
7421      (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g+0+0",
7422        (double) width,(double) height);
7423      status=XDialogWidget(display,windows,"Resize",
7424        "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7425      if (*geometry == '\0')
7426        break;
7427      if (status == 0)
7428        (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7429      (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7430      windows->image.window_changes.width=(int) width;
7431      windows->image.window_changes.height=(int) height;
7432      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7433      break;
7434    }
7435    case ApplyCommand:
7436    {
7437      char
7438        image_geometry[MaxTextExtent];
7439
7440      if ((windows->image.crop_geometry == (char *) NULL) &&
7441          ((int) (*image)->columns == windows->image.ximage->width) &&
7442          ((int) (*image)->rows == windows->image.ximage->height))
7443        break;
7444      /*
7445        Apply size transforms to image.
7446      */
7447      XSetCursorState(display,windows,MagickTrue);
7448      XCheckRefreshWindows(display,windows);
7449      /*
7450        Crop and/or scale displayed image.
7451      */
7452      (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
7453        windows->image.ximage->width,windows->image.ximage->height);
7454      (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
7455        exception);
7456      if (windows->image.crop_geometry != (char *) NULL)
7457        windows->image.crop_geometry=(char *) RelinquishMagickMemory(
7458          windows->image.crop_geometry);
7459      windows->image.x=0;
7460      windows->image.y=0;
7461      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7462      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7463      break;
7464    }
7465    case RefreshCommand:
7466    {
7467      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7468      break;
7469    }
7470    case RestoreCommand:
7471    {
7472      /*
7473        Restore Image window to its original size.
7474      */
7475      if ((windows->image.width == (unsigned int) (*image)->columns) &&
7476          (windows->image.height == (unsigned int) (*image)->rows) &&
7477          (windows->image.crop_geometry == (char *) NULL))
7478        {
7479          (void) XBell(display,0);
7480          break;
7481        }
7482      windows->image.window_changes.width=(int) (*image)->columns;
7483      windows->image.window_changes.height=(int) (*image)->rows;
7484      if (windows->image.crop_geometry != (char *) NULL)
7485        {
7486          windows->image.crop_geometry=(char *)
7487            RelinquishMagickMemory(windows->image.crop_geometry);
7488          windows->image.crop_geometry=(char *) NULL;
7489          windows->image.x=0;
7490          windows->image.y=0;
7491        }
7492      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7493      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7494      break;
7495    }
7496    case CropCommand:
7497    {
7498      /*
7499        Crop image.
7500      */
7501      (void) XCropImage(display,resource_info,windows,*image,CropMode,
7502        exception);
7503      break;
7504    }
7505    case ChopCommand:
7506    {
7507      /*
7508        Chop image.
7509      */
7510      status=XChopImage(display,resource_info,windows,image,exception);
7511      if (status == MagickFalse)
7512        {
7513          XNoticeWidget(display,windows,"Unable to cut X image",
7514            (*image)->filename);
7515          break;
7516        }
7517      break;
7518    }
7519    case FlopCommand:
7520    {
7521      Image
7522        *flop_image;
7523
7524      /*
7525        Flop image scanlines.
7526      */
7527      XSetCursorState(display,windows,MagickTrue);
7528      XCheckRefreshWindows(display,windows);
7529      flop_image=FlopImage(*image,exception);
7530      if (flop_image != (Image *) NULL)
7531        {
7532          *image=DestroyImage(*image);
7533          *image=flop_image;
7534        }
7535      CatchException(exception);
7536      XSetCursorState(display,windows,MagickFalse);
7537      if (windows->image.crop_geometry != (char *) NULL)
7538        {
7539          /*
7540            Flop crop geometry.
7541          */
7542          width=(unsigned int) (*image)->columns;
7543          height=(unsigned int) (*image)->rows;
7544          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7545            &width,&height);
7546          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7547            "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7548        }
7549      if (windows->image.orphan != MagickFalse)
7550        break;
7551      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7552      break;
7553    }
7554    case FlipCommand:
7555    {
7556      Image
7557        *flip_image;
7558
7559      /*
7560        Flip image scanlines.
7561      */
7562      XSetCursorState(display,windows,MagickTrue);
7563      XCheckRefreshWindows(display,windows);
7564      flip_image=FlipImage(*image,exception);
7565      if (flip_image != (Image *) NULL)
7566        {
7567          *image=DestroyImage(*image);
7568          *image=flip_image;
7569        }
7570      CatchException(exception);
7571      XSetCursorState(display,windows,MagickFalse);
7572      if (windows->image.crop_geometry != (char *) NULL)
7573        {
7574          /*
7575            Flip crop geometry.
7576          */
7577          width=(unsigned int) (*image)->columns;
7578          height=(unsigned int) (*image)->rows;
7579          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7580            &width,&height);
7581          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7582            "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7583        }
7584      if (windows->image.orphan != MagickFalse)
7585        break;
7586      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7587      break;
7588    }
7589    case RotateRightCommand:
7590    {
7591      /*
7592        Rotate image 90 degrees clockwise.
7593      */
7594      status=XRotateImage(display,resource_info,windows,90.0,image,exception);
7595      if (status == MagickFalse)
7596        {
7597          XNoticeWidget(display,windows,"Unable to rotate X image",
7598            (*image)->filename);
7599          break;
7600        }
7601      break;
7602    }
7603    case RotateLeftCommand:
7604    {
7605      /*
7606        Rotate image 90 degrees counter-clockwise.
7607      */
7608      status=XRotateImage(display,resource_info,windows,-90.0,image,exception);
7609      if (status == MagickFalse)
7610        {
7611          XNoticeWidget(display,windows,"Unable to rotate X image",
7612            (*image)->filename);
7613          break;
7614        }
7615      break;
7616    }
7617    case RotateCommand:
7618    {
7619      /*
7620        Rotate image.
7621      */
7622      status=XRotateImage(display,resource_info,windows,0.0,image,exception);
7623      if (status == MagickFalse)
7624        {
7625          XNoticeWidget(display,windows,"Unable to rotate X image",
7626            (*image)->filename);
7627          break;
7628        }
7629      break;
7630    }
7631    case ShearCommand:
7632    {
7633      Image
7634        *shear_image;
7635
7636      static char
7637        geometry[MaxTextExtent] = "45.0x45.0";
7638
7639      /*
7640        Query user for shear color and geometry.
7641      */
7642      XColorBrowserWidget(display,windows,"Select",color);
7643      if (*color == '\0')
7644        break;
7645      (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7646        geometry);
7647      if (*geometry == '\0')
7648        break;
7649      /*
7650        Shear image.
7651      */
7652      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7653        exception);
7654      XSetCursorState(display,windows,MagickTrue);
7655      XCheckRefreshWindows(display,windows);
7656      (void) QueryColorCompliance(color,AllCompliance,
7657        &(*image)->background_color,exception);
7658      flags=ParseGeometry(geometry,&geometry_info);
7659      if ((flags & SigmaValue) == 0)
7660        geometry_info.sigma=geometry_info.rho;
7661      shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7662        exception);
7663      if (shear_image != (Image *) NULL)
7664        {
7665          *image=DestroyImage(*image);
7666          *image=shear_image;
7667        }
7668      CatchException(exception);
7669      XSetCursorState(display,windows,MagickFalse);
7670      if (windows->image.orphan != MagickFalse)
7671        break;
7672      windows->image.window_changes.width=(int) (*image)->columns;
7673      windows->image.window_changes.height=(int) (*image)->rows;
7674      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7675      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7676      break;
7677    }
7678    case RollCommand:
7679    {
7680      Image
7681        *roll_image;
7682
7683      static char
7684        geometry[MaxTextExtent] = "+2+2";
7685
7686      /*
7687        Query user for the roll geometry.
7688      */
7689      (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7690        geometry);
7691      if (*geometry == '\0')
7692        break;
7693      /*
7694        Roll image.
7695      */
7696      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7697        exception);
7698      XSetCursorState(display,windows,MagickTrue);
7699      XCheckRefreshWindows(display,windows);
7700      (void) ParsePageGeometry(*image,geometry,&page_geometry,
7701        exception);
7702      roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7703        exception);
7704      if (roll_image != (Image *) NULL)
7705        {
7706          *image=DestroyImage(*image);
7707          *image=roll_image;
7708        }
7709      CatchException(exception);
7710      XSetCursorState(display,windows,MagickFalse);
7711      if (windows->image.orphan != MagickFalse)
7712        break;
7713      windows->image.window_changes.width=(int) (*image)->columns;
7714      windows->image.window_changes.height=(int) (*image)->rows;
7715      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7716      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7717      break;
7718    }
7719    case TrimCommand:
7720    {
7721      static char
7722        fuzz[MaxTextExtent];
7723
7724      /*
7725        Query user for the fuzz factor.
7726      */
7727      (void) FormatLocaleString(fuzz,MaxTextExtent,"%g%%",100.0*
7728        (*image)->fuzz/(QuantumRange+1.0));
7729      (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7730      if (*fuzz == '\0')
7731        break;
7732      (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+1.0);
7733      /*
7734        Trim image.
7735      */
7736      status=XTrimImage(display,resource_info,windows,*image,exception);
7737      if (status == MagickFalse)
7738        {
7739          XNoticeWidget(display,windows,"Unable to trim X image",
7740            (*image)->filename);
7741          break;
7742        }
7743      break;
7744    }
7745    case HueCommand:
7746    {
7747      static char
7748        hue_percent[MaxTextExtent] = "110";
7749
7750      /*
7751        Query user for percent hue change.
7752      */
7753      (void) XDialogWidget(display,windows,"Apply",
7754        "Enter percent change in image hue (0-200):",hue_percent);
7755      if (*hue_percent == '\0')
7756        break;
7757      /*
7758        Vary the image hue.
7759      */
7760      XSetCursorState(display,windows,MagickTrue);
7761      XCheckRefreshWindows(display,windows);
7762      (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7763      (void) ConcatenateMagickString(modulate_factors,hue_percent,
7764        MaxTextExtent);
7765      (void) ModulateImage(*image,modulate_factors,exception);
7766      XSetCursorState(display,windows,MagickFalse);
7767      if (windows->image.orphan != MagickFalse)
7768        break;
7769      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7770      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7771      break;
7772    }
7773    case SaturationCommand:
7774    {
7775      static char
7776        saturation_percent[MaxTextExtent] = "110";
7777
7778      /*
7779        Query user for percent saturation change.
7780      */
7781      (void) XDialogWidget(display,windows,"Apply",
7782        "Enter percent change in color saturation (0-200):",saturation_percent);
7783      if (*saturation_percent == '\0')
7784        break;
7785      /*
7786        Vary color saturation.
7787      */
7788      XSetCursorState(display,windows,MagickTrue);
7789      XCheckRefreshWindows(display,windows);
7790      (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7791      (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7792        MaxTextExtent);
7793      (void) ModulateImage(*image,modulate_factors,exception);
7794      XSetCursorState(display,windows,MagickFalse);
7795      if (windows->image.orphan != MagickFalse)
7796        break;
7797      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7798      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7799      break;
7800    }
7801    case BrightnessCommand:
7802    {
7803      static char
7804        brightness_percent[MaxTextExtent] = "110";
7805
7806      /*
7807        Query user for percent brightness change.
7808      */
7809      (void) XDialogWidget(display,windows,"Apply",
7810        "Enter percent change in color brightness (0-200):",brightness_percent);
7811      if (*brightness_percent == '\0')
7812        break;
7813      /*
7814        Vary the color brightness.
7815      */
7816      XSetCursorState(display,windows,MagickTrue);
7817      XCheckRefreshWindows(display,windows);
7818      (void) CopyMagickString(modulate_factors,brightness_percent,
7819        MaxTextExtent);
7820      (void) ModulateImage(*image,modulate_factors,exception);
7821      XSetCursorState(display,windows,MagickFalse);
7822      if (windows->image.orphan != MagickFalse)
7823        break;
7824      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7825      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7826      break;
7827    }
7828    case GammaCommand:
7829    {
7830      static char
7831        factor[MaxTextExtent] = "1.6";
7832
7833      /*
7834        Query user for gamma value.
7835      */
7836      (void) XDialogWidget(display,windows,"Gamma",
7837        "Enter gamma value (e.g. 1.2):",factor);
7838      if (*factor == '\0')
7839        break;
7840      /*
7841        Gamma correct image.
7842      */
7843      XSetCursorState(display,windows,MagickTrue);
7844      XCheckRefreshWindows(display,windows);
7845      (void) GammaImage(*image,atof(factor),exception);
7846      XSetCursorState(display,windows,MagickFalse);
7847      if (windows->image.orphan != MagickFalse)
7848        break;
7849      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7850      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7851      break;
7852    }
7853    case SpiffCommand:
7854    {
7855      /*
7856        Sharpen the image contrast.
7857      */
7858      XSetCursorState(display,windows,MagickTrue);
7859      XCheckRefreshWindows(display,windows);
7860      (void) ContrastImage(*image,MagickTrue,exception);
7861      XSetCursorState(display,windows,MagickFalse);
7862      if (windows->image.orphan != MagickFalse)
7863        break;
7864      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7865      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7866      break;
7867    }
7868    case DullCommand:
7869    {
7870      /*
7871        Dull the image contrast.
7872      */
7873      XSetCursorState(display,windows,MagickTrue);
7874      XCheckRefreshWindows(display,windows);
7875      (void) ContrastImage(*image,MagickFalse,exception);
7876      XSetCursorState(display,windows,MagickFalse);
7877      if (windows->image.orphan != MagickFalse)
7878        break;
7879      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7880      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7881      break;
7882    }
7883    case ContrastStretchCommand:
7884    {
7885      double
7886        black_point,
7887        white_point;
7888
7889      static char
7890        levels[MaxTextExtent] = "1%";
7891
7892      /*
7893        Query user for gamma value.
7894      */
7895      (void) XDialogWidget(display,windows,"Contrast Stretch",
7896        "Enter black and white points:",levels);
7897      if (*levels == '\0')
7898        break;
7899      /*
7900        Contrast stretch image.
7901      */
7902      XSetCursorState(display,windows,MagickTrue);
7903      XCheckRefreshWindows(display,windows);
7904      flags=ParseGeometry(levels,&geometry_info);
7905      black_point=geometry_info.rho;
7906      white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7907      if ((flags & PercentValue) != 0)
7908        {
7909          black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7910          white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7911        }
7912      white_point=(MagickRealType) (*image)->columns*(*image)->rows-white_point;
7913      (void) ContrastStretchImage(*image,black_point,white_point,
7914        exception);
7915      XSetCursorState(display,windows,MagickFalse);
7916      if (windows->image.orphan != MagickFalse)
7917        break;
7918      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7919      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7920      break;
7921    }
7922    case SigmoidalContrastCommand:
7923    {
7924      GeometryInfo
7925        geometry_info;
7926
7927      MagickStatusType
7928        flags;
7929
7930      static char
7931        levels[MaxTextExtent] = "3x50%";
7932
7933      /*
7934        Query user for gamma value.
7935      */
7936      (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7937        "Enter contrast and midpoint:",levels);
7938      if (*levels == '\0')
7939        break;
7940      /*
7941        Contrast stretch image.
7942      */
7943      XSetCursorState(display,windows,MagickTrue);
7944      XCheckRefreshWindows(display,windows);
7945      flags=ParseGeometry(levels,&geometry_info);
7946      if ((flags & SigmaValue) == 0)
7947        geometry_info.sigma=1.0*QuantumRange/2.0;
7948      if ((flags & PercentValue) != 0)
7949        geometry_info.sigma=1.0*QuantumRange*geometry_info.sigma/100.0;
7950      (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
7951        geometry_info.sigma,exception);
7952      XSetCursorState(display,windows,MagickFalse);
7953      if (windows->image.orphan != MagickFalse)
7954        break;
7955      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7956      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7957      break;
7958    }
7959    case NormalizeCommand:
7960    {
7961      /*
7962        Perform histogram normalization on the image.
7963      */
7964      XSetCursorState(display,windows,MagickTrue);
7965      XCheckRefreshWindows(display,windows);
7966      (void) NormalizeImage(*image,exception);
7967      XSetCursorState(display,windows,MagickFalse);
7968      if (windows->image.orphan != MagickFalse)
7969        break;
7970      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7971      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7972      break;
7973    }
7974    case EqualizeCommand:
7975    {
7976      /*
7977        Perform histogram equalization on the image.
7978      */
7979      XSetCursorState(display,windows,MagickTrue);
7980      XCheckRefreshWindows(display,windows);
7981      (void) EqualizeImage(*image,exception);
7982      XSetCursorState(display,windows,MagickFalse);
7983      if (windows->image.orphan != MagickFalse)
7984        break;
7985      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7986      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7987      break;
7988    }
7989    case NegateCommand:
7990    {
7991      /*
7992        Negate colors in image.
7993      */
7994      XSetCursorState(display,windows,MagickTrue);
7995      XCheckRefreshWindows(display,windows);
7996      (void) NegateImage(*image,MagickFalse,exception);
7997      XSetCursorState(display,windows,MagickFalse);
7998      if (windows->image.orphan != MagickFalse)
7999        break;
8000      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8001      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8002      break;
8003    }
8004    case GrayscaleCommand:
8005    {
8006      /*
8007        Convert image to grayscale.
8008      */
8009      XSetCursorState(display,windows,MagickTrue);
8010      XCheckRefreshWindows(display,windows);
8011      (void) SetImageType(*image,(*image)->matte == MagickFalse ?
8012        GrayscaleType : GrayscaleMatteType,exception);
8013      XSetCursorState(display,windows,MagickFalse);
8014      if (windows->image.orphan != MagickFalse)
8015        break;
8016      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8017      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8018      break;
8019    }
8020    case MapCommand:
8021    {
8022      Image
8023        *affinity_image;
8024
8025      static char
8026        filename[MaxTextExtent] = "\0";
8027
8028      /*
8029        Request image file name from user.
8030      */
8031      XFileBrowserWidget(display,windows,"Map",filename);
8032      if (*filename == '\0')
8033        break;
8034      /*
8035        Map image.
8036      */
8037      XSetCursorState(display,windows,MagickTrue);
8038      XCheckRefreshWindows(display,windows);
8039      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
8040      affinity_image=ReadImage(image_info,exception);
8041      if (affinity_image != (Image *) NULL)
8042        {
8043          (void) RemapImage(&quantize_info,*image,affinity_image,exception);
8044          affinity_image=DestroyImage(affinity_image);
8045        }
8046      CatchException(exception);
8047      XSetCursorState(display,windows,MagickFalse);
8048      if (windows->image.orphan != MagickFalse)
8049        break;
8050      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8051      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8052      break;
8053    }
8054    case QuantizeCommand:
8055    {
8056      int
8057        status;
8058
8059      static char
8060        colors[MaxTextExtent] = "256";
8061
8062      /*
8063        Query user for maximum number of colors.
8064      */
8065      status=XDialogWidget(display,windows,"Quantize",
8066        "Maximum number of colors:",colors);
8067      if (*colors == '\0')
8068        break;
8069      /*
8070        Color reduce the image.
8071      */
8072      XSetCursorState(display,windows,MagickTrue);
8073      XCheckRefreshWindows(display,windows);
8074      quantize_info.number_colors=StringToUnsignedLong(colors);
8075      quantize_info.dither=status != 0 ? MagickTrue : MagickFalse;
8076      (void) QuantizeImage(&quantize_info,*image,exception);
8077      XSetCursorState(display,windows,MagickFalse);
8078      if (windows->image.orphan != MagickFalse)
8079        break;
8080      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8081      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8082      break;
8083    }
8084    case DespeckleCommand:
8085    {
8086      Image
8087        *despeckle_image;
8088
8089      /*
8090        Despeckle image.
8091      */
8092      XSetCursorState(display,windows,MagickTrue);
8093      XCheckRefreshWindows(display,windows);
8094      despeckle_image=DespeckleImage(*image,exception);
8095      if (despeckle_image != (Image *) NULL)
8096        {
8097          *image=DestroyImage(*image);
8098          *image=despeckle_image;
8099        }
8100      CatchException(exception);
8101      XSetCursorState(display,windows,MagickFalse);
8102      if (windows->image.orphan != MagickFalse)
8103        break;
8104      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8105      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8106      break;
8107    }
8108    case EmbossCommand:
8109    {
8110      Image
8111        *emboss_image;
8112
8113      static char
8114        radius[MaxTextExtent] = "0.0x1.0";
8115
8116      /*
8117        Query user for emboss radius.
8118      */
8119      (void) XDialogWidget(display,windows,"Emboss",
8120        "Enter the emboss radius and standard deviation:",radius);
8121      if (*radius == '\0')
8122        break;
8123      /*
8124        Reduce noise in the image.
8125      */
8126      XSetCursorState(display,windows,MagickTrue);
8127      XCheckRefreshWindows(display,windows);
8128      flags=ParseGeometry(radius,&geometry_info);
8129      if ((flags & SigmaValue) == 0)
8130        geometry_info.sigma=1.0;
8131      emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8132        exception);
8133      if (emboss_image != (Image *) NULL)
8134        {
8135          *image=DestroyImage(*image);
8136          *image=emboss_image;
8137        }
8138      CatchException(exception);
8139      XSetCursorState(display,windows,MagickFalse);
8140      if (windows->image.orphan != MagickFalse)
8141        break;
8142      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8143      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8144      break;
8145    }
8146    case ReduceNoiseCommand:
8147    {
8148      Image
8149        *noise_image;
8150
8151      static char
8152        radius[MaxTextExtent] = "0";
8153
8154      /*
8155        Query user for noise radius.
8156      */
8157      (void) XDialogWidget(display,windows,"Reduce Noise",
8158        "Enter the noise radius:",radius);
8159      if (*radius == '\0')
8160        break;
8161      /*
8162        Reduce noise in the image.
8163      */
8164      XSetCursorState(display,windows,MagickTrue);
8165      XCheckRefreshWindows(display,windows);
8166      flags=ParseGeometry(radius,&geometry_info);
8167      noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8168        geometry_info.rho,(size_t) geometry_info.rho,exception);
8169      if (noise_image != (Image *) NULL)
8170        {
8171          *image=DestroyImage(*image);
8172          *image=noise_image;
8173        }
8174      CatchException(exception);
8175      XSetCursorState(display,windows,MagickFalse);
8176      if (windows->image.orphan != MagickFalse)
8177        break;
8178      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8179      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8180      break;
8181    }
8182    case AddNoiseCommand:
8183    {
8184      char
8185        **noises;
8186
8187      Image
8188        *noise_image;
8189
8190      static char
8191        noise_type[MaxTextExtent] = "Gaussian";
8192
8193      /*
8194        Add noise to the image.
8195      */
8196      noises=GetCommandOptions(MagickNoiseOptions);
8197      if (noises == (char **) NULL)
8198        break;
8199      XListBrowserWidget(display,windows,&windows->widget,
8200        (const char **) noises,"Add Noise",
8201        "Select a type of noise to add to your image:",noise_type);
8202      noises=DestroyStringList(noises);
8203      if (*noise_type == '\0')
8204        break;
8205      XSetCursorState(display,windows,MagickTrue);
8206      XCheckRefreshWindows(display,windows);
8207      noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8208        MagickNoiseOptions,MagickFalse,noise_type),1.0,exception);
8209      if (noise_image != (Image *) NULL)
8210        {
8211          *image=DestroyImage(*image);
8212          *image=noise_image;
8213        }
8214      CatchException(exception);
8215      XSetCursorState(display,windows,MagickFalse);
8216      if (windows->image.orphan != MagickFalse)
8217        break;
8218      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8219      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8220      break;
8221    }
8222    case SharpenCommand:
8223    {
8224      Image
8225        *sharp_image;
8226
8227      static char
8228        radius[MaxTextExtent] = "0.0x1.0";
8229
8230      /*
8231        Query user for sharpen radius.
8232      */
8233      (void) XDialogWidget(display,windows,"Sharpen",
8234        "Enter the sharpen radius and standard deviation:",radius);
8235      if (*radius == '\0')
8236        break;
8237      /*
8238        Sharpen image scanlines.
8239      */
8240      XSetCursorState(display,windows,MagickTrue);
8241      XCheckRefreshWindows(display,windows);
8242      flags=ParseGeometry(radius,&geometry_info);
8243      sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8244        geometry_info.xi,exception);
8245      if (sharp_image != (Image *) NULL)
8246        {
8247          *image=DestroyImage(*image);
8248          *image=sharp_image;
8249        }
8250      CatchException(exception);
8251      XSetCursorState(display,windows,MagickFalse);
8252      if (windows->image.orphan != MagickFalse)
8253        break;
8254      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8255      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8256      break;
8257    }
8258    case BlurCommand:
8259    {
8260      Image
8261        *blur_image;
8262
8263      static char
8264        radius[MaxTextExtent] = "0.0x1.0";
8265
8266      /*
8267        Query user for blur radius.
8268      */
8269      (void) XDialogWidget(display,windows,"Blur",
8270        "Enter the blur radius and standard deviation:",radius);
8271      if (*radius == '\0')
8272        break;
8273      /*
8274        Blur an image.
8275      */
8276      XSetCursorState(display,windows,MagickTrue);
8277      XCheckRefreshWindows(display,windows);
8278      flags=ParseGeometry(radius,&geometry_info);
8279      blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8280        geometry_info.xi,exception);
8281      if (blur_image != (Image *) NULL)
8282        {
8283          *image=DestroyImage(*image);
8284          *image=blur_image;
8285        }
8286      CatchException(exception);
8287      XSetCursorState(display,windows,MagickFalse);
8288      if (windows->image.orphan != MagickFalse)
8289        break;
8290      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8291      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8292      break;
8293    }
8294    case ThresholdCommand:
8295    {
8296      double
8297        threshold;
8298
8299      static char
8300        factor[MaxTextExtent] = "128";
8301
8302      /*
8303        Query user for threshold value.
8304      */
8305      (void) XDialogWidget(display,windows,"Threshold",
8306        "Enter threshold value:",factor);
8307      if (*factor == '\0')
8308        break;
8309      /*
8310        Gamma correct image.
8311      */
8312      XSetCursorState(display,windows,MagickTrue);
8313      XCheckRefreshWindows(display,windows);
8314      threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8315      (void) BilevelImage(*image,threshold,exception);
8316      XSetCursorState(display,windows,MagickFalse);
8317      if (windows->image.orphan != MagickFalse)
8318        break;
8319      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8320      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8321      break;
8322    }
8323    case EdgeDetectCommand:
8324    {
8325      Image
8326        *edge_image;
8327
8328      static char
8329        radius[MaxTextExtent] = "0";
8330
8331      /*
8332        Query user for edge factor.
8333      */
8334      (void) XDialogWidget(display,windows,"Detect Edges",
8335        "Enter the edge detect radius:",radius);
8336      if (*radius == '\0')
8337        break;
8338      /*
8339        Detect edge in image.
8340      */
8341      XSetCursorState(display,windows,MagickTrue);
8342      XCheckRefreshWindows(display,windows);
8343      flags=ParseGeometry(radius,&geometry_info);
8344      edge_image=EdgeImage(*image,geometry_info.rho,geometry_info.sigma,
8345        exception);
8346      if (edge_image != (Image *) NULL)
8347        {
8348          *image=DestroyImage(*image);
8349          *image=edge_image;
8350        }
8351      CatchException(exception);
8352      XSetCursorState(display,windows,MagickFalse);
8353      if (windows->image.orphan != MagickFalse)
8354        break;
8355      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8356      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8357      break;
8358    }
8359    case SpreadCommand:
8360    {
8361      Image
8362        *spread_image;
8363
8364      static char
8365        amount[MaxTextExtent] = "2";
8366
8367      /*
8368        Query user for spread amount.
8369      */
8370      (void) XDialogWidget(display,windows,"Spread",
8371        "Enter the displacement amount:",amount);
8372      if (*amount == '\0')
8373        break;
8374      /*
8375        Displace image pixels by a random amount.
8376      */
8377      XSetCursorState(display,windows,MagickTrue);
8378      XCheckRefreshWindows(display,windows);
8379      flags=ParseGeometry(amount,&geometry_info);
8380      spread_image=EdgeImage(*image,geometry_info.rho,geometry_info.sigma,
8381        exception);
8382      if (spread_image != (Image *) NULL)
8383        {
8384          *image=DestroyImage(*image);
8385          *image=spread_image;
8386        }
8387      CatchException(exception);
8388      XSetCursorState(display,windows,MagickFalse);
8389      if (windows->image.orphan != MagickFalse)
8390        break;
8391      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8392      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8393      break;
8394    }
8395    case ShadeCommand:
8396    {
8397      Image
8398        *shade_image;
8399
8400      int
8401        status;
8402
8403      static char
8404        geometry[MaxTextExtent] = "30x30";
8405
8406      /*
8407        Query user for the shade geometry.
8408      */
8409      status=XDialogWidget(display,windows,"Shade",
8410        "Enter the azimuth and elevation of the light source:",geometry);
8411      if (*geometry == '\0')
8412        break;
8413      /*
8414        Shade image pixels.
8415      */
8416      XSetCursorState(display,windows,MagickTrue);
8417      XCheckRefreshWindows(display,windows);
8418      flags=ParseGeometry(geometry,&geometry_info);
8419      if ((flags & SigmaValue) == 0)
8420        geometry_info.sigma=1.0;
8421      shade_image=ShadeImage(*image,status != 0 ? MagickFalse : MagickTrue,
8422        geometry_info.rho,geometry_info.sigma,exception);
8423      if (shade_image != (Image *) NULL)
8424        {
8425          *image=DestroyImage(*image);
8426          *image=shade_image;
8427        }
8428      CatchException(exception);
8429      XSetCursorState(display,windows,MagickFalse);
8430      if (windows->image.orphan != MagickFalse)
8431        break;
8432      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8433      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8434      break;
8435    }
8436    case RaiseCommand:
8437    {
8438      static char
8439        bevel_width[MaxTextExtent] = "10";
8440
8441      /*
8442        Query user for bevel width.
8443      */
8444      (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8445      if (*bevel_width == '\0')
8446        break;
8447      /*
8448        Raise an image.
8449      */
8450      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8451        exception);
8452      XSetCursorState(display,windows,MagickTrue);
8453      XCheckRefreshWindows(display,windows);
8454      (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8455        exception);
8456      (void) RaiseImage(*image,&page_geometry,MagickTrue,exception);
8457      XSetCursorState(display,windows,MagickFalse);
8458      if (windows->image.orphan != MagickFalse)
8459        break;
8460      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8461      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8462      break;
8463    }
8464    case SegmentCommand:
8465    {
8466      static char
8467        threshold[MaxTextExtent] = "1.0x1.5";
8468
8469      /*
8470        Query user for smoothing threshold.
8471      */
8472      (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8473        threshold);
8474      if (*threshold == '\0')
8475        break;
8476      /*
8477        Segment an image.
8478      */
8479      XSetCursorState(display,windows,MagickTrue);
8480      XCheckRefreshWindows(display,windows);
8481      flags=ParseGeometry(threshold,&geometry_info);
8482      if ((flags & SigmaValue) == 0)
8483        geometry_info.sigma=1.0;
8484      (void) SegmentImage(*image,RGBColorspace,MagickFalse,geometry_info.rho,
8485        geometry_info.sigma,exception);
8486      XSetCursorState(display,windows,MagickFalse);
8487      if (windows->image.orphan != MagickFalse)
8488        break;
8489      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8490      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8491      break;
8492    }
8493    case SepiaToneCommand:
8494    {
8495      double
8496        threshold;
8497
8498      Image
8499        *sepia_image;
8500
8501      static char
8502        factor[MaxTextExtent] = "80%";
8503
8504      /*
8505        Query user for sepia-tone factor.
8506      */
8507      (void) XDialogWidget(display,windows,"Sepia Tone",
8508        "Enter the sepia tone factor (0 - 99.9%):",factor);
8509      if (*factor == '\0')
8510        break;
8511      /*
8512        Sepia tone image pixels.
8513      */
8514      XSetCursorState(display,windows,MagickTrue);
8515      XCheckRefreshWindows(display,windows);
8516      threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8517      sepia_image=SepiaToneImage(*image,threshold,exception);
8518      if (sepia_image != (Image *) NULL)
8519        {
8520          *image=DestroyImage(*image);
8521          *image=sepia_image;
8522        }
8523      CatchException(exception);
8524      XSetCursorState(display,windows,MagickFalse);
8525      if (windows->image.orphan != MagickFalse)
8526        break;
8527      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8528      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8529      break;
8530    }
8531    case SolarizeCommand:
8532    {
8533      double
8534        threshold;
8535
8536      static char
8537        factor[MaxTextExtent] = "60%";
8538
8539      /*
8540        Query user for solarize factor.
8541      */
8542      (void) XDialogWidget(display,windows,"Solarize",
8543        "Enter the solarize factor (0 - 99.9%):",factor);
8544      if (*factor == '\0')
8545        break;
8546      /*
8547        Solarize image pixels.
8548      */
8549      XSetCursorState(display,windows,MagickTrue);
8550      XCheckRefreshWindows(display,windows);
8551      threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8552      (void) SolarizeImage(*image,threshold,exception);
8553      XSetCursorState(display,windows,MagickFalse);
8554      if (windows->image.orphan != MagickFalse)
8555        break;
8556      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8557      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8558      break;
8559    }
8560    case SwirlCommand:
8561    {
8562      Image
8563        *swirl_image;
8564
8565      static char
8566        degrees[MaxTextExtent] = "60";
8567
8568      /*
8569        Query user for swirl angle.
8570      */
8571      (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8572        degrees);
8573      if (*degrees == '\0')
8574        break;
8575      /*
8576        Swirl image pixels about the center.
8577      */
8578      XSetCursorState(display,windows,MagickTrue);
8579      XCheckRefreshWindows(display,windows);
8580      flags=ParseGeometry(degrees,&geometry_info);
8581      swirl_image=SwirlImage(*image,geometry_info.rho,(*image)->interpolate,
8582        exception);
8583      if (swirl_image != (Image *) NULL)
8584        {
8585          *image=DestroyImage(*image);
8586          *image=swirl_image;
8587        }
8588      CatchException(exception);
8589      XSetCursorState(display,windows,MagickFalse);
8590      if (windows->image.orphan != MagickFalse)
8591        break;
8592      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8593      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8594      break;
8595    }
8596    case ImplodeCommand:
8597    {
8598      Image
8599        *implode_image;
8600
8601      static char
8602        factor[MaxTextExtent] = "0.3";
8603
8604      /*
8605        Query user for implode factor.
8606      */
8607      (void) XDialogWidget(display,windows,"Implode",
8608        "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8609      if (*factor == '\0')
8610        break;
8611      /*
8612        Implode image pixels about the center.
8613      */
8614      XSetCursorState(display,windows,MagickTrue);
8615      XCheckRefreshWindows(display,windows);
8616      flags=ParseGeometry(factor,&geometry_info);
8617      implode_image=ImplodeImage(*image,geometry_info.rho,(*image)->interpolate,
8618        exception);
8619      if (implode_image != (Image *) NULL)
8620        {
8621          *image=DestroyImage(*image);
8622          *image=implode_image;
8623        }
8624      CatchException(exception);
8625      XSetCursorState(display,windows,MagickFalse);
8626      if (windows->image.orphan != MagickFalse)
8627        break;
8628      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8629      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8630      break;
8631    }
8632    case VignetteCommand:
8633    {
8634      Image
8635        *vignette_image;
8636
8637      static char
8638        geometry[MaxTextExtent] = "0x20";
8639
8640      /*
8641        Query user for the vignette geometry.
8642      */
8643      (void) XDialogWidget(display,windows,"Vignette",
8644        "Enter the radius, sigma, and x and y offsets:",geometry);
8645      if (*geometry == '\0')
8646        break;
8647      /*
8648        Soften the edges of the image in vignette style
8649      */
8650      XSetCursorState(display,windows,MagickTrue);
8651      XCheckRefreshWindows(display,windows);
8652      flags=ParseGeometry(geometry,&geometry_info);
8653      if ((flags & SigmaValue) == 0)
8654        geometry_info.sigma=1.0;
8655      if ((flags & XiValue) == 0)
8656        geometry_info.xi=0.1*(*image)->columns;
8657      if ((flags & PsiValue) == 0)
8658        geometry_info.psi=0.1*(*image)->rows;
8659      vignette_image=VignetteImage(*image,geometry_info.rho,geometry_info.sigma,
8660        (ssize_t) ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-
8661        0.5),exception);
8662      if (vignette_image != (Image *) NULL)
8663        {
8664          *image=DestroyImage(*image);
8665          *image=vignette_image;
8666        }
8667      CatchException(exception);
8668      XSetCursorState(display,windows,MagickFalse);
8669      if (windows->image.orphan != MagickFalse)
8670        break;
8671      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8672      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8673      break;
8674    }
8675    case WaveCommand:
8676    {
8677      Image
8678        *wave_image;
8679
8680      static char
8681        geometry[MaxTextExtent] = "25x150";
8682
8683      /*
8684        Query user for the wave geometry.
8685      */
8686      (void) XDialogWidget(display,windows,"Wave",
8687        "Enter the amplitude and length of the wave:",geometry);
8688      if (*geometry == '\0')
8689        break;
8690      /*
8691        Alter an image along a sine wave.
8692      */
8693      XSetCursorState(display,windows,MagickTrue);
8694      XCheckRefreshWindows(display,windows);
8695      flags=ParseGeometry(geometry,&geometry_info);
8696      if ((flags & SigmaValue) == 0)
8697        geometry_info.sigma=1.0;
8698      wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8699        (*image)->interpolate,exception);
8700      if (wave_image != (Image *) NULL)
8701        {
8702          *image=DestroyImage(*image);
8703          *image=wave_image;
8704        }
8705      CatchException(exception);
8706      XSetCursorState(display,windows,MagickFalse);
8707      if (windows->image.orphan != MagickFalse)
8708        break;
8709      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8710      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8711      break;
8712    }
8713    case OilPaintCommand:
8714    {
8715      Image
8716        *paint_image;
8717
8718      static char
8719        radius[MaxTextExtent] = "0";
8720
8721      /*
8722        Query user for circular neighborhood radius.
8723      */
8724      (void) XDialogWidget(display,windows,"Oil Paint",
8725        "Enter the mask radius:",radius);
8726      if (*radius == '\0')
8727        break;
8728      /*
8729        OilPaint image scanlines.
8730      */
8731      XSetCursorState(display,windows,MagickTrue);
8732      XCheckRefreshWindows(display,windows);
8733      flags=ParseGeometry(radius,&geometry_info);
8734      paint_image=OilPaintImage(*image,geometry_info.rho,geometry_info.sigma,
8735        exception);
8736      if (paint_image != (Image *) NULL)
8737        {
8738          *image=DestroyImage(*image);
8739          *image=paint_image;
8740        }
8741      CatchException(exception);
8742      XSetCursorState(display,windows,MagickFalse);
8743      if (windows->image.orphan != MagickFalse)
8744        break;
8745      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8746      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8747      break;
8748    }
8749    case CharcoalDrawCommand:
8750    {
8751      Image
8752        *charcoal_image;
8753
8754      static char
8755        radius[MaxTextExtent] = "0x1";
8756
8757      /*
8758        Query user for charcoal radius.
8759      */
8760      (void) XDialogWidget(display,windows,"Charcoal Draw",
8761        "Enter the charcoal radius and sigma:",radius);
8762      if (*radius == '\0')
8763        break;
8764      /*
8765        Charcoal the image.
8766      */
8767      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8768        exception);
8769      XSetCursorState(display,windows,MagickTrue);
8770      XCheckRefreshWindows(display,windows);
8771      flags=ParseGeometry(radius,&geometry_info);
8772      if ((flags & SigmaValue) == 0)
8773        geometry_info.sigma=geometry_info.rho;
8774      charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8775        geometry_info.xi,exception);
8776      if (charcoal_image != (Image *) NULL)
8777        {
8778          *image=DestroyImage(*image);
8779          *image=charcoal_image;
8780        }
8781      CatchException(exception);
8782      XSetCursorState(display,windows,MagickFalse);
8783      if (windows->image.orphan != MagickFalse)
8784        break;
8785      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8786      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8787      break;
8788    }
8789    case AnnotateCommand:
8790    {
8791      /*
8792        Annotate the image with text.
8793      */
8794      status=XAnnotateEditImage(display,resource_info,windows,*image,exception);
8795      if (status == MagickFalse)
8796        {
8797          XNoticeWidget(display,windows,"Unable to annotate X image",
8798            (*image)->filename);
8799          break;
8800        }
8801      break;
8802    }
8803    case DrawCommand:
8804    {
8805      /*
8806        Draw image.
8807      */
8808      status=XDrawEditImage(display,resource_info,windows,image,exception);
8809      if (status == MagickFalse)
8810        {
8811          XNoticeWidget(display,windows,"Unable to draw on the X image",
8812            (*image)->filename);
8813          break;
8814        }
8815      break;
8816    }
8817    case ColorCommand:
8818    {
8819      /*
8820        Color edit.
8821      */
8822      status=XColorEditImage(display,resource_info,windows,image,exception);
8823      if (status == MagickFalse)
8824        {
8825          XNoticeWidget(display,windows,"Unable to pixel edit X image",
8826            (*image)->filename);
8827          break;
8828        }
8829      break;
8830    }
8831    case MatteCommand:
8832    {
8833      /*
8834        Matte edit.
8835      */
8836      status=XMatteEditImage(display,resource_info,windows,image,exception);
8837      if (status == MagickFalse)
8838        {
8839          XNoticeWidget(display,windows,"Unable to matte edit X image",
8840            (*image)->filename);
8841          break;
8842        }
8843      break;
8844    }
8845    case CompositeCommand:
8846    {
8847      /*
8848        Composite image.
8849      */
8850      status=XCompositeImage(display,resource_info,windows,*image,
8851        exception);
8852      if (status == MagickFalse)
8853        {
8854          XNoticeWidget(display,windows,"Unable to composite X image",
8855            (*image)->filename);
8856          break;
8857        }
8858      break;
8859    }
8860    case AddBorderCommand:
8861    {
8862      Image
8863        *border_image;
8864
8865      static char
8866        geometry[MaxTextExtent] = "6x6";
8867
8868      /*
8869        Query user for border color and geometry.
8870      */
8871      XColorBrowserWidget(display,windows,"Select",color);
8872      if (*color == '\0')
8873        break;
8874      (void) XDialogWidget(display,windows,"Add Border",
8875        "Enter border geometry:",geometry);
8876      if (*geometry == '\0')
8877        break;
8878      /*
8879        Add a border to the image.
8880      */
8881      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8882        exception);
8883      XSetCursorState(display,windows,MagickTrue);
8884      XCheckRefreshWindows(display,windows);
8885      (void) QueryColorCompliance(color,AllCompliance,&(*image)->border_color,
8886        exception);
8887      (void) ParsePageGeometry(*image,geometry,&page_geometry,
8888        exception);
8889      border_image=BorderImage(*image,&page_geometry,(*image)->compose,
8890        exception);
8891      if (border_image != (Image *) NULL)
8892        {
8893          *image=DestroyImage(*image);
8894          *image=border_image;
8895        }
8896      CatchException(exception);
8897      XSetCursorState(display,windows,MagickFalse);
8898      if (windows->image.orphan != MagickFalse)
8899        break;
8900      windows->image.window_changes.width=(int) (*image)->columns;
8901      windows->image.window_changes.height=(int) (*image)->rows;
8902      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8903      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8904      break;
8905    }
8906    case AddFrameCommand:
8907    {
8908      FrameInfo
8909        frame_info;
8910
8911      Image
8912        *frame_image;
8913
8914      static char
8915        geometry[MaxTextExtent] = "6x6";
8916
8917      /*
8918        Query user for frame color and geometry.
8919      */
8920      XColorBrowserWidget(display,windows,"Select",color);
8921      if (*color == '\0')
8922        break;
8923      (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8924        geometry);
8925      if (*geometry == '\0')
8926        break;
8927      /*
8928        Surround image with an ornamental border.
8929      */
8930      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8931        exception);
8932      XSetCursorState(display,windows,MagickTrue);
8933      XCheckRefreshWindows(display,windows);
8934      (void) QueryColorCompliance(color,AllCompliance,&(*image)->matte_color,
8935        exception);
8936      (void) ParsePageGeometry(*image,geometry,&page_geometry,
8937        exception);
8938      frame_info.width=page_geometry.width;
8939      frame_info.height=page_geometry.height;
8940      frame_info.outer_bevel=page_geometry.x;
8941      frame_info.inner_bevel=page_geometry.y;
8942      frame_info.x=(ssize_t) frame_info.width;
8943      frame_info.y=(ssize_t) frame_info.height;
8944      frame_info.width=(*image)->columns+2*frame_info.width;
8945      frame_info.height=(*image)->rows+2*frame_info.height;
8946      frame_image=FrameImage(*image,&frame_info,(*image)->compose,exception);
8947      if (frame_image != (Image *) NULL)
8948        {
8949          *image=DestroyImage(*image);
8950          *image=frame_image;
8951        }
8952      CatchException(exception);
8953      XSetCursorState(display,windows,MagickFalse);
8954      if (windows->image.orphan != MagickFalse)
8955        break;
8956      windows->image.window_changes.width=(int) (*image)->columns;
8957      windows->image.window_changes.height=(int) (*image)->rows;
8958      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8959      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8960      break;
8961    }
8962    case CommentCommand:
8963    {
8964      const char
8965        *value;
8966
8967      FILE
8968        *file;
8969
8970      int
8971        unique_file;
8972
8973      /*
8974        Edit image comment.
8975      */
8976      unique_file=AcquireUniqueFileResource(image_info->filename);
8977      if (unique_file == -1)
8978        XNoticeWidget(display,windows,"Unable to edit image comment",
8979          image_info->filename);
8980      value=GetImageProperty(*image,"comment",exception);
8981      if (value == (char *) NULL)
8982        unique_file=close(unique_file)-1;
8983      else
8984        {
8985          register const char
8986            *p;
8987
8988          file=fdopen(unique_file,"w");
8989          if (file == (FILE *) NULL)
8990            {
8991              XNoticeWidget(display,windows,"Unable to edit image comment",
8992                image_info->filename);
8993              break;
8994            }
8995          for (p=value; *p != '\0'; p++)
8996            (void) fputc((int) *p,file);
8997          (void) fputc('\n',file);
8998          (void) fclose(file);
8999        }
9000      XSetCursorState(display,windows,MagickTrue);
9001      XCheckRefreshWindows(display,windows);
9002      status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
9003        exception);
9004      if (status == MagickFalse)
9005        XNoticeWidget(display,windows,"Unable to edit image comment",
9006          (char *) NULL);
9007      else
9008        {
9009          char
9010            *comment;
9011
9012          comment=FileToString(image_info->filename,~0UL,exception);
9013          if (comment != (char *) NULL)
9014            {
9015              (void) SetImageProperty(*image,"comment",comment,exception);
9016              (*image)->taint=MagickTrue;
9017            }
9018        }
9019      (void) RelinquishUniqueFileResource(image_info->filename);
9020      XSetCursorState(display,windows,MagickFalse);
9021      break;
9022    }
9023    case LaunchCommand:
9024    {
9025      /*
9026        Launch program.
9027      */
9028      XSetCursorState(display,windows,MagickTrue);
9029      XCheckRefreshWindows(display,windows);
9030      (void) AcquireUniqueFilename(filename);
9031      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"launch:%s",
9032        filename);
9033      status=WriteImage(image_info,*image,exception);
9034      if (status == MagickFalse)
9035        XNoticeWidget(display,windows,"Unable to launch image editor",
9036          (char *) NULL);
9037      else
9038        {
9039          nexus=ReadImage(resource_info->image_info,exception);
9040          CatchException(exception);
9041          XClientMessage(display,windows->image.id,windows->im_protocols,
9042            windows->im_next_image,CurrentTime);
9043        }
9044      (void) RelinquishUniqueFileResource(filename);
9045      XSetCursorState(display,windows,MagickFalse);
9046      break;
9047    }
9048    case RegionofInterestCommand:
9049    {
9050      /*
9051        Apply an image processing technique to a region of interest.
9052      */
9053      (void) XROIImage(display,resource_info,windows,image,exception);
9054      break;
9055    }
9056    case InfoCommand:
9057      break;
9058    case ZoomCommand:
9059    {
9060      /*
9061        Zoom image.
9062      */
9063      if (windows->magnify.mapped != MagickFalse)
9064        (void) XRaiseWindow(display,windows->magnify.id);
9065      else
9066        {
9067          /*
9068            Make magnify image.
9069          */
9070          XSetCursorState(display,windows,MagickTrue);
9071          (void) XMapRaised(display,windows->magnify.id);
9072          XSetCursorState(display,windows,MagickFalse);
9073        }
9074      break;
9075    }
9076    case ShowPreviewCommand:
9077    {
9078      char
9079        **previews;
9080
9081      Image
9082        *preview_image;
9083
9084      static char
9085        preview_type[MaxTextExtent] = "Gamma";
9086
9087      /*
9088        Select preview type from menu.
9089      */
9090      previews=GetCommandOptions(MagickPreviewOptions);
9091      if (previews == (char **) NULL)
9092        break;
9093      XListBrowserWidget(display,windows,&windows->widget,
9094        (const char **) previews,"Preview",
9095        "Select an enhancement, effect, or F/X:",preview_type);
9096      previews=DestroyStringList(previews);
9097      if (*preview_type == '\0')
9098        break;
9099      /*
9100        Show image preview.
9101      */
9102      XSetCursorState(display,windows,MagickTrue);
9103      XCheckRefreshWindows(display,windows);
9104      image_info->preview_type=(PreviewType)
9105        ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
9106      image_info->group=(ssize_t) windows->image.id;
9107      (void) DeleteImageProperty(*image,"label");
9108      (void) SetImageProperty(*image,"label","Preview",exception);
9109      (void) AcquireUniqueFilename(filename);
9110      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"preview:%s",
9111        filename);
9112      status=WriteImage(image_info,*image,exception);
9113      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9114      preview_image=ReadImage(image_info,exception);
9115      (void) RelinquishUniqueFileResource(filename);
9116      if (preview_image == (Image *) NULL)
9117        break;
9118      (void) FormatLocaleString(preview_image->filename,MaxTextExtent,"show:%s",
9119        filename);
9120      status=WriteImage(image_info,preview_image,exception);
9121      preview_image=DestroyImage(preview_image);
9122      if (status == MagickFalse)
9123        XNoticeWidget(display,windows,"Unable to show image preview",
9124          (*image)->filename);
9125      XDelay(display,1500);
9126      XSetCursorState(display,windows,MagickFalse);
9127      break;
9128    }
9129    case ShowHistogramCommand:
9130    {
9131      Image
9132        *histogram_image;
9133
9134      /*
9135        Show image histogram.
9136      */
9137      XSetCursorState(display,windows,MagickTrue);
9138      XCheckRefreshWindows(display,windows);
9139      image_info->group=(ssize_t) windows->image.id;
9140      (void) DeleteImageProperty(*image,"label");
9141      (void) SetImageProperty(*image,"label","Histogram",exception);
9142      (void) AcquireUniqueFilename(filename);
9143      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"histogram:%s",
9144        filename);
9145      status=WriteImage(image_info,*image,exception);
9146      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9147      histogram_image=ReadImage(image_info,exception);
9148      (void) RelinquishUniqueFileResource(filename);
9149      if (histogram_image == (Image *) NULL)
9150        break;
9151      (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
9152        "show:%s",filename);
9153      status=WriteImage(image_info,histogram_image,exception);
9154      histogram_image=DestroyImage(histogram_image);
9155      if (status == MagickFalse)
9156        XNoticeWidget(display,windows,"Unable to show histogram",
9157          (*image)->filename);
9158      XDelay(display,1500);
9159      XSetCursorState(display,windows,MagickFalse);
9160      break;
9161    }
9162    case ShowMatteCommand:
9163    {
9164      Image
9165        *matte_image;
9166
9167      if ((*image)->matte == MagickFalse)
9168        {
9169          XNoticeWidget(display,windows,
9170            "Image does not have any matte information",(*image)->filename);
9171          break;
9172        }
9173      /*
9174        Show image matte.
9175      */
9176      XSetCursorState(display,windows,MagickTrue);
9177      XCheckRefreshWindows(display,windows);
9178      image_info->group=(ssize_t) windows->image.id;
9179      (void) DeleteImageProperty(*image,"label");
9180      (void) SetImageProperty(*image,"label","Matte",exception);
9181      (void) AcquireUniqueFilename(filename);
9182      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"matte:%s",
9183        filename);
9184      status=WriteImage(image_info,*image,exception);
9185      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9186      matte_image=ReadImage(image_info,exception);
9187      (void) RelinquishUniqueFileResource(filename);
9188      if (matte_image == (Image *) NULL)
9189        break;
9190      (void) FormatLocaleString(matte_image->filename,MaxTextExtent,"show:%s",
9191        filename);
9192      status=WriteImage(image_info,matte_image,exception);
9193      matte_image=DestroyImage(matte_image);
9194      if (status == MagickFalse)
9195        XNoticeWidget(display,windows,"Unable to show matte",
9196          (*image)->filename);
9197      XDelay(display,1500);
9198      XSetCursorState(display,windows,MagickFalse);
9199      break;
9200    }
9201    case BackgroundCommand:
9202    {
9203      /*
9204        Background image.
9205      */
9206      status=XBackgroundImage(display,resource_info,windows,image,exception);
9207      if (status == MagickFalse)
9208        break;
9209      nexus=CloneImage(*image,0,0,MagickTrue,exception);
9210      if (nexus != (Image *) NULL)
9211        XClientMessage(display,windows->image.id,windows->im_protocols,
9212          windows->im_next_image,CurrentTime);
9213      break;
9214    }
9215    case SlideShowCommand:
9216    {
9217      static char
9218        delay[MaxTextExtent] = "5";
9219
9220      /*
9221        Display next image after pausing.
9222      */
9223      (void) XDialogWidget(display,windows,"Slide Show",
9224        "Pause how many 1/100ths of a second between images:",delay);
9225      if (*delay == '\0')
9226        break;
9227      resource_info->delay=StringToUnsignedLong(delay);
9228      XClientMessage(display,windows->image.id,windows->im_protocols,
9229        windows->im_next_image,CurrentTime);
9230      break;
9231    }
9232    case PreferencesCommand:
9233    {
9234      /*
9235        Set user preferences.
9236      */
9237      status=XPreferencesWidget(display,resource_info,windows);
9238      if (status == MagickFalse)
9239        break;
9240      nexus=CloneImage(*image,0,0,MagickTrue,exception);
9241      if (nexus != (Image *) NULL)
9242        XClientMessage(display,windows->image.id,windows->im_protocols,
9243          windows->im_next_image,CurrentTime);
9244      break;
9245    }
9246    case HelpCommand:
9247    {
9248      /*
9249        User requested help.
9250      */
9251      XTextViewWidget(display,resource_info,windows,MagickFalse,
9252        "Help Viewer - Display",DisplayHelp);
9253      break;
9254    }
9255    case BrowseDocumentationCommand:
9256    {
9257      Atom
9258        mozilla_atom;
9259
9260      Window
9261        mozilla_window,
9262        root_window;
9263
9264      /*
9265        Browse the ImageMagick documentation.
9266      */
9267      root_window=XRootWindow(display,XDefaultScreen(display));
9268      mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9269      mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9270      if (mozilla_window != (Window) NULL)
9271        {
9272          char
9273            command[MaxTextExtent],
9274            *url;
9275
9276          /*
9277            Display documentation using Netscape remote control.
9278          */
9279          url=GetMagickHomeURL();
9280          (void) FormatLocaleString(command,MaxTextExtent,
9281            "openurl(%s,new-tab)",url);
9282          url=DestroyString(url);
9283          mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9284          (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9285            8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9286          XSetCursorState(display,windows,MagickFalse);
9287          break;
9288        }
9289      XSetCursorState(display,windows,MagickTrue);
9290      XCheckRefreshWindows(display,windows);
9291      status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9292        exception);
9293      if (status == MagickFalse)
9294        XNoticeWidget(display,windows,"Unable to browse documentation",
9295          (char *) NULL);
9296      XDelay(display,1500);
9297      XSetCursorState(display,windows,MagickFalse);
9298      break;
9299    }
9300    case VersionCommand:
9301    {
9302      XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9303        GetMagickCopyright());
9304      break;
9305    }
9306    case SaveToUndoBufferCommand:
9307      break;
9308    default:
9309    {
9310      (void) XBell(display,0);
9311      break;
9312    }
9313  }
9314  image_info=DestroyImageInfo(image_info);
9315  return(nexus);
9316}
9317
9318/*
9319%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9320%                                                                             %
9321%                                                                             %
9322%                                                                             %
9323+   X M a g n i f y I m a g e                                                 %
9324%                                                                             %
9325%                                                                             %
9326%                                                                             %
9327%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9328%
9329%  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9330%  The magnified portion is displayed in a separate window.
9331%
9332%  The format of the XMagnifyImage method is:
9333%
9334%      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9335%        ExceptionInfo *exception)
9336%
9337%  A description of each parameter follows:
9338%
9339%    o display: Specifies a connection to an X server;  returned from
9340%      XOpenDisplay.
9341%
9342%    o windows: Specifies a pointer to a XWindows structure.
9343%
9344%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
9345%      the entire image is refreshed.
9346%
9347%    o exception: return any errors or warnings in this structure.
9348%
9349*/
9350static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9351  ExceptionInfo *exception)
9352{
9353  char
9354    text[MaxTextExtent];
9355
9356  register int
9357    x,
9358    y;
9359
9360  size_t
9361    state;
9362
9363  /*
9364    Update magnified image until the mouse button is released.
9365  */
9366  (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9367  state=DefaultState;
9368  x=event->xbutton.x;
9369  y=event->xbutton.y;
9370  windows->magnify.x=(int) windows->image.x+x;
9371  windows->magnify.y=(int) windows->image.y+y;
9372  do
9373  {
9374    /*
9375      Map and unmap Info widget as text cursor crosses its boundaries.
9376    */
9377    if (windows->info.mapped != MagickFalse)
9378      {
9379        if ((x < (int) (windows->info.x+windows->info.width)) &&
9380            (y < (int) (windows->info.y+windows->info.height)))
9381          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9382      }
9383    else
9384      if ((x > (int) (windows->info.x+windows->info.width)) ||
9385          (y > (int) (windows->info.y+windows->info.height)))
9386        (void) XMapWindow(display,windows->info.id);
9387    if (windows->info.mapped != MagickFalse)
9388      {
9389        /*
9390          Display pointer position.
9391        */
9392        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9393          windows->magnify.x,windows->magnify.y);
9394        XInfoWidget(display,windows,text);
9395      }
9396    /*
9397      Wait for next event.
9398    */
9399    XScreenEvent(display,windows,event,exception);
9400    switch (event->type)
9401    {
9402      case ButtonPress:
9403        break;
9404      case ButtonRelease:
9405      {
9406        /*
9407          User has finished magnifying image.
9408        */
9409        x=event->xbutton.x;
9410        y=event->xbutton.y;
9411        state|=ExitState;
9412        break;
9413      }
9414      case Expose:
9415        break;
9416      case MotionNotify:
9417      {
9418        x=event->xmotion.x;
9419        y=event->xmotion.y;
9420        break;
9421      }
9422      default:
9423        break;
9424    }
9425    /*
9426      Check boundary conditions.
9427    */
9428    if (x < 0)
9429      x=0;
9430    else
9431      if (x >= (int) windows->image.width)
9432        x=(int) windows->image.width-1;
9433    if (y < 0)
9434      y=0;
9435    else
9436     if (y >= (int) windows->image.height)
9437       y=(int) windows->image.height-1;
9438  } while ((state & ExitState) == 0);
9439  /*
9440    Display magnified image.
9441  */
9442  XSetCursorState(display,windows,MagickFalse);
9443}
9444
9445/*
9446%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9447%                                                                             %
9448%                                                                             %
9449%                                                                             %
9450+   X M a g n i f y W i n d o w C o m m a n d                                 %
9451%                                                                             %
9452%                                                                             %
9453%                                                                             %
9454%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9455%
9456%  XMagnifyWindowCommand() moves the image within an Magnify window by one
9457%  pixel as specified by the key symbol.
9458%
9459%  The format of the XMagnifyWindowCommand method is:
9460%
9461%      void XMagnifyWindowCommand(Display *display,XWindows *windows,
9462%        const MagickStatusType state,const KeySym key_symbol,
9463%        ExceptionInfo *exception)
9464%
9465%  A description of each parameter follows:
9466%
9467%    o display: Specifies a connection to an X server; returned from
9468%      XOpenDisplay.
9469%
9470%    o windows: Specifies a pointer to a XWindows structure.
9471%
9472%    o state: key mask.
9473%
9474%    o key_symbol: Specifies a KeySym which indicates which side of the image
9475%      to trim.
9476%
9477%    o exception: return any errors or warnings in this structure.
9478%
9479*/
9480static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9481  const MagickStatusType state,const KeySym key_symbol,ExceptionInfo *exception)
9482{
9483  unsigned int
9484    quantum;
9485
9486  /*
9487    User specified a magnify factor or position.
9488  */
9489  quantum=1;
9490  if ((state & Mod1Mask) != 0)
9491    quantum=10;
9492  switch ((int) key_symbol)
9493  {
9494    case QuitCommand:
9495    {
9496      (void) XWithdrawWindow(display,windows->magnify.id,
9497        windows->magnify.screen);
9498      break;
9499    }
9500    case XK_Home:
9501    case XK_KP_Home:
9502    {
9503      windows->magnify.x=(int) windows->image.width/2;
9504      windows->magnify.y=(int) windows->image.height/2;
9505      break;
9506    }
9507    case XK_Left:
9508    case XK_KP_Left:
9509    {
9510      if (windows->magnify.x > 0)
9511        windows->magnify.x-=quantum;
9512      break;
9513    }
9514    case XK_Up:
9515    case XK_KP_Up:
9516    {
9517      if (windows->magnify.y > 0)
9518        windows->magnify.y-=quantum;
9519      break;
9520    }
9521    case XK_Right:
9522    case XK_KP_Right:
9523    {
9524      if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9525        windows->magnify.x+=quantum;
9526      break;
9527    }
9528    case XK_Down:
9529    case XK_KP_Down:
9530    {
9531      if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9532        windows->magnify.y+=quantum;
9533      break;
9534    }
9535    case XK_0:
9536    case XK_1:
9537    case XK_2:
9538    case XK_3:
9539    case XK_4:
9540    case XK_5:
9541    case XK_6:
9542    case XK_7:
9543    case XK_8:
9544    case XK_9:
9545    {
9546      windows->magnify.data=(key_symbol-XK_0);
9547      break;
9548    }
9549    case XK_KP_0:
9550    case XK_KP_1:
9551    case XK_KP_2:
9552    case XK_KP_3:
9553    case XK_KP_4:
9554    case XK_KP_5:
9555    case XK_KP_6:
9556    case XK_KP_7:
9557    case XK_KP_8:
9558    case XK_KP_9:
9559    {
9560      windows->magnify.data=(key_symbol-XK_KP_0);
9561      break;
9562    }
9563    default:
9564      break;
9565  }
9566  XMakeMagnifyImage(display,windows,exception);
9567}
9568
9569/*
9570%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9571%                                                                             %
9572%                                                                             %
9573%                                                                             %
9574+   X M a k e P a n I m a g e                                                 %
9575%                                                                             %
9576%                                                                             %
9577%                                                                             %
9578%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9579%
9580%  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9581%  icon window.
9582%
9583%  The format of the XMakePanImage method is:
9584%
9585%        void XMakePanImage(Display *display,XResourceInfo *resource_info,
9586%          XWindows *windows,Image *image,ExceptionInfo *exception)
9587%
9588%  A description of each parameter follows:
9589%
9590%    o display: Specifies a connection to an X server;  returned from
9591%      XOpenDisplay.
9592%
9593%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9594%
9595%    o windows: Specifies a pointer to a XWindows structure.
9596%
9597%    o image: the image.
9598%
9599%    o exception: return any errors or warnings in this structure.
9600%
9601*/
9602static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9603  XWindows *windows,Image *image,ExceptionInfo *exception)
9604{
9605  MagickStatusType
9606    status;
9607
9608  /*
9609    Create and display image for panning icon.
9610  */
9611  XSetCursorState(display,windows,MagickTrue);
9612  XCheckRefreshWindows(display,windows);
9613  windows->pan.x=(int) windows->image.x;
9614  windows->pan.y=(int) windows->image.y;
9615  status=XMakeImage(display,resource_info,&windows->pan,image,
9616    windows->pan.width,windows->pan.height,exception);
9617  if (status == MagickFalse)
9618    ThrowXWindowFatalException(ResourceLimitError,
9619     "MemoryAllocationFailed",image->filename);
9620  (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9621    windows->pan.pixmap);
9622  (void) XClearWindow(display,windows->pan.id);
9623  XDrawPanRectangle(display,windows);
9624  XSetCursorState(display,windows,MagickFalse);
9625}
9626
9627/*
9628%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9629%                                                                             %
9630%                                                                             %
9631%                                                                             %
9632+   X M a t t a E d i t I m a g e                                             %
9633%                                                                             %
9634%                                                                             %
9635%                                                                             %
9636%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9637%
9638%  XMatteEditImage() allows the user to interactively change the Matte channel
9639%  of an image.  If the image is PseudoClass it is promoted to DirectClass
9640%  before the matte information is stored.
9641%
9642%  The format of the XMatteEditImage method is:
9643%
9644%      MagickBooleanType XMatteEditImage(Display *display,
9645%        XResourceInfo *resource_info,XWindows *windows,Image **image,
9646%        ExceptionInfo *exception)
9647%
9648%  A description of each parameter follows:
9649%
9650%    o display: Specifies a connection to an X server;  returned from
9651%      XOpenDisplay.
9652%
9653%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9654%
9655%    o windows: Specifies a pointer to a XWindows structure.
9656%
9657%    o image: the image; returned from ReadImage.
9658%
9659%    o exception: return any errors or warnings in this structure.
9660%
9661*/
9662static MagickBooleanType XMatteEditImage(Display *display,
9663  XResourceInfo *resource_info,XWindows *windows,Image **image,
9664  ExceptionInfo *exception)
9665{
9666  static char
9667    matte[MaxTextExtent] = "0";
9668
9669  static const char
9670    *MatteEditMenu[] =
9671    {
9672      "Method",
9673      "Border Color",
9674      "Fuzz",
9675      "Matte Value",
9676      "Undo",
9677      "Help",
9678      "Dismiss",
9679      (char *) NULL
9680    };
9681
9682  static const ModeType
9683    MatteEditCommands[] =
9684    {
9685      MatteEditMethod,
9686      MatteEditBorderCommand,
9687      MatteEditFuzzCommand,
9688      MatteEditValueCommand,
9689      MatteEditUndoCommand,
9690      MatteEditHelpCommand,
9691      MatteEditDismissCommand
9692    };
9693
9694  static PaintMethod
9695    method = PointMethod;
9696
9697  static XColor
9698    border_color = { 0, 0, 0, 0, 0, 0 };
9699
9700  char
9701    command[MaxTextExtent],
9702    text[MaxTextExtent];
9703
9704  Cursor
9705    cursor;
9706
9707  int
9708    entry,
9709    id,
9710    x,
9711    x_offset,
9712    y,
9713    y_offset;
9714
9715  register int
9716    i;
9717
9718  register Quantum
9719    *q;
9720
9721  unsigned int
9722    height,
9723    width;
9724
9725  size_t
9726    state;
9727
9728  XEvent
9729    event;
9730
9731  /*
9732    Map Command widget.
9733  */
9734  (void) CloneString(&windows->command.name,"Matte Edit");
9735  windows->command.data=4;
9736  (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9737  (void) XMapRaised(display,windows->command.id);
9738  XClientMessage(display,windows->image.id,windows->im_protocols,
9739    windows->im_update_widget,CurrentTime);
9740  /*
9741    Make cursor.
9742  */
9743  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9744    resource_info->background_color,resource_info->foreground_color);
9745  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9746  /*
9747    Track pointer until button 1 is pressed.
9748  */
9749  XQueryPosition(display,windows->image.id,&x,&y);
9750  (void) XSelectInput(display,windows->image.id,
9751    windows->image.attributes.event_mask | PointerMotionMask);
9752  state=DefaultState;
9753  do
9754  {
9755    if (windows->info.mapped != MagickFalse)
9756      {
9757        /*
9758          Display pointer position.
9759        */
9760        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9761          x+windows->image.x,y+windows->image.y);
9762        XInfoWidget(display,windows,text);
9763      }
9764    /*
9765      Wait for next event.
9766    */
9767    XScreenEvent(display,windows,&event,exception);
9768    if (event.xany.window == windows->command.id)
9769      {
9770        /*
9771          Select a command from the Command widget.
9772        */
9773        id=XCommandWidget(display,windows,MatteEditMenu,&event);
9774        if (id < 0)
9775          {
9776            (void) XCheckDefineCursor(display,windows->image.id,cursor);
9777            continue;
9778          }
9779        switch (MatteEditCommands[id])
9780        {
9781          case MatteEditMethod:
9782          {
9783            char
9784              **methods;
9785
9786            /*
9787              Select a method from the pop-up menu.
9788            */
9789            methods=GetCommandOptions(MagickMethodOptions);
9790            if (methods == (char **) NULL)
9791              break;
9792            entry=XMenuWidget(display,windows,MatteEditMenu[id],
9793              (const char **) methods,command);
9794            if (entry >= 0)
9795              method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9796                MagickFalse,methods[entry]);
9797            methods=DestroyStringList(methods);
9798            break;
9799          }
9800          case MatteEditBorderCommand:
9801          {
9802            const char
9803              *ColorMenu[MaxNumberPens];
9804
9805            int
9806              pen_number;
9807
9808            /*
9809              Initialize menu selections.
9810            */
9811            for (i=0; i < (int) (MaxNumberPens-2); i++)
9812              ColorMenu[i]=resource_info->pen_colors[i];
9813            ColorMenu[MaxNumberPens-2]="Browser...";
9814            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9815            /*
9816              Select a pen color from the pop-up menu.
9817            */
9818            pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9819              (const char **) ColorMenu,command);
9820            if (pen_number < 0)
9821              break;
9822            if (pen_number == (MaxNumberPens-2))
9823              {
9824                static char
9825                  color_name[MaxTextExtent] = "gray";
9826
9827                /*
9828                  Select a pen color from a dialog.
9829                */
9830                resource_info->pen_colors[pen_number]=color_name;
9831                XColorBrowserWidget(display,windows,"Select",color_name);
9832                if (*color_name == '\0')
9833                  break;
9834              }
9835            /*
9836              Set border color.
9837            */
9838            (void) XParseColor(display,windows->map_info->colormap,
9839              resource_info->pen_colors[pen_number],&border_color);
9840            break;
9841          }
9842          case MatteEditFuzzCommand:
9843          {
9844            static char
9845              fuzz[MaxTextExtent];
9846
9847            static const char
9848              *FuzzMenu[] =
9849              {
9850                "0%",
9851                "2%",
9852                "5%",
9853                "10%",
9854                "15%",
9855                "Dialog...",
9856                (char *) NULL,
9857              };
9858
9859            /*
9860              Select a command from the pop-up menu.
9861            */
9862            entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9863              command);
9864            if (entry < 0)
9865              break;
9866            if (entry != 5)
9867              {
9868                (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
9869                  QuantumRange+1.0);
9870                break;
9871              }
9872            (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9873            (void) XDialogWidget(display,windows,"Ok",
9874              "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9875            if (*fuzz == '\0')
9876              break;
9877            (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
9878            (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
9879              1.0);
9880            break;
9881          }
9882          case MatteEditValueCommand:
9883          {
9884            static char
9885              message[MaxTextExtent];
9886
9887            static const char
9888              *MatteMenu[] =
9889              {
9890                "Opaque",
9891                "Transparent",
9892                "Dialog...",
9893                (char *) NULL,
9894              };
9895
9896            /*
9897              Select a command from the pop-up menu.
9898            */
9899            entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9900              command);
9901            if (entry < 0)
9902              break;
9903            if (entry != 2)
9904              {
9905                (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9906                  OpaqueAlpha);
9907                if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9908                  (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9909                    (Quantum) TransparentAlpha);
9910                break;
9911              }
9912            (void) FormatLocaleString(message,MaxTextExtent,
9913              "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9914              QuantumRange);
9915            (void) XDialogWidget(display,windows,"Matte",message,matte);
9916            if (*matte == '\0')
9917              break;
9918            break;
9919          }
9920          case MatteEditUndoCommand:
9921          {
9922            (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9923              image,exception);
9924            break;
9925          }
9926          case MatteEditHelpCommand:
9927          {
9928            XTextViewWidget(display,resource_info,windows,MagickFalse,
9929              "Help Viewer - Matte Edit",ImageMatteEditHelp);
9930            break;
9931          }
9932          case MatteEditDismissCommand:
9933          {
9934            /*
9935              Prematurely exit.
9936            */
9937            state|=EscapeState;
9938            state|=ExitState;
9939            break;
9940          }
9941          default:
9942            break;
9943        }
9944        (void) XCheckDefineCursor(display,windows->image.id,cursor);
9945        continue;
9946      }
9947    switch (event.type)
9948    {
9949      case ButtonPress:
9950      {
9951        if (event.xbutton.button != Button1)
9952          break;
9953        if ((event.xbutton.window != windows->image.id) &&
9954            (event.xbutton.window != windows->magnify.id))
9955          break;
9956        /*
9957          Update matte data.
9958        */
9959        x=event.xbutton.x;
9960        y=event.xbutton.y;
9961        (void) XMagickCommand(display,resource_info,windows,
9962          SaveToUndoBufferCommand,image,exception);
9963        state|=UpdateConfigurationState;
9964        break;
9965      }
9966      case ButtonRelease:
9967      {
9968        if (event.xbutton.button != Button1)
9969          break;
9970        if ((event.xbutton.window != windows->image.id) &&
9971            (event.xbutton.window != windows->magnify.id))
9972          break;
9973        /*
9974          Update colormap information.
9975        */
9976        x=event.xbutton.x;
9977        y=event.xbutton.y;
9978        XConfigureImageColormap(display,resource_info,windows,*image,exception);
9979        (void) XConfigureImage(display,resource_info,windows,*image,exception);
9980        XInfoWidget(display,windows,text);
9981        (void) XCheckDefineCursor(display,windows->image.id,cursor);
9982        state&=(~UpdateConfigurationState);
9983        break;
9984      }
9985      case Expose:
9986        break;
9987      case KeyPress:
9988      {
9989        char
9990          command[MaxTextExtent];
9991
9992        KeySym
9993          key_symbol;
9994
9995        if (event.xkey.window == windows->magnify.id)
9996          {
9997            Window
9998              window;
9999
10000            window=windows->magnify.id;
10001            while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
10002          }
10003        if (event.xkey.window != windows->image.id)
10004          break;
10005        /*
10006          Respond to a user key press.
10007        */
10008        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
10009          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10010        switch ((int) key_symbol)
10011        {
10012          case XK_Escape:
10013          case XK_F20:
10014          {
10015            /*
10016              Prematurely exit.
10017            */
10018            state|=ExitState;
10019            break;
10020          }
10021          case XK_F1:
10022          case XK_Help:
10023          {
10024            XTextViewWidget(display,resource_info,windows,MagickFalse,
10025              "Help Viewer - Matte Edit",ImageMatteEditHelp);
10026            break;
10027          }
10028          default:
10029          {
10030            (void) XBell(display,0);
10031            break;
10032          }
10033        }
10034        break;
10035      }
10036      case MotionNotify:
10037      {
10038        /*
10039          Map and unmap Info widget as cursor crosses its boundaries.
10040        */
10041        x=event.xmotion.x;
10042        y=event.xmotion.y;
10043        if (windows->info.mapped != MagickFalse)
10044          {
10045            if ((x < (int) (windows->info.x+windows->info.width)) &&
10046                (y < (int) (windows->info.y+windows->info.height)))
10047              (void) XWithdrawWindow(display,windows->info.id,
10048                windows->info.screen);
10049          }
10050        else
10051          if ((x > (int) (windows->info.x+windows->info.width)) ||
10052              (y > (int) (windows->info.y+windows->info.height)))
10053            (void) XMapWindow(display,windows->info.id);
10054        break;
10055      }
10056      default:
10057        break;
10058    }
10059    if (event.xany.window == windows->magnify.id)
10060      {
10061        x=windows->magnify.x-windows->image.x;
10062        y=windows->magnify.y-windows->image.y;
10063      }
10064    x_offset=x;
10065    y_offset=y;
10066    if ((state & UpdateConfigurationState) != 0)
10067      {
10068        CacheView
10069          *image_view;
10070
10071        int
10072          x,
10073          y;
10074
10075        /*
10076          Matte edit is relative to image configuration.
10077        */
10078        (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
10079          MagickTrue);
10080        XPutPixel(windows->image.ximage,x_offset,y_offset,
10081          windows->pixel_info->background_color.pixel);
10082        width=(unsigned int) (*image)->columns;
10083        height=(unsigned int) (*image)->rows;
10084        x=0;
10085        y=0;
10086        if (windows->image.crop_geometry != (char *) NULL)
10087          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
10088            &height);
10089        x_offset=(int) (width*(windows->image.x+x_offset)/
10090          windows->image.ximage->width+x);
10091        y_offset=(int) (height*(windows->image.y+y_offset)/
10092          windows->image.ximage->height+y);
10093        if ((x_offset < 0) || (y_offset < 0))
10094          continue;
10095        if ((x_offset >= (int) (*image)->columns) ||
10096            (y_offset >= (int) (*image)->rows))
10097          continue;
10098        if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10099          return(MagickFalse);
10100        (*image)->matte=MagickTrue;
10101        image_view=AcquireCacheView(*image);
10102        switch (method)
10103        {
10104          case PointMethod:
10105          default:
10106          {
10107            /*
10108              Update matte information using point algorithm.
10109            */
10110            q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10111              (ssize_t) y_offset,1,1,exception);
10112            if (q == (Quantum *) NULL)
10113              break;
10114            SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10115            (void) SyncCacheViewAuthenticPixels(image_view,exception);
10116            break;
10117          }
10118          case ReplaceMethod:
10119          {
10120            PixelInfo
10121              pixel,
10122              target;
10123
10124            Quantum
10125              virtual_pixel[CompositePixelChannel];
10126
10127            /*
10128              Update matte information using replace algorithm.
10129            */
10130            (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
10131              (ssize_t) y_offset,virtual_pixel,exception);
10132            target.red=virtual_pixel[RedPixelChannel];
10133            target.green=virtual_pixel[GreenPixelChannel];
10134            target.blue=virtual_pixel[BluePixelChannel];
10135            target.alpha=virtual_pixel[AlphaPixelChannel];
10136            for (y=0; y < (int) (*image)->rows; y++)
10137            {
10138              q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10139                (*image)->columns,1,exception);
10140              if (q == (Quantum *) NULL)
10141                break;
10142              for (x=0; x < (int) (*image)->columns; x++)
10143              {
10144                GetPixelInfoPixel(*image,q,&pixel);
10145                if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
10146                  SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10147                q+=GetPixelChannels(*image);
10148              }
10149              if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10150                break;
10151            }
10152            break;
10153          }
10154          case FloodfillMethod:
10155          case FillToBorderMethod:
10156          {
10157            ChannelType
10158              channel_mask;
10159
10160            DrawInfo
10161              *draw_info;
10162
10163            PixelInfo
10164              target;
10165
10166            /*
10167              Update matte information using floodfill algorithm.
10168            */
10169            (void) GetOneVirtualMagickPixel(*image,
10170              GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
10171              y_offset,&target,exception);
10172            if (method == FillToBorderMethod)
10173              {
10174                target.red=(MagickRealType) ScaleShortToQuantum(
10175                  border_color.red);
10176                target.green=(MagickRealType) ScaleShortToQuantum(
10177                  border_color.green);
10178                target.blue=(MagickRealType) ScaleShortToQuantum(
10179                  border_color.blue);
10180              }
10181            draw_info=CloneDrawInfo(resource_info->image_info,
10182              (DrawInfo *) NULL);
10183            draw_info->fill.alpha=ClampToQuantum(StringToDouble(matte,
10184              (char **) NULL));
10185            channel_mask=SetPixelChannelMask(*image,AlphaChannel);
10186            (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10187              x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
10188              MagickFalse : MagickTrue,exception);
10189            (void) SetPixelChannelMap(*image,channel_mask);
10190            draw_info=DestroyDrawInfo(draw_info);
10191            break;
10192          }
10193          case ResetMethod:
10194          {
10195            /*
10196              Update matte information using reset algorithm.
10197            */
10198            if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10199              return(MagickFalse);
10200            for (y=0; y < (int) (*image)->rows; y++)
10201            {
10202              q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10203                (*image)->columns,1,exception);
10204              if (q == (Quantum *) NULL)
10205                break;
10206              for (x=0; x < (int) (*image)->columns; x++)
10207              {
10208                SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10209                q+=GetPixelChannels(*image);
10210              }
10211              if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10212                break;
10213            }
10214            if (StringToLong(matte) == (long) OpaqueAlpha)
10215              (*image)->matte=MagickFalse;
10216            break;
10217          }
10218        }
10219        image_view=DestroyCacheView(image_view);
10220        state&=(~UpdateConfigurationState);
10221      }
10222  } while ((state & ExitState) == 0);
10223  (void) XSelectInput(display,windows->image.id,
10224    windows->image.attributes.event_mask);
10225  XSetCursorState(display,windows,MagickFalse);
10226  (void) XFreeCursor(display,cursor);
10227  return(MagickTrue);
10228}
10229
10230/*
10231%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10232%                                                                             %
10233%                                                                             %
10234%                                                                             %
10235+   X O p e n I m a g e                                                       %
10236%                                                                             %
10237%                                                                             %
10238%                                                                             %
10239%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10240%
10241%  XOpenImage() loads an image from a file.
10242%
10243%  The format of the XOpenImage method is:
10244%
10245%     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10246%       XWindows *windows,const unsigned int command)
10247%
10248%  A description of each parameter follows:
10249%
10250%    o display: Specifies a connection to an X server; returned from
10251%      XOpenDisplay.
10252%
10253%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10254%
10255%    o windows: Specifies a pointer to a XWindows structure.
10256%
10257%    o command: A value other than zero indicates that the file is selected
10258%      from the command line argument list.
10259%
10260*/
10261static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10262  XWindows *windows,const MagickBooleanType command)
10263{
10264  const MagickInfo
10265    *magick_info;
10266
10267  ExceptionInfo
10268    *exception;
10269
10270  Image
10271    *nexus;
10272
10273  ImageInfo
10274    *image_info;
10275
10276  static char
10277    filename[MaxTextExtent] = "\0";
10278
10279  /*
10280    Request file name from user.
10281  */
10282  if (command == MagickFalse)
10283    XFileBrowserWidget(display,windows,"Open",filename);
10284  else
10285    {
10286      char
10287        **filelist,
10288        **files;
10289
10290      int
10291        count,
10292        status;
10293
10294      register int
10295        i,
10296        j;
10297
10298      /*
10299        Select next image from the command line.
10300      */
10301      status=XGetCommand(display,windows->image.id,&files,&count);
10302      if (status == 0)
10303        {
10304          ThrowXWindowFatalException(XServerError,"UnableToGetProperty","...");
10305          return((Image *) NULL);
10306        }
10307      filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10308      if (filelist == (char **) NULL)
10309        {
10310          ThrowXWindowFatalException(ResourceLimitError,
10311            "MemoryAllocationFailed","...");
10312          (void) XFreeStringList(files);
10313          return((Image *) NULL);
10314        }
10315      j=0;
10316      for (i=1; i < count; i++)
10317        if (*files[i] != '-')
10318          filelist[j++]=files[i];
10319      filelist[j]=(char *) NULL;
10320      XListBrowserWidget(display,windows,&windows->widget,
10321        (const char **) filelist,"Load","Select Image to Load:",filename);
10322      filelist=(char **) RelinquishMagickMemory(filelist);
10323      (void) XFreeStringList(files);
10324    }
10325  if (*filename == '\0')
10326    return((Image *) NULL);
10327  image_info=CloneImageInfo(resource_info->image_info);
10328  (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10329    (void *) NULL);
10330  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10331  exception=AcquireExceptionInfo();
10332  (void) SetImageInfo(image_info,0,exception);
10333  if (LocaleCompare(image_info->magick,"X") == 0)
10334    {
10335      char
10336        seconds[MaxTextExtent];
10337
10338      /*
10339        User may want to delay the X server screen grab.
10340      */
10341      (void) CopyMagickString(seconds,"0",MaxTextExtent);
10342      (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10343        seconds);
10344      if (*seconds == '\0')
10345        return((Image *) NULL);
10346      XDelay(display,(size_t) (1000*StringToLong(seconds)));
10347    }
10348  magick_info=GetMagickInfo(image_info->magick,exception);
10349  if ((magick_info != (const MagickInfo *) NULL) &&
10350      (magick_info->raw != MagickFalse))
10351    {
10352      char
10353        geometry[MaxTextExtent];
10354
10355      /*
10356        Request image size from the user.
10357      */
10358      (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10359      if (image_info->size != (char *) NULL)
10360        (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10361      (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10362        geometry);
10363      (void) CloneString(&image_info->size,geometry);
10364    }
10365  /*
10366    Load the image.
10367  */
10368  XSetCursorState(display,windows,MagickTrue);
10369  XCheckRefreshWindows(display,windows);
10370  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10371  nexus=ReadImage(image_info,exception);
10372  CatchException(exception);
10373  XSetCursorState(display,windows,MagickFalse);
10374  if (nexus != (Image *) NULL)
10375    XClientMessage(display,windows->image.id,windows->im_protocols,
10376      windows->im_next_image,CurrentTime);
10377  else
10378    {
10379      char
10380        *text,
10381        **textlist;
10382
10383      /*
10384        Unknown image format.
10385      */
10386      text=FileToString(filename,~0,exception);
10387      if (text == (char *) NULL)
10388        return((Image *) NULL);
10389      textlist=StringToList(text);
10390      if (textlist != (char **) NULL)
10391        {
10392          char
10393            title[MaxTextExtent];
10394
10395          register int
10396            i;
10397
10398          (void) FormatLocaleString(title,MaxTextExtent,
10399            "Unknown format: %s",filename);
10400          XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10401            (const char **) textlist);
10402          for (i=0; textlist[i] != (char *) NULL; i++)
10403            textlist[i]=DestroyString(textlist[i]);
10404          textlist=(char **) RelinquishMagickMemory(textlist);
10405        }
10406      text=DestroyString(text);
10407    }
10408  exception=DestroyExceptionInfo(exception);
10409  image_info=DestroyImageInfo(image_info);
10410  return(nexus);
10411}
10412
10413/*
10414%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10415%                                                                             %
10416%                                                                             %
10417%                                                                             %
10418+   X P a n I m a g e                                                         %
10419%                                                                             %
10420%                                                                             %
10421%                                                                             %
10422%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10423%
10424%  XPanImage() pans the image until the mouse button is released.
10425%
10426%  The format of the XPanImage method is:
10427%
10428%      void XPanImage(Display *display,XWindows *windows,XEvent *event,
10429%        ExceptionInfo *exception)
10430%
10431%  A description of each parameter follows:
10432%
10433%    o display: Specifies a connection to an X server;  returned from
10434%      XOpenDisplay.
10435%
10436%    o windows: Specifies a pointer to a XWindows structure.
10437%
10438%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10439%      the entire image is refreshed.
10440%
10441%    o exception: return any errors or warnings in this structure.
10442%
10443*/
10444static void XPanImage(Display *display,XWindows *windows,XEvent *event,
10445  ExceptionInfo *exception)
10446{
10447  char
10448    text[MaxTextExtent];
10449
10450  Cursor
10451    cursor;
10452
10453  MagickRealType
10454    x_factor,
10455    y_factor;
10456
10457  RectangleInfo
10458    pan_info;
10459
10460  size_t
10461    state;
10462
10463  /*
10464    Define cursor.
10465  */
10466  if ((windows->image.ximage->width > (int) windows->image.width) &&
10467      (windows->image.ximage->height > (int) windows->image.height))
10468    cursor=XCreateFontCursor(display,XC_fleur);
10469  else
10470    if (windows->image.ximage->width > (int) windows->image.width)
10471      cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10472    else
10473      if (windows->image.ximage->height > (int) windows->image.height)
10474        cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10475      else
10476        cursor=XCreateFontCursor(display,XC_arrow);
10477  (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10478  /*
10479    Pan image as pointer moves until the mouse button is released.
10480  */
10481  x_factor=(MagickRealType) windows->image.ximage->width/windows->pan.width;
10482  y_factor=(MagickRealType) windows->image.ximage->height/windows->pan.height;
10483  pan_info.width=windows->pan.width*windows->image.width/
10484    windows->image.ximage->width;
10485  pan_info.height=windows->pan.height*windows->image.height/
10486    windows->image.ximage->height;
10487  pan_info.x=0;
10488  pan_info.y=0;
10489  state=UpdateConfigurationState;
10490  do
10491  {
10492    switch (event->type)
10493    {
10494      case ButtonPress:
10495      {
10496        /*
10497          User choose an initial pan location.
10498        */
10499        pan_info.x=(ssize_t) event->xbutton.x;
10500        pan_info.y=(ssize_t) event->xbutton.y;
10501        state|=UpdateConfigurationState;
10502        break;
10503      }
10504      case ButtonRelease:
10505      {
10506        /*
10507          User has finished panning the image.
10508        */
10509        pan_info.x=(ssize_t) event->xbutton.x;
10510        pan_info.y=(ssize_t) event->xbutton.y;
10511        state|=UpdateConfigurationState | ExitState;
10512        break;
10513      }
10514      case MotionNotify:
10515      {
10516        pan_info.x=(ssize_t) event->xmotion.x;
10517        pan_info.y=(ssize_t) event->xmotion.y;
10518        state|=UpdateConfigurationState;
10519      }
10520      default:
10521        break;
10522    }
10523    if ((state & UpdateConfigurationState) != 0)
10524      {
10525        /*
10526          Check boundary conditions.
10527        */
10528        if (pan_info.x < (ssize_t) (pan_info.width/2))
10529          pan_info.x=0;
10530        else
10531          pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10532        if (pan_info.x < 0)
10533          pan_info.x=0;
10534        else
10535          if ((int) (pan_info.x+windows->image.width) >
10536              windows->image.ximage->width)
10537            pan_info.x=(ssize_t)
10538              (windows->image.ximage->width-windows->image.width);
10539        if (pan_info.y < (ssize_t) (pan_info.height/2))
10540          pan_info.y=0;
10541        else
10542          pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10543        if (pan_info.y < 0)
10544          pan_info.y=0;
10545        else
10546          if ((int) (pan_info.y+windows->image.height) >
10547              windows->image.ximage->height)
10548            pan_info.y=(ssize_t)
10549              (windows->image.ximage->height-windows->image.height);
10550        if ((windows->image.x != (int) pan_info.x) ||
10551            (windows->image.y != (int) pan_info.y))
10552          {
10553            /*
10554              Display image pan offset.
10555            */
10556            windows->image.x=(int) pan_info.x;
10557            windows->image.y=(int) pan_info.y;
10558            (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
10559              windows->image.width,windows->image.height,windows->image.x,
10560              windows->image.y);
10561            XInfoWidget(display,windows,text);
10562            /*
10563              Refresh Image window.
10564            */
10565            XDrawPanRectangle(display,windows);
10566            XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10567          }
10568        state&=(~UpdateConfigurationState);
10569      }
10570    /*
10571      Wait for next event.
10572    */
10573    if ((state & ExitState) == 0)
10574      XScreenEvent(display,windows,event,exception);
10575  } while ((state & ExitState) == 0);
10576  /*
10577    Restore cursor.
10578  */
10579  (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10580  (void) XFreeCursor(display,cursor);
10581  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10582}
10583
10584/*
10585%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10586%                                                                             %
10587%                                                                             %
10588%                                                                             %
10589+   X P a s t e I m a g e                                                     %
10590%                                                                             %
10591%                                                                             %
10592%                                                                             %
10593%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10594%
10595%  XPasteImage() pastes an image previously saved with XCropImage in the X
10596%  window image at a location the user chooses with the pointer.
10597%
10598%  The format of the XPasteImage method is:
10599%
10600%      MagickBooleanType XPasteImage(Display *display,
10601%        XResourceInfo *resource_info,XWindows *windows,Image *image,
10602%        ExceptionInfo *exception)
10603%
10604%  A description of each parameter follows:
10605%
10606%    o display: Specifies a connection to an X server;  returned from
10607%      XOpenDisplay.
10608%
10609%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10610%
10611%    o windows: Specifies a pointer to a XWindows structure.
10612%
10613%    o image: the image; returned from ReadImage.
10614%
10615%    o exception: return any errors or warnings in this structure.
10616%
10617*/
10618static MagickBooleanType XPasteImage(Display *display,
10619  XResourceInfo *resource_info,XWindows *windows,Image *image,
10620  ExceptionInfo *exception)
10621{
10622  static const char
10623    *PasteMenu[] =
10624    {
10625      "Operator",
10626      "Help",
10627      "Dismiss",
10628      (char *) NULL
10629    };
10630
10631  static const ModeType
10632    PasteCommands[] =
10633    {
10634      PasteOperatorsCommand,
10635      PasteHelpCommand,
10636      PasteDismissCommand
10637    };
10638
10639  static CompositeOperator
10640    compose = CopyCompositeOp;
10641
10642  char
10643    text[MaxTextExtent];
10644
10645  Cursor
10646    cursor;
10647
10648  Image
10649    *paste_image;
10650
10651  int
10652    entry,
10653    id,
10654    x,
10655    y;
10656
10657  MagickRealType
10658    scale_factor;
10659
10660  RectangleInfo
10661    highlight_info,
10662    paste_info;
10663
10664  unsigned int
10665    height,
10666    width;
10667
10668  size_t
10669    state;
10670
10671  XEvent
10672    event;
10673
10674  /*
10675    Copy image.
10676  */
10677  if (resource_info->copy_image == (Image *) NULL)
10678    return(MagickFalse);
10679  paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,exception);
10680  /*
10681    Map Command widget.
10682  */
10683  (void) CloneString(&windows->command.name,"Paste");
10684  windows->command.data=1;
10685  (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10686  (void) XMapRaised(display,windows->command.id);
10687  XClientMessage(display,windows->image.id,windows->im_protocols,
10688    windows->im_update_widget,CurrentTime);
10689  /*
10690    Track pointer until button 1 is pressed.
10691  */
10692  XSetCursorState(display,windows,MagickFalse);
10693  XQueryPosition(display,windows->image.id,&x,&y);
10694  (void) XSelectInput(display,windows->image.id,
10695    windows->image.attributes.event_mask | PointerMotionMask);
10696  paste_info.x=(ssize_t) windows->image.x+x;
10697  paste_info.y=(ssize_t) windows->image.y+y;
10698  paste_info.width=0;
10699  paste_info.height=0;
10700  cursor=XCreateFontCursor(display,XC_ul_angle);
10701  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10702  state=DefaultState;
10703  do
10704  {
10705    if (windows->info.mapped != MagickFalse)
10706      {
10707        /*
10708          Display pointer position.
10709        */
10710        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
10711          (long) paste_info.x,(long) paste_info.y);
10712        XInfoWidget(display,windows,text);
10713      }
10714    highlight_info=paste_info;
10715    highlight_info.x=paste_info.x-windows->image.x;
10716    highlight_info.y=paste_info.y-windows->image.y;
10717    XHighlightRectangle(display,windows->image.id,
10718      windows->image.highlight_context,&highlight_info);
10719    /*
10720      Wait for next event.
10721    */
10722    XScreenEvent(display,windows,&event,exception);
10723    XHighlightRectangle(display,windows->image.id,
10724      windows->image.highlight_context,&highlight_info);
10725    if (event.xany.window == windows->command.id)
10726      {
10727        /*
10728          Select a command from the Command widget.
10729        */
10730        id=XCommandWidget(display,windows,PasteMenu,&event);
10731        if (id < 0)
10732          continue;
10733        switch (PasteCommands[id])
10734        {
10735          case PasteOperatorsCommand:
10736          {
10737            char
10738              command[MaxTextExtent],
10739              **operators;
10740
10741            /*
10742              Select a command from the pop-up menu.
10743            */
10744            operators=GetCommandOptions(MagickComposeOptions);
10745            if (operators == (char **) NULL)
10746              break;
10747            entry=XMenuWidget(display,windows,PasteMenu[id],
10748              (const char **) operators,command);
10749            if (entry >= 0)
10750              compose=(CompositeOperator) ParseCommandOption(
10751                MagickComposeOptions,MagickFalse,operators[entry]);
10752            operators=DestroyStringList(operators);
10753            break;
10754          }
10755          case PasteHelpCommand:
10756          {
10757            XTextViewWidget(display,resource_info,windows,MagickFalse,
10758              "Help Viewer - Image Composite",ImagePasteHelp);
10759            break;
10760          }
10761          case PasteDismissCommand:
10762          {
10763            /*
10764              Prematurely exit.
10765            */
10766            state|=EscapeState;
10767            state|=ExitState;
10768            break;
10769          }
10770          default:
10771            break;
10772        }
10773        continue;
10774      }
10775    switch (event.type)
10776    {
10777      case ButtonPress:
10778      {
10779        if (image->debug != MagickFalse)
10780          (void) LogMagickEvent(X11Event,GetMagickModule(),
10781            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10782            event.xbutton.button,event.xbutton.x,event.xbutton.y);
10783        if (event.xbutton.button != Button1)
10784          break;
10785        if (event.xbutton.window != windows->image.id)
10786          break;
10787        /*
10788          Paste rectangle is relative to image configuration.
10789        */
10790        width=(unsigned int) image->columns;
10791        height=(unsigned int) image->rows;
10792        x=0;
10793        y=0;
10794        if (windows->image.crop_geometry != (char *) NULL)
10795          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10796            &width,&height);
10797        scale_factor=(MagickRealType) windows->image.ximage->width/width;
10798        paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10799        scale_factor=(MagickRealType) windows->image.ximage->height/height;
10800        paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10801        (void) XCheckDefineCursor(display,windows->image.id,cursor);
10802        paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10803        paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10804        break;
10805      }
10806      case ButtonRelease:
10807      {
10808        if (image->debug != MagickFalse)
10809          (void) LogMagickEvent(X11Event,GetMagickModule(),
10810            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10811            event.xbutton.button,event.xbutton.x,event.xbutton.y);
10812        if (event.xbutton.button != Button1)
10813          break;
10814        if (event.xbutton.window != windows->image.id)
10815          break;
10816        if ((paste_info.width != 0) && (paste_info.height != 0))
10817          {
10818            /*
10819              User has selected the location of the paste image.
10820            */
10821            paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10822            paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10823            state|=ExitState;
10824          }
10825        break;
10826      }
10827      case Expose:
10828        break;
10829      case KeyPress:
10830      {
10831        char
10832          command[MaxTextExtent];
10833
10834        KeySym
10835          key_symbol;
10836
10837        int
10838          length;
10839
10840        if (event.xkey.window != windows->image.id)
10841          break;
10842        /*
10843          Respond to a user key press.
10844        */
10845        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10846          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10847        *(command+length)='\0';
10848        if (image->debug != MagickFalse)
10849          (void) LogMagickEvent(X11Event,GetMagickModule(),
10850            "Key press: 0x%lx (%s)",(long) key_symbol,command);
10851        switch ((int) key_symbol)
10852        {
10853          case XK_Escape:
10854          case XK_F20:
10855          {
10856            /*
10857              Prematurely exit.
10858            */
10859            paste_image=DestroyImage(paste_image);
10860            state|=EscapeState;
10861            state|=ExitState;
10862            break;
10863          }
10864          case XK_F1:
10865          case XK_Help:
10866          {
10867            (void) XSetFunction(display,windows->image.highlight_context,
10868              GXcopy);
10869            XTextViewWidget(display,resource_info,windows,MagickFalse,
10870              "Help Viewer - Image Composite",ImagePasteHelp);
10871            (void) XSetFunction(display,windows->image.highlight_context,
10872              GXinvert);
10873            break;
10874          }
10875          default:
10876          {
10877            (void) XBell(display,0);
10878            break;
10879          }
10880        }
10881        break;
10882      }
10883      case MotionNotify:
10884      {
10885        /*
10886          Map and unmap Info widget as text cursor crosses its boundaries.
10887        */
10888        x=event.xmotion.x;
10889        y=event.xmotion.y;
10890        if (windows->info.mapped != MagickFalse)
10891          {
10892            if ((x < (int) (windows->info.x+windows->info.width)) &&
10893                (y < (int) (windows->info.y+windows->info.height)))
10894              (void) XWithdrawWindow(display,windows->info.id,
10895                windows->info.screen);
10896          }
10897        else
10898          if ((x > (int) (windows->info.x+windows->info.width)) ||
10899              (y > (int) (windows->info.y+windows->info.height)))
10900            (void) XMapWindow(display,windows->info.id);
10901        paste_info.x=(ssize_t) windows->image.x+x;
10902        paste_info.y=(ssize_t) windows->image.y+y;
10903        break;
10904      }
10905      default:
10906      {
10907        if (image->debug != MagickFalse)
10908          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10909            event.type);
10910        break;
10911      }
10912    }
10913  } while ((state & ExitState) == 0);
10914  (void) XSelectInput(display,windows->image.id,
10915    windows->image.attributes.event_mask);
10916  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10917  XSetCursorState(display,windows,MagickFalse);
10918  (void) XFreeCursor(display,cursor);
10919  if ((state & EscapeState) != 0)
10920    return(MagickTrue);
10921  /*
10922    Image pasting is relative to image configuration.
10923  */
10924  XSetCursorState(display,windows,MagickTrue);
10925  XCheckRefreshWindows(display,windows);
10926  width=(unsigned int) image->columns;
10927  height=(unsigned int) image->rows;
10928  x=0;
10929  y=0;
10930  if (windows->image.crop_geometry != (char *) NULL)
10931    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10932  scale_factor=(MagickRealType) width/windows->image.ximage->width;
10933  paste_info.x+=x;
10934  paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10935  paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10936  scale_factor=(MagickRealType) height/windows->image.ximage->height;
10937  paste_info.y+=y;
10938  paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10939  paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10940  /*
10941    Paste image with X Image window.
10942  */
10943  (void) CompositeImage(image,compose,paste_image,paste_info.x,paste_info.y,
10944    exception);
10945  paste_image=DestroyImage(paste_image);
10946  XSetCursorState(display,windows,MagickFalse);
10947  /*
10948    Update image colormap.
10949  */
10950  XConfigureImageColormap(display,resource_info,windows,image,exception);
10951  (void) XConfigureImage(display,resource_info,windows,image,exception);
10952  return(MagickTrue);
10953}
10954
10955/*
10956%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10957%                                                                             %
10958%                                                                             %
10959%                                                                             %
10960+   X P r i n t I m a g e                                                     %
10961%                                                                             %
10962%                                                                             %
10963%                                                                             %
10964%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10965%
10966%  XPrintImage() prints an image to a Postscript printer.
10967%
10968%  The format of the XPrintImage method is:
10969%
10970%      MagickBooleanType XPrintImage(Display *display,
10971%        XResourceInfo *resource_info,XWindows *windows,Image *image,
10972%        ExceptionInfo *exception)
10973%
10974%  A description of each parameter follows:
10975%
10976%    o display: Specifies a connection to an X server; returned from
10977%      XOpenDisplay.
10978%
10979%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10980%
10981%    o windows: Specifies a pointer to a XWindows structure.
10982%
10983%    o image: the image.
10984%
10985%    o exception: return any errors or warnings in this structure.
10986%
10987*/
10988static MagickBooleanType XPrintImage(Display *display,
10989  XResourceInfo *resource_info,XWindows *windows,Image *image,
10990  ExceptionInfo *exception)
10991{
10992  char
10993    filename[MaxTextExtent],
10994    geometry[MaxTextExtent];
10995
10996  Image
10997    *print_image;
10998
10999  ImageInfo
11000    *image_info;
11001
11002  MagickStatusType
11003    status;
11004
11005  /*
11006    Request Postscript page geometry from user.
11007  */
11008  image_info=CloneImageInfo(resource_info->image_info);
11009  (void) FormatLocaleString(geometry,MaxTextExtent,"Letter");
11010  if (image_info->page != (char *) NULL)
11011    (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
11012  XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
11013    "Select Postscript Page Geometry:",geometry);
11014  if (*geometry == '\0')
11015    return(MagickTrue);
11016  image_info->page=GetPageGeometry(geometry);
11017  /*
11018    Apply image transforms.
11019  */
11020  XSetCursorState(display,windows,MagickTrue);
11021  XCheckRefreshWindows(display,windows);
11022  print_image=CloneImage(image,0,0,MagickTrue,exception);
11023  if (print_image == (Image *) NULL)
11024    return(MagickFalse);
11025  (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
11026    windows->image.ximage->width,windows->image.ximage->height);
11027  (void) TransformImage(&print_image,windows->image.crop_geometry,geometry,
11028    exception);
11029  /*
11030    Print image.
11031  */
11032  (void) AcquireUniqueFilename(filename);
11033  (void) FormatLocaleString(print_image->filename,MaxTextExtent,"print:%s",
11034    filename);
11035  status=WriteImage(image_info,print_image,exception);
11036  (void) RelinquishUniqueFileResource(filename);
11037  print_image=DestroyImage(print_image);
11038  image_info=DestroyImageInfo(image_info);
11039  XSetCursorState(display,windows,MagickFalse);
11040  return(status != 0 ? MagickTrue : MagickFalse);
11041}
11042
11043/*
11044%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11045%                                                                             %
11046%                                                                             %
11047%                                                                             %
11048+   X R O I I m a g e                                                         %
11049%                                                                             %
11050%                                                                             %
11051%                                                                             %
11052%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11053%
11054%  XROIImage() applies an image processing technique to a region of interest.
11055%
11056%  The format of the XROIImage method is:
11057%
11058%      MagickBooleanType XROIImage(Display *display,
11059%        XResourceInfo *resource_info,XWindows *windows,Image **image,
11060%        ExceptionInfo *exception)
11061%
11062%  A description of each parameter follows:
11063%
11064%    o display: Specifies a connection to an X server; returned from
11065%      XOpenDisplay.
11066%
11067%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11068%
11069%    o windows: Specifies a pointer to a XWindows structure.
11070%
11071%    o image: the image; returned from ReadImage.
11072%
11073%    o exception: return any errors or warnings in this structure.
11074%
11075*/
11076static MagickBooleanType XROIImage(Display *display,
11077  XResourceInfo *resource_info,XWindows *windows,Image **image,
11078  ExceptionInfo *exception)
11079{
11080#define ApplyMenus  7
11081
11082  static const char
11083    *ROIMenu[] =
11084    {
11085      "Help",
11086      "Dismiss",
11087      (char *) NULL
11088    },
11089    *ApplyMenu[] =
11090    {
11091      "File",
11092      "Edit",
11093      "Transform",
11094      "Enhance",
11095      "Effects",
11096      "F/X",
11097      "Miscellany",
11098      "Help",
11099      "Dismiss",
11100      (char *) NULL
11101    },
11102    *FileMenu[] =
11103    {
11104      "Save...",
11105      "Print...",
11106      (char *) NULL
11107    },
11108    *EditMenu[] =
11109    {
11110      "Undo",
11111      "Redo",
11112      (char *) NULL
11113    },
11114    *TransformMenu[] =
11115    {
11116      "Flop",
11117      "Flip",
11118      "Rotate Right",
11119      "Rotate Left",
11120      (char *) NULL
11121    },
11122    *EnhanceMenu[] =
11123    {
11124      "Hue...",
11125      "Saturation...",
11126      "Brightness...",
11127      "Gamma...",
11128      "Spiff",
11129      "Dull",
11130      "Contrast Stretch...",
11131      "Sigmoidal Contrast...",
11132      "Normalize",
11133      "Equalize",
11134      "Negate",
11135      "Grayscale",
11136      "Map...",
11137      "Quantize...",
11138      (char *) NULL
11139    },
11140    *EffectsMenu[] =
11141    {
11142      "Despeckle",
11143      "Emboss",
11144      "Reduce Noise",
11145      "Add Noise",
11146      "Sharpen...",
11147      "Blur...",
11148      "Threshold...",
11149      "Edge Detect...",
11150      "Spread...",
11151      "Shade...",
11152      "Raise...",
11153      "Segment...",
11154      (char *) NULL
11155    },
11156    *FXMenu[] =
11157    {
11158      "Solarize...",
11159      "Sepia Tone...",
11160      "Swirl...",
11161      "Implode...",
11162      "Vignette...",
11163      "Wave...",
11164      "Oil Paint...",
11165      "Charcoal Draw...",
11166      (char *) NULL
11167    },
11168    *MiscellanyMenu[] =
11169    {
11170      "Image Info",
11171      "Zoom Image",
11172      "Show Preview...",
11173      "Show Histogram",
11174      "Show Matte",
11175      (char *) NULL
11176    };
11177
11178  static const char
11179    **Menus[ApplyMenus] =
11180    {
11181      FileMenu,
11182      EditMenu,
11183      TransformMenu,
11184      EnhanceMenu,
11185      EffectsMenu,
11186      FXMenu,
11187      MiscellanyMenu
11188    };
11189
11190  static const CommandType
11191    ApplyCommands[] =
11192    {
11193      NullCommand,
11194      NullCommand,
11195      NullCommand,
11196      NullCommand,
11197      NullCommand,
11198      NullCommand,
11199      NullCommand,
11200      HelpCommand,
11201      QuitCommand
11202    },
11203    FileCommands[] =
11204    {
11205      SaveCommand,
11206      PrintCommand
11207    },
11208    EditCommands[] =
11209    {
11210      UndoCommand,
11211      RedoCommand
11212    },
11213    TransformCommands[] =
11214    {
11215      FlopCommand,
11216      FlipCommand,
11217      RotateRightCommand,
11218      RotateLeftCommand
11219    },
11220    EnhanceCommands[] =
11221    {
11222      HueCommand,
11223      SaturationCommand,
11224      BrightnessCommand,
11225      GammaCommand,
11226      SpiffCommand,
11227      DullCommand,
11228      ContrastStretchCommand,
11229      SigmoidalContrastCommand,
11230      NormalizeCommand,
11231      EqualizeCommand,
11232      NegateCommand,
11233      GrayscaleCommand,
11234      MapCommand,
11235      QuantizeCommand
11236    },
11237    EffectsCommands[] =
11238    {
11239      DespeckleCommand,
11240      EmbossCommand,
11241      ReduceNoiseCommand,
11242      AddNoiseCommand,
11243      SharpenCommand,
11244      BlurCommand,
11245      EdgeDetectCommand,
11246      SpreadCommand,
11247      ShadeCommand,
11248      RaiseCommand,
11249      SegmentCommand
11250    },
11251    FXCommands[] =
11252    {
11253      SolarizeCommand,
11254      SepiaToneCommand,
11255      SwirlCommand,
11256      ImplodeCommand,
11257      VignetteCommand,
11258      WaveCommand,
11259      OilPaintCommand,
11260      CharcoalDrawCommand
11261    },
11262    MiscellanyCommands[] =
11263    {
11264      InfoCommand,
11265      ZoomCommand,
11266      ShowPreviewCommand,
11267      ShowHistogramCommand,
11268      ShowMatteCommand
11269    },
11270    ROICommands[] =
11271    {
11272      ROIHelpCommand,
11273      ROIDismissCommand
11274    };
11275
11276  static const CommandType
11277    *Commands[ApplyMenus] =
11278    {
11279      FileCommands,
11280      EditCommands,
11281      TransformCommands,
11282      EnhanceCommands,
11283      EffectsCommands,
11284      FXCommands,
11285      MiscellanyCommands
11286    };
11287
11288  char
11289    command[MaxTextExtent],
11290    text[MaxTextExtent];
11291
11292  CommandType
11293    command_type;
11294
11295  Cursor
11296    cursor;
11297
11298  Image
11299    *roi_image;
11300
11301  int
11302    entry,
11303    id,
11304    x,
11305    y;
11306
11307  MagickRealType
11308    scale_factor;
11309
11310  MagickProgressMonitor
11311    progress_monitor;
11312
11313  RectangleInfo
11314    crop_info,
11315    highlight_info,
11316    roi_info;
11317
11318  unsigned int
11319    height,
11320    width;
11321
11322  size_t
11323    state;
11324
11325  XEvent
11326    event;
11327
11328  /*
11329    Map Command widget.
11330  */
11331  (void) CloneString(&windows->command.name,"ROI");
11332  windows->command.data=0;
11333  (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11334  (void) XMapRaised(display,windows->command.id);
11335  XClientMessage(display,windows->image.id,windows->im_protocols,
11336    windows->im_update_widget,CurrentTime);
11337  /*
11338    Track pointer until button 1 is pressed.
11339  */
11340  XQueryPosition(display,windows->image.id,&x,&y);
11341  (void) XSelectInput(display,windows->image.id,
11342    windows->image.attributes.event_mask | PointerMotionMask);
11343  roi_info.x=(ssize_t) windows->image.x+x;
11344  roi_info.y=(ssize_t) windows->image.y+y;
11345  roi_info.width=0;
11346  roi_info.height=0;
11347  cursor=XCreateFontCursor(display,XC_fleur);
11348  state=DefaultState;
11349  do
11350  {
11351    if (windows->info.mapped != MagickFalse)
11352      {
11353        /*
11354          Display pointer position.
11355        */
11356        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
11357          (long) roi_info.x,(long) roi_info.y);
11358        XInfoWidget(display,windows,text);
11359      }
11360    /*
11361      Wait for next event.
11362    */
11363    XScreenEvent(display,windows,&event,exception);
11364    if (event.xany.window == windows->command.id)
11365      {
11366        /*
11367          Select a command from the Command widget.
11368        */
11369        id=XCommandWidget(display,windows,ROIMenu,&event);
11370        if (id < 0)
11371          continue;
11372        switch (ROICommands[id])
11373        {
11374          case ROIHelpCommand:
11375          {
11376            XTextViewWidget(display,resource_info,windows,MagickFalse,
11377              "Help Viewer - Region of Interest",ImageROIHelp);
11378            break;
11379          }
11380          case ROIDismissCommand:
11381          {
11382            /*
11383              Prematurely exit.
11384            */
11385            state|=EscapeState;
11386            state|=ExitState;
11387            break;
11388          }
11389          default:
11390            break;
11391        }
11392        continue;
11393      }
11394    switch (event.type)
11395    {
11396      case ButtonPress:
11397      {
11398        if (event.xbutton.button != Button1)
11399          break;
11400        if (event.xbutton.window != windows->image.id)
11401          break;
11402        /*
11403          Note first corner of region of interest rectangle-- exit loop.
11404        */
11405        (void) XCheckDefineCursor(display,windows->image.id,cursor);
11406        roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11407        roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11408        state|=ExitState;
11409        break;
11410      }
11411      case ButtonRelease:
11412        break;
11413      case Expose:
11414        break;
11415      case KeyPress:
11416      {
11417        KeySym
11418          key_symbol;
11419
11420        if (event.xkey.window != windows->image.id)
11421          break;
11422        /*
11423          Respond to a user key press.
11424        */
11425        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11426          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11427        switch ((int) key_symbol)
11428        {
11429          case XK_Escape:
11430          case XK_F20:
11431          {
11432            /*
11433              Prematurely exit.
11434            */
11435            state|=EscapeState;
11436            state|=ExitState;
11437            break;
11438          }
11439          case XK_F1:
11440          case XK_Help:
11441          {
11442            XTextViewWidget(display,resource_info,windows,MagickFalse,
11443              "Help Viewer - Region of Interest",ImageROIHelp);
11444            break;
11445          }
11446          default:
11447          {
11448            (void) XBell(display,0);
11449            break;
11450          }
11451        }
11452        break;
11453      }
11454      case MotionNotify:
11455      {
11456        /*
11457          Map and unmap Info widget as text cursor crosses its boundaries.
11458        */
11459        x=event.xmotion.x;
11460        y=event.xmotion.y;
11461        if (windows->info.mapped != MagickFalse)
11462          {
11463            if ((x < (int) (windows->info.x+windows->info.width)) &&
11464                (y < (int) (windows->info.y+windows->info.height)))
11465              (void) XWithdrawWindow(display,windows->info.id,
11466                windows->info.screen);
11467          }
11468        else
11469          if ((x > (int) (windows->info.x+windows->info.width)) ||
11470              (y > (int) (windows->info.y+windows->info.height)))
11471            (void) XMapWindow(display,windows->info.id);
11472        roi_info.x=(ssize_t) windows->image.x+x;
11473        roi_info.y=(ssize_t) windows->image.y+y;
11474        break;
11475      }
11476      default:
11477        break;
11478    }
11479  } while ((state & ExitState) == 0);
11480  (void) XSelectInput(display,windows->image.id,
11481    windows->image.attributes.event_mask);
11482  if ((state & EscapeState) != 0)
11483    {
11484      /*
11485        User want to exit without region of interest.
11486      */
11487      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11488      (void) XFreeCursor(display,cursor);
11489      return(MagickTrue);
11490    }
11491  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11492  do
11493  {
11494    /*
11495      Size rectangle as pointer moves until the mouse button is released.
11496    */
11497    x=(int) roi_info.x;
11498    y=(int) roi_info.y;
11499    roi_info.width=0;
11500    roi_info.height=0;
11501    state=DefaultState;
11502    do
11503    {
11504      highlight_info=roi_info;
11505      highlight_info.x=roi_info.x-windows->image.x;
11506      highlight_info.y=roi_info.y-windows->image.y;
11507      if ((highlight_info.width > 3) && (highlight_info.height > 3))
11508        {
11509          /*
11510            Display info and draw region of interest rectangle.
11511          */
11512          if (windows->info.mapped == MagickFalse)
11513            (void) XMapWindow(display,windows->info.id);
11514          (void) FormatLocaleString(text,MaxTextExtent,
11515            " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11516            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11517          XInfoWidget(display,windows,text);
11518          XHighlightRectangle(display,windows->image.id,
11519            windows->image.highlight_context,&highlight_info);
11520        }
11521      else
11522        if (windows->info.mapped != MagickFalse)
11523          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11524      /*
11525        Wait for next event.
11526      */
11527      XScreenEvent(display,windows,&event,exception);
11528      if ((highlight_info.width > 3) && (highlight_info.height > 3))
11529        XHighlightRectangle(display,windows->image.id,
11530          windows->image.highlight_context,&highlight_info);
11531      switch (event.type)
11532      {
11533        case ButtonPress:
11534        {
11535          roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11536          roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11537          break;
11538        }
11539        case ButtonRelease:
11540        {
11541          /*
11542            User has committed to region of interest rectangle.
11543          */
11544          roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11545          roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11546          XSetCursorState(display,windows,MagickFalse);
11547          state|=ExitState;
11548          if (LocaleCompare(windows->command.name,"Apply") == 0)
11549            break;
11550          (void) CloneString(&windows->command.name,"Apply");
11551          windows->command.data=ApplyMenus;
11552          (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11553          break;
11554        }
11555        case Expose:
11556          break;
11557        case MotionNotify:
11558        {
11559          roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11560          roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11561        }
11562        default:
11563          break;
11564      }
11565      if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11566          ((state & ExitState) != 0))
11567        {
11568          /*
11569            Check boundary conditions.
11570          */
11571          if (roi_info.x < 0)
11572            roi_info.x=0;
11573          else
11574            if (roi_info.x > (ssize_t) windows->image.ximage->width)
11575              roi_info.x=(ssize_t) windows->image.ximage->width;
11576          if ((int) roi_info.x < x)
11577            roi_info.width=(unsigned int) (x-roi_info.x);
11578          else
11579            {
11580              roi_info.width=(unsigned int) (roi_info.x-x);
11581              roi_info.x=(ssize_t) x;
11582            }
11583          if (roi_info.y < 0)
11584            roi_info.y=0;
11585          else
11586            if (roi_info.y > (ssize_t) windows->image.ximage->height)
11587              roi_info.y=(ssize_t) windows->image.ximage->height;
11588          if ((int) roi_info.y < y)
11589            roi_info.height=(unsigned int) (y-roi_info.y);
11590          else
11591            {
11592              roi_info.height=(unsigned int) (roi_info.y-y);
11593              roi_info.y=(ssize_t) y;
11594            }
11595        }
11596    } while ((state & ExitState) == 0);
11597    /*
11598      Wait for user to grab a corner of the rectangle or press return.
11599    */
11600    state=DefaultState;
11601    command_type=NullCommand;
11602    (void) XMapWindow(display,windows->info.id);
11603    do
11604    {
11605      if (windows->info.mapped != MagickFalse)
11606        {
11607          /*
11608            Display pointer position.
11609          */
11610          (void) FormatLocaleString(text,MaxTextExtent,
11611            " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11612            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11613          XInfoWidget(display,windows,text);
11614        }
11615      highlight_info=roi_info;
11616      highlight_info.x=roi_info.x-windows->image.x;
11617      highlight_info.y=roi_info.y-windows->image.y;
11618      if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11619        {
11620          state|=EscapeState;
11621          state|=ExitState;
11622          break;
11623        }
11624      if ((state & UpdateRegionState) != 0)
11625        {
11626          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11627          switch (command_type)
11628          {
11629            case UndoCommand:
11630            case RedoCommand:
11631            {
11632              (void) XMagickCommand(display,resource_info,windows,command_type,
11633                image,exception);
11634              break;
11635            }
11636            default:
11637            {
11638              /*
11639                Region of interest is relative to image configuration.
11640              */
11641              progress_monitor=SetImageProgressMonitor(*image,
11642                (MagickProgressMonitor) NULL,(*image)->client_data);
11643              crop_info=roi_info;
11644              width=(unsigned int) (*image)->columns;
11645              height=(unsigned int) (*image)->rows;
11646              x=0;
11647              y=0;
11648              if (windows->image.crop_geometry != (char *) NULL)
11649                (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11650                  &width,&height);
11651              scale_factor=(MagickRealType) width/windows->image.ximage->width;
11652              crop_info.x+=x;
11653              crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11654              crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11655              scale_factor=(MagickRealType)
11656                height/windows->image.ximage->height;
11657              crop_info.y+=y;
11658              crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11659              crop_info.height=(unsigned int)
11660                (scale_factor*crop_info.height+0.5);
11661              roi_image=CropImage(*image,&crop_info,exception);
11662              (void) SetImageProgressMonitor(*image,progress_monitor,
11663                (*image)->client_data);
11664              if (roi_image == (Image *) NULL)
11665                continue;
11666              /*
11667                Apply image processing technique to the region of interest.
11668              */
11669              windows->image.orphan=MagickTrue;
11670              (void) XMagickCommand(display,resource_info,windows,command_type,
11671                &roi_image,exception);
11672              progress_monitor=SetImageProgressMonitor(*image,
11673                (MagickProgressMonitor) NULL,(*image)->client_data);
11674              (void) XMagickCommand(display,resource_info,windows,
11675                SaveToUndoBufferCommand,image,exception);
11676              windows->image.orphan=MagickFalse;
11677              (void) CompositeImage(*image,CopyCompositeOp,roi_image,
11678                crop_info.x,crop_info.y,exception);
11679              roi_image=DestroyImage(roi_image);
11680              (void) SetImageProgressMonitor(*image,progress_monitor,
11681                (*image)->client_data);
11682              break;
11683            }
11684          }
11685          if (command_type != InfoCommand)
11686            {
11687              XConfigureImageColormap(display,resource_info,windows,*image,
11688                exception);
11689              (void) XConfigureImage(display,resource_info,windows,*image,
11690                exception);
11691            }
11692          XCheckRefreshWindows(display,windows);
11693          XInfoWidget(display,windows,text);
11694          (void) XSetFunction(display,windows->image.highlight_context,
11695            GXinvert);
11696          state&=(~UpdateRegionState);
11697        }
11698      XHighlightRectangle(display,windows->image.id,
11699        windows->image.highlight_context,&highlight_info);
11700      XScreenEvent(display,windows,&event,exception);
11701      if (event.xany.window == windows->command.id)
11702        {
11703          /*
11704            Select a command from the Command widget.
11705          */
11706          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11707          command_type=NullCommand;
11708          id=XCommandWidget(display,windows,ApplyMenu,&event);
11709          if (id >= 0)
11710            {
11711              (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11712              command_type=ApplyCommands[id];
11713              if (id < ApplyMenus)
11714                {
11715                  /*
11716                    Select a command from a pop-up menu.
11717                  */
11718                  entry=XMenuWidget(display,windows,ApplyMenu[id],
11719                    (const char **) Menus[id],command);
11720                  if (entry >= 0)
11721                    {
11722                      (void) CopyMagickString(command,Menus[id][entry],
11723                        MaxTextExtent);
11724                      command_type=Commands[id][entry];
11725                    }
11726                }
11727            }
11728          (void) XSetFunction(display,windows->image.highlight_context,
11729            GXinvert);
11730          XHighlightRectangle(display,windows->image.id,
11731            windows->image.highlight_context,&highlight_info);
11732          if (command_type == HelpCommand)
11733            {
11734              (void) XSetFunction(display,windows->image.highlight_context,
11735                GXcopy);
11736              XTextViewWidget(display,resource_info,windows,MagickFalse,
11737                "Help Viewer - Region of Interest",ImageROIHelp);
11738              (void) XSetFunction(display,windows->image.highlight_context,
11739                GXinvert);
11740              continue;
11741            }
11742          if (command_type == QuitCommand)
11743            {
11744              /*
11745                exit.
11746              */
11747              state|=EscapeState;
11748              state|=ExitState;
11749              continue;
11750            }
11751          if (command_type != NullCommand)
11752            state|=UpdateRegionState;
11753          continue;
11754        }
11755      XHighlightRectangle(display,windows->image.id,
11756        windows->image.highlight_context,&highlight_info);
11757      switch (event.type)
11758      {
11759        case ButtonPress:
11760        {
11761          x=windows->image.x;
11762          y=windows->image.y;
11763          if (event.xbutton.button != Button1)
11764            break;
11765          if (event.xbutton.window != windows->image.id)
11766            break;
11767          x=windows->image.x+event.xbutton.x;
11768          y=windows->image.y+event.xbutton.y;
11769          if ((x < (int) (roi_info.x+RoiDelta)) &&
11770              (x > (int) (roi_info.x-RoiDelta)) &&
11771              (y < (int) (roi_info.y+RoiDelta)) &&
11772              (y > (int) (roi_info.y-RoiDelta)))
11773            {
11774              roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
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+RoiDelta)) &&
11780              (x > (int) (roi_info.x-RoiDelta)) &&
11781              (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11782              (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11783            {
11784              roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11785              state|=UpdateConfigurationState;
11786              break;
11787            }
11788          if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11789              (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11790              (y < (int) (roi_info.y+RoiDelta)) &&
11791              (y > (int) (roi_info.y-RoiDelta)))
11792            {
11793              roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11794              state|=UpdateConfigurationState;
11795              break;
11796            }
11797          if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11798              (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11799              (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11800              (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11801            {
11802              state|=UpdateConfigurationState;
11803              break;
11804            }
11805        }
11806        case ButtonRelease:
11807        {
11808          if (event.xbutton.window == windows->pan.id)
11809            if ((highlight_info.x != crop_info.x-windows->image.x) ||
11810                (highlight_info.y != crop_info.y-windows->image.y))
11811              XHighlightRectangle(display,windows->image.id,
11812                windows->image.highlight_context,&highlight_info);
11813          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11814            event.xbutton.time);
11815          break;
11816        }
11817        case Expose:
11818        {
11819          if (event.xexpose.window == windows->image.id)
11820            if (event.xexpose.count == 0)
11821              {
11822                event.xexpose.x=(int) highlight_info.x;
11823                event.xexpose.y=(int) highlight_info.y;
11824                event.xexpose.width=(int) highlight_info.width;
11825                event.xexpose.height=(int) highlight_info.height;
11826                XRefreshWindow(display,&windows->image,&event);
11827              }
11828          if (event.xexpose.window == windows->info.id)
11829            if (event.xexpose.count == 0)
11830              XInfoWidget(display,windows,text);
11831          break;
11832        }
11833        case KeyPress:
11834        {
11835          KeySym
11836            key_symbol;
11837
11838          if (event.xkey.window != windows->image.id)
11839            break;
11840          /*
11841            Respond to a user key press.
11842          */
11843          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11844            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11845          switch ((int) key_symbol)
11846          {
11847            case XK_Shift_L:
11848            case XK_Shift_R:
11849              break;
11850            case XK_Escape:
11851            case XK_F20:
11852              state|=EscapeState;
11853            case XK_Return:
11854            {
11855              state|=ExitState;
11856              break;
11857            }
11858            case XK_Home:
11859            case XK_KP_Home:
11860            {
11861              roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11862              roi_info.y=(ssize_t) (windows->image.height/2L-
11863                roi_info.height/2L);
11864              break;
11865            }
11866            case XK_Left:
11867            case XK_KP_Left:
11868            {
11869              roi_info.x--;
11870              break;
11871            }
11872            case XK_Up:
11873            case XK_KP_Up:
11874            case XK_Next:
11875            {
11876              roi_info.y--;
11877              break;
11878            }
11879            case XK_Right:
11880            case XK_KP_Right:
11881            {
11882              roi_info.x++;
11883              break;
11884            }
11885            case XK_Prior:
11886            case XK_Down:
11887            case XK_KP_Down:
11888            {
11889              roi_info.y++;
11890              break;
11891            }
11892            case XK_F1:
11893            case XK_Help:
11894            {
11895              (void) XSetFunction(display,windows->image.highlight_context,
11896                GXcopy);
11897              XTextViewWidget(display,resource_info,windows,MagickFalse,
11898                "Help Viewer - Region of Interest",ImageROIHelp);
11899              (void) XSetFunction(display,windows->image.highlight_context,
11900                GXinvert);
11901              break;
11902            }
11903            default:
11904            {
11905              command_type=XImageWindowCommand(display,resource_info,windows,
11906                event.xkey.state,key_symbol,image,exception);
11907              if (command_type != NullCommand)
11908                state|=UpdateRegionState;
11909              break;
11910            }
11911          }
11912          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11913            event.xkey.time);
11914          break;
11915        }
11916        case KeyRelease:
11917          break;
11918        case MotionNotify:
11919        {
11920          if (event.xbutton.window != windows->image.id)
11921            break;
11922          /*
11923            Map and unmap Info widget as text cursor crosses its boundaries.
11924          */
11925          x=event.xmotion.x;
11926          y=event.xmotion.y;
11927          if (windows->info.mapped != MagickFalse)
11928            {
11929              if ((x < (int) (windows->info.x+windows->info.width)) &&
11930                  (y < (int) (windows->info.y+windows->info.height)))
11931                (void) XWithdrawWindow(display,windows->info.id,
11932                  windows->info.screen);
11933            }
11934          else
11935            if ((x > (int) (windows->info.x+windows->info.width)) ||
11936                (y > (int) (windows->info.y+windows->info.height)))
11937              (void) XMapWindow(display,windows->info.id);
11938          roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11939          roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11940          break;
11941        }
11942        case SelectionRequest:
11943        {
11944          XSelectionEvent
11945            notify;
11946
11947          XSelectionRequestEvent
11948            *request;
11949
11950          /*
11951            Set primary selection.
11952          */
11953          (void) FormatLocaleString(text,MaxTextExtent,
11954            "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11955            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11956          request=(&(event.xselectionrequest));
11957          (void) XChangeProperty(request->display,request->requestor,
11958            request->property,request->target,8,PropModeReplace,
11959            (unsigned char *) text,(int) strlen(text));
11960          notify.type=SelectionNotify;
11961          notify.display=request->display;
11962          notify.requestor=request->requestor;
11963          notify.selection=request->selection;
11964          notify.target=request->target;
11965          notify.time=request->time;
11966          if (request->property == None)
11967            notify.property=request->target;
11968          else
11969            notify.property=request->property;
11970          (void) XSendEvent(request->display,request->requestor,False,0,
11971            (XEvent *) &notify);
11972        }
11973        default:
11974          break;
11975      }
11976      if ((state & UpdateConfigurationState) != 0)
11977        {
11978          (void) XPutBackEvent(display,&event);
11979          (void) XCheckDefineCursor(display,windows->image.id,cursor);
11980          break;
11981        }
11982    } while ((state & ExitState) == 0);
11983  } while ((state & ExitState) == 0);
11984  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11985  XSetCursorState(display,windows,MagickFalse);
11986  if ((state & EscapeState) != 0)
11987    return(MagickTrue);
11988  return(MagickTrue);
11989}
11990
11991/*
11992%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11993%                                                                             %
11994%                                                                             %
11995%                                                                             %
11996+   X R o t a t e I m a g e                                                   %
11997%                                                                             %
11998%                                                                             %
11999%                                                                             %
12000%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12001%
12002%  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
12003%  rotation angle is computed from the slope of a line drawn by the user.
12004%
12005%  The format of the XRotateImage method is:
12006%
12007%      MagickBooleanType XRotateImage(Display *display,
12008%        XResourceInfo *resource_info,XWindows *windows,double degrees,
12009%        Image **image,ExceptionInfo *exception)
12010%
12011%  A description of each parameter follows:
12012%
12013%    o display: Specifies a connection to an X server; returned from
12014%      XOpenDisplay.
12015%
12016%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12017%
12018%    o windows: Specifies a pointer to a XWindows structure.
12019%
12020%    o degrees: Specifies the number of degrees to rotate the image.
12021%
12022%    o image: the image.
12023%
12024%    o exception: return any errors or warnings in this structure.
12025%
12026*/
12027static MagickBooleanType XRotateImage(Display *display,
12028  XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
12029  ExceptionInfo *exception)
12030{
12031  static const char
12032    *RotateMenu[] =
12033    {
12034      "Pixel Color",
12035      "Direction",
12036      "Help",
12037      "Dismiss",
12038      (char *) NULL
12039    };
12040
12041  static ModeType
12042    direction = HorizontalRotateCommand;
12043
12044  static const ModeType
12045    DirectionCommands[] =
12046    {
12047      HorizontalRotateCommand,
12048      VerticalRotateCommand
12049    },
12050    RotateCommands[] =
12051    {
12052      RotateColorCommand,
12053      RotateDirectionCommand,
12054      RotateHelpCommand,
12055      RotateDismissCommand
12056    };
12057
12058  static unsigned int
12059    pen_id = 0;
12060
12061  char
12062    command[MaxTextExtent],
12063    text[MaxTextExtent];
12064
12065  Image
12066    *rotate_image;
12067
12068  int
12069    id,
12070    x,
12071    y;
12072
12073  MagickRealType
12074    normalized_degrees;
12075
12076  register int
12077    i;
12078
12079  unsigned int
12080    height,
12081    rotations,
12082    width;
12083
12084  if (degrees == 0.0)
12085    {
12086      unsigned int
12087        distance;
12088
12089      size_t
12090        state;
12091
12092      XEvent
12093        event;
12094
12095      XSegment
12096        rotate_info;
12097
12098      /*
12099        Map Command widget.
12100      */
12101      (void) CloneString(&windows->command.name,"Rotate");
12102      windows->command.data=2;
12103      (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12104      (void) XMapRaised(display,windows->command.id);
12105      XClientMessage(display,windows->image.id,windows->im_protocols,
12106        windows->im_update_widget,CurrentTime);
12107      /*
12108        Wait for first button press.
12109      */
12110      (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12111      XQueryPosition(display,windows->image.id,&x,&y);
12112      rotate_info.x1=x;
12113      rotate_info.y1=y;
12114      rotate_info.x2=x;
12115      rotate_info.y2=y;
12116      state=DefaultState;
12117      do
12118      {
12119        XHighlightLine(display,windows->image.id,
12120          windows->image.highlight_context,&rotate_info);
12121        /*
12122          Wait for next event.
12123        */
12124        XScreenEvent(display,windows,&event,exception);
12125        XHighlightLine(display,windows->image.id,
12126          windows->image.highlight_context,&rotate_info);
12127        if (event.xany.window == windows->command.id)
12128          {
12129            /*
12130              Select a command from the Command widget.
12131            */
12132            id=XCommandWidget(display,windows,RotateMenu,&event);
12133            if (id < 0)
12134              continue;
12135            (void) XSetFunction(display,windows->image.highlight_context,
12136              GXcopy);
12137            switch (RotateCommands[id])
12138            {
12139              case RotateColorCommand:
12140              {
12141                const char
12142                  *ColorMenu[MaxNumberPens];
12143
12144                int
12145                  pen_number;
12146
12147                XColor
12148                  color;
12149
12150                /*
12151                  Initialize menu selections.
12152                */
12153                for (i=0; i < (int) (MaxNumberPens-2); i++)
12154                  ColorMenu[i]=resource_info->pen_colors[i];
12155                ColorMenu[MaxNumberPens-2]="Browser...";
12156                ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12157                /*
12158                  Select a pen color from the pop-up menu.
12159                */
12160                pen_number=XMenuWidget(display,windows,RotateMenu[id],
12161                  (const char **) ColorMenu,command);
12162                if (pen_number < 0)
12163                  break;
12164                if (pen_number == (MaxNumberPens-2))
12165                  {
12166                    static char
12167                      color_name[MaxTextExtent] = "gray";
12168
12169                    /*
12170                      Select a pen color from a dialog.
12171                    */
12172                    resource_info->pen_colors[pen_number]=color_name;
12173                    XColorBrowserWidget(display,windows,"Select",color_name);
12174                    if (*color_name == '\0')
12175                      break;
12176                  }
12177                /*
12178                  Set pen color.
12179                */
12180                (void) XParseColor(display,windows->map_info->colormap,
12181                  resource_info->pen_colors[pen_number],&color);
12182                XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12183                  (unsigned int) MaxColors,&color);
12184                windows->pixel_info->pen_colors[pen_number]=color;
12185                pen_id=(unsigned int) pen_number;
12186                break;
12187              }
12188              case RotateDirectionCommand:
12189              {
12190                static const char
12191                  *Directions[] =
12192                  {
12193                    "horizontal",
12194                    "vertical",
12195                    (char *) NULL,
12196                  };
12197
12198                /*
12199                  Select a command from the pop-up menu.
12200                */
12201                id=XMenuWidget(display,windows,RotateMenu[id],
12202                  Directions,command);
12203                if (id >= 0)
12204                  direction=DirectionCommands[id];
12205                break;
12206              }
12207              case RotateHelpCommand:
12208              {
12209                XTextViewWidget(display,resource_info,windows,MagickFalse,
12210                  "Help Viewer - Image Rotation",ImageRotateHelp);
12211                break;
12212              }
12213              case RotateDismissCommand:
12214              {
12215                /*
12216                  Prematurely exit.
12217                */
12218                state|=EscapeState;
12219                state|=ExitState;
12220                break;
12221              }
12222              default:
12223                break;
12224            }
12225            (void) XSetFunction(display,windows->image.highlight_context,
12226              GXinvert);
12227            continue;
12228          }
12229        switch (event.type)
12230        {
12231          case ButtonPress:
12232          {
12233            if (event.xbutton.button != Button1)
12234              break;
12235            if (event.xbutton.window != windows->image.id)
12236              break;
12237            /*
12238              exit loop.
12239            */
12240            (void) XSetFunction(display,windows->image.highlight_context,
12241              GXcopy);
12242            rotate_info.x1=event.xbutton.x;
12243            rotate_info.y1=event.xbutton.y;
12244            state|=ExitState;
12245            break;
12246          }
12247          case ButtonRelease:
12248            break;
12249          case Expose:
12250            break;
12251          case KeyPress:
12252          {
12253            char
12254              command[MaxTextExtent];
12255
12256            KeySym
12257              key_symbol;
12258
12259            if (event.xkey.window != windows->image.id)
12260              break;
12261            /*
12262              Respond to a user key press.
12263            */
12264            (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12265              sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12266            switch ((int) key_symbol)
12267            {
12268              case XK_Escape:
12269              case XK_F20:
12270              {
12271                /*
12272                  Prematurely exit.
12273                */
12274                state|=EscapeState;
12275                state|=ExitState;
12276                break;
12277              }
12278              case XK_F1:
12279              case XK_Help:
12280              {
12281                (void) XSetFunction(display,windows->image.highlight_context,
12282                  GXcopy);
12283                XTextViewWidget(display,resource_info,windows,MagickFalse,
12284                  "Help Viewer - Image Rotation",ImageRotateHelp);
12285                (void) XSetFunction(display,windows->image.highlight_context,
12286                  GXinvert);
12287                break;
12288              }
12289              default:
12290              {
12291                (void) XBell(display,0);
12292                break;
12293              }
12294            }
12295            break;
12296          }
12297          case MotionNotify:
12298          {
12299            rotate_info.x1=event.xmotion.x;
12300            rotate_info.y1=event.xmotion.y;
12301          }
12302        }
12303        rotate_info.x2=rotate_info.x1;
12304        rotate_info.y2=rotate_info.y1;
12305        if (direction == HorizontalRotateCommand)
12306          rotate_info.x2+=32;
12307        else
12308          rotate_info.y2-=32;
12309      } while ((state & ExitState) == 0);
12310      (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12311      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12312      if ((state & EscapeState) != 0)
12313        return(MagickTrue);
12314      /*
12315        Draw line as pointer moves until the mouse button is released.
12316      */
12317      distance=0;
12318      (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12319      state=DefaultState;
12320      do
12321      {
12322        if (distance > 9)
12323          {
12324            /*
12325              Display info and draw rotation line.
12326            */
12327            if (windows->info.mapped == MagickFalse)
12328              (void) XMapWindow(display,windows->info.id);
12329            (void) FormatLocaleString(text,MaxTextExtent," %g",
12330              direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12331            XInfoWidget(display,windows,text);
12332            XHighlightLine(display,windows->image.id,
12333              windows->image.highlight_context,&rotate_info);
12334          }
12335        else
12336          if (windows->info.mapped != MagickFalse)
12337            (void) XWithdrawWindow(display,windows->info.id,
12338              windows->info.screen);
12339        /*
12340          Wait for next event.
12341        */
12342        XScreenEvent(display,windows,&event,exception);
12343        if (distance > 9)
12344          XHighlightLine(display,windows->image.id,
12345            windows->image.highlight_context,&rotate_info);
12346        switch (event.type)
12347        {
12348          case ButtonPress:
12349            break;
12350          case ButtonRelease:
12351          {
12352            /*
12353              User has committed to rotation line.
12354            */
12355            rotate_info.x2=event.xbutton.x;
12356            rotate_info.y2=event.xbutton.y;
12357            state|=ExitState;
12358            break;
12359          }
12360          case Expose:
12361            break;
12362          case MotionNotify:
12363          {
12364            rotate_info.x2=event.xmotion.x;
12365            rotate_info.y2=event.xmotion.y;
12366          }
12367          default:
12368            break;
12369        }
12370        /*
12371          Check boundary conditions.
12372        */
12373        if (rotate_info.x2 < 0)
12374          rotate_info.x2=0;
12375        else
12376          if (rotate_info.x2 > (int) windows->image.width)
12377            rotate_info.x2=(short) windows->image.width;
12378        if (rotate_info.y2 < 0)
12379          rotate_info.y2=0;
12380        else
12381          if (rotate_info.y2 > (int) windows->image.height)
12382            rotate_info.y2=(short) windows->image.height;
12383        /*
12384          Compute rotation angle from the slope of the line.
12385        */
12386        degrees=0.0;
12387        distance=(unsigned int)
12388          ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12389          ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12390        if (distance > 9)
12391          degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12392            rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12393      } while ((state & ExitState) == 0);
12394      (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12395      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12396      if (distance <= 9)
12397        return(MagickTrue);
12398    }
12399  if (direction == VerticalRotateCommand)
12400    degrees-=90.0;
12401  if (degrees == 0.0)
12402    return(MagickTrue);
12403  /*
12404    Rotate image.
12405  */
12406  normalized_degrees=degrees;
12407  while (normalized_degrees < -45.0)
12408    normalized_degrees+=360.0;
12409  for (rotations=0; normalized_degrees > 45.0; rotations++)
12410    normalized_degrees-=90.0;
12411  if (normalized_degrees != 0.0)
12412    (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12413      exception);
12414  XSetCursorState(display,windows,MagickTrue);
12415  XCheckRefreshWindows(display,windows);
12416  (*image)->background_color.red=ScaleShortToQuantum(
12417    windows->pixel_info->pen_colors[pen_id].red);
12418  (*image)->background_color.green=ScaleShortToQuantum(
12419    windows->pixel_info->pen_colors[pen_id].green);
12420  (*image)->background_color.blue=ScaleShortToQuantum(
12421    windows->pixel_info->pen_colors[pen_id].blue);
12422  rotate_image=RotateImage(*image,degrees,exception);
12423  XSetCursorState(display,windows,MagickFalse);
12424  if (rotate_image == (Image *) NULL)
12425    return(MagickFalse);
12426  *image=DestroyImage(*image);
12427  *image=rotate_image;
12428  if (windows->image.crop_geometry != (char *) NULL)
12429    {
12430      /*
12431        Rotate crop geometry.
12432      */
12433      width=(unsigned int) (*image)->columns;
12434      height=(unsigned int) (*image)->rows;
12435      (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12436      switch (rotations % 4)
12437      {
12438        default:
12439        case 0:
12440          break;
12441        case 1:
12442        {
12443          /*
12444            Rotate 90 degrees.
12445          */
12446          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12447            "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12448            (int) height-y,x);
12449          break;
12450        }
12451        case 2:
12452        {
12453          /*
12454            Rotate 180 degrees.
12455          */
12456          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12457            "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12458          break;
12459        }
12460        case 3:
12461        {
12462          /*
12463            Rotate 270 degrees.
12464          */
12465          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12466            "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12467          break;
12468        }
12469      }
12470    }
12471  if (windows->image.orphan != MagickFalse)
12472    return(MagickTrue);
12473  if (normalized_degrees != 0.0)
12474    {
12475      /*
12476        Update image colormap.
12477      */
12478      windows->image.window_changes.width=(int) (*image)->columns;
12479      windows->image.window_changes.height=(int) (*image)->rows;
12480      if (windows->image.crop_geometry != (char *) NULL)
12481        {
12482          /*
12483            Obtain dimensions of image from crop geometry.
12484          */
12485          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12486            &width,&height);
12487          windows->image.window_changes.width=(int) width;
12488          windows->image.window_changes.height=(int) height;
12489        }
12490      XConfigureImageColormap(display,resource_info,windows,*image,exception);
12491    }
12492  else
12493    if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12494      {
12495        windows->image.window_changes.width=windows->image.ximage->height;
12496        windows->image.window_changes.height=windows->image.ximage->width;
12497      }
12498  /*
12499    Update image configuration.
12500  */
12501  (void) XConfigureImage(display,resource_info,windows,*image,exception);
12502  return(MagickTrue);
12503}
12504
12505/*
12506%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12507%                                                                             %
12508%                                                                             %
12509%                                                                             %
12510+   X S a v e I m a g e                                                       %
12511%                                                                             %
12512%                                                                             %
12513%                                                                             %
12514%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12515%
12516%  XSaveImage() saves an image to a file.
12517%
12518%  The format of the XSaveImage method is:
12519%
12520%      MagickBooleanType XSaveImage(Display *display,
12521%        XResourceInfo *resource_info,XWindows *windows,Image *image,
12522%        ExceptionInfo *exception)
12523%
12524%  A description of each parameter follows:
12525%
12526%    o display: Specifies a connection to an X server; returned from
12527%      XOpenDisplay.
12528%
12529%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12530%
12531%    o windows: Specifies a pointer to a XWindows structure.
12532%
12533%    o image: the image.
12534%
12535%    o exception: return any errors or warnings in this structure.
12536%
12537*/
12538static MagickBooleanType XSaveImage(Display *display,
12539  XResourceInfo *resource_info,XWindows *windows,Image *image,
12540  ExceptionInfo *exception)
12541{
12542  char
12543    filename[MaxTextExtent],
12544    geometry[MaxTextExtent];
12545
12546  Image
12547    *save_image;
12548
12549  ImageInfo
12550    *image_info;
12551
12552  MagickStatusType
12553    status;
12554
12555  /*
12556    Request file name from user.
12557  */
12558  if (resource_info->write_filename != (char *) NULL)
12559    (void) CopyMagickString(filename,resource_info->write_filename,
12560      MaxTextExtent);
12561  else
12562    {
12563      char
12564        path[MaxTextExtent];
12565
12566      int
12567        status;
12568
12569      GetPathComponent(image->filename,HeadPath,path);
12570      GetPathComponent(image->filename,TailPath,filename);
12571      if (*path != '\0')
12572        {
12573          status=chdir(path);
12574          if (status == -1)
12575            (void) ThrowMagickException(exception,GetMagickModule(),
12576              FileOpenError,"UnableToOpenFile","%s",path);
12577        }
12578    }
12579  XFileBrowserWidget(display,windows,"Save",filename);
12580  if (*filename == '\0')
12581    return(MagickTrue);
12582  if (IsPathAccessible(filename) != MagickFalse)
12583    {
12584      int
12585        status;
12586
12587      /*
12588        File exists-- seek user's permission before overwriting.
12589      */
12590      status=XConfirmWidget(display,windows,"Overwrite",filename);
12591      if (status <= 0)
12592        return(MagickTrue);
12593    }
12594  image_info=CloneImageInfo(resource_info->image_info);
12595  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12596  (void) SetImageInfo(image_info,1,exception);
12597  if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12598      (LocaleCompare(image_info->magick,"JPG") == 0))
12599    {
12600      char
12601        quality[MaxTextExtent];
12602
12603      int
12604        status;
12605
12606      /*
12607        Request JPEG quality from user.
12608      */
12609      (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
12610        image->quality);
12611      status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12612        quality);
12613      if (*quality == '\0')
12614        return(MagickTrue);
12615      image->quality=StringToUnsignedLong(quality);
12616      image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12617    }
12618  if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12619      (LocaleCompare(image_info->magick,"PDF") == 0) ||
12620      (LocaleCompare(image_info->magick,"PS") == 0) ||
12621      (LocaleCompare(image_info->magick,"PS2") == 0))
12622    {
12623      char
12624        geometry[MaxTextExtent];
12625
12626      /*
12627        Request page geometry from user.
12628      */
12629      (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12630      if (LocaleCompare(image_info->magick,"PDF") == 0)
12631        (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12632      if (image_info->page != (char *) NULL)
12633        (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12634      XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12635        "Select page geometry:",geometry);
12636      if (*geometry != '\0')
12637        image_info->page=GetPageGeometry(geometry);
12638    }
12639  /*
12640    Apply image transforms.
12641  */
12642  XSetCursorState(display,windows,MagickTrue);
12643  XCheckRefreshWindows(display,windows);
12644  save_image=CloneImage(image,0,0,MagickTrue,exception);
12645  if (save_image == (Image *) NULL)
12646    return(MagickFalse);
12647  (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
12648    windows->image.ximage->width,windows->image.ximage->height);
12649  (void) TransformImage(&save_image,windows->image.crop_geometry,geometry,
12650    exception);
12651  /*
12652    Write image.
12653  */
12654  (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12655  status=WriteImage(image_info,save_image,exception);
12656  if (status != MagickFalse)
12657    image->taint=MagickFalse;
12658  save_image=DestroyImage(save_image);
12659  image_info=DestroyImageInfo(image_info);
12660  XSetCursorState(display,windows,MagickFalse);
12661  return(status != 0 ? MagickTrue : MagickFalse);
12662}
12663
12664/*
12665%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12666%                                                                             %
12667%                                                                             %
12668%                                                                             %
12669+   X S c r e e n E v e n t                                                   %
12670%                                                                             %
12671%                                                                             %
12672%                                                                             %
12673%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12674%
12675%  XScreenEvent() handles global events associated with the Pan and Magnify
12676%  windows.
12677%
12678%  The format of the XScreenEvent function is:
12679%
12680%      void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12681%        ExceptionInfo *exception)
12682%
12683%  A description of each parameter follows:
12684%
12685%    o display: Specifies a pointer to the Display structure;  returned from
12686%      XOpenDisplay.
12687%
12688%    o windows: Specifies a pointer to a XWindows structure.
12689%
12690%    o event: Specifies a pointer to a X11 XEvent structure.
12691%
12692%    o exception: return any errors or warnings in this structure.
12693%
12694*/
12695
12696#if defined(__cplusplus) || defined(c_plusplus)
12697extern "C" {
12698#endif
12699
12700static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12701{
12702  register XWindows
12703    *windows;
12704
12705  windows=(XWindows *) data;
12706  if ((event->type == ClientMessage) &&
12707      (event->xclient.window == windows->image.id))
12708    return(MagickFalse);
12709  return(MagickTrue);
12710}
12711
12712#if defined(__cplusplus) || defined(c_plusplus)
12713}
12714#endif
12715
12716static void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12717  ExceptionInfo *exception)
12718{
12719  register int
12720    x,
12721    y;
12722
12723  (void) XIfEvent(display,event,XPredicate,(char *) windows);
12724  if (event->xany.window == windows->command.id)
12725    return;
12726  switch (event->type)
12727  {
12728    case ButtonPress:
12729    case ButtonRelease:
12730    {
12731      if ((event->xbutton.button == Button3) &&
12732          (event->xbutton.state & Mod1Mask))
12733        {
12734          /*
12735            Convert Alt-Button3 to Button2.
12736          */
12737          event->xbutton.button=Button2;
12738          event->xbutton.state&=(~Mod1Mask);
12739        }
12740      if (event->xbutton.window == windows->backdrop.id)
12741        {
12742          (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12743            event->xbutton.time);
12744          break;
12745        }
12746      if (event->xbutton.window == windows->pan.id)
12747        {
12748          XPanImage(display,windows,event,exception);
12749          break;
12750        }
12751      if (event->xbutton.window == windows->image.id)
12752        if (event->xbutton.button == Button2)
12753          {
12754            /*
12755              Update magnified image.
12756            */
12757            x=event->xbutton.x;
12758            y=event->xbutton.y;
12759            if (x < 0)
12760              x=0;
12761            else
12762              if (x >= (int) windows->image.width)
12763                x=(int) (windows->image.width-1);
12764            windows->magnify.x=(int) windows->image.x+x;
12765            if (y < 0)
12766              y=0;
12767            else
12768             if (y >= (int) windows->image.height)
12769               y=(int) (windows->image.height-1);
12770            windows->magnify.y=windows->image.y+y;
12771            if (windows->magnify.mapped == MagickFalse)
12772              (void) XMapRaised(display,windows->magnify.id);
12773            XMakeMagnifyImage(display,windows,exception);
12774            if (event->type == ButtonRelease)
12775              (void) XWithdrawWindow(display,windows->info.id,
12776                windows->info.screen);
12777            break;
12778          }
12779      break;
12780    }
12781    case ClientMessage:
12782    {
12783      /*
12784        If client window delete message, exit.
12785      */
12786      if (event->xclient.message_type != windows->wm_protocols)
12787        break;
12788      if (*event->xclient.data.l != (long) windows->wm_delete_window)
12789        break;
12790      if (event->xclient.window == windows->magnify.id)
12791        {
12792          (void) XWithdrawWindow(display,windows->magnify.id,
12793            windows->magnify.screen);
12794          break;
12795        }
12796      break;
12797    }
12798    case ConfigureNotify:
12799    {
12800      if (event->xconfigure.window == windows->magnify.id)
12801        {
12802          unsigned int
12803            magnify;
12804
12805          /*
12806            Magnify window has a new configuration.
12807          */
12808          windows->magnify.width=(unsigned int) event->xconfigure.width;
12809          windows->magnify.height=(unsigned int) event->xconfigure.height;
12810          if (windows->magnify.mapped == MagickFalse)
12811            break;
12812          magnify=1;
12813          while ((int) magnify <= event->xconfigure.width)
12814            magnify<<=1;
12815          while ((int) magnify <= event->xconfigure.height)
12816            magnify<<=1;
12817          magnify>>=1;
12818          if (((int) magnify != event->xconfigure.width) ||
12819              ((int) magnify != event->xconfigure.height))
12820            {
12821              XWindowChanges
12822                window_changes;
12823
12824              window_changes.width=(int) magnify;
12825              window_changes.height=(int) magnify;
12826              (void) XReconfigureWMWindow(display,windows->magnify.id,
12827                windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12828                &window_changes);
12829              break;
12830            }
12831          XMakeMagnifyImage(display,windows,exception);
12832          break;
12833        }
12834      break;
12835    }
12836    case Expose:
12837    {
12838      if (event->xexpose.window == windows->image.id)
12839        {
12840          XRefreshWindow(display,&windows->image,event);
12841          break;
12842        }
12843      if (event->xexpose.window == windows->pan.id)
12844        if (event->xexpose.count == 0)
12845          {
12846            XDrawPanRectangle(display,windows);
12847            break;
12848          }
12849      if (event->xexpose.window == windows->magnify.id)
12850        if (event->xexpose.count == 0)
12851          {
12852            XMakeMagnifyImage(display,windows,exception);
12853            break;
12854          }
12855      break;
12856    }
12857    case KeyPress:
12858    {
12859      char
12860        command[MaxTextExtent];
12861
12862      KeySym
12863        key_symbol;
12864
12865      if (event->xkey.window != windows->magnify.id)
12866        break;
12867      /*
12868        Respond to a user key press.
12869      */
12870      (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12871        sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12872      XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol,
12873        exception);
12874      break;
12875    }
12876    case MapNotify:
12877    {
12878      if (event->xmap.window == windows->magnify.id)
12879        {
12880          windows->magnify.mapped=MagickTrue;
12881          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12882          break;
12883        }
12884      if (event->xmap.window == windows->info.id)
12885        {
12886          windows->info.mapped=MagickTrue;
12887          break;
12888        }
12889      break;
12890    }
12891    case MotionNotify:
12892    {
12893      while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12894      if (event->xmotion.window == windows->image.id)
12895        if (windows->magnify.mapped != MagickFalse)
12896          {
12897            /*
12898              Update magnified image.
12899            */
12900            x=event->xmotion.x;
12901            y=event->xmotion.y;
12902            if (x < 0)
12903              x=0;
12904            else
12905              if (x >= (int) windows->image.width)
12906                x=(int) (windows->image.width-1);
12907            windows->magnify.x=(int) windows->image.x+x;
12908            if (y < 0)
12909              y=0;
12910            else
12911             if (y >= (int) windows->image.height)
12912               y=(int) (windows->image.height-1);
12913            windows->magnify.y=windows->image.y+y;
12914            XMakeMagnifyImage(display,windows,exception);
12915          }
12916      break;
12917    }
12918    case UnmapNotify:
12919    {
12920      if (event->xunmap.window == windows->magnify.id)
12921        {
12922          windows->magnify.mapped=MagickFalse;
12923          break;
12924        }
12925      if (event->xunmap.window == windows->info.id)
12926        {
12927          windows->info.mapped=MagickFalse;
12928          break;
12929        }
12930      break;
12931    }
12932    default:
12933      break;
12934  }
12935}
12936
12937/*
12938%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12939%                                                                             %
12940%                                                                             %
12941%                                                                             %
12942+   X S e t C r o p G e o m e t r y                                           %
12943%                                                                             %
12944%                                                                             %
12945%                                                                             %
12946%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12947%
12948%  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12949%  and translates it to a cropping geometry relative to the image.
12950%
12951%  The format of the XSetCropGeometry method is:
12952%
12953%      void XSetCropGeometry(Display *display,XWindows *windows,
12954%        RectangleInfo *crop_info,Image *image)
12955%
12956%  A description of each parameter follows:
12957%
12958%    o display: Specifies a connection to an X server; returned from
12959%      XOpenDisplay.
12960%
12961%    o windows: Specifies a pointer to a XWindows structure.
12962%
12963%    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12964%      Image window to crop.
12965%
12966%    o image: the image.
12967%
12968*/
12969static void XSetCropGeometry(Display *display,XWindows *windows,
12970  RectangleInfo *crop_info,Image *image)
12971{
12972  char
12973    text[MaxTextExtent];
12974
12975  int
12976    x,
12977    y;
12978
12979  MagickRealType
12980    scale_factor;
12981
12982  unsigned int
12983    height,
12984    width;
12985
12986  if (windows->info.mapped != MagickFalse)
12987    {
12988      /*
12989        Display info on cropping rectangle.
12990      */
12991      (void) FormatLocaleString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
12992        (double) crop_info->width,(double) crop_info->height,(double)
12993        crop_info->x,(double) crop_info->y);
12994      XInfoWidget(display,windows,text);
12995    }
12996  /*
12997    Cropping geometry is relative to any previous crop geometry.
12998  */
12999  x=0;
13000  y=0;
13001  width=(unsigned int) image->columns;
13002  height=(unsigned int) image->rows;
13003  if (windows->image.crop_geometry != (char *) NULL)
13004    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13005  else
13006    windows->image.crop_geometry=AcquireString((char *) NULL);
13007  /*
13008    Define the crop geometry string from the cropping rectangle.
13009  */
13010  scale_factor=(MagickRealType) width/windows->image.ximage->width;
13011  if (crop_info->x > 0)
13012    x+=(int) (scale_factor*crop_info->x+0.5);
13013  width=(unsigned int) (scale_factor*crop_info->width+0.5);
13014  if (width == 0)
13015    width=1;
13016  scale_factor=(MagickRealType) height/windows->image.ximage->height;
13017  if (crop_info->y > 0)
13018    y+=(int) (scale_factor*crop_info->y+0.5);
13019  height=(unsigned int) (scale_factor*crop_info->height+0.5);
13020  if (height == 0)
13021    height=1;
13022  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
13023    "%ux%u%+d%+d",width,height,x,y);
13024}
13025
13026/*
13027%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13028%                                                                             %
13029%                                                                             %
13030%                                                                             %
13031+   X T i l e I m a g e                                                       %
13032%                                                                             %
13033%                                                                             %
13034%                                                                             %
13035%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13036%
13037%  XTileImage() loads or deletes a selected tile from a visual image directory.
13038%  The load or delete command is chosen from a menu.
13039%
13040%  The format of the XTileImage method is:
13041%
13042%      Image *XTileImage(Display *display,XResourceInfo *resource_info,
13043%        XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13044%
13045%  A description of each parameter follows:
13046%
13047%    o tile_image:  XTileImage reads or deletes the tile image
13048%      and returns it.  A null image is returned if an error occurs.
13049%
13050%    o display: Specifies a connection to an X server;  returned from
13051%      XOpenDisplay.
13052%
13053%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13054%
13055%    o windows: Specifies a pointer to a XWindows structure.
13056%
13057%    o image: the image; returned from ReadImage.
13058%
13059%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
13060%      the entire image is refreshed.
13061%
13062%    o exception: return any errors or warnings in this structure.
13063%
13064*/
13065static Image *XTileImage(Display *display,XResourceInfo *resource_info,
13066  XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13067{
13068  static const char
13069    *VerbMenu[] =
13070    {
13071      "Load",
13072      "Next",
13073      "Former",
13074      "Delete",
13075      "Update",
13076      (char *) NULL,
13077    };
13078
13079  static const ModeType
13080    TileCommands[] =
13081    {
13082      TileLoadCommand,
13083      TileNextCommand,
13084      TileFormerCommand,
13085      TileDeleteCommand,
13086      TileUpdateCommand
13087    };
13088
13089  char
13090    command[MaxTextExtent],
13091    filename[MaxTextExtent];
13092
13093  Image
13094    *tile_image;
13095
13096  int
13097    id,
13098    status,
13099    tile,
13100    x,
13101    y;
13102
13103  MagickRealType
13104    scale_factor;
13105
13106  register char
13107    *p,
13108    *q;
13109
13110  register int
13111    i;
13112
13113  unsigned int
13114    height,
13115    width;
13116
13117  /*
13118    Tile image is relative to montage image configuration.
13119  */
13120  x=0;
13121  y=0;
13122  width=(unsigned int) image->columns;
13123  height=(unsigned int) image->rows;
13124  if (windows->image.crop_geometry != (char *) NULL)
13125    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13126  scale_factor=(MagickRealType) width/windows->image.ximage->width;
13127  event->xbutton.x+=windows->image.x;
13128  event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13129  scale_factor=(MagickRealType) height/windows->image.ximage->height;
13130  event->xbutton.y+=windows->image.y;
13131  event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13132  /*
13133    Determine size and location of each tile in the visual image directory.
13134  */
13135  width=(unsigned int) image->columns;
13136  height=(unsigned int) image->rows;
13137  x=0;
13138  y=0;
13139  (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13140  tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13141    (event->xbutton.x-x)/width;
13142  if (tile < 0)
13143    {
13144      /*
13145        Button press is outside any tile.
13146      */
13147      (void) XBell(display,0);
13148      return((Image *) NULL);
13149    }
13150  /*
13151    Determine file name from the tile directory.
13152  */
13153  p=image->directory;
13154  for (i=tile; (i != 0) && (*p != '\0'); )
13155  {
13156    if (*p == '\n')
13157      i--;
13158    p++;
13159  }
13160  if (*p == '\0')
13161    {
13162      /*
13163        Button press is outside any tile.
13164      */
13165      (void) XBell(display,0);
13166      return((Image *) NULL);
13167    }
13168  /*
13169    Select a command from the pop-up menu.
13170  */
13171  id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13172  if (id < 0)
13173    return((Image *) NULL);
13174  q=p;
13175  while ((*q != '\n') && (*q != '\0'))
13176    q++;
13177  (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13178  /*
13179    Perform command for the selected tile.
13180  */
13181  XSetCursorState(display,windows,MagickTrue);
13182  XCheckRefreshWindows(display,windows);
13183  tile_image=NewImageList();
13184  switch (TileCommands[id])
13185  {
13186    case TileLoadCommand:
13187    {
13188      /*
13189        Load tile image.
13190      */
13191      XCheckRefreshWindows(display,windows);
13192      (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13193        MaxTextExtent);
13194      (void) CopyMagickString(resource_info->image_info->filename,filename,
13195        MaxTextExtent);
13196      tile_image=ReadImage(resource_info->image_info,exception);
13197      CatchException(exception);
13198      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13199      break;
13200    }
13201    case TileNextCommand:
13202    {
13203      /*
13204        Display next image.
13205      */
13206      XClientMessage(display,windows->image.id,windows->im_protocols,
13207        windows->im_next_image,CurrentTime);
13208      break;
13209    }
13210    case TileFormerCommand:
13211    {
13212      /*
13213        Display former image.
13214      */
13215      XClientMessage(display,windows->image.id,windows->im_protocols,
13216        windows->im_former_image,CurrentTime);
13217      break;
13218    }
13219    case TileDeleteCommand:
13220    {
13221      /*
13222        Delete tile image.
13223      */
13224      if (IsPathAccessible(filename) == MagickFalse)
13225        {
13226          XNoticeWidget(display,windows,"Image file does not exist:",filename);
13227          break;
13228        }
13229      status=XConfirmWidget(display,windows,"Really delete tile",filename);
13230      if (status <= 0)
13231        break;
13232      status=remove_utf8(filename) != 0 ? MagickTrue : MagickFalse;
13233      if (status != MagickFalse)
13234        {
13235          XNoticeWidget(display,windows,"Unable to delete image file:",
13236            filename);
13237          break;
13238        }
13239    }
13240    case TileUpdateCommand:
13241    {
13242      int
13243        x_offset,
13244        y_offset;
13245
13246      PixelInfo
13247        pixel;
13248
13249      Quantum
13250        virtual_pixel[CompositePixelChannel];
13251
13252      register int
13253        j;
13254
13255      register Quantum
13256        *s;
13257
13258      /*
13259        Ensure all the images exist.
13260      */
13261      tile=0;
13262      GetPixelInfo(image,&pixel);
13263      for (p=image->directory; *p != '\0'; p++)
13264      {
13265        CacheView
13266          *image_view;
13267
13268        q=p;
13269        while ((*q != '\n') && (*q != '\0'))
13270          q++;
13271        (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13272        p=q;
13273        if (IsPathAccessible(filename) != MagickFalse)
13274          {
13275            tile++;
13276            continue;
13277          }
13278        /*
13279          Overwrite tile with background color.
13280        */
13281        x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13282        y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13283        image_view=AcquireCacheView(image);
13284        (void) GetOneCacheViewVirtualPixel(image_view,0,0,virtual_pixel,
13285          exception);
13286        pixel.red=virtual_pixel[RedPixelChannel];
13287        pixel.green=virtual_pixel[GreenPixelChannel];
13288        pixel.blue=virtual_pixel[BluePixelChannel];
13289        pixel.alpha=virtual_pixel[AlphaPixelChannel];
13290        for (i=0; i < (int) height; i++)
13291        {
13292          s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13293            y_offset+i,width,1,exception);
13294          if (s == (Quantum *) NULL)
13295            break;
13296          for (j=0; j < (int) width; j++)
13297          {
13298            SetPixelInfoPixel(image,&pixel,s);
13299            s+=GetPixelChannels(image);
13300          }
13301          if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13302            break;
13303        }
13304        image_view=DestroyCacheView(image_view);
13305        tile++;
13306      }
13307      windows->image.window_changes.width=(int) image->columns;
13308      windows->image.window_changes.height=(int) image->rows;
13309      XConfigureImageColormap(display,resource_info,windows,image,exception);
13310      (void) XConfigureImage(display,resource_info,windows,image,exception);
13311      break;
13312    }
13313    default:
13314      break;
13315  }
13316  XSetCursorState(display,windows,MagickFalse);
13317  return(tile_image);
13318}
13319
13320/*
13321%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13322%                                                                             %
13323%                                                                             %
13324%                                                                             %
13325+   X T r a n s l a t e I m a g e                                             %
13326%                                                                             %
13327%                                                                             %
13328%                                                                             %
13329%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13330%
13331%  XTranslateImage() translates the image within an Image window by one pixel
13332%  as specified by the key symbol.  If the image has a `montage string the
13333%  translation is respect to the width and height contained within the string.
13334%
13335%  The format of the XTranslateImage method is:
13336%
13337%      void XTranslateImage(Display *display,XWindows *windows,
13338%        Image *image,const KeySym key_symbol)
13339%
13340%  A description of each parameter follows:
13341%
13342%    o display: Specifies a connection to an X server; returned from
13343%      XOpenDisplay.
13344%
13345%    o windows: Specifies a pointer to a XWindows structure.
13346%
13347%    o image: the image.
13348%
13349%    o key_symbol: Specifies a KeySym which indicates which side of the image
13350%      to trim.
13351%
13352*/
13353static void XTranslateImage(Display *display,XWindows *windows,
13354  Image *image,const KeySym key_symbol)
13355{
13356  char
13357    text[MaxTextExtent];
13358
13359  int
13360    x,
13361    y;
13362
13363  unsigned int
13364    x_offset,
13365    y_offset;
13366
13367  /*
13368    User specified a pan position offset.
13369  */
13370  x_offset=windows->image.width;
13371  y_offset=windows->image.height;
13372  if (image->montage != (char *) NULL)
13373    (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13374  switch ((int) key_symbol)
13375  {
13376    case XK_Home:
13377    case XK_KP_Home:
13378    {
13379      windows->image.x=(int) windows->image.width/2;
13380      windows->image.y=(int) windows->image.height/2;
13381      break;
13382    }
13383    case XK_Left:
13384    case XK_KP_Left:
13385    {
13386      windows->image.x-=x_offset;
13387      break;
13388    }
13389    case XK_Next:
13390    case XK_Up:
13391    case XK_KP_Up:
13392    {
13393      windows->image.y-=y_offset;
13394      break;
13395    }
13396    case XK_Right:
13397    case XK_KP_Right:
13398    {
13399      windows->image.x+=x_offset;
13400      break;
13401    }
13402    case XK_Prior:
13403    case XK_Down:
13404    case XK_KP_Down:
13405    {
13406      windows->image.y+=y_offset;
13407      break;
13408    }
13409    default:
13410      return;
13411  }
13412  /*
13413    Check boundary conditions.
13414  */
13415  if (windows->image.x < 0)
13416    windows->image.x=0;
13417  else
13418    if ((int) (windows->image.x+windows->image.width) >
13419        windows->image.ximage->width)
13420      windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13421  if (windows->image.y < 0)
13422    windows->image.y=0;
13423  else
13424    if ((int) (windows->image.y+windows->image.height) >
13425        windows->image.ximage->height)
13426      windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13427  /*
13428    Refresh Image window.
13429  */
13430  (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
13431    windows->image.width,windows->image.height,windows->image.x,
13432    windows->image.y);
13433  XInfoWidget(display,windows,text);
13434  XCheckRefreshWindows(display,windows);
13435  XDrawPanRectangle(display,windows);
13436  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13437  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13438}
13439
13440/*
13441%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13442%                                                                             %
13443%                                                                             %
13444%                                                                             %
13445+   X T r i m I m a g e                                                       %
13446%                                                                             %
13447%                                                                             %
13448%                                                                             %
13449%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13450%
13451%  XTrimImage() trims the edges from the Image window.
13452%
13453%  The format of the XTrimImage method is:
13454%
13455%      MagickBooleanType XTrimImage(Display *display,
13456%        XResourceInfo *resource_info,XWindows *windows,Image *image,
13457%        ExceptionInfo *exception)
13458%
13459%  A description of each parameter follows:
13460%
13461%    o display: Specifies a connection to an X server; returned from
13462%      XOpenDisplay.
13463%
13464%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13465%
13466%    o windows: Specifies a pointer to a XWindows structure.
13467%
13468%    o image: the image.
13469%
13470%    o exception: return any errors or warnings in this structure.
13471%
13472*/
13473static MagickBooleanType XTrimImage(Display *display,
13474  XResourceInfo *resource_info,XWindows *windows,Image *image,
13475  ExceptionInfo *exception)
13476{
13477  RectangleInfo
13478    trim_info;
13479
13480  register int
13481    x,
13482    y;
13483
13484  size_t
13485    background,
13486    pixel;
13487
13488  /*
13489    Trim edges from image.
13490  */
13491  XSetCursorState(display,windows,MagickTrue);
13492  XCheckRefreshWindows(display,windows);
13493  /*
13494    Crop the left edge.
13495  */
13496  background=XGetPixel(windows->image.ximage,0,0);
13497  trim_info.width=(size_t) windows->image.ximage->width;
13498  for (x=0; x < windows->image.ximage->width; x++)
13499  {
13500    for (y=0; y < windows->image.ximage->height; y++)
13501    {
13502      pixel=XGetPixel(windows->image.ximage,x,y);
13503      if (pixel != background)
13504        break;
13505    }
13506    if (y < windows->image.ximage->height)
13507      break;
13508  }
13509  trim_info.x=(ssize_t) x;
13510  if (trim_info.x == (ssize_t) windows->image.ximage->width)
13511    {
13512      XSetCursorState(display,windows,MagickFalse);
13513      return(MagickFalse);
13514    }
13515  /*
13516    Crop the right edge.
13517  */
13518  background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13519  for (x=windows->image.ximage->width-1; x != 0; x--)
13520  {
13521    for (y=0; y < windows->image.ximage->height; y++)
13522    {
13523      pixel=XGetPixel(windows->image.ximage,x,y);
13524      if (pixel != background)
13525        break;
13526    }
13527    if (y < windows->image.ximage->height)
13528      break;
13529  }
13530  trim_info.width=(size_t) (x-trim_info.x+1);
13531  /*
13532    Crop the top edge.
13533  */
13534  background=XGetPixel(windows->image.ximage,0,0);
13535  trim_info.height=(size_t) windows->image.ximage->height;
13536  for (y=0; y < windows->image.ximage->height; y++)
13537  {
13538    for (x=0; x < windows->image.ximage->width; x++)
13539    {
13540      pixel=XGetPixel(windows->image.ximage,x,y);
13541      if (pixel != background)
13542        break;
13543    }
13544    if (x < windows->image.ximage->width)
13545      break;
13546  }
13547  trim_info.y=(ssize_t) y;
13548  /*
13549    Crop the bottom edge.
13550  */
13551  background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13552  for (y=windows->image.ximage->height-1; y != 0; y--)
13553  {
13554    for (x=0; x < windows->image.ximage->width; x++)
13555    {
13556      pixel=XGetPixel(windows->image.ximage,x,y);
13557      if (pixel != background)
13558        break;
13559    }
13560    if (x < windows->image.ximage->width)
13561      break;
13562  }
13563  trim_info.height=(size_t) y-trim_info.y+1;
13564  if (((unsigned int) trim_info.width != windows->image.width) ||
13565      ((unsigned int) trim_info.height != windows->image.height))
13566    {
13567      /*
13568        Reconfigure Image window as defined by the trimming rectangle.
13569      */
13570      XSetCropGeometry(display,windows,&trim_info,image);
13571      windows->image.window_changes.width=(int) trim_info.width;
13572      windows->image.window_changes.height=(int) trim_info.height;
13573      (void) XConfigureImage(display,resource_info,windows,image,exception);
13574    }
13575  XSetCursorState(display,windows,MagickFalse);
13576  return(MagickTrue);
13577}
13578
13579/*
13580%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13581%                                                                             %
13582%                                                                             %
13583%                                                                             %
13584+   X V i s u a l D i r e c t o r y I m a g e                                 %
13585%                                                                             %
13586%                                                                             %
13587%                                                                             %
13588%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13589%
13590%  XVisualDirectoryImage() creates a Visual Image Directory.
13591%
13592%  The format of the XVisualDirectoryImage method is:
13593%
13594%      Image *XVisualDirectoryImage(Display *display,
13595%        XResourceInfo *resource_info,XWindows *windows,
13596%        ExceptionInfo *exception)
13597%
13598%  A description of each parameter follows:
13599%
13600%    o display: Specifies a connection to an X server; returned from
13601%      XOpenDisplay.
13602%
13603%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13604%
13605%    o windows: Specifies a pointer to a XWindows structure.
13606%
13607%    o exception: return any errors or warnings in this structure.
13608%
13609*/
13610static Image *XVisualDirectoryImage(Display *display,
13611  XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
13612{
13613#define TileImageTag  "Scale/Image"
13614#define XClientName  "montage"
13615
13616  char
13617    **filelist;
13618
13619  Image
13620    *images,
13621    *montage_image,
13622    *next_image,
13623    *thumbnail_image;
13624
13625  ImageInfo
13626    *read_info;
13627
13628  int
13629    number_files;
13630
13631  MagickBooleanType
13632    backdrop;
13633
13634  MagickStatusType
13635    status;
13636
13637  MontageInfo
13638    *montage_info;
13639
13640  RectangleInfo
13641    geometry;
13642
13643  register int
13644    i;
13645
13646  static char
13647    filename[MaxTextExtent] = "\0",
13648    filenames[MaxTextExtent] = "*";
13649
13650  XResourceInfo
13651    background_resources;
13652
13653  /*
13654    Request file name from user.
13655  */
13656  XFileBrowserWidget(display,windows,"Directory",filenames);
13657  if (*filenames == '\0')
13658    return((Image *) NULL);
13659  /*
13660    Expand the filenames.
13661  */
13662  filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13663  if (filelist == (char **) NULL)
13664    {
13665      ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13666        filenames);
13667      return((Image *) NULL);
13668    }
13669  number_files=1;
13670  filelist[0]=filenames;
13671  status=ExpandFilenames(&number_files,&filelist);
13672  if ((status == MagickFalse) || (number_files == 0))
13673    {
13674      if (number_files == 0)
13675        ThrowXWindowFatalException(ImageError,"NoImagesWereFound",filenames)
13676      else
13677        ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13678          filenames);
13679      return((Image *) NULL);
13680    }
13681  /*
13682    Set image background resources.
13683  */
13684  background_resources=(*resource_info);
13685  background_resources.window_id=AcquireString("");
13686  (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
13687    "0x%lx",windows->image.id);
13688  background_resources.backdrop=MagickTrue;
13689  /*
13690    Read each image and convert them to a tile.
13691  */
13692  backdrop=(windows->visual_info->klass == TrueColor) ||
13693    (windows->visual_info->klass == DirectColor) ? MagickTrue : MagickFalse;
13694  read_info=CloneImageInfo(resource_info->image_info);
13695  (void) SetImageOption(read_info,"jpeg:size","120x120");
13696  (void) CloneString(&read_info->size,DefaultTileGeometry);
13697  (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13698    (void *) NULL);
13699  images=NewImageList();
13700  XSetCursorState(display,windows,MagickTrue);
13701  XCheckRefreshWindows(display,windows);
13702  for (i=0; i < (int) number_files; i++)
13703  {
13704    (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13705    filelist[i]=DestroyString(filelist[i]);
13706    *read_info->magick='\0';
13707    next_image=ReadImage(read_info,exception);
13708    CatchException(exception);
13709    if (next_image != (Image *) NULL)
13710      {
13711        (void) DeleteImageProperty(next_image,"label");
13712        (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13713          read_info,next_image,DefaultTileLabel,exception),exception);
13714        (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13715          exception);
13716        thumbnail_image=ThumbnailImage(next_image,geometry.width,
13717          geometry.height,exception);
13718        if (thumbnail_image != (Image *) NULL)
13719          {
13720            next_image=DestroyImage(next_image);
13721            next_image=thumbnail_image;
13722          }
13723        if (backdrop)
13724          {
13725            (void) XDisplayBackgroundImage(display,&background_resources,
13726              next_image,exception);
13727            XSetCursorState(display,windows,MagickTrue);
13728          }
13729        AppendImageToList(&images,next_image);
13730        if (images->progress_monitor != (MagickProgressMonitor) NULL)
13731          {
13732            MagickBooleanType
13733              proceed;
13734
13735            proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13736              (MagickSizeType) number_files);
13737            if (proceed == MagickFalse)
13738              break;
13739          }
13740      }
13741  }
13742  filelist=(char **) RelinquishMagickMemory(filelist);
13743  if (images == (Image *) NULL)
13744    {
13745      read_info=DestroyImageInfo(read_info);
13746      XSetCursorState(display,windows,MagickFalse);
13747      ThrowXWindowFatalException(ImageError,"NoImagesWereLoaded",filenames);
13748      return((Image *) NULL);
13749    }
13750  /*
13751    Create the Visual Image Directory.
13752  */
13753  montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13754  montage_info->pointsize=10;
13755  if (resource_info->font != (char *) NULL)
13756    (void) CloneString(&montage_info->font,resource_info->font);
13757  (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13758  montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13759    images),exception);
13760  images=DestroyImageList(images);
13761  montage_info=DestroyMontageInfo(montage_info);
13762  read_info=DestroyImageInfo(read_info);
13763  XSetCursorState(display,windows,MagickFalse);
13764  if (montage_image == (Image *) NULL)
13765    return(montage_image);
13766  XClientMessage(display,windows->image.id,windows->im_protocols,
13767    windows->im_next_image,CurrentTime);
13768  return(montage_image);
13769}
13770
13771/*
13772%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13773%                                                                             %
13774%                                                                             %
13775%                                                                             %
13776%   X D i s p l a y B a c k g r o u n d I m a g e                             %
13777%                                                                             %
13778%                                                                             %
13779%                                                                             %
13780%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13781%
13782%  XDisplayBackgroundImage() displays an image in the background of a window.
13783%
13784%  The format of the XDisplayBackgroundImage method is:
13785%
13786%      MagickBooleanType XDisplayBackgroundImage(Display *display,
13787%        XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13788%
13789%  A description of each parameter follows:
13790%
13791%    o display: Specifies a connection to an X server;  returned from
13792%      XOpenDisplay.
13793%
13794%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13795%
13796%    o image: the image.
13797%
13798%    o exception: return any errors or warnings in this structure.
13799%
13800*/
13801MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13802  XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13803{
13804  char
13805    geometry[MaxTextExtent],
13806    visual_type[MaxTextExtent];
13807
13808  int
13809    height,
13810    status,
13811    width;
13812
13813  RectangleInfo
13814    geometry_info;
13815
13816  static XPixelInfo
13817    pixel;
13818
13819  static XStandardColormap
13820    *map_info;
13821
13822  static XVisualInfo
13823    *visual_info = (XVisualInfo *) NULL;
13824
13825  static XWindowInfo
13826    window_info;
13827
13828  size_t
13829    delay;
13830
13831  Window
13832    root_window;
13833
13834  XGCValues
13835    context_values;
13836
13837  XResourceInfo
13838    resources;
13839
13840  XWindowAttributes
13841    window_attributes;
13842
13843  /*
13844    Determine target window.
13845  */
13846  assert(image != (Image *) NULL);
13847  assert(image->signature == MagickSignature);
13848  if (image->debug != MagickFalse)
13849    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13850  resources=(*resource_info);
13851  window_info.id=(Window) NULL;
13852  root_window=XRootWindow(display,XDefaultScreen(display));
13853  if (LocaleCompare(resources.window_id,"root") == 0)
13854    window_info.id=root_window;
13855  else
13856    {
13857      if (isdigit((unsigned char) *resources.window_id) != 0)
13858        window_info.id=XWindowByID(display,root_window,
13859          (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13860      if (window_info.id == (Window) NULL)
13861        window_info.id=XWindowByName(display,root_window,resources.window_id);
13862    }
13863  if (window_info.id == (Window) NULL)
13864    {
13865      ThrowXWindowFatalException(XServerError,"NoWindowWithSpecifiedIDExists",
13866        resources.window_id);
13867      return(MagickFalse);
13868    }
13869  /*
13870    Determine window visual id.
13871  */
13872  window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13873  window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13874  (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13875  status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13876  if (status != 0)
13877    (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
13878      XVisualIDFromVisual(window_attributes.visual));
13879  if (visual_info == (XVisualInfo *) NULL)
13880    {
13881      /*
13882        Allocate standard colormap.
13883      */
13884      map_info=XAllocStandardColormap();
13885      if (map_info == (XStandardColormap *) NULL)
13886        ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13887          image->filename);
13888      map_info->colormap=(Colormap) NULL;
13889      pixel.pixels=(unsigned long *) NULL;
13890      /*
13891        Initialize visual info.
13892      */
13893      resources.map_type=(char *) NULL;
13894      resources.visual_type=visual_type;
13895      visual_info=XBestVisualInfo(display,map_info,&resources);
13896      if (visual_info == (XVisualInfo *) NULL)
13897        ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13898          resources.visual_type);
13899      /*
13900        Initialize window info.
13901      */
13902      window_info.ximage=(XImage *) NULL;
13903      window_info.matte_image=(XImage *) NULL;
13904      window_info.pixmap=(Pixmap) NULL;
13905      window_info.matte_pixmap=(Pixmap) NULL;
13906    }
13907  /*
13908    Free previous root colors.
13909  */
13910  if (window_info.id == root_window)
13911    (void) XDestroyWindowColors(display,root_window);
13912  /*
13913    Initialize Standard Colormap.
13914  */
13915  resources.colormap=SharedColormap;
13916  XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel,
13917    exception);
13918  /*
13919    Graphic context superclass.
13920  */
13921  context_values.background=pixel.background_color.pixel;
13922  context_values.foreground=pixel.foreground_color.pixel;
13923  pixel.annotate_context=XCreateGC(display,window_info.id,
13924    (size_t) (GCBackground | GCForeground),&context_values);
13925  if (pixel.annotate_context == (GC) NULL)
13926    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13927      image->filename);
13928  /*
13929    Initialize Image window attributes.
13930  */
13931  window_info.name=AcquireString("\0");
13932  window_info.icon_name=AcquireString("\0");
13933  XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13934    &resources,&window_info);
13935  /*
13936    Create the X image.
13937  */
13938  window_info.width=(unsigned int) image->columns;
13939  window_info.height=(unsigned int) image->rows;
13940  if ((image->columns != window_info.width) ||
13941      (image->rows != window_info.height))
13942    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13943      image->filename);
13944  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
13945    window_attributes.width,window_attributes.height);
13946  geometry_info.width=window_info.width;
13947  geometry_info.height=window_info.height;
13948  geometry_info.x=(ssize_t) window_info.x;
13949  geometry_info.y=(ssize_t) window_info.y;
13950  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13951    &geometry_info.width,&geometry_info.height);
13952  window_info.width=(unsigned int) geometry_info.width;
13953  window_info.height=(unsigned int) geometry_info.height;
13954  window_info.x=(int) geometry_info.x;
13955  window_info.y=(int) geometry_info.y;
13956  status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13957    window_info.height,exception);
13958  if (status == MagickFalse)
13959    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13960      image->filename);
13961  window_info.x=0;
13962  window_info.y=0;
13963  if (image->debug != MagickFalse)
13964    {
13965      (void) LogMagickEvent(X11Event,GetMagickModule(),
13966        "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13967        (double) image->columns,(double) image->rows);
13968      if (image->colors != 0)
13969        (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13970          image->colors);
13971      (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13972    }
13973  /*
13974    Adjust image dimensions as specified by backdrop or geometry options.
13975  */
13976  width=(int) window_info.width;
13977  height=(int) window_info.height;
13978  if (resources.backdrop != MagickFalse)
13979    {
13980      /*
13981        Center image on window.
13982      */
13983      window_info.x=(window_attributes.width/2)-
13984        (window_info.ximage->width/2);
13985      window_info.y=(window_attributes.height/2)-
13986        (window_info.ximage->height/2);
13987      width=window_attributes.width;
13988      height=window_attributes.height;
13989    }
13990  if ((resources.image_geometry != (char *) NULL) &&
13991      (*resources.image_geometry != '\0'))
13992    {
13993      char
13994        default_geometry[MaxTextExtent];
13995
13996      int
13997        flags,
13998        gravity;
13999
14000      XSizeHints
14001        *size_hints;
14002
14003      /*
14004        User specified geometry.
14005      */
14006      size_hints=XAllocSizeHints();
14007      if (size_hints == (XSizeHints *) NULL)
14008        ThrowXWindowFatalException(ResourceLimitFatalError,
14009          "MemoryAllocationFailed",image->filename);
14010      size_hints->flags=0L;
14011      (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
14012        width,height);
14013      flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
14014        default_geometry,window_info.border_width,size_hints,&window_info.x,
14015        &window_info.y,&width,&height,&gravity);
14016      if (flags & (XValue | YValue))
14017        {
14018          width=window_attributes.width;
14019          height=window_attributes.height;
14020        }
14021      (void) XFree((void *) size_hints);
14022    }
14023  /*
14024    Create the X pixmap.
14025  */
14026  window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
14027    (unsigned int) height,window_info.depth);
14028  if (window_info.pixmap == (Pixmap) NULL)
14029    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
14030      image->filename);
14031  /*
14032    Display pixmap on the window.
14033  */
14034  if (((unsigned int) width > window_info.width) ||
14035      ((unsigned int) height > window_info.height))
14036    (void) XFillRectangle(display,window_info.pixmap,
14037      window_info.annotate_context,0,0,(unsigned int) width,
14038      (unsigned int) height);
14039  (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
14040    window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
14041    window_info.width,(unsigned int) window_info.height);
14042  (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
14043  (void) XClearWindow(display,window_info.id);
14044  delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
14045  XDelay(display,delay == 0UL ? 10UL : delay);
14046  (void) XSync(display,MagickFalse);
14047  return(window_info.id == root_window ? MagickTrue : MagickFalse);
14048}
14049
14050/*
14051%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14052%                                                                             %
14053%                                                                             %
14054%                                                                             %
14055+   X D i s p l a y I m a g e                                                 %
14056%                                                                             %
14057%                                                                             %
14058%                                                                             %
14059%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14060%
14061%  XDisplayImage() displays an image via X11.  A new image is created and
14062%  returned if the user interactively transforms the displayed image.
14063%
14064%  The format of the XDisplayImage method is:
14065%
14066%      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14067%        char **argv,int argc,Image **image,size_t *state,
14068%        ExceptionInfo *exception)
14069%
14070%  A description of each parameter follows:
14071%
14072%    o nexus:  Method XDisplayImage returns an image when the
14073%      user chooses 'Open Image' from the command menu or picks a tile
14074%      from the image directory.  Otherwise a null image is returned.
14075%
14076%    o display: Specifies a connection to an X server;  returned from
14077%      XOpenDisplay.
14078%
14079%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14080%
14081%    o argv: Specifies the application's argument list.
14082%
14083%    o argc: Specifies the number of arguments.
14084%
14085%    o image: Specifies an address to an address of an Image structure;
14086%
14087%    o exception: return any errors or warnings in this structure.
14088%
14089*/
14090MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14091  char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
14092{
14093#define MagnifySize  256  /* must be a power of 2 */
14094#define MagickMenus  10
14095#define MagickTitle  "Commands"
14096
14097  static const char
14098    *CommandMenu[] =
14099    {
14100      "File",
14101      "Edit",
14102      "View",
14103      "Transform",
14104      "Enhance",
14105      "Effects",
14106      "F/X",
14107      "Image Edit",
14108      "Miscellany",
14109      "Help",
14110      (char *) NULL
14111    },
14112    *FileMenu[] =
14113    {
14114      "Open...",
14115      "Next",
14116      "Former",
14117      "Select...",
14118      "Save...",
14119      "Print...",
14120      "Delete...",
14121      "New...",
14122      "Visual Directory...",
14123      "Quit",
14124      (char *) NULL
14125    },
14126    *EditMenu[] =
14127    {
14128      "Undo",
14129      "Redo",
14130      "Cut",
14131      "Copy",
14132      "Paste",
14133      (char *) NULL
14134    },
14135    *ViewMenu[] =
14136    {
14137      "Half Size",
14138      "Original Size",
14139      "Double Size",
14140      "Resize...",
14141      "Apply",
14142      "Refresh",
14143      "Restore",
14144      (char *) NULL
14145    },
14146    *TransformMenu[] =
14147    {
14148      "Crop",
14149      "Chop",
14150      "Flop",
14151      "Flip",
14152      "Rotate Right",
14153      "Rotate Left",
14154      "Rotate...",
14155      "Shear...",
14156      "Roll...",
14157      "Trim Edges",
14158      (char *) NULL
14159    },
14160    *EnhanceMenu[] =
14161    {
14162      "Hue...",
14163      "Saturation...",
14164      "Brightness...",
14165      "Gamma...",
14166      "Spiff",
14167      "Dull",
14168      "Contrast Stretch...",
14169      "Sigmoidal Contrast...",
14170      "Normalize",
14171      "Equalize",
14172      "Negate",
14173      "Grayscale",
14174      "Map...",
14175      "Quantize...",
14176      (char *) NULL
14177    },
14178    *EffectsMenu[] =
14179    {
14180      "Despeckle",
14181      "Emboss",
14182      "Reduce Noise",
14183      "Add Noise...",
14184      "Sharpen...",
14185      "Blur...",
14186      "Threshold...",
14187      "Edge Detect...",
14188      "Spread...",
14189      "Shade...",
14190      "Raise...",
14191      "Segment...",
14192      (char *) NULL
14193    },
14194    *FXMenu[] =
14195    {
14196      "Solarize...",
14197      "Sepia Tone...",
14198      "Swirl...",
14199      "Implode...",
14200      "Vignette...",
14201      "Wave...",
14202      "Oil Paint...",
14203      "Charcoal Draw...",
14204      (char *) NULL
14205    },
14206    *ImageEditMenu[] =
14207    {
14208      "Annotate...",
14209      "Draw...",
14210      "Color...",
14211      "Matte...",
14212      "Composite...",
14213      "Add Border...",
14214      "Add Frame...",
14215      "Comment...",
14216      "Launch...",
14217      "Region of Interest...",
14218      (char *) NULL
14219    },
14220    *MiscellanyMenu[] =
14221    {
14222      "Image Info",
14223      "Zoom Image",
14224      "Show Preview...",
14225      "Show Histogram",
14226      "Show Matte",
14227      "Background...",
14228      "Slide Show...",
14229      "Preferences...",
14230      (char *) NULL
14231    },
14232    *HelpMenu[] =
14233    {
14234      "Overview",
14235      "Browse Documentation",
14236      "About Display",
14237      (char *) NULL
14238    },
14239    *ShortCutsMenu[] =
14240    {
14241      "Next",
14242      "Former",
14243      "Open...",
14244      "Save...",
14245      "Print...",
14246      "Undo",
14247      "Restore",
14248      "Image Info",
14249      "Quit",
14250      (char *) NULL
14251    },
14252    *VirtualMenu[] =
14253    {
14254      "Image Info",
14255      "Print",
14256      "Next",
14257      "Quit",
14258      (char *) NULL
14259    };
14260
14261  static const char
14262    **Menus[MagickMenus] =
14263    {
14264      FileMenu,
14265      EditMenu,
14266      ViewMenu,
14267      TransformMenu,
14268      EnhanceMenu,
14269      EffectsMenu,
14270      FXMenu,
14271      ImageEditMenu,
14272      MiscellanyMenu,
14273      HelpMenu
14274    };
14275
14276  static CommandType
14277    CommandMenus[] =
14278    {
14279      NullCommand,
14280      NullCommand,
14281      NullCommand,
14282      NullCommand,
14283      NullCommand,
14284      NullCommand,
14285      NullCommand,
14286      NullCommand,
14287      NullCommand,
14288      NullCommand,
14289    },
14290    FileCommands[] =
14291    {
14292      OpenCommand,
14293      NextCommand,
14294      FormerCommand,
14295      SelectCommand,
14296      SaveCommand,
14297      PrintCommand,
14298      DeleteCommand,
14299      NewCommand,
14300      VisualDirectoryCommand,
14301      QuitCommand
14302    },
14303    EditCommands[] =
14304    {
14305      UndoCommand,
14306      RedoCommand,
14307      CutCommand,
14308      CopyCommand,
14309      PasteCommand
14310    },
14311    ViewCommands[] =
14312    {
14313      HalfSizeCommand,
14314      OriginalSizeCommand,
14315      DoubleSizeCommand,
14316      ResizeCommand,
14317      ApplyCommand,
14318      RefreshCommand,
14319      RestoreCommand
14320    },
14321    TransformCommands[] =
14322    {
14323      CropCommand,
14324      ChopCommand,
14325      FlopCommand,
14326      FlipCommand,
14327      RotateRightCommand,
14328      RotateLeftCommand,
14329      RotateCommand,
14330      ShearCommand,
14331      RollCommand,
14332      TrimCommand
14333    },
14334    EnhanceCommands[] =
14335    {
14336      HueCommand,
14337      SaturationCommand,
14338      BrightnessCommand,
14339      GammaCommand,
14340      SpiffCommand,
14341      DullCommand,
14342      ContrastStretchCommand,
14343      SigmoidalContrastCommand,
14344      NormalizeCommand,
14345      EqualizeCommand,
14346      NegateCommand,
14347      GrayscaleCommand,
14348      MapCommand,
14349      QuantizeCommand
14350    },
14351    EffectsCommands[] =
14352    {
14353      DespeckleCommand,
14354      EmbossCommand,
14355      ReduceNoiseCommand,
14356      AddNoiseCommand,
14357      SharpenCommand,
14358      BlurCommand,
14359      ThresholdCommand,
14360      EdgeDetectCommand,
14361      SpreadCommand,
14362      ShadeCommand,
14363      RaiseCommand,
14364      SegmentCommand
14365    },
14366    FXCommands[] =
14367    {
14368      SolarizeCommand,
14369      SepiaToneCommand,
14370      SwirlCommand,
14371      ImplodeCommand,
14372      VignetteCommand,
14373      WaveCommand,
14374      OilPaintCommand,
14375      CharcoalDrawCommand
14376    },
14377    ImageEditCommands[] =
14378    {
14379      AnnotateCommand,
14380      DrawCommand,
14381      ColorCommand,
14382      MatteCommand,
14383      CompositeCommand,
14384      AddBorderCommand,
14385      AddFrameCommand,
14386      CommentCommand,
14387      LaunchCommand,
14388      RegionofInterestCommand
14389    },
14390    MiscellanyCommands[] =
14391    {
14392      InfoCommand,
14393      ZoomCommand,
14394      ShowPreviewCommand,
14395      ShowHistogramCommand,
14396      ShowMatteCommand,
14397      BackgroundCommand,
14398      SlideShowCommand,
14399      PreferencesCommand
14400    },
14401    HelpCommands[] =
14402    {
14403      HelpCommand,
14404      BrowseDocumentationCommand,
14405      VersionCommand
14406    },
14407    ShortCutsCommands[] =
14408    {
14409      NextCommand,
14410      FormerCommand,
14411      OpenCommand,
14412      SaveCommand,
14413      PrintCommand,
14414      UndoCommand,
14415      RestoreCommand,
14416      InfoCommand,
14417      QuitCommand
14418    },
14419    VirtualCommands[] =
14420    {
14421      InfoCommand,
14422      PrintCommand,
14423      NextCommand,
14424      QuitCommand
14425    };
14426
14427  static CommandType
14428    *Commands[MagickMenus] =
14429    {
14430      FileCommands,
14431      EditCommands,
14432      ViewCommands,
14433      TransformCommands,
14434      EnhanceCommands,
14435      EffectsCommands,
14436      FXCommands,
14437      ImageEditCommands,
14438      MiscellanyCommands,
14439      HelpCommands
14440    };
14441
14442  char
14443    command[MaxTextExtent],
14444    *directory,
14445    geometry[MaxTextExtent],
14446    resource_name[MaxTextExtent];
14447
14448  CommandType
14449    command_type;
14450
14451  Image
14452    *display_image,
14453    *nexus;
14454
14455  int
14456    entry,
14457    id;
14458
14459  KeySym
14460    key_symbol;
14461
14462  MagickStatusType
14463    context_mask,
14464    status;
14465
14466  RectangleInfo
14467    geometry_info;
14468
14469  register int
14470    i;
14471
14472  static char
14473    working_directory[MaxTextExtent];
14474
14475  static XPoint
14476    vid_info;
14477
14478  static XWindowInfo
14479    *magick_windows[MaxXWindows];
14480
14481  static unsigned int
14482    number_windows;
14483
14484  struct stat
14485    attributes;
14486
14487  time_t
14488    timer,
14489    timestamp,
14490    update_time;
14491
14492  unsigned int
14493    height,
14494    width;
14495
14496  size_t
14497    delay;
14498
14499  WarningHandler
14500    warning_handler;
14501
14502  Window
14503    root_window;
14504
14505  XClassHint
14506    *class_hints;
14507
14508  XEvent
14509    event;
14510
14511  XFontStruct
14512    *font_info;
14513
14514  XGCValues
14515    context_values;
14516
14517  XPixelInfo
14518    *icon_pixel,
14519    *pixel;
14520
14521  XResourceInfo
14522    *icon_resources;
14523
14524  XStandardColormap
14525    *icon_map,
14526    *map_info;
14527
14528  XVisualInfo
14529    *icon_visual,
14530    *visual_info;
14531
14532  XWindowChanges
14533    window_changes;
14534
14535  XWindows
14536    *windows;
14537
14538  XWMHints
14539    *manager_hints;
14540
14541  assert(image != (Image **) NULL);
14542  assert((*image)->signature == MagickSignature);
14543  if ((*image)->debug != MagickFalse)
14544    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14545  display_image=(*image);
14546  warning_handler=(WarningHandler) NULL;
14547  windows=XSetWindows((XWindows *) ~0);
14548  if (windows != (XWindows *) NULL)
14549    {
14550      int
14551        status;
14552
14553      status=chdir(working_directory);
14554      if (status == -1)
14555        (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14556          "UnableToOpenFile","%s",working_directory);
14557      warning_handler=resource_info->display_warnings ?
14558        SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14559      warning_handler=resource_info->display_warnings ?
14560        SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14561    }
14562  else
14563    {
14564      /*
14565        Allocate windows structure.
14566      */
14567      resource_info->colors=display_image->colors;
14568      windows=XSetWindows(XInitializeWindows(display,resource_info));
14569      if (windows == (XWindows *) NULL)
14570        ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14571          (*image)->filename);
14572      /*
14573        Initialize window id's.
14574      */
14575      number_windows=0;
14576      magick_windows[number_windows++]=(&windows->icon);
14577      magick_windows[number_windows++]=(&windows->backdrop);
14578      magick_windows[number_windows++]=(&windows->image);
14579      magick_windows[number_windows++]=(&windows->info);
14580      magick_windows[number_windows++]=(&windows->command);
14581      magick_windows[number_windows++]=(&windows->widget);
14582      magick_windows[number_windows++]=(&windows->popup);
14583      magick_windows[number_windows++]=(&windows->magnify);
14584      magick_windows[number_windows++]=(&windows->pan);
14585      for (i=0; i < (int) number_windows; i++)
14586        magick_windows[i]->id=(Window) NULL;
14587      vid_info.x=0;
14588      vid_info.y=0;
14589    }
14590  /*
14591    Initialize font info.
14592  */
14593  if (windows->font_info != (XFontStruct *) NULL)
14594    (void) XFreeFont(display,windows->font_info);
14595  windows->font_info=XBestFont(display,resource_info,MagickFalse);
14596  if (windows->font_info == (XFontStruct *) NULL)
14597    ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14598      resource_info->font);
14599  /*
14600    Initialize Standard Colormap.
14601  */
14602  map_info=windows->map_info;
14603  icon_map=windows->icon_map;
14604  visual_info=windows->visual_info;
14605  icon_visual=windows->icon_visual;
14606  pixel=windows->pixel_info;
14607  icon_pixel=windows->icon_pixel;
14608  font_info=windows->font_info;
14609  icon_resources=windows->icon_resources;
14610  class_hints=windows->class_hints;
14611  manager_hints=windows->manager_hints;
14612  root_window=XRootWindow(display,visual_info->screen);
14613  nexus=NewImageList();
14614  if (display_image->debug != MagickFalse)
14615    {
14616      (void) LogMagickEvent(X11Event,GetMagickModule(),
14617        "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14618        (double) display_image->scene,(double) display_image->columns,
14619        (double) display_image->rows);
14620      if (display_image->colors != 0)
14621        (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14622          display_image->colors);
14623      (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14624        display_image->magick);
14625    }
14626  XMakeStandardColormap(display,visual_info,resource_info,display_image,
14627    map_info,pixel,exception);
14628  display_image->taint=MagickFalse;
14629  /*
14630    Initialize graphic context.
14631  */
14632  windows->context.id=(Window) NULL;
14633  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14634    resource_info,&windows->context);
14635  (void) CloneString(&class_hints->res_name,resource_info->client_name);
14636  (void) CloneString(&class_hints->res_class,resource_info->client_name);
14637  class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14638  manager_hints->flags=InputHint | StateHint;
14639  manager_hints->input=MagickFalse;
14640  manager_hints->initial_state=WithdrawnState;
14641  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14642    &windows->context);
14643  if (display_image->debug != MagickFalse)
14644    (void) LogMagickEvent(X11Event,GetMagickModule(),
14645      "Window id: 0x%lx (context)",windows->context.id);
14646  context_values.background=pixel->background_color.pixel;
14647  context_values.font=font_info->fid;
14648  context_values.foreground=pixel->foreground_color.pixel;
14649  context_values.graphics_exposures=MagickFalse;
14650  context_mask=(MagickStatusType)
14651    (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14652  if (pixel->annotate_context != (GC) NULL)
14653    (void) XFreeGC(display,pixel->annotate_context);
14654  pixel->annotate_context=XCreateGC(display,windows->context.id,
14655    context_mask,&context_values);
14656  if (pixel->annotate_context == (GC) NULL)
14657    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14658      display_image->filename);
14659  context_values.background=pixel->depth_color.pixel;
14660  if (pixel->widget_context != (GC) NULL)
14661    (void) XFreeGC(display,pixel->widget_context);
14662  pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14663    &context_values);
14664  if (pixel->widget_context == (GC) NULL)
14665    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14666      display_image->filename);
14667  context_values.background=pixel->foreground_color.pixel;
14668  context_values.foreground=pixel->background_color.pixel;
14669  context_values.plane_mask=context_values.background ^
14670    context_values.foreground;
14671  if (pixel->highlight_context != (GC) NULL)
14672    (void) XFreeGC(display,pixel->highlight_context);
14673  pixel->highlight_context=XCreateGC(display,windows->context.id,
14674    (size_t) (context_mask | GCPlaneMask),&context_values);
14675  if (pixel->highlight_context == (GC) NULL)
14676    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14677      display_image->filename);
14678  (void) XDestroyWindow(display,windows->context.id);
14679  /*
14680    Initialize icon window.
14681  */
14682  XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14683    icon_resources,&windows->icon);
14684  windows->icon.geometry=resource_info->icon_geometry;
14685  XBestIconSize(display,&windows->icon,display_image);
14686  windows->icon.attributes.colormap=XDefaultColormap(display,
14687    icon_visual->screen);
14688  windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14689  manager_hints->flags=InputHint | StateHint;
14690  manager_hints->input=MagickFalse;
14691  manager_hints->initial_state=IconicState;
14692  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14693    &windows->icon);
14694  if (display_image->debug != MagickFalse)
14695    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14696      windows->icon.id);
14697  /*
14698    Initialize graphic context for icon window.
14699  */
14700  if (icon_pixel->annotate_context != (GC) NULL)
14701    (void) XFreeGC(display,icon_pixel->annotate_context);
14702  context_values.background=icon_pixel->background_color.pixel;
14703  context_values.foreground=icon_pixel->foreground_color.pixel;
14704  icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14705    (size_t) (GCBackground | GCForeground),&context_values);
14706  if (icon_pixel->annotate_context == (GC) NULL)
14707    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14708      display_image->filename);
14709  windows->icon.annotate_context=icon_pixel->annotate_context;
14710  /*
14711    Initialize Image window.
14712  */
14713  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14714    &windows->image);
14715  windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14716  if (resource_info->use_shared_memory == MagickFalse)
14717    windows->image.shared_memory=MagickFalse;
14718  if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14719    {
14720      char
14721        *title;
14722
14723      title=InterpretImageProperties(resource_info->image_info,display_image,
14724        resource_info->title,exception);
14725      (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
14726      (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
14727      title=DestroyString(title);
14728    }
14729  else
14730    {
14731      char
14732        filename[MaxTextExtent];
14733
14734      /*
14735        Window name is the base of the filename.
14736      */
14737      GetPathComponent(display_image->magick_filename,TailPath,filename);
14738      if (display_image->scene == 0)
14739        (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14740          "%s: %s",MagickPackageName,filename);
14741      else
14742        (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14743          "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14744          (double) display_image->scene,(double) GetImageListLength(
14745          display_image));
14746      (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
14747    }
14748  if (resource_info->immutable)
14749    windows->image.immutable=MagickTrue;
14750  windows->image.use_pixmap=resource_info->use_pixmap;
14751  windows->image.geometry=resource_info->image_geometry;
14752  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14753    XDisplayWidth(display,visual_info->screen),
14754    XDisplayHeight(display,visual_info->screen));
14755  geometry_info.width=display_image->columns;
14756  geometry_info.height=display_image->rows;
14757  geometry_info.x=0;
14758  geometry_info.y=0;
14759  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14760    &geometry_info.width,&geometry_info.height);
14761  windows->image.width=(unsigned int) geometry_info.width;
14762  windows->image.height=(unsigned int) geometry_info.height;
14763  windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14764    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14765    KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14766    PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14767  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14768    resource_info,&windows->backdrop);
14769  if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14770    {
14771      /*
14772        Initialize backdrop window.
14773      */
14774      windows->backdrop.x=0;
14775      windows->backdrop.y=0;
14776      (void) CloneString(&windows->backdrop.name,"Backdrop");
14777      windows->backdrop.flags=(size_t) (USSize | USPosition);
14778      windows->backdrop.width=(unsigned int)
14779        XDisplayWidth(display,visual_info->screen);
14780      windows->backdrop.height=(unsigned int)
14781        XDisplayHeight(display,visual_info->screen);
14782      windows->backdrop.border_width=0;
14783      windows->backdrop.immutable=MagickTrue;
14784      windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14785        ButtonReleaseMask;
14786      windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14787        StructureNotifyMask;
14788      manager_hints->flags=IconWindowHint | InputHint | StateHint;
14789      manager_hints->icon_window=windows->icon.id;
14790      manager_hints->input=MagickTrue;
14791      manager_hints->initial_state=resource_info->iconic ? IconicState :
14792        NormalState;
14793      XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14794        &windows->backdrop);
14795      if (display_image->debug != MagickFalse)
14796        (void) LogMagickEvent(X11Event,GetMagickModule(),
14797          "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14798      (void) XMapWindow(display,windows->backdrop.id);
14799      (void) XClearWindow(display,windows->backdrop.id);
14800      if (windows->image.id != (Window) NULL)
14801        {
14802          (void) XDestroyWindow(display,windows->image.id);
14803          windows->image.id=(Window) NULL;
14804        }
14805      /*
14806        Position image in the center the backdrop.
14807      */
14808      windows->image.flags|=USPosition;
14809      windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14810        (windows->image.width/2);
14811      windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14812        (windows->image.height/2);
14813    }
14814  manager_hints->flags=IconWindowHint | InputHint | StateHint;
14815  manager_hints->icon_window=windows->icon.id;
14816  manager_hints->input=MagickTrue;
14817  manager_hints->initial_state=resource_info->iconic ? IconicState :
14818    NormalState;
14819  if (windows->group_leader.id != (Window) NULL)
14820    {
14821      /*
14822        Follow the leader.
14823      */
14824      manager_hints->flags|=WindowGroupHint;
14825      manager_hints->window_group=windows->group_leader.id;
14826      (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14827      if (display_image->debug != MagickFalse)
14828        (void) LogMagickEvent(X11Event,GetMagickModule(),
14829          "Window id: 0x%lx (group leader)",windows->group_leader.id);
14830    }
14831  XMakeWindow(display,
14832    (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14833    argv,argc,class_hints,manager_hints,&windows->image);
14834  (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14835    XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14836  if (windows->group_leader.id != (Window) NULL)
14837    (void) XSetTransientForHint(display,windows->image.id,
14838      windows->group_leader.id);
14839  if (display_image->debug != MagickFalse)
14840    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14841      windows->image.id);
14842  /*
14843    Initialize Info widget.
14844  */
14845  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14846    &windows->info);
14847  (void) CloneString(&windows->info.name,"Info");
14848  (void) CloneString(&windows->info.icon_name,"Info");
14849  windows->info.border_width=1;
14850  windows->info.x=2;
14851  windows->info.y=2;
14852  windows->info.flags|=PPosition;
14853  windows->info.attributes.win_gravity=UnmapGravity;
14854  windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14855    StructureNotifyMask;
14856  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14857  manager_hints->input=MagickFalse;
14858  manager_hints->initial_state=NormalState;
14859  manager_hints->window_group=windows->image.id;
14860  XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14861    &windows->info);
14862  windows->info.highlight_stipple=XCreateBitmapFromData(display,
14863    windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14864  windows->info.shadow_stipple=XCreateBitmapFromData(display,
14865    windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14866  (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14867  if (windows->image.mapped != MagickFalse)
14868    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14869  if (display_image->debug != MagickFalse)
14870    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14871      windows->info.id);
14872  /*
14873    Initialize Command widget.
14874  */
14875  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14876    resource_info,&windows->command);
14877  windows->command.data=MagickMenus;
14878  (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14879  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
14880    resource_info->client_name);
14881  windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14882    resource_name,"geometry",(char *) NULL);
14883  (void) CloneString(&windows->command.name,MagickTitle);
14884  windows->command.border_width=0;
14885  windows->command.flags|=PPosition;
14886  windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14887    ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14888    OwnerGrabButtonMask | StructureNotifyMask;
14889  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14890  manager_hints->input=MagickTrue;
14891  manager_hints->initial_state=NormalState;
14892  manager_hints->window_group=windows->image.id;
14893  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14894    &windows->command);
14895  windows->command.highlight_stipple=XCreateBitmapFromData(display,
14896    windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14897    HighlightHeight);
14898  windows->command.shadow_stipple=XCreateBitmapFromData(display,
14899    windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14900  (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14901  if (windows->command.mapped != MagickFalse)
14902    (void) XMapRaised(display,windows->command.id);
14903  if (display_image->debug != MagickFalse)
14904    (void) LogMagickEvent(X11Event,GetMagickModule(),
14905      "Window id: 0x%lx (command)",windows->command.id);
14906  /*
14907    Initialize Widget window.
14908  */
14909  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14910    resource_info,&windows->widget);
14911  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
14912    resource_info->client_name);
14913  windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14914    resource_name,"geometry",(char *) NULL);
14915  windows->widget.border_width=0;
14916  windows->widget.flags|=PPosition;
14917  windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14918    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14919    KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14920    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->widget);
14927  windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14928    windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14929  windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14930    windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14931  (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14932  if (display_image->debug != MagickFalse)
14933    (void) LogMagickEvent(X11Event,GetMagickModule(),
14934      "Window id: 0x%lx (widget)",windows->widget.id);
14935  /*
14936    Initialize popup window.
14937  */
14938  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14939    resource_info,&windows->popup);
14940  windows->popup.border_width=0;
14941  windows->popup.flags|=PPosition;
14942  windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14943    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14944    KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14945  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14946  manager_hints->input=MagickTrue;
14947  manager_hints->initial_state=NormalState;
14948  manager_hints->window_group=windows->image.id;
14949  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14950    &windows->popup);
14951  windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14952    windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14953  windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14954    windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14955  (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14956  if (display_image->debug != MagickFalse)
14957    (void) LogMagickEvent(X11Event,GetMagickModule(),
14958      "Window id: 0x%lx (pop up)",windows->popup.id);
14959  /*
14960    Initialize Magnify window and cursor.
14961  */
14962  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14963    resource_info,&windows->magnify);
14964  if (resource_info->use_shared_memory == MagickFalse)
14965    windows->magnify.shared_memory=MagickFalse;
14966  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
14967    resource_info->client_name);
14968  windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14969    resource_name,"geometry",(char *) NULL);
14970  (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14971    resource_info->magnify);
14972  if (windows->magnify.cursor != (Cursor) NULL)
14973    (void) XFreeCursor(display,windows->magnify.cursor);
14974  windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14975    map_info->colormap,resource_info->background_color,
14976    resource_info->foreground_color);
14977  if (windows->magnify.cursor == (Cursor) NULL)
14978    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14979      display_image->filename);
14980  windows->magnify.width=MagnifySize;
14981  windows->magnify.height=MagnifySize;
14982  windows->magnify.flags|=PPosition;
14983  windows->magnify.min_width=MagnifySize;
14984  windows->magnify.min_height=MagnifySize;
14985  windows->magnify.width_inc=MagnifySize;
14986  windows->magnify.height_inc=MagnifySize;
14987  windows->magnify.data=resource_info->magnify;
14988  windows->magnify.attributes.cursor=windows->magnify.cursor;
14989  windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14990    ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14991    StructureNotifyMask;
14992  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14993  manager_hints->input=MagickTrue;
14994  manager_hints->initial_state=NormalState;
14995  manager_hints->window_group=windows->image.id;
14996  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14997    &windows->magnify);
14998  if (display_image->debug != MagickFalse)
14999    (void) LogMagickEvent(X11Event,GetMagickModule(),
15000      "Window id: 0x%lx (magnify)",windows->magnify.id);
15001  (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
15002  /*
15003    Initialize panning window.
15004  */
15005  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
15006    resource_info,&windows->pan);
15007  (void) CloneString(&windows->pan.name,"Pan Icon");
15008  windows->pan.width=windows->icon.width;
15009  windows->pan.height=windows->icon.height;
15010  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
15011    resource_info->client_name);
15012  windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
15013    resource_name,"geometry",(char *) NULL);
15014  (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
15015    &windows->pan.width,&windows->pan.height);
15016  windows->pan.flags|=PPosition;
15017  windows->pan.immutable=MagickTrue;
15018  windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
15019    ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
15020    StructureNotifyMask;
15021  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
15022  manager_hints->input=MagickFalse;
15023  manager_hints->initial_state=NormalState;
15024  manager_hints->window_group=windows->image.id;
15025  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
15026    &windows->pan);
15027  if (display_image->debug != MagickFalse)
15028    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
15029      windows->pan.id);
15030  (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
15031  if (windows->info.mapped != MagickFalse)
15032    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15033  if ((windows->image.mapped == MagickFalse) ||
15034      (windows->backdrop.id != (Window) NULL))
15035    (void) XMapWindow(display,windows->image.id);
15036  /*
15037    Set our progress monitor and warning handlers.
15038  */
15039  if (warning_handler == (WarningHandler) NULL)
15040    {
15041      warning_handler=resource_info->display_warnings ?
15042        SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
15043      warning_handler=resource_info->display_warnings ?
15044        SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
15045    }
15046  /*
15047    Initialize Image and Magnify X images.
15048  */
15049  windows->image.x=0;
15050  windows->image.y=0;
15051  windows->magnify.shape=MagickFalse;
15052  width=(unsigned int) display_image->columns;
15053  height=(unsigned int) display_image->rows;
15054  if ((display_image->columns != width) || (display_image->rows != height))
15055    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15056      display_image->filename);
15057  status=XMakeImage(display,resource_info,&windows->image,display_image,
15058    width,height,exception);
15059  if (status == MagickFalse)
15060    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15061      display_image->filename);
15062  status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
15063    windows->magnify.width,windows->magnify.height,exception);
15064  if (status == MagickFalse)
15065    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15066      display_image->filename);
15067  if (windows->magnify.mapped != MagickFalse)
15068    (void) XMapRaised(display,windows->magnify.id);
15069  if (windows->pan.mapped != MagickFalse)
15070    (void) XMapRaised(display,windows->pan.id);
15071  windows->image.window_changes.width=(int) display_image->columns;
15072  windows->image.window_changes.height=(int) display_image->rows;
15073  (void) XConfigureImage(display,resource_info,windows,display_image,exception);
15074  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15075  (void) XSync(display,MagickFalse);
15076  /*
15077    Respond to events.
15078  */
15079  delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
15080  timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15081  update_time=0;
15082  if (resource_info->update != MagickFalse)
15083    {
15084      MagickBooleanType
15085        status;
15086
15087      /*
15088        Determine when file data was last modified.
15089      */
15090      status=GetPathAttributes(display_image->filename,&attributes);
15091      if (status != MagickFalse)
15092        update_time=attributes.st_mtime;
15093    }
15094  *state&=(~FormerImageState);
15095  *state&=(~MontageImageState);
15096  *state&=(~NextImageState);
15097  do
15098  {
15099    /*
15100      Handle a window event.
15101    */
15102    if (windows->image.mapped != MagickFalse)
15103      if ((display_image->delay != 0) || (resource_info->update != 0))
15104        {
15105          if (timer < time((time_t *) NULL))
15106            {
15107              if (resource_info->update == MagickFalse)
15108                *state|=NextImageState | ExitState;
15109              else
15110                {
15111                  MagickBooleanType
15112                    status;
15113
15114                  /*
15115                    Determine if image file was modified.
15116                  */
15117                  status=GetPathAttributes(display_image->filename,&attributes);
15118                  if (status != MagickFalse)
15119                    if (update_time != attributes.st_mtime)
15120                      {
15121                        /*
15122                          Redisplay image.
15123                        */
15124                        (void) FormatLocaleString(
15125                          resource_info->image_info->filename,MaxTextExtent,
15126                          "%s:%s",display_image->magick,
15127                          display_image->filename);
15128                        nexus=ReadImage(resource_info->image_info,exception);
15129                        if (nexus != (Image *) NULL)
15130                          {
15131                            nexus=DestroyImage(nexus);
15132                            *state|=NextImageState | ExitState;
15133                          }
15134                      }
15135                  delay=display_image->delay/MagickMax(
15136                    display_image->ticks_per_second,1L);
15137                  timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15138                }
15139            }
15140          if (XEventsQueued(display,QueuedAfterFlush) == 0)
15141            {
15142              /*
15143                Do not block if delay > 0.
15144              */
15145              XDelay(display,SuspendTime << 2);
15146              continue;
15147            }
15148        }
15149    timestamp=time((time_t *) NULL);
15150    (void) XNextEvent(display,&event);
15151    if (windows->image.stasis == MagickFalse)
15152      windows->image.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15153        MagickTrue : MagickFalse;
15154    if (windows->magnify.stasis == MagickFalse)
15155      windows->magnify.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15156        MagickTrue : MagickFalse;
15157    if (event.xany.window == windows->command.id)
15158      {
15159        /*
15160          Select a command from the Command widget.
15161        */
15162        id=XCommandWidget(display,windows,CommandMenu,&event);
15163        if (id < 0)
15164          continue;
15165        (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15166        command_type=CommandMenus[id];
15167        if (id < MagickMenus)
15168          {
15169            /*
15170              Select a command from a pop-up menu.
15171            */
15172            entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15173              command);
15174            if (entry < 0)
15175              continue;
15176            (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15177            command_type=Commands[id][entry];
15178          }
15179        if (command_type != NullCommand)
15180          nexus=XMagickCommand(display,resource_info,windows,command_type,
15181            &display_image,exception);
15182        continue;
15183      }
15184    switch (event.type)
15185    {
15186      case ButtonPress:
15187      {
15188        if (display_image->debug != MagickFalse)
15189          (void) LogMagickEvent(X11Event,GetMagickModule(),
15190            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15191            event.xbutton.button,event.xbutton.x,event.xbutton.y);
15192        if ((event.xbutton.button == Button3) &&
15193            (event.xbutton.state & Mod1Mask))
15194          {
15195            /*
15196              Convert Alt-Button3 to Button2.
15197            */
15198            event.xbutton.button=Button2;
15199            event.xbutton.state&=(~Mod1Mask);
15200          }
15201        if (event.xbutton.window == windows->backdrop.id)
15202          {
15203            (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15204              event.xbutton.time);
15205            break;
15206          }
15207        if (event.xbutton.window == windows->image.id)
15208          {
15209            switch (event.xbutton.button)
15210            {
15211              case Button1:
15212              {
15213                if (resource_info->immutable)
15214                  {
15215                    /*
15216                      Select a command from the Virtual menu.
15217                    */
15218                    entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15219                      command);
15220                    if (entry >= 0)
15221                      nexus=XMagickCommand(display,resource_info,windows,
15222                        VirtualCommands[entry],&display_image,exception);
15223                    break;
15224                  }
15225                /*
15226                  Map/unmap Command widget.
15227                */
15228                if (windows->command.mapped != MagickFalse)
15229                  (void) XWithdrawWindow(display,windows->command.id,
15230                    windows->command.screen);
15231                else
15232                  {
15233                    (void) XCommandWidget(display,windows,CommandMenu,
15234                      (XEvent *) NULL);
15235                    (void) XMapRaised(display,windows->command.id);
15236                  }
15237                break;
15238              }
15239              case Button2:
15240              {
15241                /*
15242                  User pressed the image magnify button.
15243                */
15244                (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15245                  &display_image,exception);
15246                XMagnifyImage(display,windows,&event,exception);
15247                break;
15248              }
15249              case Button3:
15250              {
15251                if (resource_info->immutable)
15252                  {
15253                    /*
15254                      Select a command from the Virtual menu.
15255                    */
15256                    entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15257                      command);
15258                    if (entry >= 0)
15259                      nexus=XMagickCommand(display,resource_info,windows,
15260                        VirtualCommands[entry],&display_image,exception);
15261                    break;
15262                  }
15263                if (display_image->montage != (char *) NULL)
15264                  {
15265                    /*
15266                      Open or delete a tile from a visual image directory.
15267                    */
15268                    nexus=XTileImage(display,resource_info,windows,
15269                      display_image,&event,exception);
15270                    if (nexus != (Image *) NULL)
15271                      *state|=MontageImageState | NextImageState | ExitState;
15272                    vid_info.x=(short int) windows->image.x;
15273                    vid_info.y=(short int) windows->image.y;
15274                    break;
15275                  }
15276                /*
15277                  Select a command from the Short Cuts menu.
15278                */
15279                entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15280                  command);
15281                if (entry >= 0)
15282                  nexus=XMagickCommand(display,resource_info,windows,
15283                    ShortCutsCommands[entry],&display_image,exception);
15284                break;
15285              }
15286              case Button4:
15287              {
15288                /*
15289                  Wheel up.
15290                */
15291                XTranslateImage(display,windows,*image,XK_Up);
15292                break;
15293              }
15294              case Button5:
15295              {
15296                /*
15297                  Wheel down.
15298                */
15299                XTranslateImage(display,windows,*image,XK_Down);
15300                break;
15301              }
15302              default:
15303                break;
15304            }
15305            break;
15306          }
15307        if (event.xbutton.window == windows->magnify.id)
15308          {
15309            int
15310              factor;
15311
15312            static const char
15313              *MagnifyMenu[] =
15314              {
15315                "2",
15316                "4",
15317                "5",
15318                "6",
15319                "7",
15320                "8",
15321                "9",
15322                "3",
15323                (char *) NULL,
15324              };
15325
15326            static KeySym
15327              MagnifyCommands[] =
15328              {
15329                XK_2,
15330                XK_4,
15331                XK_5,
15332                XK_6,
15333                XK_7,
15334                XK_8,
15335                XK_9,
15336                XK_3
15337              };
15338
15339            /*
15340              Select a magnify factor from the pop-up menu.
15341            */
15342            factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15343            if (factor >= 0)
15344              XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor],
15345                exception);
15346            break;
15347          }
15348        if (event.xbutton.window == windows->pan.id)
15349          {
15350            switch (event.xbutton.button)
15351            {
15352              case Button4:
15353              {
15354                /*
15355                  Wheel up.
15356                */
15357                XTranslateImage(display,windows,*image,XK_Up);
15358                break;
15359              }
15360              case Button5:
15361              {
15362                /*
15363                  Wheel down.
15364                */
15365                XTranslateImage(display,windows,*image,XK_Down);
15366                break;
15367              }
15368              default:
15369              {
15370                XPanImage(display,windows,&event,exception);
15371                break;
15372              }
15373            }
15374            break;
15375          }
15376        delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15377          1L);
15378        timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15379        break;
15380      }
15381      case ButtonRelease:
15382      {
15383        if (display_image->debug != MagickFalse)
15384          (void) LogMagickEvent(X11Event,GetMagickModule(),
15385            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15386            event.xbutton.button,event.xbutton.x,event.xbutton.y);
15387        break;
15388      }
15389      case ClientMessage:
15390      {
15391        if (display_image->debug != MagickFalse)
15392          (void) LogMagickEvent(X11Event,GetMagickModule(),
15393            "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15394            event.xclient.message_type,event.xclient.format,(unsigned long)
15395            event.xclient.data.l[0]);
15396        if (event.xclient.message_type == windows->im_protocols)
15397          {
15398            if (*event.xclient.data.l == (long) windows->im_update_widget)
15399              {
15400                (void) CloneString(&windows->command.name,MagickTitle);
15401                windows->command.data=MagickMenus;
15402                (void) XCommandWidget(display,windows,CommandMenu,
15403                  (XEvent *) NULL);
15404                break;
15405              }
15406            if (*event.xclient.data.l == (long) windows->im_update_colormap)
15407              {
15408                /*
15409                  Update graphic context and window colormap.
15410                */
15411                for (i=0; i < (int) number_windows; i++)
15412                {
15413                  if (magick_windows[i]->id == windows->icon.id)
15414                    continue;
15415                  context_values.background=pixel->background_color.pixel;
15416                  context_values.foreground=pixel->foreground_color.pixel;
15417                  (void) XChangeGC(display,magick_windows[i]->annotate_context,
15418                    context_mask,&context_values);
15419                  (void) XChangeGC(display,magick_windows[i]->widget_context,
15420                    context_mask,&context_values);
15421                  context_values.background=pixel->foreground_color.pixel;
15422                  context_values.foreground=pixel->background_color.pixel;
15423                  context_values.plane_mask=context_values.background ^
15424                    context_values.foreground;
15425                  (void) XChangeGC(display,magick_windows[i]->highlight_context,
15426                    (size_t) (context_mask | GCPlaneMask),
15427                    &context_values);
15428                  magick_windows[i]->attributes.background_pixel=
15429                    pixel->background_color.pixel;
15430                  magick_windows[i]->attributes.border_pixel=
15431                    pixel->border_color.pixel;
15432                  magick_windows[i]->attributes.colormap=map_info->colormap;
15433                  (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15434                    (unsigned long) magick_windows[i]->mask,
15435                    &magick_windows[i]->attributes);
15436                }
15437                if (windows->pan.mapped != MagickFalse)
15438                  {
15439                    (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15440                      windows->pan.pixmap);
15441                    (void) XClearWindow(display,windows->pan.id);
15442                    XDrawPanRectangle(display,windows);
15443                  }
15444                if (windows->backdrop.id != (Window) NULL)
15445                  (void) XInstallColormap(display,map_info->colormap);
15446                break;
15447              }
15448            if (*event.xclient.data.l == (long) windows->im_former_image)
15449              {
15450                *state|=FormerImageState | ExitState;
15451                break;
15452              }
15453            if (*event.xclient.data.l == (long) windows->im_next_image)
15454              {
15455                *state|=NextImageState | ExitState;
15456                break;
15457              }
15458            if (*event.xclient.data.l == (long) windows->im_retain_colors)
15459              {
15460                *state|=RetainColorsState;
15461                break;
15462              }
15463            if (*event.xclient.data.l == (long) windows->im_exit)
15464              {
15465                *state|=ExitState;
15466                break;
15467              }
15468            break;
15469          }
15470        if (event.xclient.message_type == windows->dnd_protocols)
15471          {
15472            Atom
15473              selection,
15474              type;
15475
15476            int
15477              format,
15478              status;
15479
15480            unsigned char
15481              *data;
15482
15483            unsigned long
15484              after,
15485              length;
15486
15487            /*
15488              Display image named by the Drag-and-Drop selection.
15489            */
15490            if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15491              break;
15492            selection=XInternAtom(display,"DndSelection",MagickFalse);
15493            status=XGetWindowProperty(display,root_window,selection,0L,(long)
15494              MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15495              &length,&after,&data);
15496            if ((status != Success) || (length == 0))
15497              break;
15498            if (*event.xclient.data.l == 2)
15499              {
15500                /*
15501                  Offix DND.
15502                */
15503                (void) CopyMagickString(resource_info->image_info->filename,
15504                  (char *) data,MaxTextExtent);
15505              }
15506            else
15507              {
15508                /*
15509                  XDND.
15510                */
15511                if (strncmp((char *) data, "file:", 5) != 0)
15512                  {
15513                    (void) XFree((void *) data);
15514                    break;
15515                  }
15516                (void) CopyMagickString(resource_info->image_info->filename,
15517                  ((char *) data)+5,MaxTextExtent);
15518              }
15519            nexus=ReadImage(resource_info->image_info,exception);
15520            CatchException(exception);
15521            if (nexus != (Image *) NULL)
15522              *state|=NextImageState | ExitState;
15523            (void) XFree((void *) data);
15524            break;
15525          }
15526        /*
15527          If client window delete message, exit.
15528        */
15529        if (event.xclient.message_type != windows->wm_protocols)
15530          break;
15531        if (*event.xclient.data.l != (long) windows->wm_delete_window)
15532          break;
15533        (void) XWithdrawWindow(display,event.xclient.window,
15534          visual_info->screen);
15535        if (event.xclient.window == windows->image.id)
15536          {
15537            *state|=ExitState;
15538            break;
15539          }
15540        if (event.xclient.window == windows->pan.id)
15541          {
15542            /*
15543              Restore original image size when pan window is deleted.
15544            */
15545            windows->image.window_changes.width=windows->image.ximage->width;
15546            windows->image.window_changes.height=windows->image.ximage->height;
15547            (void) XConfigureImage(display,resource_info,windows,
15548              display_image,exception);
15549          }
15550        break;
15551      }
15552      case ConfigureNotify:
15553      {
15554        if (display_image->debug != MagickFalse)
15555          (void) LogMagickEvent(X11Event,GetMagickModule(),
15556            "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15557            event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15558            event.xconfigure.y,event.xconfigure.send_event);
15559        if (event.xconfigure.window == windows->image.id)
15560          {
15561            /*
15562              Image window has a new configuration.
15563            */
15564            if (event.xconfigure.send_event != 0)
15565              {
15566                XWindowChanges
15567                  window_changes;
15568
15569                /*
15570                  Position the transient windows relative of the Image window.
15571                */
15572                if (windows->command.geometry == (char *) NULL)
15573                  if (windows->command.mapped == MagickFalse)
15574                    {
15575                      windows->command.x=event.xconfigure.x-
15576                        windows->command.width-25;
15577                      windows->command.y=event.xconfigure.y;
15578                      XConstrainWindowPosition(display,&windows->command);
15579                      window_changes.x=windows->command.x;
15580                      window_changes.y=windows->command.y;
15581                      (void) XReconfigureWMWindow(display,windows->command.id,
15582                        windows->command.screen,(unsigned int) (CWX | CWY),
15583                        &window_changes);
15584                    }
15585                if (windows->widget.geometry == (char *) NULL)
15586                  if (windows->widget.mapped == MagickFalse)
15587                    {
15588                      windows->widget.x=event.xconfigure.x+
15589                        event.xconfigure.width/10;
15590                      windows->widget.y=event.xconfigure.y+
15591                        event.xconfigure.height/10;
15592                      XConstrainWindowPosition(display,&windows->widget);
15593                      window_changes.x=windows->widget.x;
15594                      window_changes.y=windows->widget.y;
15595                      (void) XReconfigureWMWindow(display,windows->widget.id,
15596                        windows->widget.screen,(unsigned int) (CWX | CWY),
15597                        &window_changes);
15598                    }
15599                if (windows->magnify.geometry == (char *) NULL)
15600                  if (windows->magnify.mapped == MagickFalse)
15601                    {
15602                      windows->magnify.x=event.xconfigure.x+
15603                        event.xconfigure.width+25;
15604                      windows->magnify.y=event.xconfigure.y;
15605                      XConstrainWindowPosition(display,&windows->magnify);
15606                      window_changes.x=windows->magnify.x;
15607                      window_changes.y=windows->magnify.y;
15608                      (void) XReconfigureWMWindow(display,windows->magnify.id,
15609                        windows->magnify.screen,(unsigned int) (CWX | CWY),
15610                        &window_changes);
15611                    }
15612                if (windows->pan.geometry == (char *) NULL)
15613                  if (windows->pan.mapped == MagickFalse)
15614                    {
15615                      windows->pan.x=event.xconfigure.x+
15616                        event.xconfigure.width+25;
15617                      windows->pan.y=event.xconfigure.y+
15618                        windows->magnify.height+50;
15619                      XConstrainWindowPosition(display,&windows->pan);
15620                      window_changes.x=windows->pan.x;
15621                      window_changes.y=windows->pan.y;
15622                      (void) XReconfigureWMWindow(display,windows->pan.id,
15623                        windows->pan.screen,(unsigned int) (CWX | CWY),
15624                        &window_changes);
15625                    }
15626              }
15627            if ((event.xconfigure.width == (int) windows->image.width) &&
15628                (event.xconfigure.height == (int) windows->image.height))
15629              break;
15630            windows->image.width=(unsigned int) event.xconfigure.width;
15631            windows->image.height=(unsigned int) event.xconfigure.height;
15632            windows->image.x=0;
15633            windows->image.y=0;
15634            if (display_image->montage != (char *) NULL)
15635              {
15636                windows->image.x=vid_info.x;
15637                windows->image.y=vid_info.y;
15638              }
15639            if ((windows->image.mapped != MagickFalse) &&
15640                (windows->image.stasis != MagickFalse))
15641              {
15642                /*
15643                  Update image window configuration.
15644                */
15645                windows->image.window_changes.width=event.xconfigure.width;
15646                windows->image.window_changes.height=event.xconfigure.height;
15647                (void) XConfigureImage(display,resource_info,windows,
15648                  display_image,exception);
15649              }
15650            /*
15651              Update pan window configuration.
15652            */
15653            if ((event.xconfigure.width < windows->image.ximage->width) ||
15654                (event.xconfigure.height < windows->image.ximage->height))
15655              {
15656                (void) XMapRaised(display,windows->pan.id);
15657                XDrawPanRectangle(display,windows);
15658              }
15659            else
15660              if (windows->pan.mapped != MagickFalse)
15661                (void) XWithdrawWindow(display,windows->pan.id,
15662                  windows->pan.screen);
15663            break;
15664          }
15665        if (event.xconfigure.window == windows->magnify.id)
15666          {
15667            unsigned int
15668              magnify;
15669
15670            /*
15671              Magnify window has a new configuration.
15672            */
15673            windows->magnify.width=(unsigned int) event.xconfigure.width;
15674            windows->magnify.height=(unsigned int) event.xconfigure.height;
15675            if (windows->magnify.mapped == MagickFalse)
15676              break;
15677            magnify=1;
15678            while ((int) magnify <= event.xconfigure.width)
15679              magnify<<=1;
15680            while ((int) magnify <= event.xconfigure.height)
15681              magnify<<=1;
15682            magnify>>=1;
15683            if (((int) magnify != event.xconfigure.width) ||
15684                ((int) magnify != event.xconfigure.height))
15685              {
15686                window_changes.width=(int) magnify;
15687                window_changes.height=(int) magnify;
15688                (void) XReconfigureWMWindow(display,windows->magnify.id,
15689                  windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15690                  &window_changes);
15691                break;
15692              }
15693            if ((windows->magnify.mapped != MagickFalse) &&
15694                (windows->magnify.stasis != MagickFalse))
15695              {
15696                status=XMakeImage(display,resource_info,&windows->magnify,
15697                  display_image,windows->magnify.width,windows->magnify.height,
15698                  exception);
15699                XMakeMagnifyImage(display,windows,exception);
15700              }
15701            break;
15702          }
15703        if ((windows->magnify.mapped != MagickFalse) &&
15704            (event.xconfigure.window == windows->pan.id))
15705          {
15706            /*
15707              Pan icon window has a new configuration.
15708            */
15709            if (event.xconfigure.send_event != 0)
15710              {
15711                windows->pan.x=event.xconfigure.x;
15712                windows->pan.y=event.xconfigure.y;
15713              }
15714            windows->pan.width=(unsigned int) event.xconfigure.width;
15715            windows->pan.height=(unsigned int) event.xconfigure.height;
15716            break;
15717          }
15718        if (event.xconfigure.window == windows->icon.id)
15719          {
15720            /*
15721              Icon window has a new configuration.
15722            */
15723            windows->icon.width=(unsigned int) event.xconfigure.width;
15724            windows->icon.height=(unsigned int) event.xconfigure.height;
15725            break;
15726          }
15727        break;
15728      }
15729      case DestroyNotify:
15730      {
15731        /*
15732          Group leader has exited.
15733        */
15734        if (display_image->debug != MagickFalse)
15735          (void) LogMagickEvent(X11Event,GetMagickModule(),
15736            "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15737        if (event.xdestroywindow.window == windows->group_leader.id)
15738          {
15739            *state|=ExitState;
15740            break;
15741          }
15742        break;
15743      }
15744      case EnterNotify:
15745      {
15746        /*
15747          Selectively install colormap.
15748        */
15749        if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15750          if (event.xcrossing.mode != NotifyUngrab)
15751            XInstallColormap(display,map_info->colormap);
15752        break;
15753      }
15754      case Expose:
15755      {
15756        if (display_image->debug != MagickFalse)
15757          (void) LogMagickEvent(X11Event,GetMagickModule(),
15758            "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15759            event.xexpose.width,event.xexpose.height,event.xexpose.x,
15760            event.xexpose.y);
15761        /*
15762          Refresh windows that are now exposed.
15763        */
15764        if ((event.xexpose.window == windows->image.id) &&
15765            (windows->image.mapped != MagickFalse))
15766          {
15767            XRefreshWindow(display,&windows->image,&event);
15768            delay=display_image->delay/MagickMax(
15769              display_image->ticks_per_second,1L);
15770            timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15771            break;
15772          }
15773        if ((event.xexpose.window == windows->magnify.id) &&
15774            (windows->magnify.mapped != MagickFalse))
15775          {
15776            XMakeMagnifyImage(display,windows,exception);
15777            break;
15778          }
15779        if (event.xexpose.window == windows->pan.id)
15780          {
15781            XDrawPanRectangle(display,windows);
15782            break;
15783          }
15784        if (event.xexpose.window == windows->icon.id)
15785          {
15786            XRefreshWindow(display,&windows->icon,&event);
15787            break;
15788          }
15789        break;
15790      }
15791      case KeyPress:
15792      {
15793        int
15794          length;
15795
15796        /*
15797          Respond to a user key press.
15798        */
15799        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15800          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15801        *(command+length)='\0';
15802        if (display_image->debug != MagickFalse)
15803          (void) LogMagickEvent(X11Event,GetMagickModule(),
15804            "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15805            key_symbol,command);
15806        if (event.xkey.window == windows->image.id)
15807          {
15808            command_type=XImageWindowCommand(display,resource_info,windows,
15809              event.xkey.state,key_symbol,&display_image,exception);
15810            if (command_type != NullCommand)
15811              nexus=XMagickCommand(display,resource_info,windows,command_type,
15812                &display_image,exception);
15813          }
15814        if (event.xkey.window == windows->magnify.id)
15815          XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol,
15816            exception);
15817        if (event.xkey.window == windows->pan.id)
15818          {
15819            if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15820              (void) XWithdrawWindow(display,windows->pan.id,
15821                windows->pan.screen);
15822            else
15823              if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15824                XTextViewWidget(display,resource_info,windows,MagickFalse,
15825                  "Help Viewer - Image Pan",ImagePanHelp);
15826              else
15827                XTranslateImage(display,windows,*image,key_symbol);
15828          }
15829        delay=display_image->delay/MagickMax(
15830          display_image->ticks_per_second,1L);
15831        timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15832        break;
15833      }
15834      case KeyRelease:
15835      {
15836        /*
15837          Respond to a user key release.
15838        */
15839        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15840          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15841        if (display_image->debug != MagickFalse)
15842          (void) LogMagickEvent(X11Event,GetMagickModule(),
15843            "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15844        break;
15845      }
15846      case LeaveNotify:
15847      {
15848        /*
15849          Selectively uninstall colormap.
15850        */
15851        if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15852          if (event.xcrossing.mode != NotifyUngrab)
15853            XUninstallColormap(display,map_info->colormap);
15854        break;
15855      }
15856      case MapNotify:
15857      {
15858        if (display_image->debug != MagickFalse)
15859          (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15860            event.xmap.window);
15861        if (event.xmap.window == windows->backdrop.id)
15862          {
15863            (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15864              CurrentTime);
15865            windows->backdrop.mapped=MagickTrue;
15866            break;
15867          }
15868        if (event.xmap.window == windows->image.id)
15869          {
15870            if (windows->backdrop.id != (Window) NULL)
15871              (void) XInstallColormap(display,map_info->colormap);
15872            if (LocaleCompare(display_image->magick,"LOGO") == 0)
15873              {
15874                if (LocaleCompare(display_image->filename,"LOGO") == 0)
15875                  nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15876              }
15877            if (((int) windows->image.width < windows->image.ximage->width) ||
15878                ((int) windows->image.height < windows->image.ximage->height))
15879              (void) XMapRaised(display,windows->pan.id);
15880            windows->image.mapped=MagickTrue;
15881            break;
15882          }
15883        if (event.xmap.window == windows->magnify.id)
15884          {
15885            XMakeMagnifyImage(display,windows,exception);
15886            windows->magnify.mapped=MagickTrue;
15887            (void) XWithdrawWindow(display,windows->info.id,
15888              windows->info.screen);
15889            break;
15890          }
15891        if (event.xmap.window == windows->pan.id)
15892          {
15893            XMakePanImage(display,resource_info,windows,display_image,
15894              exception);
15895            windows->pan.mapped=MagickTrue;
15896            break;
15897          }
15898        if (event.xmap.window == windows->info.id)
15899          {
15900            windows->info.mapped=MagickTrue;
15901            break;
15902          }
15903        if (event.xmap.window == windows->icon.id)
15904          {
15905            MagickBooleanType
15906              taint;
15907
15908            /*
15909              Create an icon image.
15910            */
15911            taint=display_image->taint;
15912            XMakeStandardColormap(display,icon_visual,icon_resources,
15913              display_image,icon_map,icon_pixel,exception);
15914            (void) XMakeImage(display,icon_resources,&windows->icon,
15915              display_image,windows->icon.width,windows->icon.height,
15916              exception);
15917            display_image->taint=taint;
15918            (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15919              windows->icon.pixmap);
15920            (void) XClearWindow(display,windows->icon.id);
15921            (void) XWithdrawWindow(display,windows->info.id,
15922              windows->info.screen);
15923            windows->icon.mapped=MagickTrue;
15924            break;
15925          }
15926        if (event.xmap.window == windows->command.id)
15927          {
15928            windows->command.mapped=MagickTrue;
15929            break;
15930          }
15931        if (event.xmap.window == windows->popup.id)
15932          {
15933            windows->popup.mapped=MagickTrue;
15934            break;
15935          }
15936        if (event.xmap.window == windows->widget.id)
15937          {
15938            windows->widget.mapped=MagickTrue;
15939            break;
15940          }
15941        break;
15942      }
15943      case MappingNotify:
15944      {
15945        (void) XRefreshKeyboardMapping(&event.xmapping);
15946        break;
15947      }
15948      case NoExpose:
15949        break;
15950      case PropertyNotify:
15951      {
15952        Atom
15953          type;
15954
15955        int
15956          format,
15957          status;
15958
15959        unsigned char
15960          *data;
15961
15962        unsigned long
15963          after,
15964          length;
15965
15966        if (display_image->debug != MagickFalse)
15967          (void) LogMagickEvent(X11Event,GetMagickModule(),
15968            "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15969            event.xproperty.atom,event.xproperty.state);
15970        if (event.xproperty.atom != windows->im_remote_command)
15971          break;
15972        /*
15973          Display image named by the remote command protocol.
15974        */
15975        status=XGetWindowProperty(display,event.xproperty.window,
15976          event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15977          AnyPropertyType,&type,&format,&length,&after,&data);
15978        if ((status != Success) || (length == 0))
15979          break;
15980        if (LocaleCompare((char *) data,"-quit") == 0)
15981          {
15982            XClientMessage(display,windows->image.id,windows->im_protocols,
15983              windows->im_exit,CurrentTime);
15984            (void) XFree((void *) data);
15985            break;
15986          }
15987        (void) CopyMagickString(resource_info->image_info->filename,
15988          (char *) data,MaxTextExtent);
15989        (void) XFree((void *) data);
15990        nexus=ReadImage(resource_info->image_info,exception);
15991        CatchException(exception);
15992        if (nexus != (Image *) NULL)
15993          *state|=NextImageState | ExitState;
15994        break;
15995      }
15996      case ReparentNotify:
15997      {
15998        if (display_image->debug != MagickFalse)
15999          (void) LogMagickEvent(X11Event,GetMagickModule(),
16000            "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
16001            event.xreparent.window);
16002        break;
16003      }
16004      case UnmapNotify:
16005      {
16006        if (display_image->debug != MagickFalse)
16007          (void) LogMagickEvent(X11Event,GetMagickModule(),
16008            "Unmap Notify: 0x%lx",event.xunmap.window);
16009        if (event.xunmap.window == windows->backdrop.id)
16010          {
16011            windows->backdrop.mapped=MagickFalse;
16012            break;
16013          }
16014        if (event.xunmap.window == windows->image.id)
16015          {
16016            windows->image.mapped=MagickFalse;
16017            break;
16018          }
16019        if (event.xunmap.window == windows->magnify.id)
16020          {
16021            windows->magnify.mapped=MagickFalse;
16022            break;
16023          }
16024        if (event.xunmap.window == windows->pan.id)
16025          {
16026            windows->pan.mapped=MagickFalse;
16027            break;
16028          }
16029        if (event.xunmap.window == windows->info.id)
16030          {
16031            windows->info.mapped=MagickFalse;
16032            break;
16033          }
16034        if (event.xunmap.window == windows->icon.id)
16035          {
16036            if (map_info->colormap == icon_map->colormap)
16037              XConfigureImageColormap(display,resource_info,windows,
16038                display_image,exception);
16039            (void) XFreeStandardColormap(display,icon_visual,icon_map,
16040              icon_pixel);
16041            windows->icon.mapped=MagickFalse;
16042            break;
16043          }
16044        if (event.xunmap.window == windows->command.id)
16045          {
16046            windows->command.mapped=MagickFalse;
16047            break;
16048          }
16049        if (event.xunmap.window == windows->popup.id)
16050          {
16051            if (windows->backdrop.id != (Window) NULL)
16052              (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16053                CurrentTime);
16054            windows->popup.mapped=MagickFalse;
16055            break;
16056          }
16057        if (event.xunmap.window == windows->widget.id)
16058          {
16059            if (windows->backdrop.id != (Window) NULL)
16060              (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16061                CurrentTime);
16062            windows->widget.mapped=MagickFalse;
16063            break;
16064          }
16065        break;
16066      }
16067      default:
16068      {
16069        if (display_image->debug != MagickFalse)
16070          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
16071            event.type);
16072        break;
16073      }
16074    }
16075  } while (!(*state & ExitState));
16076  if ((*state & ExitState) == 0)
16077    (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
16078      &display_image,exception);
16079  else
16080    if (resource_info->confirm_edit != MagickFalse)
16081      {
16082        /*
16083          Query user if image has changed.
16084        */
16085        if ((resource_info->immutable == MagickFalse) &&
16086            (display_image->taint != MagickFalse))
16087          {
16088            int
16089              status;
16090
16091            status=XConfirmWidget(display,windows,"Your image changed.",
16092              "Do you want to save it");
16093            if (status == 0)
16094              *state&=(~ExitState);
16095            else
16096              if (status > 0)
16097                (void) XMagickCommand(display,resource_info,windows,SaveCommand,
16098                  &display_image,exception);
16099          }
16100      }
16101  if ((windows->visual_info->klass == GrayScale) ||
16102      (windows->visual_info->klass == PseudoColor) ||
16103      (windows->visual_info->klass == DirectColor))
16104    {
16105      /*
16106        Withdraw pan and Magnify window.
16107      */
16108      if (windows->info.mapped != MagickFalse)
16109        (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16110      if (windows->magnify.mapped != MagickFalse)
16111        (void) XWithdrawWindow(display,windows->magnify.id,
16112          windows->magnify.screen);
16113      if (windows->command.mapped != MagickFalse)
16114        (void) XWithdrawWindow(display,windows->command.id,
16115          windows->command.screen);
16116    }
16117  if (windows->pan.mapped != MagickFalse)
16118    (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16119  if (resource_info->backdrop == MagickFalse)
16120    if (windows->backdrop.mapped)
16121      {
16122        (void) XWithdrawWindow(display,windows->backdrop.id,
16123          windows->backdrop.screen);
16124        (void) XDestroyWindow(display,windows->backdrop.id);
16125        windows->backdrop.id=(Window) NULL;
16126        (void) XWithdrawWindow(display,windows->image.id,
16127          windows->image.screen);
16128        (void) XDestroyWindow(display,windows->image.id);
16129        windows->image.id=(Window) NULL;
16130      }
16131  XSetCursorState(display,windows,MagickTrue);
16132  XCheckRefreshWindows(display,windows);
16133  if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16134    *state&=(~ExitState);
16135  if (*state & ExitState)
16136    {
16137      /*
16138        Free Standard Colormap.
16139      */
16140      (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16141      if (resource_info->map_type == (char *) NULL)
16142        (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16143      /*
16144        Free X resources.
16145      */
16146      if (resource_info->copy_image != (Image *) NULL)
16147        {
16148          resource_info->copy_image=DestroyImage(resource_info->copy_image);
16149          resource_info->copy_image=NewImageList();
16150        }
16151      DestroyXResources();
16152    }
16153  (void) XSync(display,MagickFalse);
16154  /*
16155    Restore our progress monitor and warning handlers.
16156  */
16157  (void) SetErrorHandler(warning_handler);
16158  (void) SetWarningHandler(warning_handler);
16159  /*
16160    Change to home directory.
16161  */
16162  directory=getcwd(working_directory,MaxTextExtent);
16163  (void) directory;
16164  {
16165    int
16166      status;
16167
16168    status=chdir(resource_info->home_directory);
16169    if (status == -1)
16170      (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
16171        "UnableToOpenFile","%s",resource_info->home_directory);
16172  }
16173  *image=display_image;
16174  return(nexus);
16175}
16176#else
16177
16178/*
16179%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16180%                                                                             %
16181%                                                                             %
16182%                                                                             %
16183+   D i s p l a y I m a g e s                                                 %
16184%                                                                             %
16185%                                                                             %
16186%                                                                             %
16187%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16188%
16189%  DisplayImages() displays an image sequence to any X window screen.  It
16190%  returns a value other than 0 if successful.  Check the exception member
16191%  of image to determine the reason for any failure.
16192%
16193%  The format of the DisplayImages method is:
16194%
16195%      MagickBooleanType DisplayImages(const ImageInfo *image_info,
16196%        Image *images,ExceptionInfo *exception)
16197%
16198%  A description of each parameter follows:
16199%
16200%    o image_info: the image info.
16201%
16202%    o image: the image.
16203%
16204%    o exception: return any errors or warnings in this structure.
16205%
16206*/
16207MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16208  Image *image,ExceptionInfo *exception)
16209{
16210  assert(image_info != (const ImageInfo *) NULL);
16211  assert(image_info->signature == MagickSignature);
16212  assert(image != (Image *) NULL);
16213  assert(image->signature == MagickSignature);
16214  if (image->debug != MagickFalse)
16215    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16216  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16217    "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image->filename);
16218  return(MagickFalse);
16219}
16220
16221/*
16222%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16223%                                                                             %
16224%                                                                             %
16225%                                                                             %
16226+   R e m o t e D i s p l a y C o m m a n d                                   %
16227%                                                                             %
16228%                                                                             %
16229%                                                                             %
16230%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16231%
16232%  RemoteDisplayCommand() encourages a remote display program to display the
16233%  specified image filename.
16234%
16235%  The format of the RemoteDisplayCommand method is:
16236%
16237%      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16238%        const char *window,const char *filename,ExceptionInfo *exception)
16239%
16240%  A description of each parameter follows:
16241%
16242%    o image_info: the image info.
16243%
16244%    o window: Specifies the name or id of an X window.
16245%
16246%    o filename: the name of the image filename to display.
16247%
16248%    o exception: return any errors or warnings in this structure.
16249%
16250*/
16251MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16252  const char *window,const char *filename,ExceptionInfo *exception)
16253{
16254  assert(image_info != (const ImageInfo *) NULL);
16255  assert(image_info->signature == MagickSignature);
16256  assert(filename != (char *) NULL);
16257  (void) window;
16258  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16259  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16260    "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image_info->filename);
16261  return(MagickFalse);
16262}
16263#endif
16264