display.c revision 05c0c9ada50c0973e9449f1f1c3ea3717d248eb5
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/client.h"
47#include "MagickCore/color.h"
48#include "MagickCore/colorspace.h"
49#include "MagickCore/composite.h"
50#include "MagickCore/constitute.h"
51#include "MagickCore/decorate.h"
52#include "MagickCore/delegate.h"
53#include "MagickCore/display.h"
54#include "MagickCore/display-private.h"
55#include "MagickCore/draw.h"
56#include "MagickCore/effect.h"
57#include "MagickCore/enhance.h"
58#include "MagickCore/exception.h"
59#include "MagickCore/exception-private.h"
60#include "MagickCore/fx.h"
61#include "MagickCore/geometry.h"
62#include "MagickCore/image.h"
63#include "MagickCore/image-private.h"
64#include "MagickCore/list.h"
65#include "MagickCore/log.h"
66#include "MagickCore/magick.h"
67#include "MagickCore/memory_.h"
68#include "MagickCore/monitor.h"
69#include "MagickCore/monitor-private.h"
70#include "MagickCore/montage.h"
71#include "MagickCore/option.h"
72#include "MagickCore/paint.h"
73#include "MagickCore/pixel.h"
74#include "MagickCore/pixel-accessor.h"
75#include "MagickCore/PreRvIcccm.h"
76#include "MagickCore/property.h"
77#include "MagickCore/quantum.h"
78#include "MagickCore/quantum-private.h"
79#include "MagickCore/resize.h"
80#include "MagickCore/resource_.h"
81#include "MagickCore/shear.h"
82#include "MagickCore/segment.h"
83#include "MagickCore/string_.h"
84#include "MagickCore/string-private.h"
85#include "MagickCore/transform.h"
86#include "MagickCore/threshold.h"
87#include "MagickCore/utility.h"
88#include "MagickCore/utility-private.h"
89#include "MagickCore/version.h"
90#include "MagickCore/widget.h"
91#include "MagickCore/widget-private.h"
92#include "MagickCore/xwindow.h"
93#include "MagickCore/xwindow-private.h"
94
95#if defined(MAGICKCORE_X11_DELEGATE)
96/*
97  Define declarations.
98*/
99#define MaxColors  MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
100
101/*
102  Constant declarations.
103*/
104static const unsigned char
105  HighlightBitmap[8] =
106  {
107    0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
108  },
109  OpaqueBitmap[8] =
110  {
111    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
112  },
113  ShadowBitmap[8] =
114  {
115    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
116  };
117
118static const char
119  *PageSizes[] =
120  {
121    "Letter",
122    "Tabloid",
123    "Ledger",
124    "Legal",
125    "Statement",
126    "Executive",
127    "A3",
128    "A4",
129    "A5",
130    "B4",
131    "B5",
132    "Folio",
133    "Quarto",
134    "10x14",
135    (char *) NULL
136  };
137
138/*
139  Help widget declarations.
140*/
141static const char
142  *ImageAnnotateHelp[] =
143  {
144    "In annotate mode, the Command widget has these options:",
145    "",
146    "    Font Name",
147    "      fixed",
148    "      variable",
149    "      5x8",
150    "      6x10",
151    "      7x13bold",
152    "      8x13bold",
153    "      9x15bold",
154    "      10x20",
155    "      12x24",
156    "      Browser...",
157    "    Font Color",
158    "      black",
159    "      blue",
160    "      cyan",
161    "      green",
162    "      gray",
163    "      red",
164    "      magenta",
165    "      yellow",
166    "      white",
167    "      transparent",
168    "      Browser...",
169    "    Font Color",
170    "      black",
171    "      blue",
172    "      cyan",
173    "      green",
174    "      gray",
175    "      red",
176    "      magenta",
177    "      yellow",
178    "      white",
179    "      transparent",
180    "      Browser...",
181    "    Rotate Text",
182    "      -90",
183    "      -45",
184    "      -30",
185    "      0",
186    "      30",
187    "      45",
188    "      90",
189    "      180",
190    "      Dialog...",
191    "    Help",
192    "    Dismiss",
193    "",
194    "Choose a font name from the Font Name sub-menu.  Additional",
195    "font names can be specified with the font browser.  You can",
196    "change the menu names by setting the X resources font1",
197    "through font9.",
198    "",
199    "Choose a font color from the Font Color sub-menu.",
200    "Additional font colors can be specified with the color",
201    "browser.  You can change the menu colors by setting the X",
202    "resources pen1 through pen9.",
203    "",
204    "If you select the color browser and press Grab, you can",
205    "choose the font color by moving the pointer to the desired",
206    "color on the screen and press any button.",
207    "",
208    "If you choose to rotate the text, choose Rotate Text from the",
209    "menu and select an angle.  Typically you will only want to",
210    "rotate one line of text at a time.  Depending on the angle you",
211    "choose, subsequent lines may end up overwriting each other.",
212    "",
213    "Choosing a font and its color is optional.  The default font",
214    "is fixed and the default color is black.  However, you must",
215    "choose a location to begin entering text and press button 1.",
216    "An underscore character will appear at the location of the",
217    "pointer.  The cursor changes to a pencil to indicate you are",
218    "in text mode.  To exit immediately, press Dismiss.",
219    "",
220    "In text mode, any key presses will display the character at",
221    "the location of the underscore and advance the underscore",
222    "cursor.  Enter your text and once completed press Apply to",
223    "finish your image annotation.  To correct errors press BACK",
224    "SPACE.  To delete an entire line of text, press DELETE.  Any",
225    "text that exceeds the boundaries of the image window is",
226    "automagically continued onto the next line.",
227    "",
228    "The actual color you request for the font is saved in the",
229    "image.  However, the color that appears in your image window",
230    "may be different.  For example, on a monochrome screen the",
231    "text will appear black or white even if you choose the color",
232    "red as the font color.  However, the image saved to a file",
233    "with -write is written with red lettering.  To assure the",
234    "correct color text in the final image, any PseudoClass image",
235    "is promoted to DirectClass (see miff(5)).  To force a",
236    "PseudoClass image to remain PseudoClass, use -colors.",
237    (char *) NULL,
238  },
239  *ImageChopHelp[] =
240  {
241    "In chop mode, the Command widget has these options:",
242    "",
243    "    Direction",
244    "      horizontal",
245    "      vertical",
246    "    Help",
247    "    Dismiss",
248    "",
249    "If the you choose the horizontal direction (this the",
250    "default), the area of the image between the two horizontal",
251    "endpoints of the chop line is removed.  Otherwise, the area",
252    "of the image between the two vertical endpoints of the chop",
253    "line is removed.",
254    "",
255    "Select a location within the image window to begin your chop,",
256    "press and hold any button.  Next, move the pointer to",
257    "another location in the image.  As you move a line will",
258    "connect the initial location and the pointer.  When you",
259    "release the button, the area within the image to chop is",
260    "determined by which direction you choose from the Command",
261    "widget.",
262    "",
263    "To cancel the image chopping, move the pointer back to the",
264    "starting point of the line and release the button.",
265    (char *) NULL,
266  },
267  *ImageColorEditHelp[] =
268  {
269    "In color edit mode, the Command widget has these options:",
270    "",
271    "    Method",
272    "      point",
273    "      replace",
274    "      floodfill",
275    "      filltoborder",
276    "      reset",
277    "    Pixel Color",
278    "      black",
279    "      blue",
280    "      cyan",
281    "      green",
282    "      gray",
283    "      red",
284    "      magenta",
285    "      yellow",
286    "      white",
287    "      Browser...",
288    "    Border Color",
289    "      black",
290    "      blue",
291    "      cyan",
292    "      green",
293    "      gray",
294    "      red",
295    "      magenta",
296    "      yellow",
297    "      white",
298    "      Browser...",
299    "    Fuzz",
300    "      0%",
301    "      2%",
302    "      5%",
303    "      10%",
304    "      15%",
305    "      Dialog...",
306    "    Undo",
307    "    Help",
308    "    Dismiss",
309    "",
310    "Choose a color editing method from the Method sub-menu",
311    "of the Command widget.  The point method recolors any pixel",
312    "selected with the pointer until the button is released.  The",
313    "replace method recolors any pixel that matches the color of",
314    "the pixel you select with a button press.  Floodfill recolors",
315    "any pixel that matches the color of the pixel you select with",
316    "a button press and is a neighbor.  Whereas filltoborder recolors",
317    "any neighbor pixel that is not the border color.  Finally reset",
318    "changes the entire image to the designated color.",
319    "",
320    "Next, choose a pixel color from the Pixel Color sub-menu.",
321    "Additional pixel colors can be specified with the color",
322    "browser.  You can change the menu colors by setting the X",
323    "resources pen1 through pen9.",
324    "",
325    "Now press button 1 to select a pixel within the image window",
326    "to change its color.  Additional pixels may be recolored as",
327    "prescribed by the method you choose.",
328    "",
329    "If the Magnify widget is mapped, it can be helpful in positioning",
330    "your pointer within the image (refer to button 2).",
331    "",
332    "The actual color you request for the pixels is saved in the",
333    "image.  However, the color that appears in your image window",
334    "may be different.  For example, on a monochrome screen the",
335    "pixel will appear black or white even if you choose the",
336    "color red as the pixel color.  However, the image saved to a",
337    "file with -write is written with red pixels.  To assure the",
338    "correct color text in the final image, any PseudoClass image",
339    "is promoted to DirectClass (see miff(5)).  To force a",
340    "PseudoClass image to remain PseudoClass, use -colors.",
341    (char *) NULL,
342  },
343  *ImageCompositeHelp[] =
344  {
345    "First a widget window is displayed requesting you to enter an",
346    "image name. Press Composite, Grab or type a file name.",
347    "Press Cancel if you choose not to create a composite image.",
348    "When you choose Grab, move the pointer to the desired window",
349    "and press any button.",
350    "",
351    "If the Composite image does not have any matte information,",
352    "you are informed and the file browser is displayed again.",
353    "Enter the name of a mask image.  The image is typically",
354    "grayscale and the same size as the composite image.  If the",
355    "image is not grayscale, it is converted to grayscale and the",
356    "resulting intensities are used as matte information.",
357    "",
358    "A small window appears showing the location of the cursor in",
359    "the image window. You are now in composite mode.  To exit",
360    "immediately, press Dismiss.  In composite mode, the Command",
361    "widget has these options:",
362    "",
363    "    Operators",
364    "      Over",
365    "      In",
366    "      Out",
367    "      Atop",
368    "      Xor",
369    "      Plus",
370    "      Minus",
371    "      Add",
372    "      Subtract",
373    "      Difference",
374    "      Multiply",
375    "      Bumpmap",
376    "      Copy",
377    "      CopyRed",
378    "      CopyGreen",
379    "      CopyBlue",
380    "      CopyOpacity",
381    "      Clear",
382    "    Dissolve",
383    "    Displace",
384    "    Help",
385    "    Dismiss",
386    "",
387    "Choose a composite operation from the Operators sub-menu of",
388    "the Command widget.  How each operator behaves is described",
389    "below.  Image window is the image currently displayed on",
390    "your X server and image is the image obtained with the File",
391    "Browser widget.",
392    "",
393    "Over     The result is the union of the two image shapes,",
394    "         with image obscuring image window in the region of",
395    "         overlap.",
396    "",
397    "In       The result is simply image cut by the shape of",
398    "         image window.  None of the image data of image",
399    "         window is in the result.",
400    "",
401    "Out      The resulting image is image with the shape of",
402    "         image window cut out.",
403    "",
404    "Atop     The result is the same shape as image image window,",
405    "         with image obscuring image window where the image",
406    "         shapes overlap.  Note this differs from over",
407    "         because the portion of image outside image window's",
408    "         shape does not appear in the result.",
409    "",
410    "Xor      The result is the image data from both image and",
411    "         image window that is outside the overlap region.",
412    "         The overlap region is blank.",
413    "",
414    "Plus     The result is just the sum of the image data.",
415    "         Output values are cropped to QuantumRange (no overflow).",
416    "",
417    "Minus    The result of image - image window, with underflow",
418    "         cropped to zero.",
419    "",
420    "Add      The result of image + image window, with overflow",
421    "         wrapping around (mod 256).",
422    "",
423    "Subtract The result of image - image window, with underflow",
424    "         wrapping around (mod 256).  The add and subtract",
425    "         operators can be used to perform reversible",
426    "         transformations.",
427    "",
428    "Difference",
429    "         The result of abs(image - image window).  This",
430    "         useful for comparing two very similar images.",
431    "",
432    "Multiply",
433    "         The result of image * image window.  This",
434    "         useful for the creation of drop-shadows.",
435    "",
436    "Bumpmap  The result of surface normals from image * image",
437    "         window.",
438    "",
439    "Copy     The resulting image is image window replaced with",
440    "         image.  Here the matte information is ignored.",
441    "",
442    "CopyRed  The red layer of the image window is replace with",
443    "         the red layer of the image.  The other layers are",
444    "         untouched.",
445    "",
446    "CopyGreen",
447    "         The green layer of the image window is replace with",
448    "         the green layer of the image.  The other layers are",
449    "         untouched.",
450    "",
451    "CopyBlue The blue layer of the image window is replace with",
452    "         the blue layer of the image.  The other layers are",
453    "         untouched.",
454    "",
455    "CopyOpacity",
456    "         The matte layer of the image window is replace with",
457    "         the matte layer of the image.  The other layers are",
458    "         untouched.",
459    "",
460    "The image compositor requires a matte, or alpha channel in",
461    "the image for some operations.  This extra channel usually",
462    "defines a mask which represents a sort of a cookie-cutter",
463    "for the image.  This the case when matte is opaque (full",
464    "coverage) for pixels inside the shape, zero outside, and",
465    "between 0 and QuantumRange on the boundary.  If image does not",
466    "have a matte channel, it is initialized with 0 for any pixel",
467    "matching in color to pixel location (0,0), otherwise QuantumRange.",
468    "",
469    "If you choose Dissolve, the composite operator becomes Over.  The",
470    "image matte channel percent transparency is initialized to factor.",
471    "The image window is initialized to (100-factor). Where factor is the",
472    "value you specify in the Dialog widget.",
473    "",
474    "Displace shifts the image pixels as defined by a displacement",
475    "map.  With this option, image is used as a displacement map.",
476    "Black, within the displacement map, is a maximum positive",
477    "displacement.  White is a maximum negative displacement and",
478    "middle gray is neutral.  The displacement is scaled to determine",
479    "the pixel shift.  By default, the displacement applies in both the",
480    "horizontal and vertical directions.  However, if you specify a mask,",
481    "image is the horizontal X displacement and mask the vertical Y",
482    "displacement.",
483    "",
484    "Note that matte information for image window is not retained",
485    "for colormapped X server visuals (e.g. StaticColor,",
486    "StaticColor, GrayScale, PseudoColor).  Correct compositing",
487    "behavior may require a TrueColor or DirectColor visual or a",
488    "Standard Colormap.",
489    "",
490    "Choosing a composite operator is optional.  The default",
491    "operator is replace.  However, you must choose a location to",
492    "composite your image and press button 1.  Press and hold the",
493    "button before releasing and an outline of the image will",
494    "appear to help you identify your location.",
495    "",
496    "The actual colors of the composite image is saved.  However,",
497    "the color that appears in image window may be different.",
498    "For example, on a monochrome screen image window will appear",
499    "black or white even though your composited image may have",
500    "many colors.  If the image is saved to a file it is written",
501    "with the correct colors.  To assure the correct colors are",
502    "saved in the final image, any PseudoClass image is promoted",
503    "to DirectClass (see miff(5)).  To force a PseudoClass image",
504    "to remain PseudoClass, use -colors.",
505    (char *) NULL,
506  },
507  *ImageCutHelp[] =
508  {
509    "In cut mode, the Command widget has these options:",
510    "",
511    "    Help",
512    "    Dismiss",
513    "",
514    "To define a cut region, press button 1 and drag.  The",
515    "cut region is defined by a highlighted rectangle that",
516    "expands or contracts as it follows the pointer.  Once you",
517    "are satisfied with the cut region, release the button.",
518    "You are now in rectify mode.  In rectify mode, the Command",
519    "widget has these options:",
520    "",
521    "    Cut",
522    "    Help",
523    "    Dismiss",
524    "",
525    "You can make adjustments by moving the pointer to one of the",
526    "cut rectangle corners, pressing a button, and dragging.",
527    "Finally, press Cut to commit your copy region.  To",
528    "exit without cutting the image, press Dismiss.",
529    (char *) NULL,
530  },
531  *ImageCopyHelp[] =
532  {
533    "In copy mode, the Command widget has these options:",
534    "",
535    "    Help",
536    "    Dismiss",
537    "",
538    "To define a copy region, press button 1 and drag.  The",
539    "copy region is defined by a highlighted rectangle that",
540    "expands or contracts as it follows the pointer.  Once you",
541    "are satisfied with the copy region, release the button.",
542    "You are now in rectify mode.  In rectify mode, the Command",
543    "widget has these options:",
544    "",
545    "    Copy",
546    "    Help",
547    "    Dismiss",
548    "",
549    "You can make adjustments by moving the pointer to one of the",
550    "copy rectangle corners, pressing a button, and dragging.",
551    "Finally, press Copy to commit your copy region.  To",
552    "exit without copying the image, press Dismiss.",
553    (char *) NULL,
554  },
555  *ImageCropHelp[] =
556  {
557    "In crop mode, the Command widget has these options:",
558    "",
559    "    Help",
560    "    Dismiss",
561    "",
562    "To define a cropping region, press button 1 and drag.  The",
563    "cropping region is defined by a highlighted rectangle that",
564    "expands or contracts as it follows the pointer.  Once you",
565    "are satisfied with the cropping region, release the button.",
566    "You are now in rectify mode.  In rectify mode, the Command",
567    "widget has these options:",
568    "",
569    "    Crop",
570    "    Help",
571    "    Dismiss",
572    "",
573    "You can make adjustments by moving the pointer to one of the",
574    "cropping rectangle corners, pressing a button, and dragging.",
575    "Finally, press Crop to commit your cropping region.  To",
576    "exit without cropping the image, press Dismiss.",
577    (char *) NULL,
578  },
579  *ImageDrawHelp[] =
580  {
581    "The cursor changes to a crosshair to indicate you are in",
582    "draw mode.  To exit immediately, press Dismiss.  In draw mode,",
583    "the Command widget has these options:",
584    "",
585    "    Element",
586    "      point",
587    "      line",
588    "      rectangle",
589    "      fill rectangle",
590    "      circle",
591    "      fill circle",
592    "      ellipse",
593    "      fill ellipse",
594    "      polygon",
595    "      fill polygon",
596    "    Color",
597    "      black",
598    "      blue",
599    "      cyan",
600    "      green",
601    "      gray",
602    "      red",
603    "      magenta",
604    "      yellow",
605    "      white",
606    "      transparent",
607    "      Browser...",
608    "    Stipple",
609    "      Brick",
610    "      Diagonal",
611    "      Scales",
612    "      Vertical",
613    "      Wavy",
614    "      Translucent",
615    "      Opaque",
616    "      Open...",
617    "    Width",
618    "      1",
619    "      2",
620    "      4",
621    "      8",
622    "      16",
623    "      Dialog...",
624    "    Undo",
625    "    Help",
626    "    Dismiss",
627    "",
628    "Choose a drawing primitive from the Element sub-menu.",
629    "",
630    "Choose a color from the Color sub-menu.  Additional",
631    "colors can be specified with the color browser.",
632    "",
633    "If you choose the color browser and press Grab, you can",
634    "select the color by moving the pointer to the desired",
635    "color on the screen and press any button.  The transparent",
636    "color updates the image matte channel and is useful for",
637    "image compositing.",
638    "",
639    "Choose a stipple, if appropriate, from the Stipple sub-menu.",
640    "Additional stipples can be specified with the file browser.",
641    "Stipples obtained from the file browser must be on disk in the",
642    "X11 bitmap format.",
643    "",
644    "Choose a width, if appropriate, from the Width sub-menu.  To",
645    "choose a specific width select the Dialog widget.",
646    "",
647    "Choose a point in the Image window and press button 1 and",
648    "hold.  Next, move the pointer to another location in the",
649    "image.  As you move, a line connects the initial location and",
650    "the pointer.  When you release the button, the image is",
651    "updated with the primitive you just drew.  For polygons, the",
652    "image is updated when you press and release the button without",
653    "moving the pointer.",
654    "",
655    "To cancel image drawing, move the pointer back to the",
656    "starting point of the line and release the button.",
657    (char *) NULL,
658  },
659  *DisplayHelp[] =
660  {
661    "BUTTONS",
662    "  The effects of each button press is described below.  Three",
663    "  buttons are required.  If you have a two button mouse,",
664    "  button 1 and 3 are returned.  Press ALT and button 3 to",
665    "  simulate button 2.",
666    "",
667    "  1    Press this button to map or unmap the Command widget.",
668    "",
669    "  2    Press and drag to define a region of the image to",
670    "       magnify.",
671    "",
672    "  3    Press and drag to choose from a select set of commands.",
673    "       This button behaves differently if the image being",
674    "       displayed is a visual image directory.  Here, choose a",
675    "       particular tile of the directory and press this button and",
676    "       drag to select a command from a pop-up menu.  Choose from",
677    "       these menu items:",
678    "",
679    "           Open",
680    "           Next",
681    "           Former",
682    "           Delete",
683    "           Update",
684    "",
685    "       If you choose Open, the image represented by the tile is",
686    "       displayed.  To return to the visual image directory, choose",
687    "       Next from the Command widget.  Next and Former moves to the",
688    "       next or former image respectively.  Choose Delete to delete",
689    "       a particular image tile.  Finally, choose Update to",
690    "       synchronize all the image tiles with their respective",
691    "       images.",
692    "",
693    "COMMAND WIDGET",
694    "  The Command widget lists a number of sub-menus and commands.",
695    "  They are",
696    "",
697    "      File",
698    "        Open...",
699    "        Next",
700    "        Former",
701    "        Select...",
702    "        Save...",
703    "        Print...",
704    "        Delete...",
705    "        New...",
706    "        Visual Directory...",
707    "        Quit",
708    "      Edit",
709    "        Undo",
710    "        Redo",
711    "        Cut",
712    "        Copy",
713    "        Paste",
714    "      View",
715    "        Half Size",
716    "        Original Size",
717    "        Double Size",
718    "        Resize...",
719    "        Apply",
720    "        Refresh",
721    "        Restore",
722    "      Transform",
723    "        Crop",
724    "        Chop",
725    "        Flop",
726    "        Flip",
727    "        Rotate Right",
728    "        Rotate Left",
729    "        Rotate...",
730    "        Shear...",
731    "        Roll...",
732    "        Trim Edges",
733    "      Enhance",
734    "        Brightness...",
735    "        Saturation...",
736    "        Hue...",
737    "        Gamma...",
738    "        Sharpen...",
739    "        Dull",
740    "        Contrast Stretch...",
741    "        Sigmoidal Contrast...",
742    "        Normalize",
743    "        Equalize",
744    "        Negate",
745    "        Grayscale",
746    "        Map...",
747    "        Quantize...",
748    "      Effects",
749    "        Despeckle",
750    "        Emboss",
751    "        Reduce Noise",
752    "        Add Noise",
753    "        Sharpen...",
754    "        Blur...",
755    "        Threshold...",
756    "        Edge Detect...",
757    "        Spread...",
758    "        Shade...",
759    "        Painting...",
760    "        Segment...",
761    "      F/X",
762    "        Solarize...",
763    "        Sepia Tone...",
764    "        Swirl...",
765    "        Implode...",
766    "        Vignette...",
767    "        Wave...",
768    "        Oil Painting...",
769    "        Charcoal Drawing...",
770    "      Image Edit",
771    "        Annotate...",
772    "        Draw...",
773    "        Color...",
774    "        Matte...",
775    "        Composite...",
776    "        Add Border...",
777    "        Add Frame...",
778    "        Comment...",
779    "        Launch...",
780    "        Region of Interest...",
781    "      Miscellany",
782    "        Image Info",
783    "        Zoom Image",
784    "        Show Preview...",
785    "        Show Histogram",
786    "        Show Matte",
787    "        Background...",
788    "        Slide Show",
789    "        Preferences...",
790    "      Help",
791    "        Overview",
792    "        Browse Documentation",
793    "        About Display",
794    "",
795    "  Menu items with a indented triangle have a sub-menu.  They",
796    "  are represented above as the indented items.  To access a",
797    "  sub-menu item, move the pointer to the appropriate menu and",
798    "  press a button and drag.  When you find the desired sub-menu",
799    "  item, release the button and the command is executed.  Move",
800    "  the pointer away from the sub-menu if you decide not to",
801    "  execute a particular command.",
802    "",
803    "KEYBOARD ACCELERATORS",
804    "  Accelerators are one or two key presses that effect a",
805    "  particular command.  The keyboard accelerators that",
806    "  display(1) understands is:",
807    "",
808    "  Ctl+O     Press to open an image from a file.",
809    "",
810    "  space     Press to display the next image.",
811    "",
812    "            If the image is a multi-paged document such as a Postscript",
813    "            document, you can skip ahead several pages by preceding",
814    "            this command with a number.  For example to display the",
815    "            third page beyond the current page, press 3<space>.",
816    "",
817    "  backspace Press to display the former image.",
818    "",
819    "            If the image is a multi-paged document such as a Postscript",
820    "            document, you can skip behind several pages by preceding",
821    "            this command with a number.  For example to display the",
822    "            third page preceding the current page, press 3<backspace>.",
823    "",
824    "  Ctl+S     Press to write the image to a file.",
825    "",
826    "  Ctl+P     Press to print the image to a Postscript printer.",
827    "",
828    "  Ctl+D     Press to delete an image file.",
829    "",
830    "  Ctl+N     Press to create a blank canvas.",
831    "",
832    "  Ctl+Q     Press to discard all images and exit program.",
833    "",
834    "  Ctl+Z     Press to undo last image transformation.",
835    "",
836    "  Ctl+R     Press to redo last image transformation.",
837    "",
838    "  Ctl+X     Press to cut a region of the image.",
839    "",
840    "  Ctl+C     Press to copy a region of the image.",
841    "",
842    "  Ctl+V     Press to paste a region to the image.",
843    "",
844    "  <         Press to half the image size.",
845    "",
846    "  -         Press to return to the original image size.",
847    "",
848    "  >         Press to double the image size.",
849    "",
850    "  %         Press to resize the image to a width and height you",
851    "            specify.",
852    "",
853    "Cmd-A       Press to make any image transformations permanent."
854    "",
855    "            By default, any image size transformations are applied",
856    "            to the original image to create the image displayed on",
857    "            the X server.  However, the transformations are not",
858    "            permanent (i.e. the original image does not change",
859    "            size only the X image does).  For example, if you",
860    "            press > the X image will appear to double in size,",
861    "            but the original image will in fact remain the same size.",
862    "            To force the original image to double in size, press >",
863    "            followed by Cmd-A.",
864    "",
865    "  @         Press to refresh the image window.",
866    "",
867    "  C         Press to cut out a rectangular region of the image.",
868    "",
869    "  [         Press to chop the image.",
870    "",
871    "  H         Press to flop image in the horizontal direction.",
872    "",
873    "  V         Press to flip image in the vertical direction.",
874    "",
875    "  /         Press to rotate the image 90 degrees clockwise.",
876    "",
877    " \\         Press to rotate the image 90 degrees counter-clockwise.",
878    "",
879    "  *         Press to rotate the image the number of degrees you",
880    "            specify.",
881    "",
882    "  S         Press to shear the image the number of degrees you",
883    "            specify.",
884    "",
885    "  R         Press to roll the image.",
886    "",
887    "  T         Press to trim the image edges.",
888    "",
889    "  Shft-H    Press to vary the image hue.",
890    "",
891    "  Shft-S    Press to vary the color saturation.",
892    "",
893    "  Shft-L    Press to vary the color brightness.",
894    "",
895    "  Shft-G    Press to gamma correct the image.",
896    "",
897    "  Shft-C    Press to sharpen the image contrast.",
898    "",
899    "  Shft-Z    Press to dull the image contrast.",
900    "",
901    "  =         Press to perform histogram equalization on the image.",
902    "",
903    "  Shft-N    Press to perform histogram normalization on the image.",
904    "",
905    "  Shft-~    Press to negate the colors of the image.",
906    "",
907    "  .         Press to convert the image colors to gray.",
908    "",
909    "  Shft-#    Press to set the maximum number of unique colors in the",
910    "            image.",
911    "",
912    "  F2        Press to reduce the speckles in an image.",
913    "",
914    "  F3        Press to eliminate peak noise from an image.",
915    "",
916    "  F4        Press to add noise to an image.",
917    "",
918    "  F5        Press to sharpen an image.",
919    "",
920    "  F6        Press to delete an image file.",
921    "",
922    "  F7        Press to threshold the image.",
923    "",
924    "  F8        Press to detect edges within an image.",
925    "",
926    "  F9        Press to emboss an image.",
927    "",
928    "  F10       Press to displace pixels by a random amount.",
929    "",
930    "  F11       Press to negate all pixels above the threshold level.",
931    "",
932    "  F12       Press to shade the image using a distant light source.",
933    "",
934    "  F13       Press to lighten or darken image edges to create a 3-D effect.",
935    "",
936    "  F14       Press to segment the image by color.",
937    "",
938    "  Meta-S    Press to swirl image pixels about the center.",
939    "",
940    "  Meta-I    Press to implode image pixels about the center.",
941    "",
942    "  Meta-W    Press to alter an image along a sine wave.",
943    "",
944    "  Meta-P    Press to simulate an oil painting.",
945    "",
946    "  Meta-C    Press to simulate a charcoal drawing.",
947    "",
948    "  Alt-A     Press to annotate the image with text.",
949    "",
950    "  Alt-D     Press to draw on an image.",
951    "",
952    "  Alt-P     Press to edit an image pixel color.",
953    "",
954    "  Alt-M     Press to edit the image matte information.",
955    "",
956    "  Alt-V     Press to composite the image with another.",
957    "",
958    "  Alt-B     Press to add a border to the image.",
959    "",
960    "  Alt-F     Press to add an ornamental border to the image.",
961    "",
962    "  Alt-Shft-!",
963    "            Press to add an image comment.",
964    "",
965    "  Ctl-A     Press to apply image processing techniques to a region",
966    "            of interest.",
967    "",
968    "  Shft-?    Press to display information about the image.",
969    "",
970    "  Shft-+    Press to map the zoom image window.",
971    "",
972    "  Shft-P    Press to preview an image enhancement, effect, or f/x.",
973    "",
974    "  F1        Press to display helpful information about display(1).",
975    "",
976    "  Find      Press to browse documentation about ImageMagick.",
977    "",
978    "  1-9       Press to change the level of magnification.",
979    "",
980    "  Use the arrow keys to move the image one pixel up, down,",
981    "  left, or right within the magnify window.  Be sure to first",
982    "  map the magnify window by pressing button 2.",
983    "",
984    "  Press ALT and one of the arrow keys to trim off one pixel",
985    "  from any side of the image.",
986    (char *) NULL,
987  },
988  *ImageMatteEditHelp[] =
989  {
990    "Matte information within an image is useful for some",
991    "operations such as image compositing (See IMAGE",
992    "COMPOSITING).  This extra channel usually defines a mask",
993    "which represents a sort of a cookie-cutter for the image.",
994    "This the case when matte is opaque (full coverage) for",
995    "pixels inside the shape, zero outside, and between 0 and",
996    "QuantumRange on the boundary.",
997    "",
998    "A small window appears showing the location of the cursor in",
999    "the image window. You are now in matte edit mode.  To exit",
1000    "immediately, press Dismiss.  In matte edit mode, the Command",
1001    "widget has these options:",
1002    "",
1003    "    Method",
1004    "      point",
1005    "      replace",
1006    "      floodfill",
1007    "      filltoborder",
1008    "      reset",
1009    "    Border Color",
1010    "      black",
1011    "      blue",
1012    "      cyan",
1013    "      green",
1014    "      gray",
1015    "      red",
1016    "      magenta",
1017    "      yellow",
1018    "      white",
1019    "      Browser...",
1020    "    Fuzz",
1021    "      0%",
1022    "      2%",
1023    "      5%",
1024    "      10%",
1025    "      15%",
1026    "      Dialog...",
1027    "    Matte",
1028    "      Opaque",
1029    "      Transparent",
1030    "      Dialog...",
1031    "    Undo",
1032    "    Help",
1033    "    Dismiss",
1034    "",
1035    "Choose a matte editing method from the Method sub-menu of",
1036    "the Command widget.  The point method changes the matte value",
1037    "of any pixel selected with the pointer until the button is",
1038    "is released.  The replace method changes the matte value of",
1039    "any pixel that matches the color of the pixel you select with",
1040    "a button press.  Floodfill changes the matte value of any pixel",
1041    "that matches the color of the pixel you select with a button",
1042    "press and is a neighbor.  Whereas filltoborder changes the matte",
1043    "value any neighbor pixel that is not the border color.  Finally",
1044    "reset changes the entire image to the designated matte value.",
1045    "",
1046    "Choose Matte Value and pick Opaque or Transarent.  For other values",
1047    "select the Dialog entry.  Here a dialog appears requesting a matte",
1048    "value.  The value you select is assigned as the opacity value of the",
1049    "selected pixel or pixels.",
1050    "",
1051    "Now, press any button to select a pixel within the image",
1052    "window to change its matte value.",
1053    "",
1054    "If the Magnify widget is mapped, it can be helpful in positioning",
1055    "your pointer within the image (refer to button 2).",
1056    "",
1057    "Matte information is only valid in a DirectClass image.",
1058    "Therefore, any PseudoClass image is promoted to DirectClass",
1059    "(see miff(5)).  Note that matte information for PseudoClass",
1060    "is not retained for colormapped X server visuals (e.g.",
1061    "StaticColor, StaticColor, GrayScale, PseudoColor) unless you",
1062    "immediately save your image to a file (refer to Write).",
1063    "Correct matte editing behavior may require a TrueColor or",
1064    "DirectColor visual or a Standard Colormap.",
1065    (char *) NULL,
1066  },
1067  *ImagePanHelp[] =
1068  {
1069    "When an image exceeds the width or height of the X server",
1070    "screen, display maps a small panning icon.  The rectangle",
1071    "within the panning icon shows the area that is currently",
1072    "displayed in the image window.  To pan about the image,",
1073    "press any button and drag the pointer within the panning",
1074    "icon.  The pan rectangle moves with the pointer and the",
1075    "image window is updated to reflect the location of the",
1076    "rectangle within the panning icon.  When you have selected",
1077    "the area of the image you wish to view, release the button.",
1078    "",
1079    "Use the arrow keys to pan the image one pixel up, down,",
1080    "left, or right within the image window.",
1081    "",
1082    "The panning icon is withdrawn if the image becomes smaller",
1083    "than the dimensions of the X server screen.",
1084    (char *) NULL,
1085  },
1086  *ImagePasteHelp[] =
1087  {
1088    "A small window appears showing the location of the cursor in",
1089    "the image window. You are now in paste mode.  To exit",
1090    "immediately, press Dismiss.  In paste mode, the Command",
1091    "widget has these options:",
1092    "",
1093    "    Operators",
1094    "      over",
1095    "      in",
1096    "      out",
1097    "      atop",
1098    "      xor",
1099    "      plus",
1100    "      minus",
1101    "      add",
1102    "      subtract",
1103    "      difference",
1104    "      replace",
1105    "    Help",
1106    "    Dismiss",
1107    "",
1108    "Choose a composite operation from the Operators sub-menu of",
1109    "the Command widget.  How each operator behaves is described",
1110    "below.  Image window is the image currently displayed on",
1111    "your X server and image is the image obtained with the File",
1112    "Browser widget.",
1113    "",
1114    "Over     The result is the union of the two image shapes,",
1115    "         with image obscuring image window in the region of",
1116    "         overlap.",
1117    "",
1118    "In       The result is simply image cut by the shape of",
1119    "         image window.  None of the image data of image",
1120    "         window is in the result.",
1121    "",
1122    "Out      The resulting image is image with the shape of",
1123    "         image window cut out.",
1124    "",
1125    "Atop     The result is the same shape as image image window,",
1126    "         with image obscuring image window where the image",
1127    "         shapes overlap.  Note this differs from over",
1128    "         because the portion of image outside image window's",
1129    "         shape does not appear in the result.",
1130    "",
1131    "Xor      The result is the image data from both image and",
1132    "         image window that is outside the overlap region.",
1133    "         The overlap region is blank.",
1134    "",
1135    "Plus     The result is just the sum of the image data.",
1136    "         Output values are cropped to QuantumRange (no overflow).",
1137    "         This operation is independent of the matte",
1138    "         channels.",
1139    "",
1140    "Minus    The result of image - image window, with underflow",
1141    "         cropped to zero.",
1142    "",
1143    "Add      The result of image + image window, with overflow",
1144    "         wrapping around (mod 256).",
1145    "",
1146    "Subtract The result of image - image window, with underflow",
1147    "         wrapping around (mod 256).  The add and subtract",
1148    "         operators can be used to perform reversible",
1149    "         transformations.",
1150    "",
1151    "Difference",
1152    "         The result of abs(image - image window).  This",
1153    "         useful for comparing two very similar images.",
1154    "",
1155    "Copy     The resulting image is image window replaced with",
1156    "         image.  Here the matte information is ignored.",
1157    "",
1158    "CopyRed  The red layer of the image window is replace with",
1159    "         the red layer of the image.  The other layers are",
1160    "         untouched.",
1161    "",
1162    "CopyGreen",
1163    "         The green layer of the image window is replace with",
1164    "         the green layer of the image.  The other layers are",
1165    "         untouched.",
1166    "",
1167    "CopyBlue The blue layer of the image window is replace with",
1168    "         the blue layer of the image.  The other layers are",
1169    "         untouched.",
1170    "",
1171    "CopyOpacity",
1172    "         The matte layer of the image window is replace with",
1173    "         the matte layer of the image.  The other layers are",
1174    "         untouched.",
1175    "",
1176    "The image compositor requires a matte, or alpha channel in",
1177    "the image for some operations.  This extra channel usually",
1178    "defines a mask which represents a sort of a cookie-cutter",
1179    "for the image.  This the case when matte is opaque (full",
1180    "coverage) for pixels inside the shape, zero outside, and",
1181    "between 0 and QuantumRange on the boundary.  If image does not",
1182    "have a matte channel, it is initialized with 0 for any pixel",
1183    "matching in color to pixel location (0,0), otherwise QuantumRange.",
1184    "",
1185    "Note that matte information for image window is not retained",
1186    "for colormapped X server visuals (e.g. StaticColor,",
1187    "StaticColor, GrayScale, PseudoColor).  Correct compositing",
1188    "behavior may require a TrueColor or DirectColor visual or a",
1189    "Standard Colormap.",
1190    "",
1191    "Choosing a composite operator is optional.  The default",
1192    "operator is replace.  However, you must choose a location to",
1193    "paste your image and press button 1.  Press and hold the",
1194    "button before releasing and an outline of the image will",
1195    "appear to help you identify your location.",
1196    "",
1197    "The actual colors of the pasted image is saved.  However,",
1198    "the color that appears in image window may be different.",
1199    "For example, on a monochrome screen image window will appear",
1200    "black or white even though your pasted image may have",
1201    "many colors.  If the image is saved to a file it is written",
1202    "with the correct colors.  To assure the correct colors are",
1203    "saved in the final image, any PseudoClass image is promoted",
1204    "to DirectClass (see miff(5)).  To force a PseudoClass image",
1205    "to remain PseudoClass, use -colors.",
1206    (char *) NULL,
1207  },
1208  *ImageROIHelp[] =
1209  {
1210    "In region of interest mode, the Command widget has these",
1211    "options:",
1212    "",
1213    "    Help",
1214    "    Dismiss",
1215    "",
1216    "To define a region of interest, press button 1 and drag.",
1217    "The region of interest is defined by a highlighted rectangle",
1218    "that expands or contracts as it follows the pointer.  Once",
1219    "you are satisfied with the region of interest, release the",
1220    "button.  You are now in apply mode.  In apply mode the",
1221    "Command widget has these options:",
1222    "",
1223    "      File",
1224    "        Save...",
1225    "        Print...",
1226    "      Edit",
1227    "        Undo",
1228    "        Redo",
1229    "      Transform",
1230    "        Flop",
1231    "        Flip",
1232    "        Rotate Right",
1233    "        Rotate Left",
1234    "      Enhance",
1235    "        Hue...",
1236    "        Saturation...",
1237    "        Brightness...",
1238    "        Gamma...",
1239    "        Spiff",
1240    "        Dull",
1241    "        Contrast Stretch",
1242    "        Sigmoidal Contrast...",
1243    "        Normalize",
1244    "        Equalize",
1245    "        Negate",
1246    "        Grayscale",
1247    "        Map...",
1248    "        Quantize...",
1249    "      Effects",
1250    "        Despeckle",
1251    "        Emboss",
1252    "        Reduce Noise",
1253    "        Sharpen...",
1254    "        Blur...",
1255    "        Threshold...",
1256    "        Edge Detect...",
1257    "        Spread...",
1258    "        Shade...",
1259    "        Raise...",
1260    "        Segment...",
1261    "      F/X",
1262    "        Solarize...",
1263    "        Sepia Tone...",
1264    "        Swirl...",
1265    "        Implode...",
1266    "        Vignette...",
1267    "        Wave...",
1268    "        Oil Painting...",
1269    "        Charcoal Drawing...",
1270    "      Miscellany",
1271    "        Image Info",
1272    "        Zoom Image",
1273    "        Show Preview...",
1274    "        Show Histogram",
1275    "        Show Matte",
1276    "      Help",
1277    "      Dismiss",
1278    "",
1279    "You can make adjustments to the region of interest by moving",
1280    "the pointer to one of the rectangle corners, pressing a",
1281    "button, and dragging.  Finally, choose an image processing",
1282    "technique from the Command widget.  You can choose more than",
1283    "one image processing technique to apply to an area.",
1284    "Alternatively, you can move the region of interest before",
1285    "applying another image processing technique.  To exit, press",
1286    "Dismiss.",
1287    (char *) NULL,
1288  },
1289  *ImageRotateHelp[] =
1290  {
1291    "In rotate mode, the Command widget has these options:",
1292    "",
1293    "    Pixel Color",
1294    "      black",
1295    "      blue",
1296    "      cyan",
1297    "      green",
1298    "      gray",
1299    "      red",
1300    "      magenta",
1301    "      yellow",
1302    "      white",
1303    "      Browser...",
1304    "    Direction",
1305    "      horizontal",
1306    "      vertical",
1307    "    Help",
1308    "    Dismiss",
1309    "",
1310    "Choose a background color from the Pixel Color sub-menu.",
1311    "Additional background colors can be specified with the color",
1312    "browser.  You can change the menu colors by setting the X",
1313    "resources pen1 through pen9.",
1314    "",
1315    "If you choose the color browser and press Grab, you can",
1316    "select the background color by moving the pointer to the",
1317    "desired color on the screen and press any button.",
1318    "",
1319    "Choose a point in the image window and press this button and",
1320    "hold.  Next, move the pointer to another location in the",
1321    "image.  As you move a line connects the initial location and",
1322    "the pointer.  When you release the button, the degree of",
1323    "image rotation is determined by the slope of the line you",
1324    "just drew.  The slope is relative to the direction you",
1325    "choose from the Direction sub-menu of the Command widget.",
1326    "",
1327    "To cancel the image rotation, move the pointer back to the",
1328    "starting point of the line and release the button.",
1329    (char *) NULL,
1330  };
1331
1332/*
1333  Enumeration declarations.
1334*/
1335typedef enum
1336{
1337  CopyMode,
1338  CropMode,
1339  CutMode
1340} ClipboardMode;
1341
1342typedef enum
1343{
1344  OpenCommand,
1345  NextCommand,
1346  FormerCommand,
1347  SelectCommand,
1348  SaveCommand,
1349  PrintCommand,
1350  DeleteCommand,
1351  NewCommand,
1352  VisualDirectoryCommand,
1353  QuitCommand,
1354  UndoCommand,
1355  RedoCommand,
1356  CutCommand,
1357  CopyCommand,
1358  PasteCommand,
1359  HalfSizeCommand,
1360  OriginalSizeCommand,
1361  DoubleSizeCommand,
1362  ResizeCommand,
1363  ApplyCommand,
1364  RefreshCommand,
1365  RestoreCommand,
1366  CropCommand,
1367  ChopCommand,
1368  FlopCommand,
1369  FlipCommand,
1370  RotateRightCommand,
1371  RotateLeftCommand,
1372  RotateCommand,
1373  ShearCommand,
1374  RollCommand,
1375  TrimCommand,
1376  HueCommand,
1377  SaturationCommand,
1378  BrightnessCommand,
1379  GammaCommand,
1380  SpiffCommand,
1381  DullCommand,
1382  ContrastStretchCommand,
1383  SigmoidalContrastCommand,
1384  NormalizeCommand,
1385  EqualizeCommand,
1386  NegateCommand,
1387  GrayscaleCommand,
1388  MapCommand,
1389  QuantizeCommand,
1390  DespeckleCommand,
1391  EmbossCommand,
1392  ReduceNoiseCommand,
1393  AddNoiseCommand,
1394  SharpenCommand,
1395  BlurCommand,
1396  ThresholdCommand,
1397  EdgeDetectCommand,
1398  SpreadCommand,
1399  ShadeCommand,
1400  RaiseCommand,
1401  SegmentCommand,
1402  SolarizeCommand,
1403  SepiaToneCommand,
1404  SwirlCommand,
1405  ImplodeCommand,
1406  VignetteCommand,
1407  WaveCommand,
1408  OilPaintCommand,
1409  CharcoalDrawCommand,
1410  AnnotateCommand,
1411  DrawCommand,
1412  ColorCommand,
1413  MatteCommand,
1414  CompositeCommand,
1415  AddBorderCommand,
1416  AddFrameCommand,
1417  CommentCommand,
1418  LaunchCommand,
1419  RegionofInterestCommand,
1420  ROIHelpCommand,
1421  ROIDismissCommand,
1422  InfoCommand,
1423  ZoomCommand,
1424  ShowPreviewCommand,
1425  ShowHistogramCommand,
1426  ShowMatteCommand,
1427  BackgroundCommand,
1428  SlideShowCommand,
1429  PreferencesCommand,
1430  HelpCommand,
1431  BrowseDocumentationCommand,
1432  VersionCommand,
1433  SaveToUndoBufferCommand,
1434  FreeBuffersCommand,
1435  NullCommand
1436} CommandType;
1437
1438typedef enum
1439{
1440  AnnotateNameCommand,
1441  AnnotateFontColorCommand,
1442  AnnotateBackgroundColorCommand,
1443  AnnotateRotateCommand,
1444  AnnotateHelpCommand,
1445  AnnotateDismissCommand,
1446  TextHelpCommand,
1447  TextApplyCommand,
1448  ChopDirectionCommand,
1449  ChopHelpCommand,
1450  ChopDismissCommand,
1451  HorizontalChopCommand,
1452  VerticalChopCommand,
1453  ColorEditMethodCommand,
1454  ColorEditColorCommand,
1455  ColorEditBorderCommand,
1456  ColorEditFuzzCommand,
1457  ColorEditUndoCommand,
1458  ColorEditHelpCommand,
1459  ColorEditDismissCommand,
1460  CompositeOperatorsCommand,
1461  CompositeDissolveCommand,
1462  CompositeDisplaceCommand,
1463  CompositeHelpCommand,
1464  CompositeDismissCommand,
1465  CropHelpCommand,
1466  CropDismissCommand,
1467  RectifyCopyCommand,
1468  RectifyHelpCommand,
1469  RectifyDismissCommand,
1470  DrawElementCommand,
1471  DrawColorCommand,
1472  DrawStippleCommand,
1473  DrawWidthCommand,
1474  DrawUndoCommand,
1475  DrawHelpCommand,
1476  DrawDismissCommand,
1477  MatteEditMethod,
1478  MatteEditBorderCommand,
1479  MatteEditFuzzCommand,
1480  MatteEditValueCommand,
1481  MatteEditUndoCommand,
1482  MatteEditHelpCommand,
1483  MatteEditDismissCommand,
1484  PasteOperatorsCommand,
1485  PasteHelpCommand,
1486  PasteDismissCommand,
1487  RotateColorCommand,
1488  RotateDirectionCommand,
1489  RotateCropCommand,
1490  RotateSharpenCommand,
1491  RotateHelpCommand,
1492  RotateDismissCommand,
1493  HorizontalRotateCommand,
1494  VerticalRotateCommand,
1495  TileLoadCommand,
1496  TileNextCommand,
1497  TileFormerCommand,
1498  TileDeleteCommand,
1499  TileUpdateCommand
1500} ModeType;
1501
1502/*
1503  Stipples.
1504*/
1505#define BricksWidth  20
1506#define BricksHeight  20
1507#define DiagonalWidth  16
1508#define DiagonalHeight  16
1509#define HighlightWidth  8
1510#define HighlightHeight  8
1511#define OpaqueWidth  8
1512#define OpaqueHeight  8
1513#define ScalesWidth  16
1514#define ScalesHeight  16
1515#define ShadowWidth  8
1516#define ShadowHeight  8
1517#define VerticalWidth  16
1518#define VerticalHeight  16
1519#define WavyWidth  16
1520#define WavyHeight  16
1521
1522/*
1523  Constant declaration.
1524*/
1525static const int
1526  RoiDelta = 8;
1527
1528static const unsigned char
1529  BricksBitmap[] =
1530  {
1531    0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1532    0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1533    0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1534    0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1535    0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1536  },
1537  DiagonalBitmap[] =
1538  {
1539    0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1540    0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1541    0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1542  },
1543  ScalesBitmap[] =
1544  {
1545    0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1546    0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1547    0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1548  },
1549  VerticalBitmap[] =
1550  {
1551    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1552    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1553    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1554  },
1555  WavyBitmap[] =
1556  {
1557    0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1558    0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1559    0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1560  };
1561
1562/*
1563  Function prototypes.
1564*/
1565static CommandType
1566  XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1567    const MagickStatusType,KeySym,Image **,ExceptionInfo *);
1568
1569static Image
1570  *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
1571    Image **,ExceptionInfo *),
1572  *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1573  *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *,
1574    ExceptionInfo *),
1575  *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *,
1576    ExceptionInfo *);
1577
1578static MagickBooleanType
1579  XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *,
1580    ExceptionInfo *),
1581  XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **,
1582    ExceptionInfo *),
1583  XChopImage(Display *,XResourceInfo *,XWindows *,Image **,
1584    ExceptionInfo *),
1585  XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode,
1586    ExceptionInfo *),
1587  XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1588    ExceptionInfo *),
1589  XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *,
1590    ExceptionInfo *),
1591  XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1592  XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1593    ExceptionInfo *),
1594  XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1595    ExceptionInfo *),
1596  XPasteImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1597  XPrintImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1598  XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **,
1599    ExceptionInfo *),
1600  XROIImage(Display *,XResourceInfo *,XWindows *,Image **,ExceptionInfo *),
1601  XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1602  XTrimImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
1603
1604static void
1605  XDrawPanRectangle(Display *,XWindows *),
1606  XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **,
1607    ExceptionInfo *),
1608  XMagnifyImage(Display *,XWindows *,XEvent *),
1609  XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1610  XPanImage(Display *,XWindows *,XEvent *),
1611  XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1612    const KeySym),
1613  XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1614  XScreenEvent(Display *,XWindows *,XEvent *),
1615  XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1616
1617/*
1618%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1619%                                                                             %
1620%                                                                             %
1621%                                                                             %
1622%   D i s p l a y I m a g e s                                                 %
1623%                                                                             %
1624%                                                                             %
1625%                                                                             %
1626%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1627%
1628%  DisplayImages() displays an image sequence to any X window screen.  It
1629%  returns a value other than 0 if successful.  Check the exception member
1630%  of image to determine the reason for any failure.
1631%
1632%  The format of the DisplayImages method is:
1633%
1634%      MagickBooleanType DisplayImages(const ImageInfo *image_info,
1635%        Image *images,ExceptionInfo *exception)
1636%
1637%  A description of each parameter follows:
1638%
1639%    o image_info: the image info.
1640%
1641%    o image: the image.
1642%
1643%    o exception: return any errors or warnings in this structure.
1644%
1645*/
1646MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1647  Image *images,ExceptionInfo *exception)
1648{
1649  char
1650    *argv[1];
1651
1652  Display
1653    *display;
1654
1655  Image
1656    *image;
1657
1658  register ssize_t
1659    i;
1660
1661  size_t
1662    state;
1663
1664  XrmDatabase
1665    resource_database;
1666
1667  XResourceInfo
1668    resource_info;
1669
1670  assert(image_info != (const ImageInfo *) NULL);
1671  assert(image_info->signature == MagickSignature);
1672  assert(images != (Image *) NULL);
1673  assert(images->signature == MagickSignature);
1674  if (images->debug != MagickFalse)
1675    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1676  display=XOpenDisplay(image_info->server_name);
1677  if (display == (Display *) NULL)
1678    {
1679      (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1680        "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1681      return(MagickFalse);
1682    }
1683  if (exception->severity != UndefinedException)
1684    CatchException(exception);
1685  (void) XSetErrorHandler(XError);
1686  resource_database=XGetResourceDatabase(display,GetClientName());
1687  (void) ResetMagickMemory(&resource_info,0,sizeof(resource_info));
1688  XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1689  if (image_info->page != (char *) NULL)
1690    resource_info.image_geometry=AcquireString(image_info->page);
1691  resource_info.immutable=MagickTrue;
1692  argv[0]=AcquireString(GetClientName());
1693  state=DefaultState;
1694  for (i=0; (state & ExitState) == 0; i++)
1695  {
1696    if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1697      break;
1698    image=GetImageFromList(images,i % GetImageListLength(images));
1699    (void) XDisplayImage(display,&resource_info,argv,1,&image,&state,exception);
1700  }
1701  SetErrorHandler((ErrorHandler) NULL);
1702  SetWarningHandler((WarningHandler) NULL);
1703  argv[0]=DestroyString(argv[0]);
1704  (void) XCloseDisplay(display);
1705  XDestroyResourceInfo(&resource_info);
1706  if (exception->severity != UndefinedException)
1707    return(MagickFalse);
1708  return(MagickTrue);
1709}
1710
1711/*
1712%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1713%                                                                             %
1714%                                                                             %
1715%                                                                             %
1716%   R e m o t e D i s p l a y C o m m a n d                                   %
1717%                                                                             %
1718%                                                                             %
1719%                                                                             %
1720%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1721%
1722%  RemoteDisplayCommand() encourages a remote display program to display the
1723%  specified image filename.
1724%
1725%  The format of the RemoteDisplayCommand method is:
1726%
1727%      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1728%        const char *window,const char *filename,ExceptionInfo *exception)
1729%
1730%  A description of each parameter follows:
1731%
1732%    o image_info: the image info.
1733%
1734%    o window: Specifies the name or id of an X window.
1735%
1736%    o filename: the name of the image filename to display.
1737%
1738%    o exception: return any errors or warnings in this structure.
1739%
1740*/
1741MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1742  const char *window,const char *filename,ExceptionInfo *exception)
1743{
1744  Display
1745    *display;
1746
1747  MagickStatusType
1748    status;
1749
1750  assert(image_info != (const ImageInfo *) NULL);
1751  assert(image_info->signature == MagickSignature);
1752  assert(filename != (char *) NULL);
1753  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1754  display=XOpenDisplay(image_info->server_name);
1755  if (display == (Display *) NULL)
1756    {
1757      (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1758        "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1759      return(MagickFalse);
1760    }
1761  (void) XSetErrorHandler(XError);
1762  status=XRemoteCommand(display,window,filename);
1763  (void) XCloseDisplay(display);
1764  return(status != 0 ? MagickTrue : MagickFalse);
1765}
1766
1767/*
1768%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1769%                                                                             %
1770%                                                                             %
1771%                                                                             %
1772+   X A n n o t a t e E d i t I m a g e                                       %
1773%                                                                             %
1774%                                                                             %
1775%                                                                             %
1776%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1777%
1778%  XAnnotateEditImage() annotates the image with text.
1779%
1780%  The format of the XAnnotateEditImage method is:
1781%
1782%      MagickBooleanType XAnnotateEditImage(Display *display,
1783%        XResourceInfo *resource_info,XWindows *windows,Image *image,
1784%        ExceptionInfo *exception)
1785%
1786%  A description of each parameter follows:
1787%
1788%    o display: Specifies a connection to an X server;  returned from
1789%      XOpenDisplay.
1790%
1791%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1792%
1793%    o windows: Specifies a pointer to a XWindows structure.
1794%
1795%    o image: the image; returned from ReadImage.
1796%
1797*/
1798
1799static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
1800{
1801  if (x > y)
1802    return(x);
1803  return(y);
1804}
1805
1806static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
1807{
1808  if (x < y)
1809    return(x);
1810  return(y);
1811}
1812
1813static MagickBooleanType XAnnotateEditImage(Display *display,
1814  XResourceInfo *resource_info,XWindows *windows,Image *image,
1815  ExceptionInfo *exception)
1816{
1817  static const char
1818    *AnnotateMenu[] =
1819    {
1820      "Font Name",
1821      "Font Color",
1822      "Box Color",
1823      "Rotate Text",
1824      "Help",
1825      "Dismiss",
1826      (char *) NULL
1827    },
1828    *TextMenu[] =
1829    {
1830      "Help",
1831      "Apply",
1832      (char *) NULL
1833    };
1834
1835  static const ModeType
1836    AnnotateCommands[] =
1837    {
1838      AnnotateNameCommand,
1839      AnnotateFontColorCommand,
1840      AnnotateBackgroundColorCommand,
1841      AnnotateRotateCommand,
1842      AnnotateHelpCommand,
1843      AnnotateDismissCommand
1844    },
1845    TextCommands[] =
1846    {
1847      TextHelpCommand,
1848      TextApplyCommand
1849    };
1850
1851  static MagickBooleanType
1852    transparent_box = MagickTrue,
1853    transparent_pen = MagickFalse;
1854
1855  static MagickRealType
1856    degrees = 0.0;
1857
1858  static unsigned int
1859    box_id = MaxNumberPens-2,
1860    font_id = 0,
1861    pen_id = 0;
1862
1863  char
1864    command[MaxTextExtent],
1865    text[MaxTextExtent];
1866
1867  const char
1868    *ColorMenu[MaxNumberPens+1];
1869
1870  Cursor
1871    cursor;
1872
1873  GC
1874    annotate_context;
1875
1876  int
1877    id,
1878    pen_number,
1879    status,
1880    x,
1881    y;
1882
1883  KeySym
1884    key_symbol;
1885
1886  register char
1887    *p;
1888
1889  register ssize_t
1890    i;
1891
1892  unsigned int
1893    height,
1894    width;
1895
1896  size_t
1897    state;
1898
1899  XAnnotateInfo
1900    *annotate_info,
1901    *previous_info;
1902
1903  XColor
1904    color;
1905
1906  XFontStruct
1907    *font_info;
1908
1909  XEvent
1910    event,
1911    text_event;
1912
1913  /*
1914    Map Command widget.
1915  */
1916  (void) CloneString(&windows->command.name,"Annotate");
1917  windows->command.data=4;
1918  (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1919  (void) XMapRaised(display,windows->command.id);
1920  XClientMessage(display,windows->image.id,windows->im_protocols,
1921    windows->im_update_widget,CurrentTime);
1922  /*
1923    Track pointer until button 1 is pressed.
1924  */
1925  XQueryPosition(display,windows->image.id,&x,&y);
1926  (void) XSelectInput(display,windows->image.id,
1927    windows->image.attributes.event_mask | PointerMotionMask);
1928  cursor=XCreateFontCursor(display,XC_left_side);
1929  (void) XCheckDefineCursor(display,windows->image.id,cursor);
1930  state=DefaultState;
1931  do
1932  {
1933    if (windows->info.mapped != MagickFalse)
1934      {
1935        /*
1936          Display pointer position.
1937        */
1938        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
1939          x+windows->image.x,y+windows->image.y);
1940        XInfoWidget(display,windows,text);
1941      }
1942    /*
1943      Wait for next event.
1944    */
1945    XScreenEvent(display,windows,&event);
1946    if (event.xany.window == windows->command.id)
1947      {
1948        /*
1949          Select a command from the Command widget.
1950        */
1951        id=XCommandWidget(display,windows,AnnotateMenu,&event);
1952        (void) XCheckDefineCursor(display,windows->image.id,cursor);
1953        if (id < 0)
1954          continue;
1955        switch (AnnotateCommands[id])
1956        {
1957          case AnnotateNameCommand:
1958          {
1959            const char
1960              *FontMenu[MaxNumberFonts];
1961
1962            int
1963              font_number;
1964
1965            /*
1966              Initialize menu selections.
1967            */
1968            for (i=0; i < MaxNumberFonts; i++)
1969              FontMenu[i]=resource_info->font_name[i];
1970            FontMenu[MaxNumberFonts-2]="Browser...";
1971            FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1972            /*
1973              Select a font name from the pop-up menu.
1974            */
1975            font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1976              (const char **) FontMenu,command);
1977            if (font_number < 0)
1978              break;
1979            if (font_number == (MaxNumberFonts-2))
1980              {
1981                static char
1982                  font_name[MaxTextExtent] = "fixed";
1983
1984                /*
1985                  Select a font name from a browser.
1986                */
1987                resource_info->font_name[font_number]=font_name;
1988                XFontBrowserWidget(display,windows,"Select",font_name);
1989                if (*font_name == '\0')
1990                  break;
1991              }
1992            /*
1993              Initialize font info.
1994            */
1995            font_info=XLoadQueryFont(display,resource_info->font_name[
1996              font_number]);
1997            if (font_info == (XFontStruct *) NULL)
1998              {
1999                XNoticeWidget(display,windows,"Unable to load font:",
2000                  resource_info->font_name[font_number]);
2001                break;
2002              }
2003            font_id=(unsigned int) font_number;
2004            (void) XFreeFont(display,font_info);
2005            break;
2006          }
2007          case AnnotateFontColorCommand:
2008          {
2009            /*
2010              Initialize menu selections.
2011            */
2012            for (i=0; i < (int) (MaxNumberPens-2); i++)
2013              ColorMenu[i]=resource_info->pen_colors[i];
2014            ColorMenu[MaxNumberPens-2]="transparent";
2015            ColorMenu[MaxNumberPens-1]="Browser...";
2016            ColorMenu[MaxNumberPens]=(const char *) NULL;
2017            /*
2018              Select a pen color from the pop-up menu.
2019            */
2020            pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2021              (const char **) ColorMenu,command);
2022            if (pen_number < 0)
2023              break;
2024            transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
2025              MagickFalse;
2026            if (transparent_pen != MagickFalse)
2027              break;
2028            if (pen_number == (MaxNumberPens-1))
2029              {
2030                static char
2031                  color_name[MaxTextExtent] = "gray";
2032
2033                /*
2034                  Select a pen color from a dialog.
2035                */
2036                resource_info->pen_colors[pen_number]=color_name;
2037                XColorBrowserWidget(display,windows,"Select",color_name);
2038                if (*color_name == '\0')
2039                  break;
2040              }
2041            /*
2042              Set pen color.
2043            */
2044            (void) XParseColor(display,windows->map_info->colormap,
2045              resource_info->pen_colors[pen_number],&color);
2046            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2047              (unsigned int) MaxColors,&color);
2048            windows->pixel_info->pen_colors[pen_number]=color;
2049            pen_id=(unsigned int) pen_number;
2050            break;
2051          }
2052          case AnnotateBackgroundColorCommand:
2053          {
2054            /*
2055              Initialize menu selections.
2056            */
2057            for (i=0; i < (int) (MaxNumberPens-2); i++)
2058              ColorMenu[i]=resource_info->pen_colors[i];
2059            ColorMenu[MaxNumberPens-2]="transparent";
2060            ColorMenu[MaxNumberPens-1]="Browser...";
2061            ColorMenu[MaxNumberPens]=(const char *) NULL;
2062            /*
2063              Select a pen color from the pop-up menu.
2064            */
2065            pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2066              (const char **) ColorMenu,command);
2067            if (pen_number < 0)
2068              break;
2069            transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2070              MagickFalse;
2071            if (transparent_box != MagickFalse)
2072              break;
2073            if (pen_number == (MaxNumberPens-1))
2074              {
2075                static char
2076                  color_name[MaxTextExtent] = "gray";
2077
2078                /*
2079                  Select a pen color from a dialog.
2080                */
2081                resource_info->pen_colors[pen_number]=color_name;
2082                XColorBrowserWidget(display,windows,"Select",color_name);
2083                if (*color_name == '\0')
2084                  break;
2085              }
2086            /*
2087              Set pen color.
2088            */
2089            (void) XParseColor(display,windows->map_info->colormap,
2090              resource_info->pen_colors[pen_number],&color);
2091            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2092              (unsigned int) MaxColors,&color);
2093            windows->pixel_info->pen_colors[pen_number]=color;
2094            box_id=(unsigned int) pen_number;
2095            break;
2096          }
2097          case AnnotateRotateCommand:
2098          {
2099            int
2100              entry;
2101
2102            static char
2103              angle[MaxTextExtent] = "30.0";
2104
2105            static const char
2106              *RotateMenu[] =
2107              {
2108                "-90",
2109                "-45",
2110                "-30",
2111                "0",
2112                "30",
2113                "45",
2114                "90",
2115                "180",
2116                "Dialog...",
2117                (char *) NULL,
2118              };
2119
2120            /*
2121              Select a command from the pop-up menu.
2122            */
2123            entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2124              command);
2125            if (entry < 0)
2126              break;
2127            if (entry != 8)
2128              {
2129                degrees=InterpretLocaleValue(RotateMenu[entry],(char **) NULL);
2130                break;
2131              }
2132            (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2133              angle);
2134            if (*angle == '\0')
2135              break;
2136            degrees=InterpretLocaleValue(angle,(char **) NULL);
2137            break;
2138          }
2139          case AnnotateHelpCommand:
2140          {
2141            XTextViewWidget(display,resource_info,windows,MagickFalse,
2142              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2143            break;
2144          }
2145          case AnnotateDismissCommand:
2146          {
2147            /*
2148              Prematurely exit.
2149            */
2150            state|=EscapeState;
2151            state|=ExitState;
2152            break;
2153          }
2154          default:
2155            break;
2156        }
2157        continue;
2158      }
2159    switch (event.type)
2160    {
2161      case ButtonPress:
2162      {
2163        if (event.xbutton.button != Button1)
2164          break;
2165        if (event.xbutton.window != windows->image.id)
2166          break;
2167        /*
2168          Change to text entering mode.
2169        */
2170        x=event.xbutton.x;
2171        y=event.xbutton.y;
2172        state|=ExitState;
2173        break;
2174      }
2175      case ButtonRelease:
2176        break;
2177      case Expose:
2178        break;
2179      case KeyPress:
2180      {
2181        if (event.xkey.window != windows->image.id)
2182          break;
2183        /*
2184          Respond to a user key press.
2185        */
2186        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2187          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2188        switch ((int) key_symbol)
2189        {
2190          case XK_Escape:
2191          case XK_F20:
2192          {
2193            /*
2194              Prematurely exit.
2195            */
2196            state|=EscapeState;
2197            state|=ExitState;
2198            break;
2199          }
2200          case XK_F1:
2201          case XK_Help:
2202          {
2203            XTextViewWidget(display,resource_info,windows,MagickFalse,
2204              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2205            break;
2206          }
2207          default:
2208          {
2209            (void) XBell(display,0);
2210            break;
2211          }
2212        }
2213        break;
2214      }
2215      case MotionNotify:
2216      {
2217        /*
2218          Map and unmap Info widget as cursor crosses its boundaries.
2219        */
2220        x=event.xmotion.x;
2221        y=event.xmotion.y;
2222        if (windows->info.mapped != MagickFalse)
2223          {
2224            if ((x < (int) (windows->info.x+windows->info.width)) &&
2225                (y < (int) (windows->info.y+windows->info.height)))
2226              (void) XWithdrawWindow(display,windows->info.id,
2227                windows->info.screen);
2228          }
2229        else
2230          if ((x > (int) (windows->info.x+windows->info.width)) ||
2231              (y > (int) (windows->info.y+windows->info.height)))
2232            (void) XMapWindow(display,windows->info.id);
2233        break;
2234      }
2235      default:
2236        break;
2237    }
2238  } while ((state & ExitState) == 0);
2239  (void) XSelectInput(display,windows->image.id,
2240    windows->image.attributes.event_mask);
2241  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2242  if ((state & EscapeState) != 0)
2243    return(MagickTrue);
2244  /*
2245    Set font info and check boundary conditions.
2246  */
2247  font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2248  if (font_info == (XFontStruct *) NULL)
2249    {
2250      XNoticeWidget(display,windows,"Unable to load font:",
2251        resource_info->font_name[font_id]);
2252      font_info=windows->font_info;
2253    }
2254  if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2255    x=(int) windows->image.width-font_info->max_bounds.width;
2256  if (y < (int) (font_info->ascent+font_info->descent))
2257    y=(int) font_info->ascent+font_info->descent;
2258  if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2259      ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2260    return(MagickFalse);
2261  /*
2262    Initialize annotate structure.
2263  */
2264  annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2265  if (annotate_info == (XAnnotateInfo *) NULL)
2266    return(MagickFalse);
2267  XGetAnnotateInfo(annotate_info);
2268  annotate_info->x=x;
2269  annotate_info->y=y;
2270  if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
2271    annotate_info->stencil=OpaqueStencil;
2272  else
2273    if (transparent_box == MagickFalse)
2274      annotate_info->stencil=BackgroundStencil;
2275    else
2276      annotate_info->stencil=ForegroundStencil;
2277  annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2278  annotate_info->degrees=degrees;
2279  annotate_info->font_info=font_info;
2280  annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2281    windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
2282    sizeof(*annotate_info->text));
2283  if (annotate_info->text == (char *) NULL)
2284    return(MagickFalse);
2285  /*
2286    Create cursor and set graphic context.
2287  */
2288  cursor=XCreateFontCursor(display,XC_pencil);
2289  (void) XCheckDefineCursor(display,windows->image.id,cursor);
2290  annotate_context=windows->image.annotate_context;
2291  (void) XSetFont(display,annotate_context,font_info->fid);
2292  (void) XSetBackground(display,annotate_context,
2293    windows->pixel_info->pen_colors[box_id].pixel);
2294  (void) XSetForeground(display,annotate_context,
2295    windows->pixel_info->pen_colors[pen_id].pixel);
2296  /*
2297    Begin annotating the image with text.
2298  */
2299  (void) CloneString(&windows->command.name,"Text");
2300  windows->command.data=0;
2301  (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2302  state=DefaultState;
2303  (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2304  text_event.xexpose.width=(int) font_info->max_bounds.width;
2305  text_event.xexpose.height=font_info->max_bounds.ascent+
2306    font_info->max_bounds.descent;
2307  p=annotate_info->text;
2308  do
2309  {
2310    /*
2311      Display text cursor.
2312    */
2313    *p='\0';
2314    (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2315    /*
2316      Wait for next event.
2317    */
2318    XScreenEvent(display,windows,&event);
2319    if (event.xany.window == windows->command.id)
2320      {
2321        /*
2322          Select a command from the Command widget.
2323        */
2324        (void) XSetBackground(display,annotate_context,
2325          windows->pixel_info->background_color.pixel);
2326        (void) XSetForeground(display,annotate_context,
2327          windows->pixel_info->foreground_color.pixel);
2328        id=XCommandWidget(display,windows,AnnotateMenu,&event);
2329        (void) XSetBackground(display,annotate_context,
2330          windows->pixel_info->pen_colors[box_id].pixel);
2331        (void) XSetForeground(display,annotate_context,
2332          windows->pixel_info->pen_colors[pen_id].pixel);
2333        if (id < 0)
2334          continue;
2335        switch (TextCommands[id])
2336        {
2337          case TextHelpCommand:
2338          {
2339            XTextViewWidget(display,resource_info,windows,MagickFalse,
2340              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2341            (void) XCheckDefineCursor(display,windows->image.id,cursor);
2342            break;
2343          }
2344          case TextApplyCommand:
2345          {
2346            /*
2347              Finished annotating.
2348            */
2349            annotate_info->width=(unsigned int) XTextWidth(font_info,
2350              annotate_info->text,(int) strlen(annotate_info->text));
2351            XRefreshWindow(display,&windows->image,&text_event);
2352            state|=ExitState;
2353            break;
2354          }
2355          default:
2356            break;
2357        }
2358        continue;
2359      }
2360    /*
2361      Erase text cursor.
2362    */
2363    text_event.xexpose.x=x;
2364    text_event.xexpose.y=y-font_info->max_bounds.ascent;
2365    (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2366      (unsigned int) text_event.xexpose.width,(unsigned int)
2367      text_event.xexpose.height,MagickFalse);
2368    XRefreshWindow(display,&windows->image,&text_event);
2369    switch (event.type)
2370    {
2371      case ButtonPress:
2372      {
2373        if (event.xbutton.window != windows->image.id)
2374          break;
2375        if (event.xbutton.button == Button2)
2376          {
2377            /*
2378              Request primary selection.
2379            */
2380            (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2381              windows->image.id,CurrentTime);
2382            break;
2383          }
2384        break;
2385      }
2386      case Expose:
2387      {
2388        if (event.xexpose.count == 0)
2389          {
2390            XAnnotateInfo
2391              *text_info;
2392
2393            /*
2394              Refresh Image window.
2395            */
2396            XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2397            text_info=annotate_info;
2398            while (text_info != (XAnnotateInfo *) NULL)
2399            {
2400              if (annotate_info->stencil == ForegroundStencil)
2401                (void) XDrawString(display,windows->image.id,annotate_context,
2402                  text_info->x,text_info->y,text_info->text,
2403                  (int) strlen(text_info->text));
2404              else
2405                (void) XDrawImageString(display,windows->image.id,
2406                  annotate_context,text_info->x,text_info->y,text_info->text,
2407                  (int) strlen(text_info->text));
2408              text_info=text_info->previous;
2409            }
2410            (void) XDrawString(display,windows->image.id,annotate_context,
2411              x,y,"_",1);
2412          }
2413        break;
2414      }
2415      case KeyPress:
2416      {
2417        int
2418          length;
2419
2420        if (event.xkey.window != windows->image.id)
2421          break;
2422        /*
2423          Respond to a user key press.
2424        */
2425        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2426          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2427        *(command+length)='\0';
2428        if (((event.xkey.state & ControlMask) != 0) ||
2429            ((event.xkey.state & Mod1Mask) != 0))
2430          state|=ModifierState;
2431        if ((state & ModifierState) != 0)
2432          switch ((int) key_symbol)
2433          {
2434            case XK_u:
2435            case XK_U:
2436            {
2437              key_symbol=DeleteCommand;
2438              break;
2439            }
2440            default:
2441              break;
2442          }
2443        switch ((int) key_symbol)
2444        {
2445          case XK_BackSpace:
2446          {
2447            /*
2448              Erase one character.
2449            */
2450            if (p == annotate_info->text)
2451              {
2452                if (annotate_info->previous == (XAnnotateInfo *) NULL)
2453                  break;
2454                else
2455                  {
2456                    /*
2457                      Go to end of the previous line of text.
2458                    */
2459                    annotate_info=annotate_info->previous;
2460                    p=annotate_info->text;
2461                    x=annotate_info->x+annotate_info->width;
2462                    y=annotate_info->y;
2463                    if (annotate_info->width != 0)
2464                      p+=strlen(annotate_info->text);
2465                    break;
2466                  }
2467              }
2468            p--;
2469            x-=XTextWidth(font_info,p,1);
2470            text_event.xexpose.x=x;
2471            text_event.xexpose.y=y-font_info->max_bounds.ascent;
2472            XRefreshWindow(display,&windows->image,&text_event);
2473            break;
2474          }
2475          case XK_bracketleft:
2476          {
2477            key_symbol=XK_Escape;
2478            break;
2479          }
2480          case DeleteCommand:
2481          {
2482            /*
2483              Erase the entire line of text.
2484            */
2485            while (p != annotate_info->text)
2486            {
2487              p--;
2488              x-=XTextWidth(font_info,p,1);
2489              text_event.xexpose.x=x;
2490              XRefreshWindow(display,&windows->image,&text_event);
2491            }
2492            break;
2493          }
2494          case XK_Escape:
2495          case XK_F20:
2496          {
2497            /*
2498              Finished annotating.
2499            */
2500            annotate_info->width=(unsigned int) XTextWidth(font_info,
2501              annotate_info->text,(int) strlen(annotate_info->text));
2502            XRefreshWindow(display,&windows->image,&text_event);
2503            state|=ExitState;
2504            break;
2505          }
2506          default:
2507          {
2508            /*
2509              Draw a single character on the Image window.
2510            */
2511            if ((state & ModifierState) != 0)
2512              break;
2513            if (*command == '\0')
2514              break;
2515            *p=(*command);
2516            if (annotate_info->stencil == ForegroundStencil)
2517              (void) XDrawString(display,windows->image.id,annotate_context,
2518                x,y,p,1);
2519            else
2520              (void) XDrawImageString(display,windows->image.id,
2521                annotate_context,x,y,p,1);
2522            x+=XTextWidth(font_info,p,1);
2523            p++;
2524            if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2525              break;
2526          }
2527          case XK_Return:
2528          case XK_KP_Enter:
2529          {
2530            /*
2531              Advance to the next line of text.
2532            */
2533            *p='\0';
2534            annotate_info->width=(unsigned int) XTextWidth(font_info,
2535              annotate_info->text,(int) strlen(annotate_info->text));
2536            if (annotate_info->next != (XAnnotateInfo *) NULL)
2537              {
2538                /*
2539                  Line of text already exists.
2540                */
2541                annotate_info=annotate_info->next;
2542                x=annotate_info->x;
2543                y=annotate_info->y;
2544                p=annotate_info->text;
2545                break;
2546              }
2547            annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2548              sizeof(*annotate_info->next));
2549            if (annotate_info->next == (XAnnotateInfo *) NULL)
2550              return(MagickFalse);
2551            *annotate_info->next=(*annotate_info);
2552            annotate_info->next->previous=annotate_info;
2553            annotate_info=annotate_info->next;
2554            annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2555              windows->image.width/MagickMax((ssize_t)
2556              font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2557            if (annotate_info->text == (char *) NULL)
2558              return(MagickFalse);
2559            annotate_info->y+=annotate_info->height;
2560            if (annotate_info->y > (int) windows->image.height)
2561              annotate_info->y=(int) annotate_info->height;
2562            annotate_info->next=(XAnnotateInfo *) NULL;
2563            x=annotate_info->x;
2564            y=annotate_info->y;
2565            p=annotate_info->text;
2566            break;
2567          }
2568        }
2569        break;
2570      }
2571      case KeyRelease:
2572      {
2573        /*
2574          Respond to a user key release.
2575        */
2576        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2577          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2578        state&=(~ModifierState);
2579        break;
2580      }
2581      case SelectionNotify:
2582      {
2583        Atom
2584          type;
2585
2586        int
2587          format;
2588
2589        unsigned char
2590          *data;
2591
2592        unsigned long
2593          after,
2594          length;
2595
2596        /*
2597          Obtain response from primary selection.
2598        */
2599        if (event.xselection.property == (Atom) None)
2600          break;
2601        status=XGetWindowProperty(display,event.xselection.requestor,
2602          event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
2603          &type,&format,&length,&after,&data);
2604        if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2605            (length == 0))
2606          break;
2607        /*
2608          Annotate Image window with primary selection.
2609        */
2610        for (i=0; i < (ssize_t) length; i++)
2611        {
2612          if ((char) data[i] != '\n')
2613            {
2614              /*
2615                Draw a single character on the Image window.
2616              */
2617              *p=(char) data[i];
2618              (void) XDrawString(display,windows->image.id,annotate_context,
2619                x,y,p,1);
2620              x+=XTextWidth(font_info,p,1);
2621              p++;
2622              if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2623                continue;
2624            }
2625          /*
2626            Advance to the next line of text.
2627          */
2628          *p='\0';
2629          annotate_info->width=(unsigned int) XTextWidth(font_info,
2630            annotate_info->text,(int) strlen(annotate_info->text));
2631          if (annotate_info->next != (XAnnotateInfo *) NULL)
2632            {
2633              /*
2634                Line of text already exists.
2635              */
2636              annotate_info=annotate_info->next;
2637              x=annotate_info->x;
2638              y=annotate_info->y;
2639              p=annotate_info->text;
2640              continue;
2641            }
2642          annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2643            sizeof(*annotate_info->next));
2644          if (annotate_info->next == (XAnnotateInfo *) NULL)
2645            return(MagickFalse);
2646          *annotate_info->next=(*annotate_info);
2647          annotate_info->next->previous=annotate_info;
2648          annotate_info=annotate_info->next;
2649          annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2650            windows->image.width/MagickMax((ssize_t)
2651            font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2652          if (annotate_info->text == (char *) NULL)
2653            return(MagickFalse);
2654          annotate_info->y+=annotate_info->height;
2655          if (annotate_info->y > (int) windows->image.height)
2656            annotate_info->y=(int) annotate_info->height;
2657          annotate_info->next=(XAnnotateInfo *) NULL;
2658          x=annotate_info->x;
2659          y=annotate_info->y;
2660          p=annotate_info->text;
2661        }
2662        (void) XFree((void *) data);
2663        break;
2664      }
2665      default:
2666        break;
2667    }
2668  } while ((state & ExitState) == 0);
2669  (void) XFreeCursor(display,cursor);
2670  /*
2671    Annotation is relative to image configuration.
2672  */
2673  width=(unsigned int) image->columns;
2674  height=(unsigned int) image->rows;
2675  x=0;
2676  y=0;
2677  if (windows->image.crop_geometry != (char *) NULL)
2678    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2679  /*
2680    Initialize annotated image.
2681  */
2682  XSetCursorState(display,windows,MagickTrue);
2683  XCheckRefreshWindows(display,windows);
2684  while (annotate_info != (XAnnotateInfo *) NULL)
2685  {
2686    if (annotate_info->width == 0)
2687      {
2688        /*
2689          No text on this line--  go to the next line of text.
2690        */
2691        previous_info=annotate_info->previous;
2692        annotate_info->text=(char *)
2693          RelinquishMagickMemory(annotate_info->text);
2694        annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2695        annotate_info=previous_info;
2696        continue;
2697      }
2698    /*
2699      Determine pixel index for box and pen color.
2700    */
2701    windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2702    if (windows->pixel_info->colors != 0)
2703      for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2704        if (windows->pixel_info->pixels[i] ==
2705            windows->pixel_info->pen_colors[box_id].pixel)
2706          {
2707            windows->pixel_info->box_index=(unsigned short) i;
2708            break;
2709          }
2710    windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2711    if (windows->pixel_info->colors != 0)
2712      for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2713        if (windows->pixel_info->pixels[i] ==
2714            windows->pixel_info->pen_colors[pen_id].pixel)
2715          {
2716            windows->pixel_info->pen_index=(unsigned short) i;
2717            break;
2718          }
2719    /*
2720      Define the annotate geometry string.
2721    */
2722    annotate_info->x=(int)
2723      width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2724    annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2725      windows->image.y)/windows->image.ximage->height;
2726    (void) FormatLocaleString(annotate_info->geometry,MaxTextExtent,
2727      "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2728      height*annotate_info->height/windows->image.ximage->height,
2729      annotate_info->x+x,annotate_info->y+y);
2730    /*
2731      Annotate image with text.
2732    */
2733    status=XAnnotateImage(display,windows->pixel_info,annotate_info,image);
2734    if (status == 0)
2735      return(MagickFalse);
2736    /*
2737      Free up memory.
2738    */
2739    previous_info=annotate_info->previous;
2740    annotate_info->text=DestroyString(annotate_info->text);
2741    annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2742    annotate_info=previous_info;
2743  }
2744  (void) XSetForeground(display,annotate_context,
2745    windows->pixel_info->foreground_color.pixel);
2746  (void) XSetBackground(display,annotate_context,
2747    windows->pixel_info->background_color.pixel);
2748  (void) XSetFont(display,annotate_context,windows->font_info->fid);
2749  XSetCursorState(display,windows,MagickFalse);
2750  (void) XFreeFont(display,font_info);
2751  /*
2752    Update image configuration.
2753  */
2754  XConfigureImageColormap(display,resource_info,windows,image);
2755  (void) XConfigureImage(display,resource_info,windows,image,exception);
2756  return(MagickTrue);
2757}
2758
2759/*
2760%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2761%                                                                             %
2762%                                                                             %
2763%                                                                             %
2764+   X B a c k g r o u n d I m a g e                                           %
2765%                                                                             %
2766%                                                                             %
2767%                                                                             %
2768%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2769%
2770%  XBackgroundImage() displays the image in the background of a window.
2771%
2772%  The format of the XBackgroundImage method is:
2773%
2774%      MagickBooleanType XBackgroundImage(Display *display,
2775%        XResourceInfo *resource_info,XWindows *windows,Image **image,
2776%        ExceptionInfo *exception)
2777%
2778%  A description of each parameter follows:
2779%
2780%    o display: Specifies a connection to an X server; returned from
2781%      XOpenDisplay.
2782%
2783%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2784%
2785%    o windows: Specifies a pointer to a XWindows structure.
2786%
2787%    o image: the image.
2788%
2789%    o exception: return any errors or warnings in this structure.
2790%
2791*/
2792static MagickBooleanType XBackgroundImage(Display *display,
2793  XResourceInfo *resource_info,XWindows *windows,Image **image,
2794  ExceptionInfo *exception)
2795{
2796#define BackgroundImageTag  "Background/Image"
2797
2798  int
2799    status;
2800
2801  static char
2802    window_id[MaxTextExtent] = "root";
2803
2804  XResourceInfo
2805    background_resources;
2806
2807  /*
2808    Put image in background.
2809  */
2810  status=XDialogWidget(display,windows,"Background",
2811    "Enter window id (id 0x00 selects window with pointer):",window_id);
2812  if (*window_id == '\0')
2813    return(MagickFalse);
2814  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
2815    exception);
2816  XInfoWidget(display,windows,BackgroundImageTag);
2817  XSetCursorState(display,windows,MagickTrue);
2818  XCheckRefreshWindows(display,windows);
2819  background_resources=(*resource_info);
2820  background_resources.window_id=window_id;
2821  background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2822  status=XDisplayBackgroundImage(display,&background_resources,*image,
2823    exception);
2824  if (status != MagickFalse)
2825    XClientMessage(display,windows->image.id,windows->im_protocols,
2826      windows->im_retain_colors,CurrentTime);
2827  XSetCursorState(display,windows,MagickFalse);
2828  (void) XMagickCommand(display,resource_info,windows,UndoCommand,image,
2829    exception);
2830  return(MagickTrue);
2831}
2832
2833/*
2834%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2835%                                                                             %
2836%                                                                             %
2837%                                                                             %
2838+   X C h o p I m a g e                                                       %
2839%                                                                             %
2840%                                                                             %
2841%                                                                             %
2842%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2843%
2844%  XChopImage() chops the X image.
2845%
2846%  The format of the XChopImage method is:
2847%
2848%    MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2849%      XWindows *windows,Image **image,ExceptionInfo *exception)
2850%
2851%  A description of each parameter follows:
2852%
2853%    o display: Specifies a connection to an X server; returned from
2854%      XOpenDisplay.
2855%
2856%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2857%
2858%    o windows: Specifies a pointer to a XWindows structure.
2859%
2860%    o image: the image.
2861%
2862%    o exception: return any errors or warnings in this structure.
2863%
2864*/
2865static MagickBooleanType XChopImage(Display *display,
2866  XResourceInfo *resource_info,XWindows *windows,Image **image,
2867  ExceptionInfo *exception)
2868{
2869  static const char
2870    *ChopMenu[] =
2871    {
2872      "Direction",
2873      "Help",
2874      "Dismiss",
2875      (char *) NULL
2876    };
2877
2878  static ModeType
2879    direction = HorizontalChopCommand;
2880
2881  static const ModeType
2882    ChopCommands[] =
2883    {
2884      ChopDirectionCommand,
2885      ChopHelpCommand,
2886      ChopDismissCommand
2887    },
2888    DirectionCommands[] =
2889    {
2890      HorizontalChopCommand,
2891      VerticalChopCommand
2892    };
2893
2894  char
2895    text[MaxTextExtent];
2896
2897  Image
2898    *chop_image;
2899
2900  int
2901    id,
2902    x,
2903    y;
2904
2905  MagickRealType
2906    scale_factor;
2907
2908  RectangleInfo
2909    chop_info;
2910
2911  unsigned int
2912    distance,
2913    height,
2914    width;
2915
2916  size_t
2917    state;
2918
2919  XEvent
2920    event;
2921
2922  XSegment
2923    segment_info;
2924
2925  /*
2926    Map Command widget.
2927  */
2928  (void) CloneString(&windows->command.name,"Chop");
2929  windows->command.data=1;
2930  (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2931  (void) XMapRaised(display,windows->command.id);
2932  XClientMessage(display,windows->image.id,windows->im_protocols,
2933    windows->im_update_widget,CurrentTime);
2934  /*
2935    Track pointer until button 1 is pressed.
2936  */
2937  XQueryPosition(display,windows->image.id,&x,&y);
2938  (void) XSelectInput(display,windows->image.id,
2939    windows->image.attributes.event_mask | PointerMotionMask);
2940  state=DefaultState;
2941  do
2942  {
2943    if (windows->info.mapped != MagickFalse)
2944      {
2945        /*
2946          Display pointer position.
2947        */
2948        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
2949          x+windows->image.x,y+windows->image.y);
2950        XInfoWidget(display,windows,text);
2951      }
2952    /*
2953      Wait for next event.
2954    */
2955    XScreenEvent(display,windows,&event);
2956    if (event.xany.window == windows->command.id)
2957      {
2958        /*
2959          Select a command from the Command widget.
2960        */
2961        id=XCommandWidget(display,windows,ChopMenu,&event);
2962        if (id < 0)
2963          continue;
2964        switch (ChopCommands[id])
2965        {
2966          case ChopDirectionCommand:
2967          {
2968            char
2969              command[MaxTextExtent];
2970
2971            static const char
2972              *Directions[] =
2973              {
2974                "horizontal",
2975                "vertical",
2976                (char *) NULL,
2977              };
2978
2979            /*
2980              Select a command from the pop-up menu.
2981            */
2982            id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2983            if (id >= 0)
2984              direction=DirectionCommands[id];
2985            break;
2986          }
2987          case ChopHelpCommand:
2988          {
2989            XTextViewWidget(display,resource_info,windows,MagickFalse,
2990              "Help Viewer - Image Chop",ImageChopHelp);
2991            break;
2992          }
2993          case ChopDismissCommand:
2994          {
2995            /*
2996              Prematurely exit.
2997            */
2998            state|=EscapeState;
2999            state|=ExitState;
3000            break;
3001          }
3002          default:
3003            break;
3004        }
3005        continue;
3006      }
3007    switch (event.type)
3008    {
3009      case ButtonPress:
3010      {
3011        if (event.xbutton.button != Button1)
3012          break;
3013        if (event.xbutton.window != windows->image.id)
3014          break;
3015        /*
3016          User has committed to start point of chopping line.
3017        */
3018        segment_info.x1=(short int) event.xbutton.x;
3019        segment_info.x2=(short int) event.xbutton.x;
3020        segment_info.y1=(short int) event.xbutton.y;
3021        segment_info.y2=(short int) event.xbutton.y;
3022        state|=ExitState;
3023        break;
3024      }
3025      case ButtonRelease:
3026        break;
3027      case Expose:
3028        break;
3029      case KeyPress:
3030      {
3031        char
3032          command[MaxTextExtent];
3033
3034        KeySym
3035          key_symbol;
3036
3037        if (event.xkey.window != windows->image.id)
3038          break;
3039        /*
3040          Respond to a user key press.
3041        */
3042        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3043          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3044        switch ((int) key_symbol)
3045        {
3046          case XK_Escape:
3047          case XK_F20:
3048          {
3049            /*
3050              Prematurely exit.
3051            */
3052            state|=EscapeState;
3053            state|=ExitState;
3054            break;
3055          }
3056          case XK_F1:
3057          case XK_Help:
3058          {
3059            (void) XSetFunction(display,windows->image.highlight_context,
3060              GXcopy);
3061            XTextViewWidget(display,resource_info,windows,MagickFalse,
3062              "Help Viewer - Image Chop",ImageChopHelp);
3063            (void) XSetFunction(display,windows->image.highlight_context,
3064              GXinvert);
3065            break;
3066          }
3067          default:
3068          {
3069            (void) XBell(display,0);
3070            break;
3071          }
3072        }
3073        break;
3074      }
3075      case MotionNotify:
3076      {
3077        /*
3078          Map and unmap Info widget as text cursor crosses its boundaries.
3079        */
3080        x=event.xmotion.x;
3081        y=event.xmotion.y;
3082        if (windows->info.mapped != MagickFalse)
3083          {
3084            if ((x < (int) (windows->info.x+windows->info.width)) &&
3085                (y < (int) (windows->info.y+windows->info.height)))
3086              (void) XWithdrawWindow(display,windows->info.id,
3087                windows->info.screen);
3088          }
3089        else
3090          if ((x > (int) (windows->info.x+windows->info.width)) ||
3091              (y > (int) (windows->info.y+windows->info.height)))
3092            (void) XMapWindow(display,windows->info.id);
3093      }
3094    }
3095  } while ((state & ExitState) == 0);
3096  (void) XSelectInput(display,windows->image.id,
3097    windows->image.attributes.event_mask);
3098  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3099  if ((state & EscapeState) != 0)
3100    return(MagickTrue);
3101  /*
3102    Draw line as pointer moves until the mouse button is released.
3103  */
3104  chop_info.width=0;
3105  chop_info.height=0;
3106  chop_info.x=0;
3107  chop_info.y=0;
3108  distance=0;
3109  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3110  state=DefaultState;
3111  do
3112  {
3113    if (distance > 9)
3114      {
3115        /*
3116          Display info and draw chopping line.
3117        */
3118        if (windows->info.mapped == MagickFalse)
3119          (void) XMapWindow(display,windows->info.id);
3120        (void) FormatLocaleString(text,MaxTextExtent,
3121          " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3122          chop_info.height,(double) chop_info.x,(double) chop_info.y);
3123        XInfoWidget(display,windows,text);
3124        XHighlightLine(display,windows->image.id,
3125          windows->image.highlight_context,&segment_info);
3126      }
3127    else
3128      if (windows->info.mapped != MagickFalse)
3129        (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3130    /*
3131      Wait for next event.
3132    */
3133    XScreenEvent(display,windows,&event);
3134    if (distance > 9)
3135      XHighlightLine(display,windows->image.id,
3136        windows->image.highlight_context,&segment_info);
3137    switch (event.type)
3138    {
3139      case ButtonPress:
3140      {
3141        segment_info.x2=(short int) event.xmotion.x;
3142        segment_info.y2=(short int) event.xmotion.y;
3143        break;
3144      }
3145      case ButtonRelease:
3146      {
3147        /*
3148          User has committed to chopping line.
3149        */
3150        segment_info.x2=(short int) event.xbutton.x;
3151        segment_info.y2=(short int) event.xbutton.y;
3152        state|=ExitState;
3153        break;
3154      }
3155      case Expose:
3156        break;
3157      case MotionNotify:
3158      {
3159        segment_info.x2=(short int) event.xmotion.x;
3160        segment_info.y2=(short int) event.xmotion.y;
3161      }
3162      default:
3163        break;
3164    }
3165    /*
3166      Check boundary conditions.
3167    */
3168    if (segment_info.x2 < 0)
3169      segment_info.x2=0;
3170    else
3171      if (segment_info.x2 > windows->image.ximage->width)
3172        segment_info.x2=windows->image.ximage->width;
3173    if (segment_info.y2 < 0)
3174      segment_info.y2=0;
3175    else
3176      if (segment_info.y2 > windows->image.ximage->height)
3177        segment_info.y2=windows->image.ximage->height;
3178    distance=(unsigned int)
3179      (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3180       ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3181    /*
3182      Compute chopping geometry.
3183    */
3184    if (direction == HorizontalChopCommand)
3185      {
3186        chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3187        chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3188        chop_info.height=0;
3189        chop_info.y=0;
3190        if (segment_info.x1 > (int) segment_info.x2)
3191          {
3192            chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3193            chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3194          }
3195      }
3196    else
3197      {
3198        chop_info.width=0;
3199        chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3200        chop_info.x=0;
3201        chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3202        if (segment_info.y1 > segment_info.y2)
3203          {
3204            chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3205            chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3206          }
3207      }
3208  } while ((state & ExitState) == 0);
3209  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3210  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3211  if (distance <= 9)
3212    return(MagickTrue);
3213  /*
3214    Image chopping is relative to image configuration.
3215  */
3216  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
3217    exception);
3218  XSetCursorState(display,windows,MagickTrue);
3219  XCheckRefreshWindows(display,windows);
3220  windows->image.window_changes.width=windows->image.ximage->width-
3221    (unsigned int) chop_info.width;
3222  windows->image.window_changes.height=windows->image.ximage->height-
3223    (unsigned int) chop_info.height;
3224  width=(unsigned int) (*image)->columns;
3225  height=(unsigned int) (*image)->rows;
3226  x=0;
3227  y=0;
3228  if (windows->image.crop_geometry != (char *) NULL)
3229    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3230  scale_factor=(MagickRealType) width/windows->image.ximage->width;
3231  chop_info.x+=x;
3232  chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3233  chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3234  scale_factor=(MagickRealType) height/windows->image.ximage->height;
3235  chop_info.y+=y;
3236  chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3237  chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3238  /*
3239    Chop image.
3240  */
3241  chop_image=ChopImage(*image,&chop_info,exception);
3242  XSetCursorState(display,windows,MagickFalse);
3243  if (chop_image == (Image *) NULL)
3244    return(MagickFalse);
3245  *image=DestroyImage(*image);
3246  *image=chop_image;
3247  /*
3248    Update image configuration.
3249  */
3250  XConfigureImageColormap(display,resource_info,windows,*image);
3251  (void) XConfigureImage(display,resource_info,windows,*image,exception);
3252  return(MagickTrue);
3253}
3254
3255/*
3256%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3257%                                                                             %
3258%                                                                             %
3259%                                                                             %
3260+   X C o l o r E d i t I m a g e                                             %
3261%                                                                             %
3262%                                                                             %
3263%                                                                             %
3264%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3265%
3266%  XColorEditImage() allows the user to interactively change the color of one
3267%  pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3268%
3269%  The format of the XColorEditImage method is:
3270%
3271%      MagickBooleanType XColorEditImage(Display *display,
3272%        XResourceInfo *resource_info,XWindows *windows,Image **image,
3273%          ExceptionInfo *exception)
3274%
3275%  A description of each parameter follows:
3276%
3277%    o display: Specifies a connection to an X server;  returned from
3278%      XOpenDisplay.
3279%
3280%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3281%
3282%    o windows: Specifies a pointer to a XWindows structure.
3283%
3284%    o image: the image; returned from ReadImage.
3285%
3286%    o exception: return any errors or warnings in this structure.
3287%
3288*/
3289static MagickBooleanType XColorEditImage(Display *display,
3290  XResourceInfo *resource_info,XWindows *windows,Image **image,
3291  ExceptionInfo *exception)
3292{
3293  static const char
3294    *ColorEditMenu[] =
3295    {
3296      "Method",
3297      "Pixel Color",
3298      "Border Color",
3299      "Fuzz",
3300      "Undo",
3301      "Help",
3302      "Dismiss",
3303      (char *) NULL
3304    };
3305
3306  static const ModeType
3307    ColorEditCommands[] =
3308    {
3309      ColorEditMethodCommand,
3310      ColorEditColorCommand,
3311      ColorEditBorderCommand,
3312      ColorEditFuzzCommand,
3313      ColorEditUndoCommand,
3314      ColorEditHelpCommand,
3315      ColorEditDismissCommand
3316    };
3317
3318  static PaintMethod
3319    method = PointMethod;
3320
3321  static unsigned int
3322    pen_id = 0;
3323
3324  static XColor
3325    border_color = { 0, 0, 0, 0, 0, 0 };
3326
3327  char
3328    command[MaxTextExtent],
3329    text[MaxTextExtent];
3330
3331  Cursor
3332    cursor;
3333
3334  int
3335    entry,
3336    id,
3337    x,
3338    x_offset,
3339    y,
3340    y_offset;
3341
3342  register Quantum
3343    *q;
3344
3345  register ssize_t
3346    i;
3347
3348  unsigned int
3349    height,
3350    width;
3351
3352  size_t
3353    state;
3354
3355  XColor
3356    color;
3357
3358  XEvent
3359    event;
3360
3361  /*
3362    Map Command widget.
3363  */
3364  (void) CloneString(&windows->command.name,"Color Edit");
3365  windows->command.data=4;
3366  (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3367  (void) XMapRaised(display,windows->command.id);
3368  XClientMessage(display,windows->image.id,windows->im_protocols,
3369    windows->im_update_widget,CurrentTime);
3370  /*
3371    Make cursor.
3372  */
3373  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3374    resource_info->background_color,resource_info->foreground_color);
3375  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3376  /*
3377    Track pointer until button 1 is pressed.
3378  */
3379  XQueryPosition(display,windows->image.id,&x,&y);
3380  (void) XSelectInput(display,windows->image.id,
3381    windows->image.attributes.event_mask | PointerMotionMask);
3382  state=DefaultState;
3383  do
3384  {
3385    if (windows->info.mapped != MagickFalse)
3386      {
3387        /*
3388          Display pointer position.
3389        */
3390        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
3391          x+windows->image.x,y+windows->image.y);
3392        XInfoWidget(display,windows,text);
3393      }
3394    /*
3395      Wait for next event.
3396    */
3397    XScreenEvent(display,windows,&event);
3398    if (event.xany.window == windows->command.id)
3399      {
3400        /*
3401          Select a command from the Command widget.
3402        */
3403        id=XCommandWidget(display,windows,ColorEditMenu,&event);
3404        if (id < 0)
3405          {
3406            (void) XCheckDefineCursor(display,windows->image.id,cursor);
3407            continue;
3408          }
3409        switch (ColorEditCommands[id])
3410        {
3411          case ColorEditMethodCommand:
3412          {
3413            char
3414              **methods;
3415
3416            /*
3417              Select a method from the pop-up menu.
3418            */
3419            methods=(char **) GetCommandOptions(MagickMethodOptions);
3420            if (methods == (char **) NULL)
3421              break;
3422            entry=XMenuWidget(display,windows,ColorEditMenu[id],
3423              (const char **) methods,command);
3424            if (entry >= 0)
3425              method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3426                MagickFalse,methods[entry]);
3427            methods=DestroyStringList(methods);
3428            break;
3429          }
3430          case ColorEditColorCommand:
3431          {
3432            const char
3433              *ColorMenu[MaxNumberPens];
3434
3435            int
3436              pen_number;
3437
3438            /*
3439              Initialize menu selections.
3440            */
3441            for (i=0; i < (int) (MaxNumberPens-2); i++)
3442              ColorMenu[i]=resource_info->pen_colors[i];
3443            ColorMenu[MaxNumberPens-2]="Browser...";
3444            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3445            /*
3446              Select a pen color from the pop-up menu.
3447            */
3448            pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3449              (const char **) ColorMenu,command);
3450            if (pen_number < 0)
3451              break;
3452            if (pen_number == (MaxNumberPens-2))
3453              {
3454                static char
3455                  color_name[MaxTextExtent] = "gray";
3456
3457                /*
3458                  Select a pen color from a dialog.
3459                */
3460                resource_info->pen_colors[pen_number]=color_name;
3461                XColorBrowserWidget(display,windows,"Select",color_name);
3462                if (*color_name == '\0')
3463                  break;
3464              }
3465            /*
3466              Set pen color.
3467            */
3468            (void) XParseColor(display,windows->map_info->colormap,
3469              resource_info->pen_colors[pen_number],&color);
3470            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3471              (unsigned int) MaxColors,&color);
3472            windows->pixel_info->pen_colors[pen_number]=color;
3473            pen_id=(unsigned int) pen_number;
3474            break;
3475          }
3476          case ColorEditBorderCommand:
3477          {
3478            const char
3479              *ColorMenu[MaxNumberPens];
3480
3481            int
3482              pen_number;
3483
3484            /*
3485              Initialize menu selections.
3486            */
3487            for (i=0; i < (int) (MaxNumberPens-2); i++)
3488              ColorMenu[i]=resource_info->pen_colors[i];
3489            ColorMenu[MaxNumberPens-2]="Browser...";
3490            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3491            /*
3492              Select a pen color from the pop-up menu.
3493            */
3494            pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3495              (const char **) ColorMenu,command);
3496            if (pen_number < 0)
3497              break;
3498            if (pen_number == (MaxNumberPens-2))
3499              {
3500                static char
3501                  color_name[MaxTextExtent] = "gray";
3502
3503                /*
3504                  Select a pen color from a dialog.
3505                */
3506                resource_info->pen_colors[pen_number]=color_name;
3507                XColorBrowserWidget(display,windows,"Select",color_name);
3508                if (*color_name == '\0')
3509                  break;
3510              }
3511            /*
3512              Set border color.
3513            */
3514            (void) XParseColor(display,windows->map_info->colormap,
3515              resource_info->pen_colors[pen_number],&border_color);
3516            break;
3517          }
3518          case ColorEditFuzzCommand:
3519          {
3520            static char
3521              fuzz[MaxTextExtent];
3522
3523            static const char
3524              *FuzzMenu[] =
3525              {
3526                "0%",
3527                "2%",
3528                "5%",
3529                "10%",
3530                "15%",
3531                "Dialog...",
3532                (char *) NULL,
3533              };
3534
3535            /*
3536              Select a command from the pop-up menu.
3537            */
3538            entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3539              command);
3540            if (entry < 0)
3541              break;
3542            if (entry != 5)
3543              {
3544                (*image)->fuzz=SiPrefixToDouble(FuzzMenu[entry],1.0*
3545                  QuantumRange+1.0);
3546                break;
3547              }
3548            (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
3549            (void) XDialogWidget(display,windows,"Ok",
3550              "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3551            if (*fuzz == '\0')
3552              break;
3553            (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
3554            (*image)->fuzz=SiPrefixToDouble(fuzz,1.0*QuantumRange+1.0);
3555            break;
3556          }
3557          case ColorEditUndoCommand:
3558          {
3559            (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3560              image,exception);
3561            break;
3562          }
3563          case ColorEditHelpCommand:
3564          default:
3565          {
3566            XTextViewWidget(display,resource_info,windows,MagickFalse,
3567              "Help Viewer - Image Annotation",ImageColorEditHelp);
3568            break;
3569          }
3570          case ColorEditDismissCommand:
3571          {
3572            /*
3573              Prematurely exit.
3574            */
3575            state|=EscapeState;
3576            state|=ExitState;
3577            break;
3578          }
3579        }
3580        (void) XCheckDefineCursor(display,windows->image.id,cursor);
3581        continue;
3582      }
3583    switch (event.type)
3584    {
3585      case ButtonPress:
3586      {
3587        if (event.xbutton.button != Button1)
3588          break;
3589        if ((event.xbutton.window != windows->image.id) &&
3590            (event.xbutton.window != windows->magnify.id))
3591          break;
3592        /*
3593          exit loop.
3594        */
3595        x=event.xbutton.x;
3596        y=event.xbutton.y;
3597        (void) XMagickCommand(display,resource_info,windows,
3598          SaveToUndoBufferCommand,image,exception);
3599        state|=UpdateConfigurationState;
3600        break;
3601      }
3602      case ButtonRelease:
3603      {
3604        if (event.xbutton.button != Button1)
3605          break;
3606        if ((event.xbutton.window != windows->image.id) &&
3607            (event.xbutton.window != windows->magnify.id))
3608          break;
3609        /*
3610          Update colormap information.
3611        */
3612        x=event.xbutton.x;
3613        y=event.xbutton.y;
3614        XConfigureImageColormap(display,resource_info,windows,*image);
3615        (void) XConfigureImage(display,resource_info,windows,*image,exception);
3616        XInfoWidget(display,windows,text);
3617        (void) XCheckDefineCursor(display,windows->image.id,cursor);
3618        state&=(~UpdateConfigurationState);
3619        break;
3620      }
3621      case Expose:
3622        break;
3623      case KeyPress:
3624      {
3625        KeySym
3626          key_symbol;
3627
3628        if (event.xkey.window == windows->magnify.id)
3629          {
3630            Window
3631              window;
3632
3633            window=windows->magnify.id;
3634            while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3635          }
3636        if (event.xkey.window != windows->image.id)
3637          break;
3638        /*
3639          Respond to a user key press.
3640        */
3641        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3642          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3643        switch ((int) key_symbol)
3644        {
3645          case XK_Escape:
3646          case XK_F20:
3647          {
3648            /*
3649              Prematurely exit.
3650            */
3651            state|=ExitState;
3652            break;
3653          }
3654          case XK_F1:
3655          case XK_Help:
3656          {
3657            XTextViewWidget(display,resource_info,windows,MagickFalse,
3658              "Help Viewer - Image Annotation",ImageColorEditHelp);
3659            break;
3660          }
3661          default:
3662          {
3663            (void) XBell(display,0);
3664            break;
3665          }
3666        }
3667        break;
3668      }
3669      case MotionNotify:
3670      {
3671        /*
3672          Map and unmap Info widget as cursor crosses its boundaries.
3673        */
3674        x=event.xmotion.x;
3675        y=event.xmotion.y;
3676        if (windows->info.mapped != MagickFalse)
3677          {
3678            if ((x < (int) (windows->info.x+windows->info.width)) &&
3679                (y < (int) (windows->info.y+windows->info.height)))
3680              (void) XWithdrawWindow(display,windows->info.id,
3681                windows->info.screen);
3682          }
3683        else
3684          if ((x > (int) (windows->info.x+windows->info.width)) ||
3685              (y > (int) (windows->info.y+windows->info.height)))
3686            (void) XMapWindow(display,windows->info.id);
3687        break;
3688      }
3689      default:
3690        break;
3691    }
3692    if (event.xany.window == windows->magnify.id)
3693      {
3694        x=windows->magnify.x-windows->image.x;
3695        y=windows->magnify.y-windows->image.y;
3696      }
3697    x_offset=x;
3698    y_offset=y;
3699    if ((state & UpdateConfigurationState) != 0)
3700      {
3701        CacheView
3702          *image_view;
3703
3704        int
3705          x,
3706          y;
3707
3708        /*
3709          Pixel edit is relative to image configuration.
3710        */
3711        (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3712          MagickTrue);
3713        color=windows->pixel_info->pen_colors[pen_id];
3714        XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3715        width=(unsigned int) (*image)->columns;
3716        height=(unsigned int) (*image)->rows;
3717        x=0;
3718        y=0;
3719        if (windows->image.crop_geometry != (char *) NULL)
3720          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3721            &width,&height);
3722        x_offset=(int)
3723          (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3724        y_offset=(int)
3725          (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3726        if ((x_offset < 0) || (y_offset < 0))
3727          continue;
3728        if ((x_offset >= (int) (*image)->columns) ||
3729            (y_offset >= (int) (*image)->rows))
3730          continue;
3731        image_view=AcquireCacheView(*image);
3732        switch (method)
3733        {
3734          case PointMethod:
3735          default:
3736          {
3737            /*
3738              Update color information using point algorithm.
3739            */
3740            if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3741              return(MagickFalse);
3742            q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3743              (ssize_t) y_offset,1,1,exception);
3744            if (q == (Quantum *) NULL)
3745              break;
3746            SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3747            SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3748            SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3749            (void) SyncCacheViewAuthenticPixels(image_view,exception);
3750            break;
3751          }
3752          case ReplaceMethod:
3753          {
3754            PixelPacket
3755              pixel,
3756              target;
3757
3758            /*
3759              Update color information using replace algorithm.
3760            */
3761            (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
3762              (ssize_t) y_offset,&target,exception);
3763            if ((*image)->storage_class == DirectClass)
3764              {
3765                for (y=0; y < (int) (*image)->rows; y++)
3766                {
3767                  q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3768                    (*image)->columns,1,exception);
3769                  if (q == (Quantum *) NULL)
3770                    break;
3771                  for (x=0; x < (int) (*image)->columns; x++)
3772                  {
3773                    GetPixelPacket(*image,q,&pixel);
3774                    if (IsFuzzyEquivalencePixelPacket(*image,&pixel,&target))
3775                      {
3776                        SetPixelRed(*image,ScaleShortToQuantum(
3777                          color.red),q);
3778                        SetPixelGreen(*image,ScaleShortToQuantum(
3779                          color.green),q);
3780                        SetPixelBlue(*image,ScaleShortToQuantum(
3781                          color.blue),q);
3782                      }
3783                    q+=GetPixelChannels(*image);
3784                  }
3785                  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3786                    break;
3787                }
3788              }
3789            else
3790              {
3791                for (i=0; i < (ssize_t) (*image)->colors; i++)
3792                  if (IsFuzzyEquivalencePixelPacket(*image,(*image)->colormap+i,&target))
3793                    {
3794                      (*image)->colormap[i].red=ScaleShortToQuantum(
3795                        color.red);
3796                      (*image)->colormap[i].green=ScaleShortToQuantum(
3797                        color.green);
3798                      (*image)->colormap[i].blue=ScaleShortToQuantum(
3799                        color.blue);
3800                    }
3801                (void) SyncImage(*image);
3802              }
3803            break;
3804          }
3805          case FloodfillMethod:
3806          case FillToBorderMethod:
3807          {
3808            DrawInfo
3809              *draw_info;
3810
3811            PixelInfo
3812              target;
3813
3814            /*
3815              Update color information using floodfill algorithm.
3816            */
3817            (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
3818              (ssize_t) y_offset,&target,exception);
3819            if (method == FillToBorderMethod)
3820              {
3821                target.red=(MagickRealType)
3822                  ScaleShortToQuantum(border_color.red);
3823                target.green=(MagickRealType)
3824                  ScaleShortToQuantum(border_color.green);
3825                target.blue=(MagickRealType)
3826                  ScaleShortToQuantum(border_color.blue);
3827              }
3828            draw_info=CloneDrawInfo(resource_info->image_info,
3829              (DrawInfo *) NULL);
3830            (void) QueryColorDatabase(resource_info->pen_colors[pen_id],
3831              &draw_info->fill,exception);
3832            (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
3833              x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
3834              MagickFalse : MagickTrue,exception);
3835            draw_info=DestroyDrawInfo(draw_info);
3836            break;
3837          }
3838          case ResetMethod:
3839          {
3840            /*
3841              Update color information using reset algorithm.
3842            */
3843            if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3844              return(MagickFalse);
3845            for (y=0; y < (int) (*image)->rows; y++)
3846            {
3847              q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3848                (*image)->columns,1,exception);
3849              if (q == (Quantum *) NULL)
3850                break;
3851              for (x=0; x < (int) (*image)->columns; x++)
3852              {
3853                SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3854                SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3855                SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3856                q+=GetPixelChannels(*image);
3857              }
3858              if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3859                break;
3860            }
3861            break;
3862          }
3863        }
3864        image_view=DestroyCacheView(image_view);
3865        state&=(~UpdateConfigurationState);
3866      }
3867  } while ((state & ExitState) == 0);
3868  (void) XSelectInput(display,windows->image.id,
3869    windows->image.attributes.event_mask);
3870  XSetCursorState(display,windows,MagickFalse);
3871  (void) XFreeCursor(display,cursor);
3872  return(MagickTrue);
3873}
3874
3875/*
3876%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3877%                                                                             %
3878%                                                                             %
3879%                                                                             %
3880+   X C o m p o s i t e I m a g e                                             %
3881%                                                                             %
3882%                                                                             %
3883%                                                                             %
3884%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3885%
3886%  XCompositeImage() requests an image name from the user, reads the image and
3887%  composites it with the X window image at a location the user chooses with
3888%  the pointer.
3889%
3890%  The format of the XCompositeImage method is:
3891%
3892%      MagickBooleanType XCompositeImage(Display *display,
3893%        XResourceInfo *resource_info,XWindows *windows,Image *image,
3894%        ExceptionInfo *exception)
3895%
3896%  A description of each parameter follows:
3897%
3898%    o display: Specifies a connection to an X server;  returned from
3899%      XOpenDisplay.
3900%
3901%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3902%
3903%    o windows: Specifies a pointer to a XWindows structure.
3904%
3905%    o image: the image; returned from ReadImage.
3906%
3907%    o exception: return any errors or warnings in this structure.
3908%
3909*/
3910static MagickBooleanType XCompositeImage(Display *display,
3911  XResourceInfo *resource_info,XWindows *windows,Image *image,
3912  ExceptionInfo *exception)
3913{
3914  static char
3915    displacement_geometry[MaxTextExtent] = "30x30",
3916    filename[MaxTextExtent] = "\0";
3917
3918  static const char
3919    *CompositeMenu[] =
3920    {
3921      "Operators",
3922      "Dissolve",
3923      "Displace",
3924      "Help",
3925      "Dismiss",
3926      (char *) NULL
3927    };
3928
3929  static CompositeOperator
3930    compose = CopyCompositeOp;
3931
3932  static const ModeType
3933    CompositeCommands[] =
3934    {
3935      CompositeOperatorsCommand,
3936      CompositeDissolveCommand,
3937      CompositeDisplaceCommand,
3938      CompositeHelpCommand,
3939      CompositeDismissCommand
3940    };
3941
3942  char
3943    text[MaxTextExtent];
3944
3945  Cursor
3946    cursor;
3947
3948  Image
3949    *composite_image;
3950
3951  int
3952    entry,
3953    id,
3954    x,
3955    y;
3956
3957  MagickRealType
3958    blend,
3959    scale_factor;
3960
3961  RectangleInfo
3962    highlight_info,
3963    composite_info;
3964
3965  unsigned int
3966    height,
3967    width;
3968
3969  size_t
3970    state;
3971
3972  XEvent
3973    event;
3974
3975  /*
3976    Request image file name from user.
3977  */
3978  XFileBrowserWidget(display,windows,"Composite",filename);
3979  if (*filename == '\0')
3980    return(MagickTrue);
3981  /*
3982    Read image.
3983  */
3984  XSetCursorState(display,windows,MagickTrue);
3985  XCheckRefreshWindows(display,windows);
3986  (void) CopyMagickString(resource_info->image_info->filename,filename,
3987    MaxTextExtent);
3988  composite_image=ReadImage(resource_info->image_info,exception);
3989  CatchException(exception);
3990  XSetCursorState(display,windows,MagickFalse);
3991  if (composite_image == (Image *) NULL)
3992    return(MagickFalse);
3993  /*
3994    Map Command widget.
3995  */
3996  (void) CloneString(&windows->command.name,"Composite");
3997  windows->command.data=1;
3998  (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
3999  (void) XMapRaised(display,windows->command.id);
4000  XClientMessage(display,windows->image.id,windows->im_protocols,
4001    windows->im_update_widget,CurrentTime);
4002  /*
4003    Track pointer until button 1 is pressed.
4004  */
4005  XQueryPosition(display,windows->image.id,&x,&y);
4006  (void) XSelectInput(display,windows->image.id,
4007    windows->image.attributes.event_mask | PointerMotionMask);
4008  composite_info.x=(ssize_t) windows->image.x+x;
4009  composite_info.y=(ssize_t) windows->image.y+y;
4010  composite_info.width=0;
4011  composite_info.height=0;
4012  cursor=XCreateFontCursor(display,XC_ul_angle);
4013  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4014  blend=0.0;
4015  state=DefaultState;
4016  do
4017  {
4018    if (windows->info.mapped != MagickFalse)
4019      {
4020        /*
4021          Display pointer position.
4022        */
4023        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4024          (long) composite_info.x,(long) composite_info.y);
4025        XInfoWidget(display,windows,text);
4026      }
4027    highlight_info=composite_info;
4028    highlight_info.x=composite_info.x-windows->image.x;
4029    highlight_info.y=composite_info.y-windows->image.y;
4030    XHighlightRectangle(display,windows->image.id,
4031      windows->image.highlight_context,&highlight_info);
4032    /*
4033      Wait for next event.
4034    */
4035    XScreenEvent(display,windows,&event);
4036    XHighlightRectangle(display,windows->image.id,
4037      windows->image.highlight_context,&highlight_info);
4038    if (event.xany.window == windows->command.id)
4039      {
4040        /*
4041          Select a command from the Command widget.
4042        */
4043        id=XCommandWidget(display,windows,CompositeMenu,&event);
4044        if (id < 0)
4045          continue;
4046        switch (CompositeCommands[id])
4047        {
4048          case CompositeOperatorsCommand:
4049          {
4050            char
4051              command[MaxTextExtent],
4052              **operators;
4053
4054            /*
4055              Select a command from the pop-up menu.
4056            */
4057            operators=GetCommandOptions(MagickComposeOptions);
4058            if (operators == (char **) NULL)
4059              break;
4060            entry=XMenuWidget(display,windows,CompositeMenu[id],
4061              (const char **) operators,command);
4062            if (entry >= 0)
4063              compose=(CompositeOperator) ParseCommandOption(
4064                MagickComposeOptions,MagickFalse,operators[entry]);
4065            operators=DestroyStringList(operators);
4066            break;
4067          }
4068          case CompositeDissolveCommand:
4069          {
4070            static char
4071              factor[MaxTextExtent] = "20.0";
4072
4073            /*
4074              Dissolve the two images a given percent.
4075            */
4076            (void) XSetFunction(display,windows->image.highlight_context,
4077              GXcopy);
4078            (void) XDialogWidget(display,windows,"Dissolve",
4079              "Enter the blend factor (0.0 - 99.9%):",factor);
4080            (void) XSetFunction(display,windows->image.highlight_context,
4081              GXinvert);
4082            if (*factor == '\0')
4083              break;
4084            blend=InterpretLocaleValue(factor,(char **) NULL);
4085            compose=DissolveCompositeOp;
4086            break;
4087          }
4088          case CompositeDisplaceCommand:
4089          {
4090            /*
4091              Get horizontal and vertical scale displacement geometry.
4092            */
4093            (void) XSetFunction(display,windows->image.highlight_context,
4094              GXcopy);
4095            (void) XDialogWidget(display,windows,"Displace",
4096              "Enter the horizontal and vertical scale:",displacement_geometry);
4097            (void) XSetFunction(display,windows->image.highlight_context,
4098              GXinvert);
4099            if (*displacement_geometry == '\0')
4100              break;
4101            compose=DisplaceCompositeOp;
4102            break;
4103          }
4104          case CompositeHelpCommand:
4105          {
4106            (void) XSetFunction(display,windows->image.highlight_context,
4107              GXcopy);
4108            XTextViewWidget(display,resource_info,windows,MagickFalse,
4109              "Help Viewer - Image Composite",ImageCompositeHelp);
4110            (void) XSetFunction(display,windows->image.highlight_context,
4111              GXinvert);
4112            break;
4113          }
4114          case CompositeDismissCommand:
4115          {
4116            /*
4117              Prematurely exit.
4118            */
4119            state|=EscapeState;
4120            state|=ExitState;
4121            break;
4122          }
4123          default:
4124            break;
4125        }
4126        continue;
4127      }
4128    switch (event.type)
4129    {
4130      case ButtonPress:
4131      {
4132        if (image->debug != MagickFalse)
4133          (void) LogMagickEvent(X11Event,GetMagickModule(),
4134            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4135            event.xbutton.button,event.xbutton.x,event.xbutton.y);
4136        if (event.xbutton.button != Button1)
4137          break;
4138        if (event.xbutton.window != windows->image.id)
4139          break;
4140        /*
4141          Change cursor.
4142        */
4143        composite_info.width=composite_image->columns;
4144        composite_info.height=composite_image->rows;
4145        (void) XCheckDefineCursor(display,windows->image.id,cursor);
4146        composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4147        composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4148        break;
4149      }
4150      case ButtonRelease:
4151      {
4152        if (image->debug != MagickFalse)
4153          (void) LogMagickEvent(X11Event,GetMagickModule(),
4154            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4155            event.xbutton.button,event.xbutton.x,event.xbutton.y);
4156        if (event.xbutton.button != Button1)
4157          break;
4158        if (event.xbutton.window != windows->image.id)
4159          break;
4160        if ((composite_info.width != 0) && (composite_info.height != 0))
4161          {
4162            /*
4163              User has selected the location of the composite image.
4164            */
4165            composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4166            composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4167            state|=ExitState;
4168          }
4169        break;
4170      }
4171      case Expose:
4172        break;
4173      case KeyPress:
4174      {
4175        char
4176          command[MaxTextExtent];
4177
4178        KeySym
4179          key_symbol;
4180
4181        int
4182          length;
4183
4184        if (event.xkey.window != windows->image.id)
4185          break;
4186        /*
4187          Respond to a user key press.
4188        */
4189        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4190          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4191        *(command+length)='\0';
4192        if (image->debug != MagickFalse)
4193          (void) LogMagickEvent(X11Event,GetMagickModule(),
4194            "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4195        switch ((int) key_symbol)
4196        {
4197          case XK_Escape:
4198          case XK_F20:
4199          {
4200            /*
4201              Prematurely exit.
4202            */
4203            composite_image=DestroyImage(composite_image);
4204            state|=EscapeState;
4205            state|=ExitState;
4206            break;
4207          }
4208          case XK_F1:
4209          case XK_Help:
4210          {
4211            (void) XSetFunction(display,windows->image.highlight_context,
4212              GXcopy);
4213            XTextViewWidget(display,resource_info,windows,MagickFalse,
4214              "Help Viewer - Image Composite",ImageCompositeHelp);
4215            (void) XSetFunction(display,windows->image.highlight_context,
4216              GXinvert);
4217            break;
4218          }
4219          default:
4220          {
4221            (void) XBell(display,0);
4222            break;
4223          }
4224        }
4225        break;
4226      }
4227      case MotionNotify:
4228      {
4229        /*
4230          Map and unmap Info widget as text cursor crosses its boundaries.
4231        */
4232        x=event.xmotion.x;
4233        y=event.xmotion.y;
4234        if (windows->info.mapped != MagickFalse)
4235          {
4236            if ((x < (int) (windows->info.x+windows->info.width)) &&
4237                (y < (int) (windows->info.y+windows->info.height)))
4238              (void) XWithdrawWindow(display,windows->info.id,
4239                windows->info.screen);
4240          }
4241        else
4242          if ((x > (int) (windows->info.x+windows->info.width)) ||
4243              (y > (int) (windows->info.y+windows->info.height)))
4244            (void) XMapWindow(display,windows->info.id);
4245        composite_info.x=(ssize_t) windows->image.x+x;
4246        composite_info.y=(ssize_t) windows->image.y+y;
4247        break;
4248      }
4249      default:
4250      {
4251        if (image->debug != MagickFalse)
4252          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4253            event.type);
4254        break;
4255      }
4256    }
4257  } while ((state & ExitState) == 0);
4258  (void) XSelectInput(display,windows->image.id,
4259    windows->image.attributes.event_mask);
4260  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4261  XSetCursorState(display,windows,MagickFalse);
4262  (void) XFreeCursor(display,cursor);
4263  if ((state & EscapeState) != 0)
4264    return(MagickTrue);
4265  /*
4266    Image compositing is relative to image configuration.
4267  */
4268  XSetCursorState(display,windows,MagickTrue);
4269  XCheckRefreshWindows(display,windows);
4270  width=(unsigned int) image->columns;
4271  height=(unsigned int) image->rows;
4272  x=0;
4273  y=0;
4274  if (windows->image.crop_geometry != (char *) NULL)
4275    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4276  scale_factor=(MagickRealType) width/windows->image.ximage->width;
4277  composite_info.x+=x;
4278  composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4279  composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4280  scale_factor=(MagickRealType) height/windows->image.ximage->height;
4281  composite_info.y+=y;
4282  composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4283  composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4284  if ((composite_info.width != composite_image->columns) ||
4285      (composite_info.height != composite_image->rows))
4286    {
4287      Image
4288        *resize_image;
4289
4290      /*
4291        Scale composite image.
4292      */
4293      resize_image=ResizeImage(composite_image,composite_info.width,
4294        composite_info.height,composite_image->filter,composite_image->blur,
4295        exception);
4296      composite_image=DestroyImage(composite_image);
4297      if (resize_image == (Image *) NULL)
4298        {
4299          XSetCursorState(display,windows,MagickFalse);
4300          return(MagickFalse);
4301        }
4302      composite_image=resize_image;
4303    }
4304  if (compose == DisplaceCompositeOp)
4305    (void) SetImageArtifact(composite_image,"compose:args",
4306      displacement_geometry);
4307  if (blend != 0.0)
4308    {
4309      CacheView
4310        *image_view;
4311
4312      int
4313        y;
4314
4315      Quantum
4316        opacity;
4317
4318      register int
4319        x;
4320
4321      register Quantum
4322        *q;
4323
4324      /*
4325        Create mattes for blending.
4326      */
4327      (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,exception);
4328      opacity=(Quantum) (ScaleQuantumToChar((Quantum) QuantumRange)-
4329        ((ssize_t) ScaleQuantumToChar((Quantum) QuantumRange)*blend)/100);
4330      if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
4331        return(MagickFalse);
4332      image->matte=MagickTrue;
4333      image_view=AcquireCacheView(image);
4334      for (y=0; y < (int) image->rows; y++)
4335      {
4336        q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4337          exception);
4338        if (q == (Quantum *) NULL)
4339          break;
4340        for (x=0; x < (int) image->columns; x++)
4341        {
4342          SetPixelAlpha(image,opacity,q);
4343          q+=GetPixelChannels(image);
4344        }
4345        if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4346          break;
4347      }
4348      image_view=DestroyCacheView(image_view);
4349    }
4350  /*
4351    Composite image with X Image window.
4352  */
4353  (void) CompositeImage(image,compose,composite_image,composite_info.x,
4354    composite_info.y);
4355  composite_image=DestroyImage(composite_image);
4356  XSetCursorState(display,windows,MagickFalse);
4357  /*
4358    Update image configuration.
4359  */
4360  XConfigureImageColormap(display,resource_info,windows,image);
4361  (void) XConfigureImage(display,resource_info,windows,image,exception);
4362  return(MagickTrue);
4363}
4364
4365/*
4366%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4367%                                                                             %
4368%                                                                             %
4369%                                                                             %
4370+   X C o n f i g u r e I m a g e                                             %
4371%                                                                             %
4372%                                                                             %
4373%                                                                             %
4374%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4375%
4376%  XConfigureImage() creates a new X image.  It also notifies the window
4377%  manager of the new image size and configures the transient widows.
4378%
4379%  The format of the XConfigureImage method is:
4380%
4381%      MagickBooleanType XConfigureImage(Display *display,
4382%        XResourceInfo *resource_info,XWindows *windows,Image *image,
4383%        ExceptionInfo *exception)
4384%
4385%  A description of each parameter follows:
4386%
4387%    o display: Specifies a connection to an X server; returned from
4388%      XOpenDisplay.
4389%
4390%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4391%
4392%    o windows: Specifies a pointer to a XWindows structure.
4393%
4394%    o image: the image.
4395%
4396%    o exception: return any errors or warnings in this structure.
4397%
4398%    o exception: return any errors or warnings in this structure.
4399%
4400*/
4401static MagickBooleanType XConfigureImage(Display *display,
4402  XResourceInfo *resource_info,XWindows *windows,Image *image,
4403  ExceptionInfo *exception)
4404{
4405  char
4406    geometry[MaxTextExtent];
4407
4408  MagickStatusType
4409    status;
4410
4411  size_t
4412    mask,
4413    height,
4414    width;
4415
4416  ssize_t
4417    x,
4418    y;
4419
4420  XSizeHints
4421    *size_hints;
4422
4423  XWindowChanges
4424    window_changes;
4425
4426  /*
4427    Dismiss if window dimensions are zero.
4428  */
4429  width=(unsigned int) windows->image.window_changes.width;
4430  height=(unsigned int) windows->image.window_changes.height;
4431  if (image->debug != MagickFalse)
4432    (void) LogMagickEvent(X11Event,GetMagickModule(),
4433      "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4434      windows->image.ximage->height,(double) width,(double) height);
4435  if ((width*height) == 0)
4436    return(MagickTrue);
4437  x=0;
4438  y=0;
4439  /*
4440    Resize image to fit Image window dimensions.
4441  */
4442  XSetCursorState(display,windows,MagickTrue);
4443  (void) XFlush(display);
4444  if (((int) width != windows->image.ximage->width) ||
4445      ((int) height != windows->image.ximage->height))
4446    image->taint=MagickTrue;
4447  windows->magnify.x=(int)
4448    width*windows->magnify.x/windows->image.ximage->width;
4449  windows->magnify.y=(int)
4450    height*windows->magnify.y/windows->image.ximage->height;
4451  windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4452  windows->image.y=(int)
4453    (height*windows->image.y/windows->image.ximage->height);
4454  status=XMakeImage(display,resource_info,&windows->image,image,
4455    (unsigned int) width,(unsigned int) height,exception);
4456  if (status == MagickFalse)
4457    XNoticeWidget(display,windows,"Unable to configure X image:",
4458      windows->image.name);
4459  /*
4460    Notify window manager of the new configuration.
4461  */
4462  if (resource_info->image_geometry != (char *) NULL)
4463    (void) FormatLocaleString(geometry,MaxTextExtent,"%s>!",
4464      resource_info->image_geometry);
4465  else
4466    (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
4467      XDisplayWidth(display,windows->image.screen),
4468      XDisplayHeight(display,windows->image.screen));
4469  (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4470  window_changes.width=(int) width;
4471  if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4472    window_changes.width=XDisplayWidth(display,windows->image.screen);
4473  window_changes.height=(int) height;
4474  if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4475    window_changes.height=XDisplayHeight(display,windows->image.screen);
4476  mask=(size_t) (CWWidth | CWHeight);
4477  if (resource_info->backdrop)
4478    {
4479      mask|=CWX | CWY;
4480      window_changes.x=(int)
4481        ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4482      window_changes.y=(int)
4483        ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4484    }
4485  (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4486    (unsigned int) mask,&window_changes);
4487  (void) XClearWindow(display,windows->image.id);
4488  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4489  /*
4490    Update Magnify window configuration.
4491  */
4492  if (windows->magnify.mapped != MagickFalse)
4493    XMakeMagnifyImage(display,windows);
4494  windows->pan.crop_geometry=windows->image.crop_geometry;
4495  XBestIconSize(display,&windows->pan,image);
4496  while (((windows->pan.width << 1) < MaxIconSize) &&
4497         ((windows->pan.height << 1) < MaxIconSize))
4498  {
4499    windows->pan.width<<=1;
4500    windows->pan.height<<=1;
4501  }
4502  if (windows->pan.geometry != (char *) NULL)
4503    (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4504      &windows->pan.width,&windows->pan.height);
4505  window_changes.width=(int) windows->pan.width;
4506  window_changes.height=(int) windows->pan.height;
4507  size_hints=XAllocSizeHints();
4508  if (size_hints != (XSizeHints *) NULL)
4509    {
4510      /*
4511        Set new size hints.
4512      */
4513      size_hints->flags=PSize | PMinSize | PMaxSize;
4514      size_hints->width=window_changes.width;
4515      size_hints->height=window_changes.height;
4516      size_hints->min_width=size_hints->width;
4517      size_hints->min_height=size_hints->height;
4518      size_hints->max_width=size_hints->width;
4519      size_hints->max_height=size_hints->height;
4520      (void) XSetNormalHints(display,windows->pan.id,size_hints);
4521      (void) XFree((void *) size_hints);
4522    }
4523  (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4524    (unsigned int) (CWWidth | CWHeight),&window_changes);
4525  /*
4526    Update icon window configuration.
4527  */
4528  windows->icon.crop_geometry=windows->image.crop_geometry;
4529  XBestIconSize(display,&windows->icon,image);
4530  window_changes.width=(int) windows->icon.width;
4531  window_changes.height=(int) windows->icon.height;
4532  (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4533    (unsigned int) (CWWidth | CWHeight),&window_changes);
4534  XSetCursorState(display,windows,MagickFalse);
4535  return(status != 0 ? MagickTrue : MagickFalse);
4536}
4537
4538/*
4539%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4540%                                                                             %
4541%                                                                             %
4542%                                                                             %
4543+   X C r o p I m a g e                                                       %
4544%                                                                             %
4545%                                                                             %
4546%                                                                             %
4547%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4548%
4549%  XCropImage() allows the user to select a region of the image and crop, copy,
4550%  or cut it.  For copy or cut, the image can subsequently be composited onto
4551%  the image with XPasteImage.
4552%
4553%  The format of the XCropImage method is:
4554%
4555%      MagickBooleanType XCropImage(Display *display,
4556%        XResourceInfo *resource_info,XWindows *windows,Image *image,
4557%        const ClipboardMode mode,ExceptionInfo *exception)
4558%
4559%  A description of each parameter follows:
4560%
4561%    o display: Specifies a connection to an X server; returned from
4562%      XOpenDisplay.
4563%
4564%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4565%
4566%    o windows: Specifies a pointer to a XWindows structure.
4567%
4568%    o image: the image; returned from ReadImage.
4569%
4570%    o mode: This unsigned value specified whether the image should be
4571%      cropped, copied, or cut.
4572%
4573%    o exception: return any errors or warnings in this structure.
4574%
4575*/
4576static MagickBooleanType XCropImage(Display *display,
4577  XResourceInfo *resource_info,XWindows *windows,Image *image,
4578  const ClipboardMode mode,ExceptionInfo *exception)
4579{
4580  static const char
4581    *CropModeMenu[] =
4582    {
4583      "Help",
4584      "Dismiss",
4585      (char *) NULL
4586    },
4587    *RectifyModeMenu[] =
4588    {
4589      "Crop",
4590      "Help",
4591      "Dismiss",
4592      (char *) NULL
4593    };
4594
4595  static const ModeType
4596    CropCommands[] =
4597    {
4598      CropHelpCommand,
4599      CropDismissCommand
4600    },
4601    RectifyCommands[] =
4602    {
4603      RectifyCopyCommand,
4604      RectifyHelpCommand,
4605      RectifyDismissCommand
4606    };
4607
4608  CacheView
4609    *image_view;
4610
4611  char
4612    command[MaxTextExtent],
4613    text[MaxTextExtent];
4614
4615  Cursor
4616    cursor;
4617
4618  int
4619    id,
4620    x,
4621    y;
4622
4623  KeySym
4624    key_symbol;
4625
4626  Image
4627    *crop_image;
4628
4629  MagickRealType
4630    scale_factor;
4631
4632  RectangleInfo
4633    crop_info,
4634    highlight_info;
4635
4636  register Quantum
4637    *q;
4638
4639  unsigned int
4640    height,
4641    width;
4642
4643  size_t
4644    state;
4645
4646  XEvent
4647    event;
4648
4649  /*
4650    Map Command widget.
4651  */
4652  switch (mode)
4653  {
4654    case CopyMode:
4655    {
4656      (void) CloneString(&windows->command.name,"Copy");
4657      break;
4658    }
4659    case CropMode:
4660    {
4661      (void) CloneString(&windows->command.name,"Crop");
4662      break;
4663    }
4664    case CutMode:
4665    {
4666      (void) CloneString(&windows->command.name,"Cut");
4667      break;
4668    }
4669  }
4670  RectifyModeMenu[0]=windows->command.name;
4671  windows->command.data=0;
4672  (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4673  (void) XMapRaised(display,windows->command.id);
4674  XClientMessage(display,windows->image.id,windows->im_protocols,
4675    windows->im_update_widget,CurrentTime);
4676  /*
4677    Track pointer until button 1 is pressed.
4678  */
4679  XQueryPosition(display,windows->image.id,&x,&y);
4680  (void) XSelectInput(display,windows->image.id,
4681    windows->image.attributes.event_mask | PointerMotionMask);
4682  crop_info.x=(ssize_t) windows->image.x+x;
4683  crop_info.y=(ssize_t) windows->image.y+y;
4684  crop_info.width=0;
4685  crop_info.height=0;
4686  cursor=XCreateFontCursor(display,XC_fleur);
4687  state=DefaultState;
4688  do
4689  {
4690    if (windows->info.mapped != MagickFalse)
4691      {
4692        /*
4693          Display pointer position.
4694        */
4695        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4696          (long) crop_info.x,(long) crop_info.y);
4697        XInfoWidget(display,windows,text);
4698      }
4699    /*
4700      Wait for next event.
4701    */
4702    XScreenEvent(display,windows,&event);
4703    if (event.xany.window == windows->command.id)
4704      {
4705        /*
4706          Select a command from the Command widget.
4707        */
4708        id=XCommandWidget(display,windows,CropModeMenu,&event);
4709        if (id < 0)
4710          continue;
4711        switch (CropCommands[id])
4712        {
4713          case CropHelpCommand:
4714          {
4715            switch (mode)
4716            {
4717              case CopyMode:
4718              {
4719                XTextViewWidget(display,resource_info,windows,MagickFalse,
4720                  "Help Viewer - Image Copy",ImageCopyHelp);
4721                break;
4722              }
4723              case CropMode:
4724              {
4725                XTextViewWidget(display,resource_info,windows,MagickFalse,
4726                  "Help Viewer - Image Crop",ImageCropHelp);
4727                break;
4728              }
4729              case CutMode:
4730              {
4731                XTextViewWidget(display,resource_info,windows,MagickFalse,
4732                  "Help Viewer - Image Cut",ImageCutHelp);
4733                break;
4734              }
4735            }
4736            break;
4737          }
4738          case CropDismissCommand:
4739          {
4740            /*
4741              Prematurely exit.
4742            */
4743            state|=EscapeState;
4744            state|=ExitState;
4745            break;
4746          }
4747          default:
4748            break;
4749        }
4750        continue;
4751      }
4752    switch (event.type)
4753    {
4754      case ButtonPress:
4755      {
4756        if (event.xbutton.button != Button1)
4757          break;
4758        if (event.xbutton.window != windows->image.id)
4759          break;
4760        /*
4761          Note first corner of cropping rectangle-- exit loop.
4762        */
4763        (void) XCheckDefineCursor(display,windows->image.id,cursor);
4764        crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4765        crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4766        state|=ExitState;
4767        break;
4768      }
4769      case ButtonRelease:
4770        break;
4771      case Expose:
4772        break;
4773      case KeyPress:
4774      {
4775        if (event.xkey.window != windows->image.id)
4776          break;
4777        /*
4778          Respond to a user key press.
4779        */
4780        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4781          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4782        switch ((int) key_symbol)
4783        {
4784          case XK_Escape:
4785          case XK_F20:
4786          {
4787            /*
4788              Prematurely exit.
4789            */
4790            state|=EscapeState;
4791            state|=ExitState;
4792            break;
4793          }
4794          case XK_F1:
4795          case XK_Help:
4796          {
4797            switch (mode)
4798            {
4799              case CopyMode:
4800              {
4801                XTextViewWidget(display,resource_info,windows,MagickFalse,
4802                  "Help Viewer - Image Copy",ImageCopyHelp);
4803                break;
4804              }
4805              case CropMode:
4806              {
4807                XTextViewWidget(display,resource_info,windows,MagickFalse,
4808                  "Help Viewer - Image Crop",ImageCropHelp);
4809                break;
4810              }
4811              case CutMode:
4812              {
4813                XTextViewWidget(display,resource_info,windows,MagickFalse,
4814                  "Help Viewer - Image Cut",ImageCutHelp);
4815                break;
4816              }
4817            }
4818            break;
4819          }
4820          default:
4821          {
4822            (void) XBell(display,0);
4823            break;
4824          }
4825        }
4826        break;
4827      }
4828      case MotionNotify:
4829      {
4830        if (event.xmotion.window != windows->image.id)
4831          break;
4832        /*
4833          Map and unmap Info widget as text cursor crosses its boundaries.
4834        */
4835        x=event.xmotion.x;
4836        y=event.xmotion.y;
4837        if (windows->info.mapped != MagickFalse)
4838          {
4839            if ((x < (int) (windows->info.x+windows->info.width)) &&
4840                (y < (int) (windows->info.y+windows->info.height)))
4841              (void) XWithdrawWindow(display,windows->info.id,
4842                windows->info.screen);
4843          }
4844        else
4845          if ((x > (int) (windows->info.x+windows->info.width)) ||
4846              (y > (int) (windows->info.y+windows->info.height)))
4847            (void) XMapWindow(display,windows->info.id);
4848        crop_info.x=(ssize_t) windows->image.x+x;
4849        crop_info.y=(ssize_t) windows->image.y+y;
4850        break;
4851      }
4852      default:
4853        break;
4854    }
4855  } while ((state & ExitState) == 0);
4856  (void) XSelectInput(display,windows->image.id,
4857    windows->image.attributes.event_mask);
4858  if ((state & EscapeState) != 0)
4859    {
4860      /*
4861        User want to exit without cropping.
4862      */
4863      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4864      (void) XFreeCursor(display,cursor);
4865      return(MagickTrue);
4866    }
4867  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4868  do
4869  {
4870    /*
4871      Size rectangle as pointer moves until the mouse button is released.
4872    */
4873    x=(int) crop_info.x;
4874    y=(int) crop_info.y;
4875    crop_info.width=0;
4876    crop_info.height=0;
4877    state=DefaultState;
4878    do
4879    {
4880      highlight_info=crop_info;
4881      highlight_info.x=crop_info.x-windows->image.x;
4882      highlight_info.y=crop_info.y-windows->image.y;
4883      if ((highlight_info.width > 3) && (highlight_info.height > 3))
4884        {
4885          /*
4886            Display info and draw cropping rectangle.
4887          */
4888          if (windows->info.mapped == MagickFalse)
4889            (void) XMapWindow(display,windows->info.id);
4890          (void) FormatLocaleString(text,MaxTextExtent,
4891            " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4892            crop_info.height,(double) crop_info.x,(double) crop_info.y);
4893          XInfoWidget(display,windows,text);
4894          XHighlightRectangle(display,windows->image.id,
4895            windows->image.highlight_context,&highlight_info);
4896        }
4897      else
4898        if (windows->info.mapped != MagickFalse)
4899          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4900      /*
4901        Wait for next event.
4902      */
4903      XScreenEvent(display,windows,&event);
4904      if ((highlight_info.width > 3) && (highlight_info.height > 3))
4905        XHighlightRectangle(display,windows->image.id,
4906          windows->image.highlight_context,&highlight_info);
4907      switch (event.type)
4908      {
4909        case ButtonPress:
4910        {
4911          crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4912          crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4913          break;
4914        }
4915        case ButtonRelease:
4916        {
4917          /*
4918            User has committed to cropping rectangle.
4919          */
4920          crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4921          crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4922          XSetCursorState(display,windows,MagickFalse);
4923          state|=ExitState;
4924          windows->command.data=0;
4925          (void) XCommandWidget(display,windows,RectifyModeMenu,
4926            (XEvent *) NULL);
4927          break;
4928        }
4929        case Expose:
4930          break;
4931        case MotionNotify:
4932        {
4933          crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4934          crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4935        }
4936        default:
4937          break;
4938      }
4939      if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4940          ((state & ExitState) != 0))
4941        {
4942          /*
4943            Check boundary conditions.
4944          */
4945          if (crop_info.x < 0)
4946            crop_info.x=0;
4947          else
4948            if (crop_info.x > (ssize_t) windows->image.ximage->width)
4949              crop_info.x=(ssize_t) windows->image.ximage->width;
4950          if ((int) crop_info.x < x)
4951            crop_info.width=(unsigned int) (x-crop_info.x);
4952          else
4953            {
4954              crop_info.width=(unsigned int) (crop_info.x-x);
4955              crop_info.x=(ssize_t) x;
4956            }
4957          if (crop_info.y < 0)
4958            crop_info.y=0;
4959          else
4960            if (crop_info.y > (ssize_t) windows->image.ximage->height)
4961              crop_info.y=(ssize_t) windows->image.ximage->height;
4962          if ((int) crop_info.y < y)
4963            crop_info.height=(unsigned int) (y-crop_info.y);
4964          else
4965            {
4966              crop_info.height=(unsigned int) (crop_info.y-y);
4967              crop_info.y=(ssize_t) y;
4968            }
4969        }
4970    } while ((state & ExitState) == 0);
4971    /*
4972      Wait for user to grab a corner of the rectangle or press return.
4973    */
4974    state=DefaultState;
4975    (void) XMapWindow(display,windows->info.id);
4976    do
4977    {
4978      if (windows->info.mapped != MagickFalse)
4979        {
4980          /*
4981            Display pointer position.
4982          */
4983          (void) FormatLocaleString(text,MaxTextExtent,
4984            " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4985            crop_info.height,(double) crop_info.x,(double) crop_info.y);
4986          XInfoWidget(display,windows,text);
4987        }
4988      highlight_info=crop_info;
4989      highlight_info.x=crop_info.x-windows->image.x;
4990      highlight_info.y=crop_info.y-windows->image.y;
4991      if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4992        {
4993          state|=EscapeState;
4994          state|=ExitState;
4995          break;
4996        }
4997      XHighlightRectangle(display,windows->image.id,
4998        windows->image.highlight_context,&highlight_info);
4999      XScreenEvent(display,windows,&event);
5000      if (event.xany.window == windows->command.id)
5001        {
5002          /*
5003            Select a command from the Command widget.
5004          */
5005          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5006          id=XCommandWidget(display,windows,RectifyModeMenu,&event);
5007          (void) XSetFunction(display,windows->image.highlight_context,
5008            GXinvert);
5009          XHighlightRectangle(display,windows->image.id,
5010            windows->image.highlight_context,&highlight_info);
5011          if (id >= 0)
5012            switch (RectifyCommands[id])
5013            {
5014              case RectifyCopyCommand:
5015              {
5016                state|=ExitState;
5017                break;
5018              }
5019              case RectifyHelpCommand:
5020              {
5021                (void) XSetFunction(display,windows->image.highlight_context,
5022                  GXcopy);
5023                switch (mode)
5024                {
5025                  case CopyMode:
5026                  {
5027                    XTextViewWidget(display,resource_info,windows,MagickFalse,
5028                      "Help Viewer - Image Copy",ImageCopyHelp);
5029                    break;
5030                  }
5031                  case CropMode:
5032                  {
5033                    XTextViewWidget(display,resource_info,windows,MagickFalse,
5034                      "Help Viewer - Image Crop",ImageCropHelp);
5035                    break;
5036                  }
5037                  case CutMode:
5038                  {
5039                    XTextViewWidget(display,resource_info,windows,MagickFalse,
5040                      "Help Viewer - Image Cut",ImageCutHelp);
5041                    break;
5042                  }
5043                }
5044                (void) XSetFunction(display,windows->image.highlight_context,
5045                  GXinvert);
5046                break;
5047              }
5048              case RectifyDismissCommand:
5049              {
5050                /*
5051                  Prematurely exit.
5052                */
5053                state|=EscapeState;
5054                state|=ExitState;
5055                break;
5056              }
5057              default:
5058                break;
5059            }
5060          continue;
5061        }
5062      XHighlightRectangle(display,windows->image.id,
5063        windows->image.highlight_context,&highlight_info);
5064      switch (event.type)
5065      {
5066        case ButtonPress:
5067        {
5068          if (event.xbutton.button != Button1)
5069            break;
5070          if (event.xbutton.window != windows->image.id)
5071            break;
5072          x=windows->image.x+event.xbutton.x;
5073          y=windows->image.y+event.xbutton.y;
5074          if ((x < (int) (crop_info.x+RoiDelta)) &&
5075              (x > (int) (crop_info.x-RoiDelta)) &&
5076              (y < (int) (crop_info.y+RoiDelta)) &&
5077              (y > (int) (crop_info.y-RoiDelta)))
5078            {
5079              crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5080              crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5081              state|=UpdateConfigurationState;
5082              break;
5083            }
5084          if ((x < (int) (crop_info.x+RoiDelta)) &&
5085              (x > (int) (crop_info.x-RoiDelta)) &&
5086              (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5087              (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5088            {
5089              crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5090              state|=UpdateConfigurationState;
5091              break;
5092            }
5093          if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5094              (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5095              (y < (int) (crop_info.y+RoiDelta)) &&
5096              (y > (int) (crop_info.y-RoiDelta)))
5097            {
5098              crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5099              state|=UpdateConfigurationState;
5100              break;
5101            }
5102          if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5103              (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5104              (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5105              (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5106            {
5107              state|=UpdateConfigurationState;
5108              break;
5109            }
5110        }
5111        case ButtonRelease:
5112        {
5113          if (event.xbutton.window == windows->pan.id)
5114            if ((highlight_info.x != crop_info.x-windows->image.x) ||
5115                (highlight_info.y != crop_info.y-windows->image.y))
5116              XHighlightRectangle(display,windows->image.id,
5117                windows->image.highlight_context,&highlight_info);
5118          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5119            event.xbutton.time);
5120          break;
5121        }
5122        case Expose:
5123        {
5124          if (event.xexpose.window == windows->image.id)
5125            if (event.xexpose.count == 0)
5126              {
5127                event.xexpose.x=(int) highlight_info.x;
5128                event.xexpose.y=(int) highlight_info.y;
5129                event.xexpose.width=(int) highlight_info.width;
5130                event.xexpose.height=(int) highlight_info.height;
5131                XRefreshWindow(display,&windows->image,&event);
5132              }
5133          if (event.xexpose.window == windows->info.id)
5134            if (event.xexpose.count == 0)
5135              XInfoWidget(display,windows,text);
5136          break;
5137        }
5138        case KeyPress:
5139        {
5140          if (event.xkey.window != windows->image.id)
5141            break;
5142          /*
5143            Respond to a user key press.
5144          */
5145          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5146            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5147          switch ((int) key_symbol)
5148          {
5149            case XK_Escape:
5150            case XK_F20:
5151              state|=EscapeState;
5152            case XK_Return:
5153            {
5154              state|=ExitState;
5155              break;
5156            }
5157            case XK_Home:
5158            case XK_KP_Home:
5159            {
5160              crop_info.x=(ssize_t) (windows->image.width/2L-
5161                crop_info.width/2L);
5162              crop_info.y=(ssize_t) (windows->image.height/2L-
5163                crop_info.height/2L);
5164              break;
5165            }
5166            case XK_Left:
5167            case XK_KP_Left:
5168            {
5169              crop_info.x--;
5170              break;
5171            }
5172            case XK_Up:
5173            case XK_KP_Up:
5174            case XK_Next:
5175            {
5176              crop_info.y--;
5177              break;
5178            }
5179            case XK_Right:
5180            case XK_KP_Right:
5181            {
5182              crop_info.x++;
5183              break;
5184            }
5185            case XK_Prior:
5186            case XK_Down:
5187            case XK_KP_Down:
5188            {
5189              crop_info.y++;
5190              break;
5191            }
5192            case XK_F1:
5193            case XK_Help:
5194            {
5195              (void) XSetFunction(display,windows->image.highlight_context,
5196                GXcopy);
5197              switch (mode)
5198              {
5199                case CopyMode:
5200                {
5201                  XTextViewWidget(display,resource_info,windows,MagickFalse,
5202                    "Help Viewer - Image Copy",ImageCopyHelp);
5203                  break;
5204                }
5205                case CropMode:
5206                {
5207                  XTextViewWidget(display,resource_info,windows,MagickFalse,
5208                    "Help Viewer - Image Cropg",ImageCropHelp);
5209                  break;
5210                }
5211                case CutMode:
5212                {
5213                  XTextViewWidget(display,resource_info,windows,MagickFalse,
5214                    "Help Viewer - Image Cutg",ImageCutHelp);
5215                  break;
5216                }
5217              }
5218              (void) XSetFunction(display,windows->image.highlight_context,
5219                GXinvert);
5220              break;
5221            }
5222            default:
5223            {
5224              (void) XBell(display,0);
5225              break;
5226            }
5227          }
5228          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5229            event.xkey.time);
5230          break;
5231        }
5232        case KeyRelease:
5233          break;
5234        case MotionNotify:
5235        {
5236          if (event.xmotion.window != windows->image.id)
5237            break;
5238          /*
5239            Map and unmap Info widget as text cursor crosses its boundaries.
5240          */
5241          x=event.xmotion.x;
5242          y=event.xmotion.y;
5243          if (windows->info.mapped != MagickFalse)
5244            {
5245              if ((x < (int) (windows->info.x+windows->info.width)) &&
5246                  (y < (int) (windows->info.y+windows->info.height)))
5247                (void) XWithdrawWindow(display,windows->info.id,
5248                  windows->info.screen);
5249            }
5250          else
5251            if ((x > (int) (windows->info.x+windows->info.width)) ||
5252                (y > (int) (windows->info.y+windows->info.height)))
5253              (void) XMapWindow(display,windows->info.id);
5254          crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5255          crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5256          break;
5257        }
5258        case SelectionRequest:
5259        {
5260          XSelectionEvent
5261            notify;
5262
5263          XSelectionRequestEvent
5264            *request;
5265
5266          /*
5267            Set primary selection.
5268          */
5269          (void) FormatLocaleString(text,MaxTextExtent,
5270            "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5271            crop_info.height,(double) crop_info.x,(double) crop_info.y);
5272          request=(&(event.xselectionrequest));
5273          (void) XChangeProperty(request->display,request->requestor,
5274            request->property,request->target,8,PropModeReplace,
5275            (unsigned char *) text,(int) strlen(text));
5276          notify.type=SelectionNotify;
5277          notify.display=request->display;
5278          notify.requestor=request->requestor;
5279          notify.selection=request->selection;
5280          notify.target=request->target;
5281          notify.time=request->time;
5282          if (request->property == None)
5283            notify.property=request->target;
5284          else
5285            notify.property=request->property;
5286          (void) XSendEvent(request->display,request->requestor,False,0,
5287            (XEvent *) &notify);
5288        }
5289        default:
5290          break;
5291      }
5292      if ((state & UpdateConfigurationState) != 0)
5293        {
5294          (void) XPutBackEvent(display,&event);
5295          (void) XCheckDefineCursor(display,windows->image.id,cursor);
5296          break;
5297        }
5298    } while ((state & ExitState) == 0);
5299  } while ((state & ExitState) == 0);
5300  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5301  XSetCursorState(display,windows,MagickFalse);
5302  if ((state & EscapeState) != 0)
5303    return(MagickTrue);
5304  if (mode == CropMode)
5305    if (((int) crop_info.width != windows->image.ximage->width) ||
5306        ((int) crop_info.height != windows->image.ximage->height))
5307      {
5308        /*
5309          Reconfigure Image window as defined by cropping rectangle.
5310        */
5311        XSetCropGeometry(display,windows,&crop_info,image);
5312        windows->image.window_changes.width=(int) crop_info.width;
5313        windows->image.window_changes.height=(int) crop_info.height;
5314        (void) XConfigureImage(display,resource_info,windows,image,exception);
5315        return(MagickTrue);
5316      }
5317  /*
5318    Copy image before applying image transforms.
5319  */
5320  XSetCursorState(display,windows,MagickTrue);
5321  XCheckRefreshWindows(display,windows);
5322  width=(unsigned int) image->columns;
5323  height=(unsigned int) image->rows;
5324  x=0;
5325  y=0;
5326  if (windows->image.crop_geometry != (char *) NULL)
5327    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5328  scale_factor=(MagickRealType) width/windows->image.ximage->width;
5329  crop_info.x+=x;
5330  crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5331  crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5332  scale_factor=(MagickRealType) height/windows->image.ximage->height;
5333  crop_info.y+=y;
5334  crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5335  crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5336  crop_image=CropImage(image,&crop_info,exception);
5337  XSetCursorState(display,windows,MagickFalse);
5338  if (crop_image == (Image *) NULL)
5339    return(MagickFalse);
5340  if (resource_info->copy_image != (Image *) NULL)
5341    resource_info->copy_image=DestroyImage(resource_info->copy_image);
5342  resource_info->copy_image=crop_image;
5343  if (mode == CopyMode)
5344    {
5345      (void) XConfigureImage(display,resource_info,windows,image,exception);
5346      return(MagickTrue);
5347    }
5348  /*
5349    Cut image.
5350  */
5351  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
5352    return(MagickFalse);
5353  image->matte=MagickTrue;
5354  image_view=AcquireCacheView(image);
5355  for (y=0; y < (int) crop_info.height; y++)
5356  {
5357    q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5358      crop_info.width,1,exception);
5359    if (q == (Quantum *) NULL)
5360      break;
5361    for (x=0; x < (int) crop_info.width; x++)
5362    {
5363      SetPixelAlpha(image,TransparentAlpha,q);
5364      q+=GetPixelChannels(image);
5365    }
5366    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5367      break;
5368  }
5369  image_view=DestroyCacheView(image_view);
5370  /*
5371    Update image configuration.
5372  */
5373  XConfigureImageColormap(display,resource_info,windows,image);
5374  (void) XConfigureImage(display,resource_info,windows,image,exception);
5375  return(MagickTrue);
5376}
5377
5378/*
5379%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5380%                                                                             %
5381%                                                                             %
5382%                                                                             %
5383+   X D r a w I m a g e                                                       %
5384%                                                                             %
5385%                                                                             %
5386%                                                                             %
5387%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5388%
5389%  XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5390%  the image.
5391%
5392%  The format of the XDrawEditImage method is:
5393%
5394%      MagickBooleanType XDrawEditImage(Display *display,
5395%        XResourceInfo *resource_info,XWindows *windows,Image **image,
5396%        ExceptionInfo *exception)
5397%
5398%  A description of each parameter follows:
5399%
5400%    o display: Specifies a connection to an X server; returned from
5401%      XOpenDisplay.
5402%
5403%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5404%
5405%    o windows: Specifies a pointer to a XWindows structure.
5406%
5407%    o image: the image.
5408%
5409%    o exception: return any errors or warnings in this structure.
5410%
5411*/
5412static MagickBooleanType XDrawEditImage(Display *display,
5413  XResourceInfo *resource_info,XWindows *windows,Image **image,
5414  ExceptionInfo *exception)
5415{
5416  static const char
5417    *DrawMenu[] =
5418    {
5419      "Element",
5420      "Color",
5421      "Stipple",
5422      "Width",
5423      "Undo",
5424      "Help",
5425      "Dismiss",
5426      (char *) NULL
5427    };
5428
5429  static ElementType
5430    element = PointElement;
5431
5432  static const ModeType
5433    DrawCommands[] =
5434    {
5435      DrawElementCommand,
5436      DrawColorCommand,
5437      DrawStippleCommand,
5438      DrawWidthCommand,
5439      DrawUndoCommand,
5440      DrawHelpCommand,
5441      DrawDismissCommand
5442    };
5443
5444  static Pixmap
5445    stipple = (Pixmap) NULL;
5446
5447  static unsigned int
5448    pen_id = 0,
5449    line_width = 1;
5450
5451  char
5452    command[MaxTextExtent],
5453    text[MaxTextExtent];
5454
5455  Cursor
5456    cursor;
5457
5458  int
5459    entry,
5460    id,
5461    number_coordinates,
5462    x,
5463    y;
5464
5465  MagickRealType
5466    degrees;
5467
5468  MagickStatusType
5469    status;
5470
5471  RectangleInfo
5472    rectangle_info;
5473
5474  register int
5475    i;
5476
5477  unsigned int
5478    distance,
5479    height,
5480    max_coordinates,
5481    width;
5482
5483  size_t
5484    state;
5485
5486  Window
5487    root_window;
5488
5489  XDrawInfo
5490    draw_info;
5491
5492  XEvent
5493    event;
5494
5495  XPoint
5496    *coordinate_info;
5497
5498  XSegment
5499    line_info;
5500
5501  /*
5502    Allocate polygon info.
5503  */
5504  max_coordinates=2048;
5505  coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5506    sizeof(*coordinate_info));
5507  if (coordinate_info == (XPoint *) NULL)
5508    {
5509      (void) ThrowMagickException(exception,GetMagickModule(),
5510        ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5511      return(MagickFalse);
5512    }
5513  /*
5514    Map Command widget.
5515  */
5516  (void) CloneString(&windows->command.name,"Draw");
5517  windows->command.data=4;
5518  (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5519  (void) XMapRaised(display,windows->command.id);
5520  XClientMessage(display,windows->image.id,windows->im_protocols,
5521    windows->im_update_widget,CurrentTime);
5522  /*
5523    Wait for first button press.
5524  */
5525  root_window=XRootWindow(display,XDefaultScreen(display));
5526  draw_info.stencil=OpaqueStencil;
5527  status=MagickTrue;
5528  cursor=XCreateFontCursor(display,XC_tcross);
5529  for ( ; ; )
5530  {
5531    XQueryPosition(display,windows->image.id,&x,&y);
5532    (void) XSelectInput(display,windows->image.id,
5533      windows->image.attributes.event_mask | PointerMotionMask);
5534    (void) XCheckDefineCursor(display,windows->image.id,cursor);
5535    state=DefaultState;
5536    do
5537    {
5538      if (windows->info.mapped != MagickFalse)
5539        {
5540          /*
5541            Display pointer position.
5542          */
5543          (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
5544            x+windows->image.x,y+windows->image.y);
5545          XInfoWidget(display,windows,text);
5546        }
5547      /*
5548        Wait for next event.
5549      */
5550      XScreenEvent(display,windows,&event);
5551      if (event.xany.window == windows->command.id)
5552        {
5553          /*
5554            Select a command from the Command widget.
5555          */
5556          id=XCommandWidget(display,windows,DrawMenu,&event);
5557          if (id < 0)
5558            continue;
5559          switch (DrawCommands[id])
5560          {
5561            case DrawElementCommand:
5562            {
5563              static const char
5564                *Elements[] =
5565                {
5566                  "point",
5567                  "line",
5568                  "rectangle",
5569                  "fill rectangle",
5570                  "circle",
5571                  "fill circle",
5572                  "ellipse",
5573                  "fill ellipse",
5574                  "polygon",
5575                  "fill polygon",
5576                  (char *) NULL,
5577                };
5578
5579              /*
5580                Select a command from the pop-up menu.
5581              */
5582              element=(ElementType) (XMenuWidget(display,windows,
5583                DrawMenu[id],Elements,command)+1);
5584              break;
5585            }
5586            case DrawColorCommand:
5587            {
5588              const char
5589                *ColorMenu[MaxNumberPens+1];
5590
5591              int
5592                pen_number;
5593
5594              MagickBooleanType
5595                transparent;
5596
5597              XColor
5598                color;
5599
5600              /*
5601                Initialize menu selections.
5602              */
5603              for (i=0; i < (int) (MaxNumberPens-2); i++)
5604                ColorMenu[i]=resource_info->pen_colors[i];
5605              ColorMenu[MaxNumberPens-2]="transparent";
5606              ColorMenu[MaxNumberPens-1]="Browser...";
5607              ColorMenu[MaxNumberPens]=(char *) NULL;
5608              /*
5609                Select a pen color from the pop-up menu.
5610              */
5611              pen_number=XMenuWidget(display,windows,DrawMenu[id],
5612                (const char **) ColorMenu,command);
5613              if (pen_number < 0)
5614                break;
5615              transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5616                MagickFalse;
5617              if (transparent != MagickFalse)
5618                {
5619                  draw_info.stencil=TransparentStencil;
5620                  break;
5621                }
5622              if (pen_number == (MaxNumberPens-1))
5623                {
5624                  static char
5625                    color_name[MaxTextExtent] = "gray";
5626
5627                  /*
5628                    Select a pen color from a dialog.
5629                  */
5630                  resource_info->pen_colors[pen_number]=color_name;
5631                  XColorBrowserWidget(display,windows,"Select",color_name);
5632                  if (*color_name == '\0')
5633                    break;
5634                }
5635              /*
5636                Set pen color.
5637              */
5638              (void) XParseColor(display,windows->map_info->colormap,
5639                resource_info->pen_colors[pen_number],&color);
5640              XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5641                (unsigned int) MaxColors,&color);
5642              windows->pixel_info->pen_colors[pen_number]=color;
5643              pen_id=(unsigned int) pen_number;
5644              draw_info.stencil=OpaqueStencil;
5645              break;
5646            }
5647            case DrawStippleCommand:
5648            {
5649              Image
5650                *stipple_image;
5651
5652              ImageInfo
5653                *image_info;
5654
5655              int
5656                status;
5657
5658              static char
5659                filename[MaxTextExtent] = "\0";
5660
5661              static const char
5662                *StipplesMenu[] =
5663                {
5664                  "Brick",
5665                  "Diagonal",
5666                  "Scales",
5667                  "Vertical",
5668                  "Wavy",
5669                  "Translucent",
5670                  "Opaque",
5671                  (char *) NULL,
5672                  (char *) NULL,
5673                };
5674
5675              /*
5676                Select a command from the pop-up menu.
5677              */
5678              StipplesMenu[7]="Open...";
5679              entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5680                command);
5681              if (entry < 0)
5682                break;
5683              if (stipple != (Pixmap) NULL)
5684                (void) XFreePixmap(display,stipple);
5685              stipple=(Pixmap) NULL;
5686              if (entry != 7)
5687                {
5688                  switch (entry)
5689                  {
5690                    case 0:
5691                    {
5692                      stipple=XCreateBitmapFromData(display,root_window,
5693                        (char *) BricksBitmap,BricksWidth,BricksHeight);
5694                      break;
5695                    }
5696                    case 1:
5697                    {
5698                      stipple=XCreateBitmapFromData(display,root_window,
5699                        (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5700                      break;
5701                    }
5702                    case 2:
5703                    {
5704                      stipple=XCreateBitmapFromData(display,root_window,
5705                        (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5706                      break;
5707                    }
5708                    case 3:
5709                    {
5710                      stipple=XCreateBitmapFromData(display,root_window,
5711                        (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5712                      break;
5713                    }
5714                    case 4:
5715                    {
5716                      stipple=XCreateBitmapFromData(display,root_window,
5717                        (char *) WavyBitmap,WavyWidth,WavyHeight);
5718                      break;
5719                    }
5720                    case 5:
5721                    {
5722                      stipple=XCreateBitmapFromData(display,root_window,
5723                        (char *) HighlightBitmap,HighlightWidth,
5724                        HighlightHeight);
5725                      break;
5726                    }
5727                    case 6:
5728                    default:
5729                    {
5730                      stipple=XCreateBitmapFromData(display,root_window,
5731                        (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5732                      break;
5733                    }
5734                  }
5735                  break;
5736                }
5737              XFileBrowserWidget(display,windows,"Stipple",filename);
5738              if (*filename == '\0')
5739                break;
5740              /*
5741                Read image.
5742              */
5743              XSetCursorState(display,windows,MagickTrue);
5744              XCheckRefreshWindows(display,windows);
5745              image_info=AcquireImageInfo();
5746              (void) CopyMagickString(image_info->filename,filename,
5747                MaxTextExtent);
5748              stipple_image=ReadImage(image_info,exception);
5749              CatchException(exception);
5750              XSetCursorState(display,windows,MagickFalse);
5751              if (stipple_image == (Image *) NULL)
5752                break;
5753              (void) AcquireUniqueFileResource(filename);
5754              (void) FormatLocaleString(stipple_image->filename,MaxTextExtent,
5755                "xbm:%s",filename);
5756              (void) WriteImage(image_info,stipple_image,exception);
5757              stipple_image=DestroyImage(stipple_image);
5758              image_info=DestroyImageInfo(image_info);
5759              status=XReadBitmapFile(display,root_window,filename,&width,
5760                &height,&stipple,&x,&y);
5761              (void) RelinquishUniqueFileResource(filename);
5762              if ((status != BitmapSuccess) != 0)
5763                XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5764                  filename);
5765              break;
5766            }
5767            case DrawWidthCommand:
5768            {
5769              static char
5770                width[MaxTextExtent] = "0";
5771
5772              static const char
5773                *WidthsMenu[] =
5774                {
5775                  "1",
5776                  "2",
5777                  "4",
5778                  "8",
5779                  "16",
5780                  "Dialog...",
5781                  (char *) NULL,
5782                };
5783
5784              /*
5785                Select a command from the pop-up menu.
5786              */
5787              entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5788                command);
5789              if (entry < 0)
5790                break;
5791              if (entry != 5)
5792                {
5793                  line_width=(unsigned int) StringToUnsignedLong(
5794                    WidthsMenu[entry]);
5795                  break;
5796                }
5797              (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5798                width);
5799              if (*width == '\0')
5800                break;
5801              line_width=(unsigned int) StringToUnsignedLong(width);
5802              break;
5803            }
5804            case DrawUndoCommand:
5805            {
5806              (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5807                image,exception);
5808              break;
5809            }
5810            case DrawHelpCommand:
5811            {
5812              XTextViewWidget(display,resource_info,windows,MagickFalse,
5813                "Help Viewer - Image Rotation",ImageDrawHelp);
5814              (void) XCheckDefineCursor(display,windows->image.id,cursor);
5815              break;
5816            }
5817            case DrawDismissCommand:
5818            {
5819              /*
5820                Prematurely exit.
5821              */
5822              state|=EscapeState;
5823              state|=ExitState;
5824              break;
5825            }
5826            default:
5827              break;
5828          }
5829          (void) XCheckDefineCursor(display,windows->image.id,cursor);
5830          continue;
5831        }
5832      switch (event.type)
5833      {
5834        case ButtonPress:
5835        {
5836          if (event.xbutton.button != Button1)
5837            break;
5838          if (event.xbutton.window != windows->image.id)
5839            break;
5840          /*
5841            exit loop.
5842          */
5843          x=event.xbutton.x;
5844          y=event.xbutton.y;
5845          state|=ExitState;
5846          break;
5847        }
5848        case ButtonRelease:
5849          break;
5850        case Expose:
5851          break;
5852        case KeyPress:
5853        {
5854          KeySym
5855            key_symbol;
5856
5857          if (event.xkey.window != windows->image.id)
5858            break;
5859          /*
5860            Respond to a user key press.
5861          */
5862          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5863            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5864          switch ((int) key_symbol)
5865          {
5866            case XK_Escape:
5867            case XK_F20:
5868            {
5869              /*
5870                Prematurely exit.
5871              */
5872              state|=EscapeState;
5873              state|=ExitState;
5874              break;
5875            }
5876            case XK_F1:
5877            case XK_Help:
5878            {
5879              XTextViewWidget(display,resource_info,windows,MagickFalse,
5880                "Help Viewer - Image Rotation",ImageDrawHelp);
5881              break;
5882            }
5883            default:
5884            {
5885              (void) XBell(display,0);
5886              break;
5887            }
5888          }
5889          break;
5890        }
5891        case MotionNotify:
5892        {
5893          /*
5894            Map and unmap Info widget as text cursor crosses its boundaries.
5895          */
5896          x=event.xmotion.x;
5897          y=event.xmotion.y;
5898          if (windows->info.mapped != MagickFalse)
5899            {
5900              if ((x < (int) (windows->info.x+windows->info.width)) &&
5901                  (y < (int) (windows->info.y+windows->info.height)))
5902                (void) XWithdrawWindow(display,windows->info.id,
5903                  windows->info.screen);
5904            }
5905          else
5906            if ((x > (int) (windows->info.x+windows->info.width)) ||
5907                (y > (int) (windows->info.y+windows->info.height)))
5908              (void) XMapWindow(display,windows->info.id);
5909          break;
5910        }
5911      }
5912    } while ((state & ExitState) == 0);
5913    (void) XSelectInput(display,windows->image.id,
5914      windows->image.attributes.event_mask);
5915    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5916    if ((state & EscapeState) != 0)
5917      break;
5918    /*
5919      Draw element as pointer moves until the button is released.
5920    */
5921    distance=0;
5922    degrees=0.0;
5923    line_info.x1=x;
5924    line_info.y1=y;
5925    line_info.x2=x;
5926    line_info.y2=y;
5927    rectangle_info.x=(ssize_t) x;
5928    rectangle_info.y=(ssize_t) y;
5929    rectangle_info.width=0;
5930    rectangle_info.height=0;
5931    number_coordinates=1;
5932    coordinate_info->x=x;
5933    coordinate_info->y=y;
5934    (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5935    state=DefaultState;
5936    do
5937    {
5938      switch (element)
5939      {
5940        case PointElement:
5941        default:
5942        {
5943          if (number_coordinates > 1)
5944            {
5945              (void) XDrawLines(display,windows->image.id,
5946                windows->image.highlight_context,coordinate_info,
5947                number_coordinates,CoordModeOrigin);
5948              (void) FormatLocaleString(text,MaxTextExtent," %+d%+d",
5949                coordinate_info[number_coordinates-1].x,
5950                coordinate_info[number_coordinates-1].y);
5951              XInfoWidget(display,windows,text);
5952            }
5953          break;
5954        }
5955        case LineElement:
5956        {
5957          if (distance > 9)
5958            {
5959              /*
5960                Display angle of the line.
5961              */
5962              degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5963                line_info.y1),(double) (line_info.x2-line_info.x1)));
5964              (void) FormatLocaleString(text,MaxTextExtent," %g",
5965                (double) degrees);
5966              XInfoWidget(display,windows,text);
5967              XHighlightLine(display,windows->image.id,
5968                windows->image.highlight_context,&line_info);
5969            }
5970          else
5971            if (windows->info.mapped != MagickFalse)
5972              (void) XWithdrawWindow(display,windows->info.id,
5973                windows->info.screen);
5974          break;
5975        }
5976        case RectangleElement:
5977        case FillRectangleElement:
5978        {
5979          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5980            {
5981              /*
5982                Display info and draw drawing rectangle.
5983              */
5984              (void) FormatLocaleString(text,MaxTextExtent,
5985                " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5986                (double) rectangle_info.height,(double) rectangle_info.x,
5987                (double) rectangle_info.y);
5988              XInfoWidget(display,windows,text);
5989              XHighlightRectangle(display,windows->image.id,
5990                windows->image.highlight_context,&rectangle_info);
5991            }
5992          else
5993            if (windows->info.mapped != MagickFalse)
5994              (void) XWithdrawWindow(display,windows->info.id,
5995                windows->info.screen);
5996          break;
5997        }
5998        case CircleElement:
5999        case FillCircleElement:
6000        case EllipseElement:
6001        case FillEllipseElement:
6002        {
6003          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6004            {
6005              /*
6006                Display info and draw drawing rectangle.
6007              */
6008              (void) FormatLocaleString(text,MaxTextExtent,
6009                " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
6010                (double) rectangle_info.height,(double) rectangle_info.x,
6011                (double) rectangle_info.y);
6012              XInfoWidget(display,windows,text);
6013              XHighlightEllipse(display,windows->image.id,
6014                windows->image.highlight_context,&rectangle_info);
6015            }
6016          else
6017            if (windows->info.mapped != MagickFalse)
6018              (void) XWithdrawWindow(display,windows->info.id,
6019                windows->info.screen);
6020          break;
6021        }
6022        case PolygonElement:
6023        case FillPolygonElement:
6024        {
6025          if (number_coordinates > 1)
6026            (void) XDrawLines(display,windows->image.id,
6027              windows->image.highlight_context,coordinate_info,
6028              number_coordinates,CoordModeOrigin);
6029          if (distance > 9)
6030            {
6031              /*
6032                Display angle of the line.
6033              */
6034              degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
6035                line_info.y1),(double) (line_info.x2-line_info.x1)));
6036              (void) FormatLocaleString(text,MaxTextExtent," %g",
6037                (double) degrees);
6038              XInfoWidget(display,windows,text);
6039              XHighlightLine(display,windows->image.id,
6040                windows->image.highlight_context,&line_info);
6041            }
6042          else
6043            if (windows->info.mapped != MagickFalse)
6044              (void) XWithdrawWindow(display,windows->info.id,
6045                windows->info.screen);
6046          break;
6047        }
6048      }
6049      /*
6050        Wait for next event.
6051      */
6052      XScreenEvent(display,windows,&event);
6053      switch (element)
6054      {
6055        case PointElement:
6056        default:
6057        {
6058          if (number_coordinates > 1)
6059            (void) XDrawLines(display,windows->image.id,
6060              windows->image.highlight_context,coordinate_info,
6061              number_coordinates,CoordModeOrigin);
6062          break;
6063        }
6064        case LineElement:
6065        {
6066          if (distance > 9)
6067            XHighlightLine(display,windows->image.id,
6068              windows->image.highlight_context,&line_info);
6069          break;
6070        }
6071        case RectangleElement:
6072        case FillRectangleElement:
6073        {
6074          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6075            XHighlightRectangle(display,windows->image.id,
6076              windows->image.highlight_context,&rectangle_info);
6077          break;
6078        }
6079        case CircleElement:
6080        case FillCircleElement:
6081        case EllipseElement:
6082        case FillEllipseElement:
6083        {
6084          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6085            XHighlightEllipse(display,windows->image.id,
6086              windows->image.highlight_context,&rectangle_info);
6087          break;
6088        }
6089        case PolygonElement:
6090        case FillPolygonElement:
6091        {
6092          if (number_coordinates > 1)
6093            (void) XDrawLines(display,windows->image.id,
6094              windows->image.highlight_context,coordinate_info,
6095              number_coordinates,CoordModeOrigin);
6096          if (distance > 9)
6097            XHighlightLine(display,windows->image.id,
6098              windows->image.highlight_context,&line_info);
6099          break;
6100        }
6101      }
6102      switch (event.type)
6103      {
6104        case ButtonPress:
6105          break;
6106        case ButtonRelease:
6107        {
6108          /*
6109            User has committed to element.
6110          */
6111          line_info.x2=event.xbutton.x;
6112          line_info.y2=event.xbutton.y;
6113          rectangle_info.x=(ssize_t) event.xbutton.x;
6114          rectangle_info.y=(ssize_t) event.xbutton.y;
6115          coordinate_info[number_coordinates].x=event.xbutton.x;
6116          coordinate_info[number_coordinates].y=event.xbutton.y;
6117          if (((element != PolygonElement) &&
6118               (element != FillPolygonElement)) || (distance <= 9))
6119            {
6120              state|=ExitState;
6121              break;
6122            }
6123          number_coordinates++;
6124          if (number_coordinates < (int) max_coordinates)
6125            {
6126              line_info.x1=event.xbutton.x;
6127              line_info.y1=event.xbutton.y;
6128              break;
6129            }
6130          max_coordinates<<=1;
6131          coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6132            max_coordinates,sizeof(*coordinate_info));
6133          if (coordinate_info == (XPoint *) NULL)
6134            (void) ThrowMagickException(exception,GetMagickModule(),
6135              ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6136          break;
6137        }
6138        case Expose:
6139          break;
6140        case MotionNotify:
6141        {
6142          if (event.xmotion.window != windows->image.id)
6143            break;
6144          if (element != PointElement)
6145            {
6146              line_info.x2=event.xmotion.x;
6147              line_info.y2=event.xmotion.y;
6148              rectangle_info.x=(ssize_t) event.xmotion.x;
6149              rectangle_info.y=(ssize_t) event.xmotion.y;
6150              break;
6151            }
6152          coordinate_info[number_coordinates].x=event.xbutton.x;
6153          coordinate_info[number_coordinates].y=event.xbutton.y;
6154          number_coordinates++;
6155          if (number_coordinates < (int) max_coordinates)
6156            break;
6157          max_coordinates<<=1;
6158          coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6159            max_coordinates,sizeof(*coordinate_info));
6160          if (coordinate_info == (XPoint *) NULL)
6161            (void) ThrowMagickException(exception,GetMagickModule(),
6162              ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6163          break;
6164        }
6165        default:
6166          break;
6167      }
6168      /*
6169        Check boundary conditions.
6170      */
6171      if (line_info.x2 < 0)
6172        line_info.x2=0;
6173      else
6174        if (line_info.x2 > (int) windows->image.width)
6175          line_info.x2=(short) windows->image.width;
6176      if (line_info.y2 < 0)
6177        line_info.y2=0;
6178      else
6179        if (line_info.y2 > (int) windows->image.height)
6180          line_info.y2=(short) windows->image.height;
6181      distance=(unsigned int)
6182        (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6183         ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6184      if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6185          ((state & ExitState) != 0))
6186        {
6187          if (rectangle_info.x < 0)
6188            rectangle_info.x=0;
6189          else
6190            if (rectangle_info.x > (ssize_t) windows->image.width)
6191              rectangle_info.x=(ssize_t) windows->image.width;
6192          if ((int) rectangle_info.x < x)
6193            rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6194          else
6195            {
6196              rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6197              rectangle_info.x=(ssize_t) x;
6198            }
6199          if (rectangle_info.y < 0)
6200            rectangle_info.y=0;
6201          else
6202            if (rectangle_info.y > (ssize_t) windows->image.height)
6203              rectangle_info.y=(ssize_t) windows->image.height;
6204          if ((int) rectangle_info.y < y)
6205            rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6206          else
6207            {
6208              rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6209              rectangle_info.y=(ssize_t) y;
6210            }
6211        }
6212    } while ((state & ExitState) == 0);
6213    (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6214    if ((element == PointElement) || (element == PolygonElement) ||
6215        (element == FillPolygonElement))
6216      {
6217        /*
6218          Determine polygon bounding box.
6219        */
6220        rectangle_info.x=(ssize_t) coordinate_info->x;
6221        rectangle_info.y=(ssize_t) coordinate_info->y;
6222        x=coordinate_info->x;
6223        y=coordinate_info->y;
6224        for (i=1; i < number_coordinates; i++)
6225        {
6226          if (coordinate_info[i].x > x)
6227            x=coordinate_info[i].x;
6228          if (coordinate_info[i].y > y)
6229            y=coordinate_info[i].y;
6230          if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6231            rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6232          if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6233            rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6234        }
6235        rectangle_info.width=(size_t) (x-rectangle_info.x);
6236        rectangle_info.height=(size_t) (y-rectangle_info.y);
6237        for (i=0; i < number_coordinates; i++)
6238        {
6239          coordinate_info[i].x-=rectangle_info.x;
6240          coordinate_info[i].y-=rectangle_info.y;
6241        }
6242      }
6243    else
6244      if (distance <= 9)
6245        continue;
6246      else
6247        if ((element == RectangleElement) ||
6248            (element == CircleElement) || (element == EllipseElement))
6249          {
6250            rectangle_info.width--;
6251            rectangle_info.height--;
6252          }
6253    /*
6254      Drawing is relative to image configuration.
6255    */
6256    draw_info.x=(int) rectangle_info.x;
6257    draw_info.y=(int) rectangle_info.y;
6258    (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6259      image,exception);
6260    width=(unsigned int) (*image)->columns;
6261    height=(unsigned int) (*image)->rows;
6262    x=0;
6263    y=0;
6264    if (windows->image.crop_geometry != (char *) NULL)
6265      (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6266    draw_info.x+=windows->image.x-(line_width/2);
6267    if (draw_info.x < 0)
6268      draw_info.x=0;
6269    draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6270    draw_info.y+=windows->image.y-(line_width/2);
6271    if (draw_info.y < 0)
6272      draw_info.y=0;
6273    draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6274    draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6275    if (draw_info.width > (unsigned int) (*image)->columns)
6276      draw_info.width=(unsigned int) (*image)->columns;
6277    draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6278    if (draw_info.height > (unsigned int) (*image)->rows)
6279      draw_info.height=(unsigned int) (*image)->rows;
6280    (void) FormatLocaleString(draw_info.geometry,MaxTextExtent,"%ux%u%+d%+d",
6281      width*draw_info.width/windows->image.ximage->width,
6282      height*draw_info.height/windows->image.ximage->height,
6283      draw_info.x+x,draw_info.y+y);
6284    /*
6285      Initialize drawing attributes.
6286    */
6287    draw_info.degrees=0.0;
6288    draw_info.element=element;
6289    draw_info.stipple=stipple;
6290    draw_info.line_width=line_width;
6291    draw_info.line_info=line_info;
6292    if (line_info.x1 > (int) (line_width/2))
6293      draw_info.line_info.x1=(short) line_width/2;
6294    if (line_info.y1 > (int) (line_width/2))
6295      draw_info.line_info.y1=(short) line_width/2;
6296    draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6297    draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6298    if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6299      {
6300        draw_info.line_info.x2=(-draw_info.line_info.x2);
6301        draw_info.line_info.y2=(-draw_info.line_info.y2);
6302      }
6303    if (draw_info.line_info.x2 < 0)
6304      {
6305        draw_info.line_info.x2=(-draw_info.line_info.x2);
6306        Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6307      }
6308    if (draw_info.line_info.y2 < 0)
6309      {
6310        draw_info.line_info.y2=(-draw_info.line_info.y2);
6311        Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6312      }
6313    draw_info.rectangle_info=rectangle_info;
6314    if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6315      draw_info.rectangle_info.x=(ssize_t) line_width/2;
6316    if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6317      draw_info.rectangle_info.y=(ssize_t) line_width/2;
6318    draw_info.number_coordinates=(unsigned int) number_coordinates;
6319    draw_info.coordinate_info=coordinate_info;
6320    windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6321    /*
6322      Draw element on image.
6323    */
6324    XSetCursorState(display,windows,MagickTrue);
6325    XCheckRefreshWindows(display,windows);
6326    status=XDrawImage(display,windows->pixel_info,&draw_info,*image);
6327    XSetCursorState(display,windows,MagickFalse);
6328    /*
6329      Update image colormap and return to image drawing.
6330    */
6331    XConfigureImageColormap(display,resource_info,windows,*image);
6332    (void) XConfigureImage(display,resource_info,windows,*image,exception);
6333  }
6334  XSetCursorState(display,windows,MagickFalse);
6335  coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6336  return(status != 0 ? MagickTrue : MagickFalse);
6337}
6338
6339/*
6340%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6341%                                                                             %
6342%                                                                             %
6343%                                                                             %
6344+   X D r a w P a n R e c t a n g l e                                         %
6345%                                                                             %
6346%                                                                             %
6347%                                                                             %
6348%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6349%
6350%  XDrawPanRectangle() draws a rectangle in the pan window.  The pan window
6351%  displays a zoom image and the rectangle shows which portion of the image is
6352%  displayed in the Image window.
6353%
6354%  The format of the XDrawPanRectangle method is:
6355%
6356%      XDrawPanRectangle(Display *display,XWindows *windows)
6357%
6358%  A description of each parameter follows:
6359%
6360%    o display: Specifies a connection to an X server;  returned from
6361%      XOpenDisplay.
6362%
6363%    o windows: Specifies a pointer to a XWindows structure.
6364%
6365*/
6366static void XDrawPanRectangle(Display *display,XWindows *windows)
6367{
6368  MagickRealType
6369    scale_factor;
6370
6371  RectangleInfo
6372    highlight_info;
6373
6374  /*
6375    Determine dimensions of the panning rectangle.
6376  */
6377  scale_factor=(MagickRealType) windows->pan.width/windows->image.ximage->width;
6378  highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6379  highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6380  scale_factor=(MagickRealType)
6381    windows->pan.height/windows->image.ximage->height;
6382  highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6383  highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6384  /*
6385    Display the panning rectangle.
6386  */
6387  (void) XClearWindow(display,windows->pan.id);
6388  XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6389    &highlight_info);
6390}
6391
6392/*
6393%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6394%                                                                             %
6395%                                                                             %
6396%                                                                             %
6397+   X I m a g e C a c h e                                                     %
6398%                                                                             %
6399%                                                                             %
6400%                                                                             %
6401%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6402%
6403%  XImageCache() handles the creation, manipulation, and destruction of the
6404%  image cache (undo and redo buffers).
6405%
6406%  The format of the XImageCache method is:
6407%
6408%      void XImageCache(Display *display,XResourceInfo *resource_info,
6409%        XWindows *windows,const CommandType command,Image **image,
6410%        ExceptionInfo *exception)
6411%
6412%  A description of each parameter follows:
6413%
6414%    o display: Specifies a connection to an X server; returned from
6415%      XOpenDisplay.
6416%
6417%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6418%
6419%    o windows: Specifies a pointer to a XWindows structure.
6420%
6421%    o command: Specifies a command to perform.
6422%
6423%    o image: the image;  XImageCache may transform the image and return a new
6424%      image pointer.
6425%
6426%    o exception: return any errors or warnings in this structure.
6427%
6428*/
6429static void XImageCache(Display *display,XResourceInfo *resource_info,
6430  XWindows *windows,const CommandType command,Image **image,
6431  ExceptionInfo *exception)
6432{
6433  Image
6434    *cache_image;
6435
6436  static Image
6437    *redo_image = (Image *) NULL,
6438    *undo_image = (Image *) NULL;
6439
6440  switch (command)
6441  {
6442    case FreeBuffersCommand:
6443    {
6444      /*
6445        Free memory from the undo and redo cache.
6446      */
6447      while (undo_image != (Image *) NULL)
6448      {
6449        cache_image=undo_image;
6450        undo_image=GetPreviousImageInList(undo_image);
6451        cache_image->list=DestroyImage(cache_image->list);
6452        cache_image=DestroyImage(cache_image);
6453      }
6454      undo_image=NewImageList();
6455      if (redo_image != (Image *) NULL)
6456        redo_image=DestroyImage(redo_image);
6457      redo_image=NewImageList();
6458      return;
6459    }
6460    case UndoCommand:
6461    {
6462      char
6463        image_geometry[MaxTextExtent];
6464
6465      /*
6466        Undo the last image transformation.
6467      */
6468      if (undo_image == (Image *) NULL)
6469        {
6470          (void) XBell(display,0);
6471          return;
6472        }
6473      cache_image=undo_image;
6474      undo_image=GetPreviousImageInList(undo_image);
6475      windows->image.window_changes.width=(int) cache_image->columns;
6476      windows->image.window_changes.height=(int) cache_image->rows;
6477      (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
6478        windows->image.ximage->width,windows->image.ximage->height);
6479      (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
6480      if (windows->image.crop_geometry != (char *) NULL)
6481        windows->image.crop_geometry=(char *)
6482          RelinquishMagickMemory(windows->image.crop_geometry);
6483      windows->image.crop_geometry=cache_image->geometry;
6484      if (redo_image != (Image *) NULL)
6485        redo_image=DestroyImage(redo_image);
6486      redo_image=(*image);
6487      *image=cache_image->list;
6488      cache_image=DestroyImage(cache_image);
6489      if (windows->image.orphan != MagickFalse)
6490        return;
6491      XConfigureImageColormap(display,resource_info,windows,*image);
6492      (void) XConfigureImage(display,resource_info,windows,*image,exception);
6493      return;
6494    }
6495    case CutCommand:
6496    case PasteCommand:
6497    case ApplyCommand:
6498    case HalfSizeCommand:
6499    case OriginalSizeCommand:
6500    case DoubleSizeCommand:
6501    case ResizeCommand:
6502    case TrimCommand:
6503    case CropCommand:
6504    case ChopCommand:
6505    case FlipCommand:
6506    case FlopCommand:
6507    case RotateRightCommand:
6508    case RotateLeftCommand:
6509    case RotateCommand:
6510    case ShearCommand:
6511    case RollCommand:
6512    case NegateCommand:
6513    case ContrastStretchCommand:
6514    case SigmoidalContrastCommand:
6515    case NormalizeCommand:
6516    case EqualizeCommand:
6517    case HueCommand:
6518    case SaturationCommand:
6519    case BrightnessCommand:
6520    case GammaCommand:
6521    case SpiffCommand:
6522    case DullCommand:
6523    case GrayscaleCommand:
6524    case MapCommand:
6525    case QuantizeCommand:
6526    case DespeckleCommand:
6527    case EmbossCommand:
6528    case ReduceNoiseCommand:
6529    case AddNoiseCommand:
6530    case SharpenCommand:
6531    case BlurCommand:
6532    case ThresholdCommand:
6533    case EdgeDetectCommand:
6534    case SpreadCommand:
6535    case ShadeCommand:
6536    case RaiseCommand:
6537    case SegmentCommand:
6538    case SolarizeCommand:
6539    case SepiaToneCommand:
6540    case SwirlCommand:
6541    case ImplodeCommand:
6542    case VignetteCommand:
6543    case WaveCommand:
6544    case OilPaintCommand:
6545    case CharcoalDrawCommand:
6546    case AnnotateCommand:
6547    case AddBorderCommand:
6548    case AddFrameCommand:
6549    case CompositeCommand:
6550    case CommentCommand:
6551    case LaunchCommand:
6552    case RegionofInterestCommand:
6553    case SaveToUndoBufferCommand:
6554    case RedoCommand:
6555    {
6556      Image
6557        *previous_image;
6558
6559      ssize_t
6560        bytes;
6561
6562      bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelPacket));
6563      if (undo_image != (Image *) NULL)
6564        {
6565          /*
6566            Ensure the undo cache has enough memory available.
6567          */
6568          previous_image=undo_image;
6569          while (previous_image != (Image *) NULL)
6570          {
6571            bytes+=previous_image->list->columns*previous_image->list->rows*
6572              sizeof(PixelPacket);
6573            if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6574              {
6575                previous_image=GetPreviousImageInList(previous_image);
6576                continue;
6577              }
6578            bytes-=previous_image->list->columns*previous_image->list->rows*
6579              sizeof(PixelPacket);
6580            if (previous_image == undo_image)
6581              undo_image=NewImageList();
6582            else
6583              previous_image->next->previous=NewImageList();
6584            break;
6585          }
6586          while (previous_image != (Image *) NULL)
6587          {
6588            /*
6589              Delete any excess memory from undo cache.
6590            */
6591            cache_image=previous_image;
6592            previous_image=GetPreviousImageInList(previous_image);
6593            cache_image->list=DestroyImage(cache_image->list);
6594            cache_image=DestroyImage(cache_image);
6595          }
6596        }
6597      if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6598        break;
6599      /*
6600        Save image before transformations are applied.
6601      */
6602      cache_image=AcquireImage((ImageInfo *) NULL);
6603      if (cache_image == (Image *) NULL)
6604        break;
6605      XSetCursorState(display,windows,MagickTrue);
6606      XCheckRefreshWindows(display,windows);
6607      cache_image->list=CloneImage(*image,0,0,MagickTrue,exception);
6608      XSetCursorState(display,windows,MagickFalse);
6609      if (cache_image->list == (Image *) NULL)
6610        {
6611          cache_image=DestroyImage(cache_image);
6612          break;
6613        }
6614      cache_image->columns=(size_t) windows->image.ximage->width;
6615      cache_image->rows=(size_t) windows->image.ximage->height;
6616      cache_image->geometry=windows->image.crop_geometry;
6617      if (windows->image.crop_geometry != (char *) NULL)
6618        {
6619          cache_image->geometry=AcquireString((char *) NULL);
6620          (void) CopyMagickString(cache_image->geometry,
6621            windows->image.crop_geometry,MaxTextExtent);
6622        }
6623      if (undo_image == (Image *) NULL)
6624        {
6625          undo_image=cache_image;
6626          break;
6627        }
6628      undo_image->next=cache_image;
6629      undo_image->next->previous=undo_image;
6630      undo_image=undo_image->next;
6631      break;
6632    }
6633    default:
6634      break;
6635  }
6636  if (command == RedoCommand)
6637    {
6638      /*
6639        Redo the last image transformation.
6640      */
6641      if (redo_image == (Image *) NULL)
6642        {
6643          (void) XBell(display,0);
6644          return;
6645        }
6646      windows->image.window_changes.width=(int) redo_image->columns;
6647      windows->image.window_changes.height=(int) redo_image->rows;
6648      if (windows->image.crop_geometry != (char *) NULL)
6649        windows->image.crop_geometry=(char *)
6650          RelinquishMagickMemory(windows->image.crop_geometry);
6651      windows->image.crop_geometry=redo_image->geometry;
6652      *image=DestroyImage(*image);
6653      *image=redo_image;
6654      redo_image=NewImageList();
6655      if (windows->image.orphan != MagickFalse)
6656        return;
6657      XConfigureImageColormap(display,resource_info,windows,*image);
6658      (void) XConfigureImage(display,resource_info,windows,*image,exception);
6659      return;
6660    }
6661  if (command != InfoCommand)
6662    return;
6663  /*
6664    Display image info.
6665  */
6666  XSetCursorState(display,windows,MagickTrue);
6667  XCheckRefreshWindows(display,windows);
6668  XDisplayImageInfo(display,resource_info,windows,undo_image,*image);
6669  XSetCursorState(display,windows,MagickFalse);
6670}
6671
6672/*
6673%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6674%                                                                             %
6675%                                                                             %
6676%                                                                             %
6677+   X I m a g e W i n d o w C o m m a n d                                     %
6678%                                                                             %
6679%                                                                             %
6680%                                                                             %
6681%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6682%
6683%  XImageWindowCommand() makes a transform to the image or Image window as
6684%  specified by a user menu button or keyboard command.
6685%
6686%  The format of the XImageWindowCommand method is:
6687%
6688%      CommandType XImageWindowCommand(Display *display,
6689%        XResourceInfo *resource_info,XWindows *windows,
6690%        const MagickStatusType state,KeySym key_symbol,Image **image,
6691%        ExceptionInfo *exception)
6692%
6693%  A description of each parameter follows:
6694%
6695%    o nexus:  Method XImageWindowCommand returns an image when the
6696%      user chooses 'Open Image' from the command menu.  Otherwise a null
6697%      image is returned.
6698%
6699%    o display: Specifies a connection to an X server; returned from
6700%      XOpenDisplay.
6701%
6702%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6703%
6704%    o windows: Specifies a pointer to a XWindows structure.
6705%
6706%    o state: key mask.
6707%
6708%    o key_symbol: Specifies a command to perform.
6709%
6710%    o image: the image;  XImageWIndowCommand may transform the image and
6711%      return a new image pointer.
6712%
6713%    o exception: return any errors or warnings in this structure.
6714%
6715*/
6716static CommandType XImageWindowCommand(Display *display,
6717  XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6718  KeySym key_symbol,Image **image,ExceptionInfo *exception)
6719{
6720  static char
6721    delta[MaxTextExtent] = "";
6722
6723  static const char
6724    Digits[] = "01234567890";
6725
6726  static KeySym
6727    last_symbol = XK_0;
6728
6729  if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6730    {
6731      if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6732        {
6733          *delta='\0';
6734          resource_info->quantum=1;
6735        }
6736      last_symbol=key_symbol;
6737      delta[strlen(delta)+1]='\0';
6738      delta[strlen(delta)]=Digits[key_symbol-XK_0];
6739      resource_info->quantum=StringToLong(delta);
6740      return(NullCommand);
6741    }
6742  last_symbol=key_symbol;
6743  if (resource_info->immutable)
6744    {
6745      /*
6746        Virtual image window has a restricted command set.
6747      */
6748      switch (key_symbol)
6749      {
6750        case XK_question:
6751          return(InfoCommand);
6752        case XK_p:
6753        case XK_Print:
6754          return(PrintCommand);
6755        case XK_space:
6756          return(NextCommand);
6757        case XK_q:
6758        case XK_Escape:
6759          return(QuitCommand);
6760        default:
6761          break;
6762      }
6763      return(NullCommand);
6764    }
6765  switch ((int) key_symbol)
6766  {
6767    case XK_o:
6768    {
6769      if ((state & ControlMask) == 0)
6770        break;
6771      return(OpenCommand);
6772    }
6773    case XK_space:
6774      return(NextCommand);
6775    case XK_BackSpace:
6776      return(FormerCommand);
6777    case XK_s:
6778    {
6779      if ((state & Mod1Mask) != 0)
6780        return(SwirlCommand);
6781      if ((state & ControlMask) == 0)
6782        return(ShearCommand);
6783      return(SaveCommand);
6784    }
6785    case XK_p:
6786    case XK_Print:
6787    {
6788      if ((state & Mod1Mask) != 0)
6789        return(OilPaintCommand);
6790      if ((state & Mod4Mask) != 0)
6791        return(ColorCommand);
6792      if ((state & ControlMask) == 0)
6793        return(NullCommand);
6794      return(PrintCommand);
6795    }
6796    case XK_d:
6797    {
6798      if ((state & Mod4Mask) != 0)
6799        return(DrawCommand);
6800      if ((state & ControlMask) == 0)
6801        return(NullCommand);
6802      return(DeleteCommand);
6803    }
6804    case XK_Select:
6805    {
6806      if ((state & ControlMask) == 0)
6807        return(NullCommand);
6808      return(SelectCommand);
6809    }
6810    case XK_n:
6811    {
6812      if ((state & ControlMask) == 0)
6813        return(NullCommand);
6814      return(NewCommand);
6815    }
6816    case XK_q:
6817    case XK_Escape:
6818      return(QuitCommand);
6819    case XK_z:
6820    case XK_Undo:
6821    {
6822      if ((state & ControlMask) == 0)
6823        return(NullCommand);
6824      return(UndoCommand);
6825    }
6826    case XK_r:
6827    case XK_Redo:
6828    {
6829      if ((state & ControlMask) == 0)
6830        return(RollCommand);
6831      return(RedoCommand);
6832    }
6833    case XK_x:
6834    {
6835      if ((state & ControlMask) == 0)
6836        return(NullCommand);
6837      return(CutCommand);
6838    }
6839    case XK_c:
6840    {
6841      if ((state & Mod1Mask) != 0)
6842        return(CharcoalDrawCommand);
6843      if ((state & ControlMask) == 0)
6844        return(CropCommand);
6845      return(CopyCommand);
6846    }
6847    case XK_v:
6848    case XK_Insert:
6849    {
6850      if ((state & Mod4Mask) != 0)
6851        return(CompositeCommand);
6852      if ((state & ControlMask) == 0)
6853        return(FlipCommand);
6854      return(PasteCommand);
6855    }
6856    case XK_less:
6857      return(HalfSizeCommand);
6858    case XK_minus:
6859      return(OriginalSizeCommand);
6860    case XK_greater:
6861      return(DoubleSizeCommand);
6862    case XK_percent:
6863      return(ResizeCommand);
6864    case XK_at:
6865      return(RefreshCommand);
6866    case XK_bracketleft:
6867      return(ChopCommand);
6868    case XK_h:
6869      return(FlopCommand);
6870    case XK_slash:
6871      return(RotateRightCommand);
6872    case XK_backslash:
6873      return(RotateLeftCommand);
6874    case XK_asterisk:
6875      return(RotateCommand);
6876    case XK_t:
6877      return(TrimCommand);
6878    case XK_H:
6879      return(HueCommand);
6880    case XK_S:
6881      return(SaturationCommand);
6882    case XK_L:
6883      return(BrightnessCommand);
6884    case XK_G:
6885      return(GammaCommand);
6886    case XK_C:
6887      return(SpiffCommand);
6888    case XK_Z:
6889      return(DullCommand);
6890    case XK_N:
6891      return(NormalizeCommand);
6892    case XK_equal:
6893      return(EqualizeCommand);
6894    case XK_asciitilde:
6895      return(NegateCommand);
6896    case XK_period:
6897      return(GrayscaleCommand);
6898    case XK_numbersign:
6899      return(QuantizeCommand);
6900    case XK_F2:
6901      return(DespeckleCommand);
6902    case XK_F3:
6903      return(EmbossCommand);
6904    case XK_F4:
6905      return(ReduceNoiseCommand);
6906    case XK_F5:
6907      return(AddNoiseCommand);
6908    case XK_F6:
6909      return(SharpenCommand);
6910    case XK_F7:
6911      return(BlurCommand);
6912    case XK_F8:
6913      return(ThresholdCommand);
6914    case XK_F9:
6915      return(EdgeDetectCommand);
6916    case XK_F10:
6917      return(SpreadCommand);
6918    case XK_F11:
6919      return(ShadeCommand);
6920    case XK_F12:
6921      return(RaiseCommand);
6922    case XK_F13:
6923      return(SegmentCommand);
6924    case XK_i:
6925    {
6926      if ((state & Mod1Mask) == 0)
6927        return(NullCommand);
6928      return(ImplodeCommand);
6929    }
6930    case XK_w:
6931    {
6932      if ((state & Mod1Mask) == 0)
6933        return(NullCommand);
6934      return(WaveCommand);
6935    }
6936    case XK_m:
6937    {
6938      if ((state & Mod4Mask) == 0)
6939        return(NullCommand);
6940      return(MatteCommand);
6941    }
6942    case XK_b:
6943    {
6944      if ((state & Mod4Mask) == 0)
6945        return(NullCommand);
6946      return(AddBorderCommand);
6947    }
6948    case XK_f:
6949    {
6950      if ((state & Mod4Mask) == 0)
6951        return(NullCommand);
6952      return(AddFrameCommand);
6953    }
6954    case XK_exclam:
6955    {
6956      if ((state & Mod4Mask) == 0)
6957        return(NullCommand);
6958      return(CommentCommand);
6959    }
6960    case XK_a:
6961    {
6962      if ((state & Mod1Mask) != 0)
6963        return(ApplyCommand);
6964      if ((state & Mod4Mask) != 0)
6965        return(AnnotateCommand);
6966      if ((state & ControlMask) == 0)
6967        return(NullCommand);
6968      return(RegionofInterestCommand);
6969    }
6970    case XK_question:
6971      return(InfoCommand);
6972    case XK_plus:
6973      return(ZoomCommand);
6974    case XK_P:
6975    {
6976      if ((state & ShiftMask) == 0)
6977        return(NullCommand);
6978      return(ShowPreviewCommand);
6979    }
6980    case XK_Execute:
6981      return(LaunchCommand);
6982    case XK_F1:
6983      return(HelpCommand);
6984    case XK_Find:
6985      return(BrowseDocumentationCommand);
6986    case XK_Menu:
6987    {
6988      (void) XMapRaised(display,windows->command.id);
6989      return(NullCommand);
6990    }
6991    case XK_Next:
6992    case XK_Prior:
6993    case XK_Home:
6994    case XK_KP_Home:
6995    {
6996      XTranslateImage(display,windows,*image,key_symbol);
6997      return(NullCommand);
6998    }
6999    case XK_Up:
7000    case XK_KP_Up:
7001    case XK_Down:
7002    case XK_KP_Down:
7003    case XK_Left:
7004    case XK_KP_Left:
7005    case XK_Right:
7006    case XK_KP_Right:
7007    {
7008      if ((state & Mod1Mask) != 0)
7009        {
7010          RectangleInfo
7011            crop_info;
7012
7013          /*
7014            Trim one pixel from edge of image.
7015          */
7016          crop_info.x=0;
7017          crop_info.y=0;
7018          crop_info.width=(size_t) windows->image.ximage->width;
7019          crop_info.height=(size_t) windows->image.ximage->height;
7020          if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
7021            {
7022              if (resource_info->quantum >= (int) crop_info.height)
7023                resource_info->quantum=(int) crop_info.height-1;
7024              crop_info.height-=resource_info->quantum;
7025            }
7026          if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
7027            {
7028              if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
7029                resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
7030              crop_info.y+=resource_info->quantum;
7031              crop_info.height-=resource_info->quantum;
7032            }
7033          if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
7034            {
7035              if (resource_info->quantum >= (int) crop_info.width)
7036                resource_info->quantum=(int) crop_info.width-1;
7037              crop_info.width-=resource_info->quantum;
7038            }
7039          if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
7040            {
7041              if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
7042                resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
7043              crop_info.x+=resource_info->quantum;
7044              crop_info.width-=resource_info->quantum;
7045            }
7046          if ((int) (windows->image.x+windows->image.width) >
7047              (int) crop_info.width)
7048            windows->image.x=(int) (crop_info.width-windows->image.width);
7049          if ((int) (windows->image.y+windows->image.height) >
7050              (int) crop_info.height)
7051            windows->image.y=(int) (crop_info.height-windows->image.height);
7052          XSetCropGeometry(display,windows,&crop_info,*image);
7053          windows->image.window_changes.width=(int) crop_info.width;
7054          windows->image.window_changes.height=(int) crop_info.height;
7055          (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
7056          (void) XConfigureImage(display,resource_info,windows,*image,
7057            exception);
7058          return(NullCommand);
7059        }
7060      XTranslateImage(display,windows,*image,key_symbol);
7061      return(NullCommand);
7062    }
7063    default:
7064      return(NullCommand);
7065  }
7066  return(NullCommand);
7067}
7068
7069/*
7070%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7071%                                                                             %
7072%                                                                             %
7073%                                                                             %
7074+   X M a g i c k C o m m a n d                                               %
7075%                                                                             %
7076%                                                                             %
7077%                                                                             %
7078%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7079%
7080%  XMagickCommand() makes a transform to the image or Image window as
7081%  specified by a user menu button or keyboard command.
7082%
7083%  The format of the XMagickCommand method is:
7084%
7085%      Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7086%        XWindows *windows,const CommandType command,Image **image,
7087%        ExceptionInfo *exception)
7088%
7089%  A description of each parameter follows:
7090%
7091%    o display: Specifies a connection to an X server; returned from
7092%      XOpenDisplay.
7093%
7094%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7095%
7096%    o windows: Specifies a pointer to a XWindows structure.
7097%
7098%    o command: Specifies a command to perform.
7099%
7100%    o image: the image;  XMagickCommand may transform the image and return a
7101%      new image pointer.
7102%
7103%    o exception: return any errors or warnings in this structure.
7104%
7105*/
7106static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7107  XWindows *windows,const CommandType command,Image **image,
7108  ExceptionInfo *exception)
7109{
7110  char
7111    filename[MaxTextExtent],
7112    geometry[MaxTextExtent],
7113    modulate_factors[MaxTextExtent];
7114
7115  GeometryInfo
7116    geometry_info;
7117
7118  Image
7119    *nexus;
7120
7121  ImageInfo
7122    *image_info;
7123
7124  int
7125    x,
7126    y;
7127
7128  MagickStatusType
7129    flags,
7130    status;
7131
7132  QuantizeInfo
7133    quantize_info;
7134
7135  RectangleInfo
7136    page_geometry;
7137
7138  register int
7139    i;
7140
7141  static char
7142    color[MaxTextExtent] = "gray";
7143
7144  unsigned int
7145    height,
7146    width;
7147
7148  /*
7149    Process user command.
7150  */
7151  XCheckRefreshWindows(display,windows);
7152  XImageCache(display,resource_info,windows,command,image,exception);
7153  nexus=NewImageList();
7154  windows->image.window_changes.width=windows->image.ximage->width;
7155  windows->image.window_changes.height=windows->image.ximage->height;
7156  image_info=CloneImageInfo(resource_info->image_info);
7157  SetGeometryInfo(&geometry_info);
7158  GetQuantizeInfo(&quantize_info);
7159  switch (command)
7160  {
7161    case OpenCommand:
7162    {
7163      /*
7164        Load image.
7165      */
7166      nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7167      break;
7168    }
7169    case NextCommand:
7170    {
7171      /*
7172        Display next image.
7173      */
7174      for (i=0; i < resource_info->quantum; i++)
7175        XClientMessage(display,windows->image.id,windows->im_protocols,
7176          windows->im_next_image,CurrentTime);
7177      break;
7178    }
7179    case FormerCommand:
7180    {
7181      /*
7182        Display former image.
7183      */
7184      for (i=0; i < resource_info->quantum; i++)
7185        XClientMessage(display,windows->image.id,windows->im_protocols,
7186          windows->im_former_image,CurrentTime);
7187      break;
7188    }
7189    case SelectCommand:
7190    {
7191      int
7192        status;
7193
7194      /*
7195        Select image.
7196      */
7197      status=chdir(resource_info->home_directory);
7198      if (status == -1)
7199        (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
7200          "UnableToOpenFile","%s",resource_info->home_directory);
7201      nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7202      break;
7203    }
7204    case SaveCommand:
7205    {
7206      /*
7207        Save image.
7208      */
7209      status=XSaveImage(display,resource_info,windows,*image,exception);
7210      if (status == MagickFalse)
7211        {
7212          XNoticeWidget(display,windows,"Unable to write X image:",
7213            (*image)->filename);
7214          break;
7215        }
7216      break;
7217    }
7218    case PrintCommand:
7219    {
7220      /*
7221        Print image.
7222      */
7223      status=XPrintImage(display,resource_info,windows,*image,exception);
7224      if (status == MagickFalse)
7225        {
7226          XNoticeWidget(display,windows,"Unable to print X image:",
7227            (*image)->filename);
7228          break;
7229        }
7230      break;
7231    }
7232    case DeleteCommand:
7233    {
7234      static char
7235        filename[MaxTextExtent] = "\0";
7236
7237      /*
7238        Delete image file.
7239      */
7240      XFileBrowserWidget(display,windows,"Delete",filename);
7241      if (*filename == '\0')
7242        break;
7243      status=remove(filename) != 0 ? MagickTrue : MagickFalse;
7244      if (status != MagickFalse)
7245        XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7246      break;
7247    }
7248    case NewCommand:
7249    {
7250      int
7251        status;
7252
7253      static char
7254        color[MaxTextExtent] = "gray",
7255        geometry[MaxTextExtent] = "640x480";
7256
7257      static const char
7258        *format = "gradient";
7259
7260      /*
7261        Query user for canvas geometry.
7262      */
7263      status=XDialogWidget(display,windows,"New","Enter image geometry:",
7264        geometry);
7265      if (*geometry == '\0')
7266        break;
7267      if (status == 0)
7268        format="xc";
7269      XColorBrowserWidget(display,windows,"Select",color);
7270      if (*color == '\0')
7271        break;
7272      /*
7273        Create canvas.
7274      */
7275      (void) FormatLocaleString(image_info->filename,MaxTextExtent,
7276        "%s:%s",format,color);
7277      (void) CloneString(&image_info->size,geometry);
7278      nexus=ReadImage(image_info,exception);
7279      CatchException(exception);
7280      XClientMessage(display,windows->image.id,windows->im_protocols,
7281        windows->im_next_image,CurrentTime);
7282      break;
7283    }
7284    case VisualDirectoryCommand:
7285    {
7286      /*
7287        Visual Image directory.
7288      */
7289      nexus=XVisualDirectoryImage(display,resource_info,windows,exception);
7290      break;
7291    }
7292    case QuitCommand:
7293    {
7294      /*
7295        exit program.
7296      */
7297      if (resource_info->confirm_exit == MagickFalse)
7298        XClientMessage(display,windows->image.id,windows->im_protocols,
7299          windows->im_exit,CurrentTime);
7300      else
7301        {
7302          int
7303            status;
7304
7305          /*
7306            Confirm program exit.
7307          */
7308          status=XConfirmWidget(display,windows,"Do you really want to exit",
7309            resource_info->client_name);
7310          if (status > 0)
7311            XClientMessage(display,windows->image.id,windows->im_protocols,
7312              windows->im_exit,CurrentTime);
7313        }
7314      break;
7315    }
7316    case CutCommand:
7317    {
7318      /*
7319        Cut image.
7320      */
7321      (void) XCropImage(display,resource_info,windows,*image,CutMode,exception);
7322      break;
7323    }
7324    case CopyCommand:
7325    {
7326      /*
7327        Copy image.
7328      */
7329      (void) XCropImage(display,resource_info,windows,*image,CopyMode,
7330        exception);
7331      break;
7332    }
7333    case PasteCommand:
7334    {
7335      /*
7336        Paste image.
7337      */
7338      status=XPasteImage(display,resource_info,windows,*image,exception);
7339      if (status == MagickFalse)
7340        {
7341          XNoticeWidget(display,windows,"Unable to paste X image",
7342            (*image)->filename);
7343          break;
7344        }
7345      break;
7346    }
7347    case HalfSizeCommand:
7348    {
7349      /*
7350        Half image size.
7351      */
7352      windows->image.window_changes.width=windows->image.ximage->width/2;
7353      windows->image.window_changes.height=windows->image.ximage->height/2;
7354      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7355      break;
7356    }
7357    case OriginalSizeCommand:
7358    {
7359      /*
7360        Original image size.
7361      */
7362      windows->image.window_changes.width=(int) (*image)->columns;
7363      windows->image.window_changes.height=(int) (*image)->rows;
7364      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7365      break;
7366    }
7367    case DoubleSizeCommand:
7368    {
7369      /*
7370        Double the image size.
7371      */
7372      windows->image.window_changes.width=windows->image.ximage->width << 1;
7373      windows->image.window_changes.height=windows->image.ximage->height << 1;
7374      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7375      break;
7376    }
7377    case ResizeCommand:
7378    {
7379      int
7380        status;
7381
7382      size_t
7383        height,
7384        width;
7385
7386      ssize_t
7387        x,
7388        y;
7389
7390      /*
7391        Resize image.
7392      */
7393      width=(size_t) windows->image.ximage->width;
7394      height=(size_t) windows->image.ximage->height;
7395      x=0;
7396      y=0;
7397      (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g+0+0",
7398        (double) width,(double) height);
7399      status=XDialogWidget(display,windows,"Resize",
7400        "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7401      if (*geometry == '\0')
7402        break;
7403      if (status == 0)
7404        (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7405      (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7406      windows->image.window_changes.width=(int) width;
7407      windows->image.window_changes.height=(int) height;
7408      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7409      break;
7410    }
7411    case ApplyCommand:
7412    {
7413      char
7414        image_geometry[MaxTextExtent];
7415
7416      if ((windows->image.crop_geometry == (char *) NULL) &&
7417          ((int) (*image)->columns == windows->image.ximage->width) &&
7418          ((int) (*image)->rows == windows->image.ximage->height))
7419        break;
7420      /*
7421        Apply size transforms to image.
7422      */
7423      XSetCursorState(display,windows,MagickTrue);
7424      XCheckRefreshWindows(display,windows);
7425      /*
7426        Crop and/or scale displayed image.
7427      */
7428      (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
7429        windows->image.ximage->width,windows->image.ximage->height);
7430      (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
7431      if (windows->image.crop_geometry != (char *) NULL)
7432        windows->image.crop_geometry=(char *)
7433          RelinquishMagickMemory(windows->image.crop_geometry);
7434      windows->image.x=0;
7435      windows->image.y=0;
7436      XConfigureImageColormap(display,resource_info,windows,*image);
7437      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7438      break;
7439    }
7440    case RefreshCommand:
7441    {
7442      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7443      break;
7444    }
7445    case RestoreCommand:
7446    {
7447      /*
7448        Restore Image window to its original size.
7449      */
7450      if ((windows->image.width == (unsigned int) (*image)->columns) &&
7451          (windows->image.height == (unsigned int) (*image)->rows) &&
7452          (windows->image.crop_geometry == (char *) NULL))
7453        {
7454          (void) XBell(display,0);
7455          break;
7456        }
7457      windows->image.window_changes.width=(int) (*image)->columns;
7458      windows->image.window_changes.height=(int) (*image)->rows;
7459      if (windows->image.crop_geometry != (char *) NULL)
7460        {
7461          windows->image.crop_geometry=(char *)
7462            RelinquishMagickMemory(windows->image.crop_geometry);
7463          windows->image.crop_geometry=(char *) NULL;
7464          windows->image.x=0;
7465          windows->image.y=0;
7466        }
7467      XConfigureImageColormap(display,resource_info,windows,*image);
7468      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7469      break;
7470    }
7471    case CropCommand:
7472    {
7473      /*
7474        Crop image.
7475      */
7476      (void) XCropImage(display,resource_info,windows,*image,CropMode,
7477        exception);
7478      break;
7479    }
7480    case ChopCommand:
7481    {
7482      /*
7483        Chop image.
7484      */
7485      status=XChopImage(display,resource_info,windows,image,exception);
7486      if (status == MagickFalse)
7487        {
7488          XNoticeWidget(display,windows,"Unable to cut X image",
7489            (*image)->filename);
7490          break;
7491        }
7492      break;
7493    }
7494    case FlopCommand:
7495    {
7496      Image
7497        *flop_image;
7498
7499      /*
7500        Flop image scanlines.
7501      */
7502      XSetCursorState(display,windows,MagickTrue);
7503      XCheckRefreshWindows(display,windows);
7504      flop_image=FlopImage(*image,exception);
7505      if (flop_image != (Image *) NULL)
7506        {
7507          *image=DestroyImage(*image);
7508          *image=flop_image;
7509        }
7510      CatchException(exception);
7511      XSetCursorState(display,windows,MagickFalse);
7512      if (windows->image.crop_geometry != (char *) NULL)
7513        {
7514          /*
7515            Flop crop geometry.
7516          */
7517          width=(unsigned int) (*image)->columns;
7518          height=(unsigned int) (*image)->rows;
7519          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7520            &width,&height);
7521          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7522            "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7523        }
7524      if (windows->image.orphan != MagickFalse)
7525        break;
7526      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7527      break;
7528    }
7529    case FlipCommand:
7530    {
7531      Image
7532        *flip_image;
7533
7534      /*
7535        Flip image scanlines.
7536      */
7537      XSetCursorState(display,windows,MagickTrue);
7538      XCheckRefreshWindows(display,windows);
7539      flip_image=FlipImage(*image,exception);
7540      if (flip_image != (Image *) NULL)
7541        {
7542          *image=DestroyImage(*image);
7543          *image=flip_image;
7544        }
7545      CatchException(exception);
7546      XSetCursorState(display,windows,MagickFalse);
7547      if (windows->image.crop_geometry != (char *) NULL)
7548        {
7549          /*
7550            Flip crop geometry.
7551          */
7552          width=(unsigned int) (*image)->columns;
7553          height=(unsigned int) (*image)->rows;
7554          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7555            &width,&height);
7556          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7557            "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7558        }
7559      if (windows->image.orphan != MagickFalse)
7560        break;
7561      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7562      break;
7563    }
7564    case RotateRightCommand:
7565    {
7566      /*
7567        Rotate image 90 degrees clockwise.
7568      */
7569      status=XRotateImage(display,resource_info,windows,90.0,image,exception);
7570      if (status == MagickFalse)
7571        {
7572          XNoticeWidget(display,windows,"Unable to rotate X image",
7573            (*image)->filename);
7574          break;
7575        }
7576      break;
7577    }
7578    case RotateLeftCommand:
7579    {
7580      /*
7581        Rotate image 90 degrees counter-clockwise.
7582      */
7583      status=XRotateImage(display,resource_info,windows,-90.0,image,exception);
7584      if (status == MagickFalse)
7585        {
7586          XNoticeWidget(display,windows,"Unable to rotate X image",
7587            (*image)->filename);
7588          break;
7589        }
7590      break;
7591    }
7592    case RotateCommand:
7593    {
7594      /*
7595        Rotate image.
7596      */
7597      status=XRotateImage(display,resource_info,windows,0.0,image,exception);
7598      if (status == MagickFalse)
7599        {
7600          XNoticeWidget(display,windows,"Unable to rotate X image",
7601            (*image)->filename);
7602          break;
7603        }
7604      break;
7605    }
7606    case ShearCommand:
7607    {
7608      Image
7609        *shear_image;
7610
7611      static char
7612        geometry[MaxTextExtent] = "45.0x45.0";
7613
7614      /*
7615        Query user for shear color and geometry.
7616      */
7617      XColorBrowserWidget(display,windows,"Select",color);
7618      if (*color == '\0')
7619        break;
7620      (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7621        geometry);
7622      if (*geometry == '\0')
7623        break;
7624      /*
7625        Shear image.
7626      */
7627      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7628        exception);
7629      XSetCursorState(display,windows,MagickTrue);
7630      XCheckRefreshWindows(display,windows);
7631      (void) QueryColorDatabase(color,&(*image)->background_color,
7632        exception);
7633      flags=ParseGeometry(geometry,&geometry_info);
7634      if ((flags & SigmaValue) == 0)
7635        geometry_info.sigma=geometry_info.rho;
7636      shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7637        exception);
7638      if (shear_image != (Image *) NULL)
7639        {
7640          *image=DestroyImage(*image);
7641          *image=shear_image;
7642        }
7643      CatchException(exception);
7644      XSetCursorState(display,windows,MagickFalse);
7645      if (windows->image.orphan != MagickFalse)
7646        break;
7647      windows->image.window_changes.width=(int) (*image)->columns;
7648      windows->image.window_changes.height=(int) (*image)->rows;
7649      XConfigureImageColormap(display,resource_info,windows,*image);
7650      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7651      break;
7652    }
7653    case RollCommand:
7654    {
7655      Image
7656        *roll_image;
7657
7658      static char
7659        geometry[MaxTextExtent] = "+2+2";
7660
7661      /*
7662        Query user for the roll geometry.
7663      */
7664      (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7665        geometry);
7666      if (*geometry == '\0')
7667        break;
7668      /*
7669        Roll image.
7670      */
7671      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7672        exception);
7673      XSetCursorState(display,windows,MagickTrue);
7674      XCheckRefreshWindows(display,windows);
7675      (void) ParsePageGeometry(*image,geometry,&page_geometry,
7676        exception);
7677      roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7678        exception);
7679      if (roll_image != (Image *) NULL)
7680        {
7681          *image=DestroyImage(*image);
7682          *image=roll_image;
7683        }
7684      CatchException(exception);
7685      XSetCursorState(display,windows,MagickFalse);
7686      if (windows->image.orphan != MagickFalse)
7687        break;
7688      windows->image.window_changes.width=(int) (*image)->columns;
7689      windows->image.window_changes.height=(int) (*image)->rows;
7690      XConfigureImageColormap(display,resource_info,windows,*image);
7691      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7692      break;
7693    }
7694    case TrimCommand:
7695    {
7696      static char
7697        fuzz[MaxTextExtent];
7698
7699      /*
7700        Query user for the fuzz factor.
7701      */
7702      (void) FormatLocaleString(fuzz,MaxTextExtent,"%g%%",100.0*
7703        (*image)->fuzz/(QuantumRange+1.0));
7704      (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7705      if (*fuzz == '\0')
7706        break;
7707      (*image)->fuzz=SiPrefixToDouble(fuzz,(double) QuantumRange+1.0);
7708      /*
7709        Trim image.
7710      */
7711      status=XTrimImage(display,resource_info,windows,*image,exception);
7712      if (status == MagickFalse)
7713        {
7714          XNoticeWidget(display,windows,"Unable to trim X image",
7715            (*image)->filename);
7716          break;
7717        }
7718      break;
7719    }
7720    case HueCommand:
7721    {
7722      static char
7723        hue_percent[MaxTextExtent] = "110";
7724
7725      /*
7726        Query user for percent hue change.
7727      */
7728      (void) XDialogWidget(display,windows,"Apply",
7729        "Enter percent change in image hue (0-200):",hue_percent);
7730      if (*hue_percent == '\0')
7731        break;
7732      /*
7733        Vary the image hue.
7734      */
7735      XSetCursorState(display,windows,MagickTrue);
7736      XCheckRefreshWindows(display,windows);
7737      (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7738      (void) ConcatenateMagickString(modulate_factors,hue_percent,
7739        MaxTextExtent);
7740      (void) ModulateImage(*image,modulate_factors,exception);
7741      XSetCursorState(display,windows,MagickFalse);
7742      if (windows->image.orphan != MagickFalse)
7743        break;
7744      XConfigureImageColormap(display,resource_info,windows,*image);
7745      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7746      break;
7747    }
7748    case SaturationCommand:
7749    {
7750      static char
7751        saturation_percent[MaxTextExtent] = "110";
7752
7753      /*
7754        Query user for percent saturation change.
7755      */
7756      (void) XDialogWidget(display,windows,"Apply",
7757        "Enter percent change in color saturation (0-200):",saturation_percent);
7758      if (*saturation_percent == '\0')
7759        break;
7760      /*
7761        Vary color saturation.
7762      */
7763      XSetCursorState(display,windows,MagickTrue);
7764      XCheckRefreshWindows(display,windows);
7765      (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7766      (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7767        MaxTextExtent);
7768      (void) ModulateImage(*image,modulate_factors,exception);
7769      XSetCursorState(display,windows,MagickFalse);
7770      if (windows->image.orphan != MagickFalse)
7771        break;
7772      XConfigureImageColormap(display,resource_info,windows,*image);
7773      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7774      break;
7775    }
7776    case BrightnessCommand:
7777    {
7778      static char
7779        brightness_percent[MaxTextExtent] = "110";
7780
7781      /*
7782        Query user for percent brightness change.
7783      */
7784      (void) XDialogWidget(display,windows,"Apply",
7785        "Enter percent change in color brightness (0-200):",brightness_percent);
7786      if (*brightness_percent == '\0')
7787        break;
7788      /*
7789        Vary the color brightness.
7790      */
7791      XSetCursorState(display,windows,MagickTrue);
7792      XCheckRefreshWindows(display,windows);
7793      (void) CopyMagickString(modulate_factors,brightness_percent,
7794        MaxTextExtent);
7795      (void) ModulateImage(*image,modulate_factors,exception);
7796      XSetCursorState(display,windows,MagickFalse);
7797      if (windows->image.orphan != MagickFalse)
7798        break;
7799      XConfigureImageColormap(display,resource_info,windows,*image);
7800      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7801      break;
7802    }
7803    case GammaCommand:
7804    {
7805      static char
7806        factor[MaxTextExtent] = "1.6";
7807
7808      /*
7809        Query user for gamma value.
7810      */
7811      (void) XDialogWidget(display,windows,"Gamma",
7812        "Enter gamma value (e.g. 1.2):",factor);
7813      if (*factor == '\0')
7814        break;
7815      /*
7816        Gamma correct image.
7817      */
7818      XSetCursorState(display,windows,MagickTrue);
7819      XCheckRefreshWindows(display,windows);
7820      (void) GammaImage(*image,atof(factor),exception);
7821      XSetCursorState(display,windows,MagickFalse);
7822      if (windows->image.orphan != MagickFalse)
7823        break;
7824      XConfigureImageColormap(display,resource_info,windows,*image);
7825      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7826      break;
7827    }
7828    case SpiffCommand:
7829    {
7830      /*
7831        Sharpen the image contrast.
7832      */
7833      XSetCursorState(display,windows,MagickTrue);
7834      XCheckRefreshWindows(display,windows);
7835      (void) ContrastImage(*image,MagickTrue,exception);
7836      XSetCursorState(display,windows,MagickFalse);
7837      if (windows->image.orphan != MagickFalse)
7838        break;
7839      XConfigureImageColormap(display,resource_info,windows,*image);
7840      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7841      break;
7842    }
7843    case DullCommand:
7844    {
7845      /*
7846        Dull the image contrast.
7847      */
7848      XSetCursorState(display,windows,MagickTrue);
7849      XCheckRefreshWindows(display,windows);
7850      (void) ContrastImage(*image,MagickFalse,exception);
7851      XSetCursorState(display,windows,MagickFalse);
7852      if (windows->image.orphan != MagickFalse)
7853        break;
7854      XConfigureImageColormap(display,resource_info,windows,*image);
7855      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7856      break;
7857    }
7858    case ContrastStretchCommand:
7859    {
7860      double
7861        black_point,
7862        white_point;
7863
7864      static char
7865        levels[MaxTextExtent] = "1%";
7866
7867      /*
7868        Query user for gamma value.
7869      */
7870      (void) XDialogWidget(display,windows,"Contrast Stretch",
7871        "Enter black and white points:",levels);
7872      if (*levels == '\0')
7873        break;
7874      /*
7875        Contrast stretch image.
7876      */
7877      XSetCursorState(display,windows,MagickTrue);
7878      XCheckRefreshWindows(display,windows);
7879      flags=ParseGeometry(levels,&geometry_info);
7880      black_point=geometry_info.rho;
7881      white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7882      if ((flags & PercentValue) != 0)
7883        {
7884          black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7885          white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7886        }
7887      white_point=(MagickRealType) (*image)->columns*(*image)->rows-white_point;
7888      (void) ContrastStretchImage(*image,black_point,white_point,
7889        exception);
7890      XSetCursorState(display,windows,MagickFalse);
7891      if (windows->image.orphan != MagickFalse)
7892        break;
7893      XConfigureImageColormap(display,resource_info,windows,*image);
7894      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7895      break;
7896    }
7897    case SigmoidalContrastCommand:
7898    {
7899      GeometryInfo
7900        geometry_info;
7901
7902      MagickStatusType
7903        flags;
7904
7905      static char
7906        levels[MaxTextExtent] = "3x50%";
7907
7908      /*
7909        Query user for gamma value.
7910      */
7911      (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7912        "Enter contrast and midpoint:",levels);
7913      if (*levels == '\0')
7914        break;
7915      /*
7916        Contrast stretch image.
7917      */
7918      XSetCursorState(display,windows,MagickTrue);
7919      XCheckRefreshWindows(display,windows);
7920      flags=ParseGeometry(levels,&geometry_info);
7921      if ((flags & SigmaValue) == 0)
7922        geometry_info.sigma=1.0*QuantumRange/2.0;
7923      if ((flags & PercentValue) != 0)
7924        geometry_info.sigma=1.0*QuantumRange*geometry_info.sigma/100.0;
7925      (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
7926        geometry_info.sigma,exception);
7927      XSetCursorState(display,windows,MagickFalse);
7928      if (windows->image.orphan != MagickFalse)
7929        break;
7930      XConfigureImageColormap(display,resource_info,windows,*image);
7931      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7932      break;
7933    }
7934    case NormalizeCommand:
7935    {
7936      /*
7937        Perform histogram normalization on the image.
7938      */
7939      XSetCursorState(display,windows,MagickTrue);
7940      XCheckRefreshWindows(display,windows);
7941      (void) NormalizeImage(*image,exception);
7942      XSetCursorState(display,windows,MagickFalse);
7943      if (windows->image.orphan != MagickFalse)
7944        break;
7945      XConfigureImageColormap(display,resource_info,windows,*image);
7946      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7947      break;
7948    }
7949    case EqualizeCommand:
7950    {
7951      /*
7952        Perform histogram equalization on the image.
7953      */
7954      XSetCursorState(display,windows,MagickTrue);
7955      XCheckRefreshWindows(display,windows);
7956      (void) EqualizeImage(*image,exception);
7957      XSetCursorState(display,windows,MagickFalse);
7958      if (windows->image.orphan != MagickFalse)
7959        break;
7960      XConfigureImageColormap(display,resource_info,windows,*image);
7961      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7962      break;
7963    }
7964    case NegateCommand:
7965    {
7966      /*
7967        Negate colors in image.
7968      */
7969      XSetCursorState(display,windows,MagickTrue);
7970      XCheckRefreshWindows(display,windows);
7971      (void) NegateImage(*image,MagickFalse,exception);
7972      XSetCursorState(display,windows,MagickFalse);
7973      if (windows->image.orphan != MagickFalse)
7974        break;
7975      XConfigureImageColormap(display,resource_info,windows,*image);
7976      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7977      break;
7978    }
7979    case GrayscaleCommand:
7980    {
7981      /*
7982        Convert image to grayscale.
7983      */
7984      XSetCursorState(display,windows,MagickTrue);
7985      XCheckRefreshWindows(display,windows);
7986      (void) SetImageType(*image,(*image)->matte == MagickFalse ?
7987        GrayscaleType : GrayscaleMatteType,exception);
7988      XSetCursorState(display,windows,MagickFalse);
7989      if (windows->image.orphan != MagickFalse)
7990        break;
7991      XConfigureImageColormap(display,resource_info,windows,*image);
7992      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7993      break;
7994    }
7995    case MapCommand:
7996    {
7997      Image
7998        *affinity_image;
7999
8000      static char
8001        filename[MaxTextExtent] = "\0";
8002
8003      /*
8004        Request image file name from user.
8005      */
8006      XFileBrowserWidget(display,windows,"Map",filename);
8007      if (*filename == '\0')
8008        break;
8009      /*
8010        Map image.
8011      */
8012      XSetCursorState(display,windows,MagickTrue);
8013      XCheckRefreshWindows(display,windows);
8014      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
8015      affinity_image=ReadImage(image_info,exception);
8016      if (affinity_image != (Image *) NULL)
8017        {
8018          (void) RemapImage(&quantize_info,*image,affinity_image,exception);
8019          affinity_image=DestroyImage(affinity_image);
8020        }
8021      CatchException(exception);
8022      XSetCursorState(display,windows,MagickFalse);
8023      if (windows->image.orphan != MagickFalse)
8024        break;
8025      XConfigureImageColormap(display,resource_info,windows,*image);
8026      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8027      break;
8028    }
8029    case QuantizeCommand:
8030    {
8031      int
8032        status;
8033
8034      static char
8035        colors[MaxTextExtent] = "256";
8036
8037      /*
8038        Query user for maximum number of colors.
8039      */
8040      status=XDialogWidget(display,windows,"Quantize",
8041        "Maximum number of colors:",colors);
8042      if (*colors == '\0')
8043        break;
8044      /*
8045        Color reduce the image.
8046      */
8047      XSetCursorState(display,windows,MagickTrue);
8048      XCheckRefreshWindows(display,windows);
8049      quantize_info.number_colors=StringToUnsignedLong(colors);
8050      quantize_info.dither=status != 0 ? MagickTrue : MagickFalse;
8051      (void) QuantizeImage(&quantize_info,*image,exception);
8052      XSetCursorState(display,windows,MagickFalse);
8053      if (windows->image.orphan != MagickFalse)
8054        break;
8055      XConfigureImageColormap(display,resource_info,windows,*image);
8056      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8057      break;
8058    }
8059    case DespeckleCommand:
8060    {
8061      Image
8062        *despeckle_image;
8063
8064      /*
8065        Despeckle image.
8066      */
8067      XSetCursorState(display,windows,MagickTrue);
8068      XCheckRefreshWindows(display,windows);
8069      despeckle_image=DespeckleImage(*image,exception);
8070      if (despeckle_image != (Image *) NULL)
8071        {
8072          *image=DestroyImage(*image);
8073          *image=despeckle_image;
8074        }
8075      CatchException(exception);
8076      XSetCursorState(display,windows,MagickFalse);
8077      if (windows->image.orphan != MagickFalse)
8078        break;
8079      XConfigureImageColormap(display,resource_info,windows,*image);
8080      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8081      break;
8082    }
8083    case EmbossCommand:
8084    {
8085      Image
8086        *emboss_image;
8087
8088      static char
8089        radius[MaxTextExtent] = "0.0x1.0";
8090
8091      /*
8092        Query user for emboss radius.
8093      */
8094      (void) XDialogWidget(display,windows,"Emboss",
8095        "Enter the emboss radius and standard deviation:",radius);
8096      if (*radius == '\0')
8097        break;
8098      /*
8099        Reduce noise in the image.
8100      */
8101      XSetCursorState(display,windows,MagickTrue);
8102      XCheckRefreshWindows(display,windows);
8103      flags=ParseGeometry(radius,&geometry_info);
8104      if ((flags & SigmaValue) == 0)
8105        geometry_info.sigma=1.0;
8106      emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8107        exception);
8108      if (emboss_image != (Image *) NULL)
8109        {
8110          *image=DestroyImage(*image);
8111          *image=emboss_image;
8112        }
8113      CatchException(exception);
8114      XSetCursorState(display,windows,MagickFalse);
8115      if (windows->image.orphan != MagickFalse)
8116        break;
8117      XConfigureImageColormap(display,resource_info,windows,*image);
8118      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8119      break;
8120    }
8121    case ReduceNoiseCommand:
8122    {
8123      Image
8124        *noise_image;
8125
8126      static char
8127        radius[MaxTextExtent] = "0";
8128
8129      /*
8130        Query user for noise radius.
8131      */
8132      (void) XDialogWidget(display,windows,"Reduce Noise",
8133        "Enter the noise radius:",radius);
8134      if (*radius == '\0')
8135        break;
8136      /*
8137        Reduce noise in the image.
8138      */
8139      XSetCursorState(display,windows,MagickTrue);
8140      XCheckRefreshWindows(display,windows);
8141      flags=ParseGeometry(radius,&geometry_info);
8142      noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8143        geometry_info.rho,(size_t) geometry_info.rho,exception);
8144      if (noise_image != (Image *) NULL)
8145        {
8146          *image=DestroyImage(*image);
8147          *image=noise_image;
8148        }
8149      CatchException(exception);
8150      XSetCursorState(display,windows,MagickFalse);
8151      if (windows->image.orphan != MagickFalse)
8152        break;
8153      XConfigureImageColormap(display,resource_info,windows,*image);
8154      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8155      break;
8156    }
8157    case AddNoiseCommand:
8158    {
8159      char
8160        **noises;
8161
8162      Image
8163        *noise_image;
8164
8165      static char
8166        noise_type[MaxTextExtent] = "Gaussian";
8167
8168      /*
8169        Add noise to the image.
8170      */
8171      noises=GetCommandOptions(MagickNoiseOptions);
8172      if (noises == (char **) NULL)
8173        break;
8174      XListBrowserWidget(display,windows,&windows->widget,
8175        (const char **) noises,"Add Noise",
8176        "Select a type of noise to add to your image:",noise_type);
8177      noises=DestroyStringList(noises);
8178      if (*noise_type == '\0')
8179        break;
8180      XSetCursorState(display,windows,MagickTrue);
8181      XCheckRefreshWindows(display,windows);
8182      noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8183        MagickNoiseOptions,MagickFalse,noise_type),exception);
8184      if (noise_image != (Image *) NULL)
8185        {
8186          *image=DestroyImage(*image);
8187          *image=noise_image;
8188        }
8189      CatchException(exception);
8190      XSetCursorState(display,windows,MagickFalse);
8191      if (windows->image.orphan != MagickFalse)
8192        break;
8193      XConfigureImageColormap(display,resource_info,windows,*image);
8194      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8195      break;
8196    }
8197    case SharpenCommand:
8198    {
8199      Image
8200        *sharp_image;
8201
8202      static char
8203        radius[MaxTextExtent] = "0.0x1.0";
8204
8205      /*
8206        Query user for sharpen radius.
8207      */
8208      (void) XDialogWidget(display,windows,"Sharpen",
8209        "Enter the sharpen radius and standard deviation:",radius);
8210      if (*radius == '\0')
8211        break;
8212      /*
8213        Sharpen image scanlines.
8214      */
8215      XSetCursorState(display,windows,MagickTrue);
8216      XCheckRefreshWindows(display,windows);
8217      flags=ParseGeometry(radius,&geometry_info);
8218      sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8219        geometry_info.xi,exception);
8220      if (sharp_image != (Image *) NULL)
8221        {
8222          *image=DestroyImage(*image);
8223          *image=sharp_image;
8224        }
8225      CatchException(exception);
8226      XSetCursorState(display,windows,MagickFalse);
8227      if (windows->image.orphan != MagickFalse)
8228        break;
8229      XConfigureImageColormap(display,resource_info,windows,*image);
8230      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8231      break;
8232    }
8233    case BlurCommand:
8234    {
8235      Image
8236        *blur_image;
8237
8238      static char
8239        radius[MaxTextExtent] = "0.0x1.0";
8240
8241      /*
8242        Query user for blur radius.
8243      */
8244      (void) XDialogWidget(display,windows,"Blur",
8245        "Enter the blur radius and standard deviation:",radius);
8246      if (*radius == '\0')
8247        break;
8248      /*
8249        Blur an image.
8250      */
8251      XSetCursorState(display,windows,MagickTrue);
8252      XCheckRefreshWindows(display,windows);
8253      flags=ParseGeometry(radius,&geometry_info);
8254      blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8255        geometry_info.xi,exception);
8256      if (blur_image != (Image *) NULL)
8257        {
8258          *image=DestroyImage(*image);
8259          *image=blur_image;
8260        }
8261      CatchException(exception);
8262      XSetCursorState(display,windows,MagickFalse);
8263      if (windows->image.orphan != MagickFalse)
8264        break;
8265      XConfigureImageColormap(display,resource_info,windows,*image);
8266      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8267      break;
8268    }
8269    case ThresholdCommand:
8270    {
8271      double
8272        threshold;
8273
8274      static char
8275        factor[MaxTextExtent] = "128";
8276
8277      /*
8278        Query user for threshold value.
8279      */
8280      (void) XDialogWidget(display,windows,"Threshold",
8281        "Enter threshold value:",factor);
8282      if (*factor == '\0')
8283        break;
8284      /*
8285        Gamma correct image.
8286      */
8287      XSetCursorState(display,windows,MagickTrue);
8288      XCheckRefreshWindows(display,windows);
8289      threshold=SiPrefixToDouble(factor,QuantumRange);
8290      (void) BilevelImage(*image,threshold);
8291      XSetCursorState(display,windows,MagickFalse);
8292      if (windows->image.orphan != MagickFalse)
8293        break;
8294      XConfigureImageColormap(display,resource_info,windows,*image);
8295      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8296      break;
8297    }
8298    case EdgeDetectCommand:
8299    {
8300      Image
8301        *edge_image;
8302
8303      static char
8304        radius[MaxTextExtent] = "0";
8305
8306      /*
8307        Query user for edge factor.
8308      */
8309      (void) XDialogWidget(display,windows,"Detect Edges",
8310        "Enter the edge detect radius:",radius);
8311      if (*radius == '\0')
8312        break;
8313      /*
8314        Detect edge in image.
8315      */
8316      XSetCursorState(display,windows,MagickTrue);
8317      XCheckRefreshWindows(display,windows);
8318      flags=ParseGeometry(radius,&geometry_info);
8319      edge_image=EdgeImage(*image,geometry_info.rho,geometry_info.sigma,
8320        exception);
8321      if (edge_image != (Image *) NULL)
8322        {
8323          *image=DestroyImage(*image);
8324          *image=edge_image;
8325        }
8326      CatchException(exception);
8327      XSetCursorState(display,windows,MagickFalse);
8328      if (windows->image.orphan != MagickFalse)
8329        break;
8330      XConfigureImageColormap(display,resource_info,windows,*image);
8331      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8332      break;
8333    }
8334    case SpreadCommand:
8335    {
8336      Image
8337        *spread_image;
8338
8339      static char
8340        amount[MaxTextExtent] = "2";
8341
8342      /*
8343        Query user for spread amount.
8344      */
8345      (void) XDialogWidget(display,windows,"Spread",
8346        "Enter the displacement amount:",amount);
8347      if (*amount == '\0')
8348        break;
8349      /*
8350        Displace image pixels by a random amount.
8351      */
8352      XSetCursorState(display,windows,MagickTrue);
8353      XCheckRefreshWindows(display,windows);
8354      flags=ParseGeometry(amount,&geometry_info);
8355      spread_image=EdgeImage(*image,geometry_info.rho,geometry_info.sigma,
8356        exception);
8357      if (spread_image != (Image *) NULL)
8358        {
8359          *image=DestroyImage(*image);
8360          *image=spread_image;
8361        }
8362      CatchException(exception);
8363      XSetCursorState(display,windows,MagickFalse);
8364      if (windows->image.orphan != MagickFalse)
8365        break;
8366      XConfigureImageColormap(display,resource_info,windows,*image);
8367      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8368      break;
8369    }
8370    case ShadeCommand:
8371    {
8372      Image
8373        *shade_image;
8374
8375      int
8376        status;
8377
8378      static char
8379        geometry[MaxTextExtent] = "30x30";
8380
8381      /*
8382        Query user for the shade geometry.
8383      */
8384      status=XDialogWidget(display,windows,"Shade",
8385        "Enter the azimuth and elevation of the light source:",geometry);
8386      if (*geometry == '\0')
8387        break;
8388      /*
8389        Shade image pixels.
8390      */
8391      XSetCursorState(display,windows,MagickTrue);
8392      XCheckRefreshWindows(display,windows);
8393      flags=ParseGeometry(geometry,&geometry_info);
8394      if ((flags & SigmaValue) == 0)
8395        geometry_info.sigma=1.0;
8396      shade_image=ShadeImage(*image,status != 0 ? MagickFalse : MagickTrue,
8397        geometry_info.rho,geometry_info.sigma,exception);
8398      if (shade_image != (Image *) NULL)
8399        {
8400          *image=DestroyImage(*image);
8401          *image=shade_image;
8402        }
8403      CatchException(exception);
8404      XSetCursorState(display,windows,MagickFalse);
8405      if (windows->image.orphan != MagickFalse)
8406        break;
8407      XConfigureImageColormap(display,resource_info,windows,*image);
8408      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8409      break;
8410    }
8411    case RaiseCommand:
8412    {
8413      static char
8414        bevel_width[MaxTextExtent] = "10";
8415
8416      /*
8417        Query user for bevel width.
8418      */
8419      (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8420      if (*bevel_width == '\0')
8421        break;
8422      /*
8423        Raise an image.
8424      */
8425      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8426        exception);
8427      XSetCursorState(display,windows,MagickTrue);
8428      XCheckRefreshWindows(display,windows);
8429      (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8430        exception);
8431      (void) RaiseImage(*image,&page_geometry,MagickTrue,exception);
8432      XSetCursorState(display,windows,MagickFalse);
8433      if (windows->image.orphan != MagickFalse)
8434        break;
8435      XConfigureImageColormap(display,resource_info,windows,*image);
8436      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8437      break;
8438    }
8439    case SegmentCommand:
8440    {
8441      static char
8442        threshold[MaxTextExtent] = "1.0x1.5";
8443
8444      /*
8445        Query user for smoothing threshold.
8446      */
8447      (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8448        threshold);
8449      if (*threshold == '\0')
8450        break;
8451      /*
8452        Segment an image.
8453      */
8454      XSetCursorState(display,windows,MagickTrue);
8455      XCheckRefreshWindows(display,windows);
8456      flags=ParseGeometry(threshold,&geometry_info);
8457      if ((flags & SigmaValue) == 0)
8458        geometry_info.sigma=1.0;
8459      (void) SegmentImage(*image,RGBColorspace,MagickFalse,geometry_info.rho,
8460        geometry_info.sigma,exception);
8461      XSetCursorState(display,windows,MagickFalse);
8462      if (windows->image.orphan != MagickFalse)
8463        break;
8464      XConfigureImageColormap(display,resource_info,windows,*image);
8465      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8466      break;
8467    }
8468    case SepiaToneCommand:
8469    {
8470      double
8471        threshold;
8472
8473      Image
8474        *sepia_image;
8475
8476      static char
8477        factor[MaxTextExtent] = "80%";
8478
8479      /*
8480        Query user for sepia-tone factor.
8481      */
8482      (void) XDialogWidget(display,windows,"Sepia Tone",
8483        "Enter the sepia tone factor (0 - 99.9%):",factor);
8484      if (*factor == '\0')
8485        break;
8486      /*
8487        Sepia tone image pixels.
8488      */
8489      XSetCursorState(display,windows,MagickTrue);
8490      XCheckRefreshWindows(display,windows);
8491      threshold=SiPrefixToDouble(factor,QuantumRange);
8492      sepia_image=SepiaToneImage(*image,threshold,exception);
8493      if (sepia_image != (Image *) NULL)
8494        {
8495          *image=DestroyImage(*image);
8496          *image=sepia_image;
8497        }
8498      CatchException(exception);
8499      XSetCursorState(display,windows,MagickFalse);
8500      if (windows->image.orphan != MagickFalse)
8501        break;
8502      XConfigureImageColormap(display,resource_info,windows,*image);
8503      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8504      break;
8505    }
8506    case SolarizeCommand:
8507    {
8508      double
8509        threshold;
8510
8511      static char
8512        factor[MaxTextExtent] = "60%";
8513
8514      /*
8515        Query user for solarize factor.
8516      */
8517      (void) XDialogWidget(display,windows,"Solarize",
8518        "Enter the solarize factor (0 - 99.9%):",factor);
8519      if (*factor == '\0')
8520        break;
8521      /*
8522        Solarize image pixels.
8523      */
8524      XSetCursorState(display,windows,MagickTrue);
8525      XCheckRefreshWindows(display,windows);
8526      threshold=SiPrefixToDouble(factor,QuantumRange);
8527      (void) SolarizeImage(*image,threshold,exception);
8528      XSetCursorState(display,windows,MagickFalse);
8529      if (windows->image.orphan != MagickFalse)
8530        break;
8531      XConfigureImageColormap(display,resource_info,windows,*image);
8532      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8533      break;
8534    }
8535    case SwirlCommand:
8536    {
8537      Image
8538        *swirl_image;
8539
8540      static char
8541        degrees[MaxTextExtent] = "60";
8542
8543      /*
8544        Query user for swirl angle.
8545      */
8546      (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8547        degrees);
8548      if (*degrees == '\0')
8549        break;
8550      /*
8551        Swirl image pixels about the center.
8552      */
8553      XSetCursorState(display,windows,MagickTrue);
8554      XCheckRefreshWindows(display,windows);
8555      flags=ParseGeometry(degrees,&geometry_info);
8556      swirl_image=SwirlImage(*image,geometry_info.rho,exception);
8557      if (swirl_image != (Image *) NULL)
8558        {
8559          *image=DestroyImage(*image);
8560          *image=swirl_image;
8561        }
8562      CatchException(exception);
8563      XSetCursorState(display,windows,MagickFalse);
8564      if (windows->image.orphan != MagickFalse)
8565        break;
8566      XConfigureImageColormap(display,resource_info,windows,*image);
8567      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8568      break;
8569    }
8570    case ImplodeCommand:
8571    {
8572      Image
8573        *implode_image;
8574
8575      static char
8576        factor[MaxTextExtent] = "0.3";
8577
8578      /*
8579        Query user for implode factor.
8580      */
8581      (void) XDialogWidget(display,windows,"Implode",
8582        "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8583      if (*factor == '\0')
8584        break;
8585      /*
8586        Implode image pixels about the center.
8587      */
8588      XSetCursorState(display,windows,MagickTrue);
8589      XCheckRefreshWindows(display,windows);
8590      flags=ParseGeometry(factor,&geometry_info);
8591      implode_image=ImplodeImage(*image,geometry_info.rho,exception);
8592      if (implode_image != (Image *) NULL)
8593        {
8594          *image=DestroyImage(*image);
8595          *image=implode_image;
8596        }
8597      CatchException(exception);
8598      XSetCursorState(display,windows,MagickFalse);
8599      if (windows->image.orphan != MagickFalse)
8600        break;
8601      XConfigureImageColormap(display,resource_info,windows,*image);
8602      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8603      break;
8604    }
8605    case VignetteCommand:
8606    {
8607      Image
8608        *vignette_image;
8609
8610      static char
8611        geometry[MaxTextExtent] = "0x20";
8612
8613      /*
8614        Query user for the vignette geometry.
8615      */
8616      (void) XDialogWidget(display,windows,"Vignette",
8617        "Enter the radius, sigma, and x and y offsets:",geometry);
8618      if (*geometry == '\0')
8619        break;
8620      /*
8621        Soften the edges of the image in vignette style
8622      */
8623      XSetCursorState(display,windows,MagickTrue);
8624      XCheckRefreshWindows(display,windows);
8625      flags=ParseGeometry(geometry,&geometry_info);
8626      if ((flags & SigmaValue) == 0)
8627        geometry_info.sigma=1.0;
8628      if ((flags & XiValue) == 0)
8629        geometry_info.xi=0.1*(*image)->columns;
8630      if ((flags & PsiValue) == 0)
8631        geometry_info.psi=0.1*(*image)->rows;
8632      vignette_image=VignetteImage(*image,geometry_info.rho,geometry_info.sigma,
8633        (ssize_t) ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-
8634        0.5),exception);
8635      if (vignette_image != (Image *) NULL)
8636        {
8637          *image=DestroyImage(*image);
8638          *image=vignette_image;
8639        }
8640      CatchException(exception);
8641      XSetCursorState(display,windows,MagickFalse);
8642      if (windows->image.orphan != MagickFalse)
8643        break;
8644      XConfigureImageColormap(display,resource_info,windows,*image);
8645      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8646      break;
8647    }
8648    case WaveCommand:
8649    {
8650      Image
8651        *wave_image;
8652
8653      static char
8654        geometry[MaxTextExtent] = "25x150";
8655
8656      /*
8657        Query user for the wave geometry.
8658      */
8659      (void) XDialogWidget(display,windows,"Wave",
8660        "Enter the amplitude and length of the wave:",geometry);
8661      if (*geometry == '\0')
8662        break;
8663      /*
8664        Alter an image along a sine wave.
8665      */
8666      XSetCursorState(display,windows,MagickTrue);
8667      XCheckRefreshWindows(display,windows);
8668      flags=ParseGeometry(geometry,&geometry_info);
8669      if ((flags & SigmaValue) == 0)
8670        geometry_info.sigma=1.0;
8671      wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8672        exception);
8673      if (wave_image != (Image *) NULL)
8674        {
8675          *image=DestroyImage(*image);
8676          *image=wave_image;
8677        }
8678      CatchException(exception);
8679      XSetCursorState(display,windows,MagickFalse);
8680      if (windows->image.orphan != MagickFalse)
8681        break;
8682      XConfigureImageColormap(display,resource_info,windows,*image);
8683      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8684      break;
8685    }
8686    case OilPaintCommand:
8687    {
8688      Image
8689        *paint_image;
8690
8691      static char
8692        radius[MaxTextExtent] = "0";
8693
8694      /*
8695        Query user for circular neighborhood radius.
8696      */
8697      (void) XDialogWidget(display,windows,"Oil Paint",
8698        "Enter the mask radius:",radius);
8699      if (*radius == '\0')
8700        break;
8701      /*
8702        OilPaint image scanlines.
8703      */
8704      XSetCursorState(display,windows,MagickTrue);
8705      XCheckRefreshWindows(display,windows);
8706      flags=ParseGeometry(radius,&geometry_info);
8707      paint_image=OilPaintImage(*image,geometry_info.rho,geometry_info.sigma,
8708        exception);
8709      if (paint_image != (Image *) NULL)
8710        {
8711          *image=DestroyImage(*image);
8712          *image=paint_image;
8713        }
8714      CatchException(exception);
8715      XSetCursorState(display,windows,MagickFalse);
8716      if (windows->image.orphan != MagickFalse)
8717        break;
8718      XConfigureImageColormap(display,resource_info,windows,*image);
8719      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8720      break;
8721    }
8722    case CharcoalDrawCommand:
8723    {
8724      Image
8725        *charcoal_image;
8726
8727      static char
8728        radius[MaxTextExtent] = "0x1";
8729
8730      /*
8731        Query user for charcoal radius.
8732      */
8733      (void) XDialogWidget(display,windows,"Charcoal Draw",
8734        "Enter the charcoal radius and sigma:",radius);
8735      if (*radius == '\0')
8736        break;
8737      /*
8738        Charcoal the image.
8739      */
8740      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8741        exception);
8742      XSetCursorState(display,windows,MagickTrue);
8743      XCheckRefreshWindows(display,windows);
8744      flags=ParseGeometry(radius,&geometry_info);
8745      if ((flags & SigmaValue) == 0)
8746        geometry_info.sigma=geometry_info.rho;
8747      charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8748        geometry_info.xi,exception);
8749      if (charcoal_image != (Image *) NULL)
8750        {
8751          *image=DestroyImage(*image);
8752          *image=charcoal_image;
8753        }
8754      CatchException(exception);
8755      XSetCursorState(display,windows,MagickFalse);
8756      if (windows->image.orphan != MagickFalse)
8757        break;
8758      XConfigureImageColormap(display,resource_info,windows,*image);
8759      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8760      break;
8761    }
8762    case AnnotateCommand:
8763    {
8764      /*
8765        Annotate the image with text.
8766      */
8767      status=XAnnotateEditImage(display,resource_info,windows,*image,exception);
8768      if (status == MagickFalse)
8769        {
8770          XNoticeWidget(display,windows,"Unable to annotate X image",
8771            (*image)->filename);
8772          break;
8773        }
8774      break;
8775    }
8776    case DrawCommand:
8777    {
8778      /*
8779        Draw image.
8780      */
8781      status=XDrawEditImage(display,resource_info,windows,image,exception);
8782      if (status == MagickFalse)
8783        {
8784          XNoticeWidget(display,windows,"Unable to draw on the X image",
8785            (*image)->filename);
8786          break;
8787        }
8788      break;
8789    }
8790    case ColorCommand:
8791    {
8792      /*
8793        Color edit.
8794      */
8795      status=XColorEditImage(display,resource_info,windows,image,exception);
8796      if (status == MagickFalse)
8797        {
8798          XNoticeWidget(display,windows,"Unable to pixel edit X image",
8799            (*image)->filename);
8800          break;
8801        }
8802      break;
8803    }
8804    case MatteCommand:
8805    {
8806      /*
8807        Matte edit.
8808      */
8809      status=XMatteEditImage(display,resource_info,windows,image,exception);
8810      if (status == MagickFalse)
8811        {
8812          XNoticeWidget(display,windows,"Unable to matte edit X image",
8813            (*image)->filename);
8814          break;
8815        }
8816      break;
8817    }
8818    case CompositeCommand:
8819    {
8820      /*
8821        Composite image.
8822      */
8823      status=XCompositeImage(display,resource_info,windows,*image,
8824        exception);
8825      if (status == MagickFalse)
8826        {
8827          XNoticeWidget(display,windows,"Unable to composite X image",
8828            (*image)->filename);
8829          break;
8830        }
8831      break;
8832    }
8833    case AddBorderCommand:
8834    {
8835      Image
8836        *border_image;
8837
8838      static char
8839        geometry[MaxTextExtent] = "6x6";
8840
8841      /*
8842        Query user for border color and geometry.
8843      */
8844      XColorBrowserWidget(display,windows,"Select",color);
8845      if (*color == '\0')
8846        break;
8847      (void) XDialogWidget(display,windows,"Add Border",
8848        "Enter border geometry:",geometry);
8849      if (*geometry == '\0')
8850        break;
8851      /*
8852        Add a border to the image.
8853      */
8854      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8855        exception);
8856      XSetCursorState(display,windows,MagickTrue);
8857      XCheckRefreshWindows(display,windows);
8858      (void) QueryColorDatabase(color,&(*image)->border_color,
8859        exception);
8860      (void) ParsePageGeometry(*image,geometry,&page_geometry,
8861        exception);
8862      border_image=BorderImage(*image,&page_geometry,exception);
8863      if (border_image != (Image *) NULL)
8864        {
8865          *image=DestroyImage(*image);
8866          *image=border_image;
8867        }
8868      CatchException(exception);
8869      XSetCursorState(display,windows,MagickFalse);
8870      if (windows->image.orphan != MagickFalse)
8871        break;
8872      windows->image.window_changes.width=(int) (*image)->columns;
8873      windows->image.window_changes.height=(int) (*image)->rows;
8874      XConfigureImageColormap(display,resource_info,windows,*image);
8875      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8876      break;
8877    }
8878    case AddFrameCommand:
8879    {
8880      FrameInfo
8881        frame_info;
8882
8883      Image
8884        *frame_image;
8885
8886      static char
8887        geometry[MaxTextExtent] = "6x6";
8888
8889      /*
8890        Query user for frame color and geometry.
8891      */
8892      XColorBrowserWidget(display,windows,"Select",color);
8893      if (*color == '\0')
8894        break;
8895      (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8896        geometry);
8897      if (*geometry == '\0')
8898        break;
8899      /*
8900        Surround image with an ornamental border.
8901      */
8902      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8903        exception);
8904      XSetCursorState(display,windows,MagickTrue);
8905      XCheckRefreshWindows(display,windows);
8906      (void) QueryColorDatabase(color,&(*image)->matte_color,
8907        exception);
8908      (void) ParsePageGeometry(*image,geometry,&page_geometry,
8909        exception);
8910      frame_info.width=page_geometry.width;
8911      frame_info.height=page_geometry.height;
8912      frame_info.outer_bevel=page_geometry.x;
8913      frame_info.inner_bevel=page_geometry.y;
8914      frame_info.x=(ssize_t) frame_info.width;
8915      frame_info.y=(ssize_t) frame_info.height;
8916      frame_info.width=(*image)->columns+2*frame_info.width;
8917      frame_info.height=(*image)->rows+2*frame_info.height;
8918      frame_image=FrameImage(*image,&frame_info,exception);
8919      if (frame_image != (Image *) NULL)
8920        {
8921          *image=DestroyImage(*image);
8922          *image=frame_image;
8923        }
8924      CatchException(exception);
8925      XSetCursorState(display,windows,MagickFalse);
8926      if (windows->image.orphan != MagickFalse)
8927        break;
8928      windows->image.window_changes.width=(int) (*image)->columns;
8929      windows->image.window_changes.height=(int) (*image)->rows;
8930      XConfigureImageColormap(display,resource_info,windows,*image);
8931      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8932      break;
8933    }
8934    case CommentCommand:
8935    {
8936      const char
8937        *value;
8938
8939      FILE
8940        *file;
8941
8942      int
8943        unique_file;
8944
8945      /*
8946        Edit image comment.
8947      */
8948      unique_file=AcquireUniqueFileResource(image_info->filename);
8949      if (unique_file == -1)
8950        XNoticeWidget(display,windows,"Unable to edit image comment",
8951          image_info->filename);
8952      value=GetImageProperty(*image,"comment");
8953      if (value == (char *) NULL)
8954        unique_file=close(unique_file)-1;
8955      else
8956        {
8957          register const char
8958            *p;
8959
8960          file=fdopen(unique_file,"w");
8961          if (file == (FILE *) NULL)
8962            {
8963              XNoticeWidget(display,windows,"Unable to edit image comment",
8964                image_info->filename);
8965              break;
8966            }
8967          for (p=value; *p != '\0'; p++)
8968            (void) fputc((int) *p,file);
8969          (void) fputc('\n',file);
8970          (void) fclose(file);
8971        }
8972      XSetCursorState(display,windows,MagickTrue);
8973      XCheckRefreshWindows(display,windows);
8974      status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
8975        exception);
8976      if (status == MagickFalse)
8977        XNoticeWidget(display,windows,"Unable to edit image comment",
8978          (char *) NULL);
8979      else
8980        {
8981          char
8982            *comment;
8983
8984          comment=FileToString(image_info->filename,~0UL,exception);
8985          if (comment != (char *) NULL)
8986            {
8987              (void) SetImageProperty(*image,"comment",comment);
8988              (*image)->taint=MagickTrue;
8989            }
8990        }
8991      (void) RelinquishUniqueFileResource(image_info->filename);
8992      XSetCursorState(display,windows,MagickFalse);
8993      break;
8994    }
8995    case LaunchCommand:
8996    {
8997      /*
8998        Launch program.
8999      */
9000      XSetCursorState(display,windows,MagickTrue);
9001      XCheckRefreshWindows(display,windows);
9002      (void) AcquireUniqueFilename(filename);
9003      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"launch:%s",
9004        filename);
9005      status=WriteImage(image_info,*image,exception);
9006      if (status == MagickFalse)
9007        XNoticeWidget(display,windows,"Unable to launch image editor",
9008          (char *) NULL);
9009      else
9010        {
9011          nexus=ReadImage(resource_info->image_info,exception);
9012          CatchException(exception);
9013          XClientMessage(display,windows->image.id,windows->im_protocols,
9014            windows->im_next_image,CurrentTime);
9015        }
9016      (void) RelinquishUniqueFileResource(filename);
9017      XSetCursorState(display,windows,MagickFalse);
9018      break;
9019    }
9020    case RegionofInterestCommand:
9021    {
9022      /*
9023        Apply an image processing technique to a region of interest.
9024      */
9025      (void) XROIImage(display,resource_info,windows,image,exception);
9026      break;
9027    }
9028    case InfoCommand:
9029      break;
9030    case ZoomCommand:
9031    {
9032      /*
9033        Zoom image.
9034      */
9035      if (windows->magnify.mapped != MagickFalse)
9036        (void) XRaiseWindow(display,windows->magnify.id);
9037      else
9038        {
9039          /*
9040            Make magnify image.
9041          */
9042          XSetCursorState(display,windows,MagickTrue);
9043          (void) XMapRaised(display,windows->magnify.id);
9044          XSetCursorState(display,windows,MagickFalse);
9045        }
9046      break;
9047    }
9048    case ShowPreviewCommand:
9049    {
9050      char
9051        **previews;
9052
9053      Image
9054        *preview_image;
9055
9056      static char
9057        preview_type[MaxTextExtent] = "Gamma";
9058
9059      /*
9060        Select preview type from menu.
9061      */
9062      previews=GetCommandOptions(MagickPreviewOptions);
9063      if (previews == (char **) NULL)
9064        break;
9065      XListBrowserWidget(display,windows,&windows->widget,
9066        (const char **) previews,"Preview",
9067        "Select an enhancement, effect, or F/X:",preview_type);
9068      previews=DestroyStringList(previews);
9069      if (*preview_type == '\0')
9070        break;
9071      /*
9072        Show image preview.
9073      */
9074      XSetCursorState(display,windows,MagickTrue);
9075      XCheckRefreshWindows(display,windows);
9076      image_info->preview_type=(PreviewType)
9077        ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
9078      image_info->group=(ssize_t) windows->image.id;
9079      (void) DeleteImageProperty(*image,"label");
9080      (void) SetImageProperty(*image,"label","Preview");
9081      (void) AcquireUniqueFilename(filename);
9082      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"preview:%s",
9083        filename);
9084      status=WriteImage(image_info,*image,exception);
9085      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9086      preview_image=ReadImage(image_info,exception);
9087      (void) RelinquishUniqueFileResource(filename);
9088      if (preview_image == (Image *) NULL)
9089        break;
9090      (void) FormatLocaleString(preview_image->filename,MaxTextExtent,"show:%s",
9091        filename);
9092      status=WriteImage(image_info,preview_image,exception);
9093      preview_image=DestroyImage(preview_image);
9094      if (status == MagickFalse)
9095        XNoticeWidget(display,windows,"Unable to show image preview",
9096          (*image)->filename);
9097      XDelay(display,1500);
9098      XSetCursorState(display,windows,MagickFalse);
9099      break;
9100    }
9101    case ShowHistogramCommand:
9102    {
9103      Image
9104        *histogram_image;
9105
9106      /*
9107        Show image histogram.
9108      */
9109      XSetCursorState(display,windows,MagickTrue);
9110      XCheckRefreshWindows(display,windows);
9111      image_info->group=(ssize_t) windows->image.id;
9112      (void) DeleteImageProperty(*image,"label");
9113      (void) SetImageProperty(*image,"label","Histogram");
9114      (void) AcquireUniqueFilename(filename);
9115      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"histogram:%s",
9116        filename);
9117      status=WriteImage(image_info,*image,exception);
9118      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9119      histogram_image=ReadImage(image_info,exception);
9120      (void) RelinquishUniqueFileResource(filename);
9121      if (histogram_image == (Image *) NULL)
9122        break;
9123      (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
9124        "show:%s",filename);
9125      status=WriteImage(image_info,histogram_image,exception);
9126      histogram_image=DestroyImage(histogram_image);
9127      if (status == MagickFalse)
9128        XNoticeWidget(display,windows,"Unable to show histogram",
9129          (*image)->filename);
9130      XDelay(display,1500);
9131      XSetCursorState(display,windows,MagickFalse);
9132      break;
9133    }
9134    case ShowMatteCommand:
9135    {
9136      Image
9137        *matte_image;
9138
9139      if ((*image)->matte == MagickFalse)
9140        {
9141          XNoticeWidget(display,windows,
9142            "Image does not have any matte information",(*image)->filename);
9143          break;
9144        }
9145      /*
9146        Show image matte.
9147      */
9148      XSetCursorState(display,windows,MagickTrue);
9149      XCheckRefreshWindows(display,windows);
9150      image_info->group=(ssize_t) windows->image.id;
9151      (void) DeleteImageProperty(*image,"label");
9152      (void) SetImageProperty(*image,"label","Matte");
9153      (void) AcquireUniqueFilename(filename);
9154      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"matte:%s",
9155        filename);
9156      status=WriteImage(image_info,*image,exception);
9157      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9158      matte_image=ReadImage(image_info,exception);
9159      (void) RelinquishUniqueFileResource(filename);
9160      if (matte_image == (Image *) NULL)
9161        break;
9162      (void) FormatLocaleString(matte_image->filename,MaxTextExtent,"show:%s",
9163        filename);
9164      status=WriteImage(image_info,matte_image,exception);
9165      matte_image=DestroyImage(matte_image);
9166      if (status == MagickFalse)
9167        XNoticeWidget(display,windows,"Unable to show matte",
9168          (*image)->filename);
9169      XDelay(display,1500);
9170      XSetCursorState(display,windows,MagickFalse);
9171      break;
9172    }
9173    case BackgroundCommand:
9174    {
9175      /*
9176        Background image.
9177      */
9178      status=XBackgroundImage(display,resource_info,windows,image,exception);
9179      if (status == MagickFalse)
9180        break;
9181      nexus=CloneImage(*image,0,0,MagickTrue,exception);
9182      if (nexus != (Image *) NULL)
9183        XClientMessage(display,windows->image.id,windows->im_protocols,
9184          windows->im_next_image,CurrentTime);
9185      break;
9186    }
9187    case SlideShowCommand:
9188    {
9189      static char
9190        delay[MaxTextExtent] = "5";
9191
9192      /*
9193        Display next image after pausing.
9194      */
9195      (void) XDialogWidget(display,windows,"Slide Show",
9196        "Pause how many 1/100ths of a second between images:",delay);
9197      if (*delay == '\0')
9198        break;
9199      resource_info->delay=StringToUnsignedLong(delay);
9200      XClientMessage(display,windows->image.id,windows->im_protocols,
9201        windows->im_next_image,CurrentTime);
9202      break;
9203    }
9204    case PreferencesCommand:
9205    {
9206      /*
9207        Set user preferences.
9208      */
9209      status=XPreferencesWidget(display,resource_info,windows);
9210      if (status == MagickFalse)
9211        break;
9212      nexus=CloneImage(*image,0,0,MagickTrue,exception);
9213      if (nexus != (Image *) NULL)
9214        XClientMessage(display,windows->image.id,windows->im_protocols,
9215          windows->im_next_image,CurrentTime);
9216      break;
9217    }
9218    case HelpCommand:
9219    {
9220      /*
9221        User requested help.
9222      */
9223      XTextViewWidget(display,resource_info,windows,MagickFalse,
9224        "Help Viewer - Display",DisplayHelp);
9225      break;
9226    }
9227    case BrowseDocumentationCommand:
9228    {
9229      Atom
9230        mozilla_atom;
9231
9232      Window
9233        mozilla_window,
9234        root_window;
9235
9236      /*
9237        Browse the ImageMagick documentation.
9238      */
9239      root_window=XRootWindow(display,XDefaultScreen(display));
9240      mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9241      mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9242      if (mozilla_window != (Window) NULL)
9243        {
9244          char
9245            command[MaxTextExtent],
9246            *url;
9247
9248          /*
9249            Display documentation using Netscape remote control.
9250          */
9251          url=GetMagickHomeURL();
9252          (void) FormatLocaleString(command,MaxTextExtent,
9253            "openurl(%s,new-tab)",url);
9254          url=DestroyString(url);
9255          mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9256          (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9257            8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9258          XSetCursorState(display,windows,MagickFalse);
9259          break;
9260        }
9261      XSetCursorState(display,windows,MagickTrue);
9262      XCheckRefreshWindows(display,windows);
9263      status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9264        exception);
9265      if (status == MagickFalse)
9266        XNoticeWidget(display,windows,"Unable to browse documentation",
9267          (char *) NULL);
9268      XDelay(display,1500);
9269      XSetCursorState(display,windows,MagickFalse);
9270      break;
9271    }
9272    case VersionCommand:
9273    {
9274      XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9275        GetMagickCopyright());
9276      break;
9277    }
9278    case SaveToUndoBufferCommand:
9279      break;
9280    default:
9281    {
9282      (void) XBell(display,0);
9283      break;
9284    }
9285  }
9286  image_info=DestroyImageInfo(image_info);
9287  return(nexus);
9288}
9289
9290/*
9291%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9292%                                                                             %
9293%                                                                             %
9294%                                                                             %
9295+   X M a g n i f y I m a g e                                                 %
9296%                                                                             %
9297%                                                                             %
9298%                                                                             %
9299%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9300%
9301%  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9302%  The magnified portion is displayed in a separate window.
9303%
9304%  The format of the XMagnifyImage method is:
9305%
9306%      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9307%
9308%  A description of each parameter follows:
9309%
9310%    o display: Specifies a connection to an X server;  returned from
9311%      XOpenDisplay.
9312%
9313%    o windows: Specifies a pointer to a XWindows structure.
9314%
9315%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
9316%      the entire image is refreshed.
9317%
9318*/
9319static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9320{
9321  char
9322    text[MaxTextExtent];
9323
9324  register int
9325    x,
9326    y;
9327
9328  size_t
9329    state;
9330
9331  /*
9332    Update magnified image until the mouse button is released.
9333  */
9334  (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9335  state=DefaultState;
9336  x=event->xbutton.x;
9337  y=event->xbutton.y;
9338  windows->magnify.x=(int) windows->image.x+x;
9339  windows->magnify.y=(int) windows->image.y+y;
9340  do
9341  {
9342    /*
9343      Map and unmap Info widget as text cursor crosses its boundaries.
9344    */
9345    if (windows->info.mapped != MagickFalse)
9346      {
9347        if ((x < (int) (windows->info.x+windows->info.width)) &&
9348            (y < (int) (windows->info.y+windows->info.height)))
9349          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9350      }
9351    else
9352      if ((x > (int) (windows->info.x+windows->info.width)) ||
9353          (y > (int) (windows->info.y+windows->info.height)))
9354        (void) XMapWindow(display,windows->info.id);
9355    if (windows->info.mapped != MagickFalse)
9356      {
9357        /*
9358          Display pointer position.
9359        */
9360        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9361          windows->magnify.x,windows->magnify.y);
9362        XInfoWidget(display,windows,text);
9363      }
9364    /*
9365      Wait for next event.
9366    */
9367    XScreenEvent(display,windows,event);
9368    switch (event->type)
9369    {
9370      case ButtonPress:
9371        break;
9372      case ButtonRelease:
9373      {
9374        /*
9375          User has finished magnifying image.
9376        */
9377        x=event->xbutton.x;
9378        y=event->xbutton.y;
9379        state|=ExitState;
9380        break;
9381      }
9382      case Expose:
9383        break;
9384      case MotionNotify:
9385      {
9386        x=event->xmotion.x;
9387        y=event->xmotion.y;
9388        break;
9389      }
9390      default:
9391        break;
9392    }
9393    /*
9394      Check boundary conditions.
9395    */
9396    if (x < 0)
9397      x=0;
9398    else
9399      if (x >= (int) windows->image.width)
9400        x=(int) windows->image.width-1;
9401    if (y < 0)
9402      y=0;
9403    else
9404     if (y >= (int) windows->image.height)
9405       y=(int) windows->image.height-1;
9406  } while ((state & ExitState) == 0);
9407  /*
9408    Display magnified image.
9409  */
9410  XSetCursorState(display,windows,MagickFalse);
9411}
9412
9413/*
9414%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9415%                                                                             %
9416%                                                                             %
9417%                                                                             %
9418+   X M a g n i f y W i n d o w C o m m a n d                                 %
9419%                                                                             %
9420%                                                                             %
9421%                                                                             %
9422%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9423%
9424%  XMagnifyWindowCommand() moves the image within an Magnify window by one
9425%  pixel as specified by the key symbol.
9426%
9427%  The format of the XMagnifyWindowCommand method is:
9428%
9429%      void XMagnifyWindowCommand(Display *display,XWindows *windows,
9430%        const MagickStatusType state,const KeySym key_symbol)
9431%
9432%  A description of each parameter follows:
9433%
9434%    o display: Specifies a connection to an X server; returned from
9435%      XOpenDisplay.
9436%
9437%    o windows: Specifies a pointer to a XWindows structure.
9438%
9439%    o state: key mask.
9440%
9441%    o key_symbol: Specifies a KeySym which indicates which side of the image
9442%      to trim.
9443%
9444*/
9445static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9446  const MagickStatusType state,const KeySym key_symbol)
9447{
9448  unsigned int
9449    quantum;
9450
9451  /*
9452    User specified a magnify factor or position.
9453  */
9454  quantum=1;
9455  if ((state & Mod1Mask) != 0)
9456    quantum=10;
9457  switch ((int) key_symbol)
9458  {
9459    case QuitCommand:
9460    {
9461      (void) XWithdrawWindow(display,windows->magnify.id,
9462        windows->magnify.screen);
9463      break;
9464    }
9465    case XK_Home:
9466    case XK_KP_Home:
9467    {
9468      windows->magnify.x=(int) windows->image.width/2;
9469      windows->magnify.y=(int) windows->image.height/2;
9470      break;
9471    }
9472    case XK_Left:
9473    case XK_KP_Left:
9474    {
9475      if (windows->magnify.x > 0)
9476        windows->magnify.x-=quantum;
9477      break;
9478    }
9479    case XK_Up:
9480    case XK_KP_Up:
9481    {
9482      if (windows->magnify.y > 0)
9483        windows->magnify.y-=quantum;
9484      break;
9485    }
9486    case XK_Right:
9487    case XK_KP_Right:
9488    {
9489      if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9490        windows->magnify.x+=quantum;
9491      break;
9492    }
9493    case XK_Down:
9494    case XK_KP_Down:
9495    {
9496      if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9497        windows->magnify.y+=quantum;
9498      break;
9499    }
9500    case XK_0:
9501    case XK_1:
9502    case XK_2:
9503    case XK_3:
9504    case XK_4:
9505    case XK_5:
9506    case XK_6:
9507    case XK_7:
9508    case XK_8:
9509    case XK_9:
9510    {
9511      windows->magnify.data=(key_symbol-XK_0);
9512      break;
9513    }
9514    case XK_KP_0:
9515    case XK_KP_1:
9516    case XK_KP_2:
9517    case XK_KP_3:
9518    case XK_KP_4:
9519    case XK_KP_5:
9520    case XK_KP_6:
9521    case XK_KP_7:
9522    case XK_KP_8:
9523    case XK_KP_9:
9524    {
9525      windows->magnify.data=(key_symbol-XK_KP_0);
9526      break;
9527    }
9528    default:
9529      break;
9530  }
9531  XMakeMagnifyImage(display,windows);
9532}
9533
9534/*
9535%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9536%                                                                             %
9537%                                                                             %
9538%                                                                             %
9539+   X M a k e P a n I m a g e                                                 %
9540%                                                                             %
9541%                                                                             %
9542%                                                                             %
9543%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9544%
9545%  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9546%  icon window.
9547%
9548%  The format of the XMakePanImage method is:
9549%
9550%        void XMakePanImage(Display *display,XResourceInfo *resource_info,
9551%          XWindows *windows,Image *image,ExceptionInfo *exception)
9552%
9553%  A description of each parameter follows:
9554%
9555%    o display: Specifies a connection to an X server;  returned from
9556%      XOpenDisplay.
9557%
9558%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9559%
9560%    o windows: Specifies a pointer to a XWindows structure.
9561%
9562%    o image: the image.
9563%
9564%    o exception: return any errors or warnings in this structure.
9565%
9566*/
9567static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9568  XWindows *windows,Image *image,ExceptionInfo *exception)
9569{
9570  MagickStatusType
9571    status;
9572
9573  /*
9574    Create and display image for panning icon.
9575  */
9576  XSetCursorState(display,windows,MagickTrue);
9577  XCheckRefreshWindows(display,windows);
9578  windows->pan.x=(int) windows->image.x;
9579  windows->pan.y=(int) windows->image.y;
9580  status=XMakeImage(display,resource_info,&windows->pan,image,
9581    windows->pan.width,windows->pan.height,exception);
9582  if (status == MagickFalse)
9583    ThrowXWindowFatalException(ResourceLimitError,
9584     "MemoryAllocationFailed",image->filename);
9585  (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9586    windows->pan.pixmap);
9587  (void) XClearWindow(display,windows->pan.id);
9588  XDrawPanRectangle(display,windows);
9589  XSetCursorState(display,windows,MagickFalse);
9590}
9591
9592/*
9593%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9594%                                                                             %
9595%                                                                             %
9596%                                                                             %
9597+   X M a t t a E d i t I m a g e                                             %
9598%                                                                             %
9599%                                                                             %
9600%                                                                             %
9601%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9602%
9603%  XMatteEditImage() allows the user to interactively change the Matte channel
9604%  of an image.  If the image is PseudoClass it is promoted to DirectClass
9605%  before the matte information is stored.
9606%
9607%  The format of the XMatteEditImage method is:
9608%
9609%      MagickBooleanType XMatteEditImage(Display *display,
9610%        XResourceInfo *resource_info,XWindows *windows,Image **image,
9611%        ExceptionInfo *exception)
9612%
9613%  A description of each parameter follows:
9614%
9615%    o display: Specifies a connection to an X server;  returned from
9616%      XOpenDisplay.
9617%
9618%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9619%
9620%    o windows: Specifies a pointer to a XWindows structure.
9621%
9622%    o image: the image; returned from ReadImage.
9623%
9624%    o exception: return any errors or warnings in this structure.
9625%
9626*/
9627static MagickBooleanType XMatteEditImage(Display *display,
9628  XResourceInfo *resource_info,XWindows *windows,Image **image,
9629  ExceptionInfo *exception)
9630{
9631  static char
9632    matte[MaxTextExtent] = "0";
9633
9634  static const char
9635    *MatteEditMenu[] =
9636    {
9637      "Method",
9638      "Border Color",
9639      "Fuzz",
9640      "Matte Value",
9641      "Undo",
9642      "Help",
9643      "Dismiss",
9644      (char *) NULL
9645    };
9646
9647  static const ModeType
9648    MatteEditCommands[] =
9649    {
9650      MatteEditMethod,
9651      MatteEditBorderCommand,
9652      MatteEditFuzzCommand,
9653      MatteEditValueCommand,
9654      MatteEditUndoCommand,
9655      MatteEditHelpCommand,
9656      MatteEditDismissCommand
9657    };
9658
9659  static PaintMethod
9660    method = PointMethod;
9661
9662  static XColor
9663    border_color = { 0, 0, 0, 0, 0, 0 };
9664
9665  char
9666    command[MaxTextExtent],
9667    text[MaxTextExtent];
9668
9669  Cursor
9670    cursor;
9671
9672  int
9673    entry,
9674    id,
9675    x,
9676    x_offset,
9677    y,
9678    y_offset;
9679
9680  register int
9681    i;
9682
9683  register Quantum
9684    *q;
9685
9686  unsigned int
9687    height,
9688    width;
9689
9690  size_t
9691    state;
9692
9693  XEvent
9694    event;
9695
9696  /*
9697    Map Command widget.
9698  */
9699  (void) CloneString(&windows->command.name,"Matte Edit");
9700  windows->command.data=4;
9701  (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9702  (void) XMapRaised(display,windows->command.id);
9703  XClientMessage(display,windows->image.id,windows->im_protocols,
9704    windows->im_update_widget,CurrentTime);
9705  /*
9706    Make cursor.
9707  */
9708  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9709    resource_info->background_color,resource_info->foreground_color);
9710  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9711  /*
9712    Track pointer until button 1 is pressed.
9713  */
9714  XQueryPosition(display,windows->image.id,&x,&y);
9715  (void) XSelectInput(display,windows->image.id,
9716    windows->image.attributes.event_mask | PointerMotionMask);
9717  state=DefaultState;
9718  do
9719  {
9720    if (windows->info.mapped != MagickFalse)
9721      {
9722        /*
9723          Display pointer position.
9724        */
9725        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9726          x+windows->image.x,y+windows->image.y);
9727        XInfoWidget(display,windows,text);
9728      }
9729    /*
9730      Wait for next event.
9731    */
9732    XScreenEvent(display,windows,&event);
9733    if (event.xany.window == windows->command.id)
9734      {
9735        /*
9736          Select a command from the Command widget.
9737        */
9738        id=XCommandWidget(display,windows,MatteEditMenu,&event);
9739        if (id < 0)
9740          {
9741            (void) XCheckDefineCursor(display,windows->image.id,cursor);
9742            continue;
9743          }
9744        switch (MatteEditCommands[id])
9745        {
9746          case MatteEditMethod:
9747          {
9748            char
9749              **methods;
9750
9751            /*
9752              Select a method from the pop-up menu.
9753            */
9754            methods=GetCommandOptions(MagickMethodOptions);
9755            if (methods == (char **) NULL)
9756              break;
9757            entry=XMenuWidget(display,windows,MatteEditMenu[id],
9758              (const char **) methods,command);
9759            if (entry >= 0)
9760              method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9761                MagickFalse,methods[entry]);
9762            methods=DestroyStringList(methods);
9763            break;
9764          }
9765          case MatteEditBorderCommand:
9766          {
9767            const char
9768              *ColorMenu[MaxNumberPens];
9769
9770            int
9771              pen_number;
9772
9773            /*
9774              Initialize menu selections.
9775            */
9776            for (i=0; i < (int) (MaxNumberPens-2); i++)
9777              ColorMenu[i]=resource_info->pen_colors[i];
9778            ColorMenu[MaxNumberPens-2]="Browser...";
9779            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9780            /*
9781              Select a pen color from the pop-up menu.
9782            */
9783            pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9784              (const char **) ColorMenu,command);
9785            if (pen_number < 0)
9786              break;
9787            if (pen_number == (MaxNumberPens-2))
9788              {
9789                static char
9790                  color_name[MaxTextExtent] = "gray";
9791
9792                /*
9793                  Select a pen color from a dialog.
9794                */
9795                resource_info->pen_colors[pen_number]=color_name;
9796                XColorBrowserWidget(display,windows,"Select",color_name);
9797                if (*color_name == '\0')
9798                  break;
9799              }
9800            /*
9801              Set border color.
9802            */
9803            (void) XParseColor(display,windows->map_info->colormap,
9804              resource_info->pen_colors[pen_number],&border_color);
9805            break;
9806          }
9807          case MatteEditFuzzCommand:
9808          {
9809            static char
9810              fuzz[MaxTextExtent];
9811
9812            static const char
9813              *FuzzMenu[] =
9814              {
9815                "0%",
9816                "2%",
9817                "5%",
9818                "10%",
9819                "15%",
9820                "Dialog...",
9821                (char *) NULL,
9822              };
9823
9824            /*
9825              Select a command from the pop-up menu.
9826            */
9827            entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9828              command);
9829            if (entry < 0)
9830              break;
9831            if (entry != 5)
9832              {
9833                (*image)->fuzz=SiPrefixToDouble(FuzzMenu[entry],1.0*
9834                  QuantumRange+1.0);
9835                break;
9836              }
9837            (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9838            (void) XDialogWidget(display,windows,"Ok",
9839              "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9840            if (*fuzz == '\0')
9841              break;
9842            (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
9843            (*image)->fuzz=SiPrefixToDouble(fuzz,1.0*QuantumRange+1.0);
9844            break;
9845          }
9846          case MatteEditValueCommand:
9847          {
9848            static char
9849              message[MaxTextExtent];
9850
9851            static const char
9852              *MatteMenu[] =
9853              {
9854                "Opaque",
9855                "Transparent",
9856                "Dialog...",
9857                (char *) NULL,
9858              };
9859
9860            /*
9861              Select a command from the pop-up menu.
9862            */
9863            entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9864              command);
9865            if (entry < 0)
9866              break;
9867            if (entry != 2)
9868              {
9869                (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9870                  OpaqueAlpha);
9871                if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9872                  (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9873                    (Quantum) TransparentAlpha);
9874                break;
9875              }
9876            (void) FormatLocaleString(message,MaxTextExtent,
9877              "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9878              QuantumRange);
9879            (void) XDialogWidget(display,windows,"Matte",message,matte);
9880            if (*matte == '\0')
9881              break;
9882            break;
9883          }
9884          case MatteEditUndoCommand:
9885          {
9886            (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9887              image,exception);
9888            break;
9889          }
9890          case MatteEditHelpCommand:
9891          {
9892            XTextViewWidget(display,resource_info,windows,MagickFalse,
9893              "Help Viewer - Matte Edit",ImageMatteEditHelp);
9894            break;
9895          }
9896          case MatteEditDismissCommand:
9897          {
9898            /*
9899              Prematurely exit.
9900            */
9901            state|=EscapeState;
9902            state|=ExitState;
9903            break;
9904          }
9905          default:
9906            break;
9907        }
9908        (void) XCheckDefineCursor(display,windows->image.id,cursor);
9909        continue;
9910      }
9911    switch (event.type)
9912    {
9913      case ButtonPress:
9914      {
9915        if (event.xbutton.button != Button1)
9916          break;
9917        if ((event.xbutton.window != windows->image.id) &&
9918            (event.xbutton.window != windows->magnify.id))
9919          break;
9920        /*
9921          Update matte data.
9922        */
9923        x=event.xbutton.x;
9924        y=event.xbutton.y;
9925        (void) XMagickCommand(display,resource_info,windows,
9926          SaveToUndoBufferCommand,image,exception);
9927        state|=UpdateConfigurationState;
9928        break;
9929      }
9930      case ButtonRelease:
9931      {
9932        if (event.xbutton.button != Button1)
9933          break;
9934        if ((event.xbutton.window != windows->image.id) &&
9935            (event.xbutton.window != windows->magnify.id))
9936          break;
9937        /*
9938          Update colormap information.
9939        */
9940        x=event.xbutton.x;
9941        y=event.xbutton.y;
9942        XConfigureImageColormap(display,resource_info,windows,*image);
9943        (void) XConfigureImage(display,resource_info,windows,*image,exception);
9944        XInfoWidget(display,windows,text);
9945        (void) XCheckDefineCursor(display,windows->image.id,cursor);
9946        state&=(~UpdateConfigurationState);
9947        break;
9948      }
9949      case Expose:
9950        break;
9951      case KeyPress:
9952      {
9953        char
9954          command[MaxTextExtent];
9955
9956        KeySym
9957          key_symbol;
9958
9959        if (event.xkey.window == windows->magnify.id)
9960          {
9961            Window
9962              window;
9963
9964            window=windows->magnify.id;
9965            while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9966          }
9967        if (event.xkey.window != windows->image.id)
9968          break;
9969        /*
9970          Respond to a user key press.
9971        */
9972        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
9973          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
9974        switch ((int) key_symbol)
9975        {
9976          case XK_Escape:
9977          case XK_F20:
9978          {
9979            /*
9980              Prematurely exit.
9981            */
9982            state|=ExitState;
9983            break;
9984          }
9985          case XK_F1:
9986          case XK_Help:
9987          {
9988            XTextViewWidget(display,resource_info,windows,MagickFalse,
9989              "Help Viewer - Matte Edit",ImageMatteEditHelp);
9990            break;
9991          }
9992          default:
9993          {
9994            (void) XBell(display,0);
9995            break;
9996          }
9997        }
9998        break;
9999      }
10000      case MotionNotify:
10001      {
10002        /*
10003          Map and unmap Info widget as cursor crosses its boundaries.
10004        */
10005        x=event.xmotion.x;
10006        y=event.xmotion.y;
10007        if (windows->info.mapped != MagickFalse)
10008          {
10009            if ((x < (int) (windows->info.x+windows->info.width)) &&
10010                (y < (int) (windows->info.y+windows->info.height)))
10011              (void) XWithdrawWindow(display,windows->info.id,
10012                windows->info.screen);
10013          }
10014        else
10015          if ((x > (int) (windows->info.x+windows->info.width)) ||
10016              (y > (int) (windows->info.y+windows->info.height)))
10017            (void) XMapWindow(display,windows->info.id);
10018        break;
10019      }
10020      default:
10021        break;
10022    }
10023    if (event.xany.window == windows->magnify.id)
10024      {
10025        x=windows->magnify.x-windows->image.x;
10026        y=windows->magnify.y-windows->image.y;
10027      }
10028    x_offset=x;
10029    y_offset=y;
10030    if ((state & UpdateConfigurationState) != 0)
10031      {
10032        CacheView
10033          *image_view;
10034
10035        int
10036          x,
10037          y;
10038
10039        /*
10040          Matte edit is relative to image configuration.
10041        */
10042        (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
10043          MagickTrue);
10044        XPutPixel(windows->image.ximage,x_offset,y_offset,
10045          windows->pixel_info->background_color.pixel);
10046        width=(unsigned int) (*image)->columns;
10047        height=(unsigned int) (*image)->rows;
10048        x=0;
10049        y=0;
10050        if (windows->image.crop_geometry != (char *) NULL)
10051          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
10052            &height);
10053        x_offset=(int) (width*(windows->image.x+x_offset)/
10054          windows->image.ximage->width+x);
10055        y_offset=(int) (height*(windows->image.y+y_offset)/
10056          windows->image.ximage->height+y);
10057        if ((x_offset < 0) || (y_offset < 0))
10058          continue;
10059        if ((x_offset >= (int) (*image)->columns) ||
10060            (y_offset >= (int) (*image)->rows))
10061          continue;
10062        if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10063          return(MagickFalse);
10064        (*image)->matte=MagickTrue;
10065        image_view=AcquireCacheView(*image);
10066        switch (method)
10067        {
10068          case PointMethod:
10069          default:
10070          {
10071            /*
10072              Update matte information using point algorithm.
10073            */
10074            q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10075              (ssize_t) y_offset,1,1,exception);
10076            if (q == (Quantum *) NULL)
10077              break;
10078            SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10079            (void) SyncCacheViewAuthenticPixels(image_view,exception);
10080            break;
10081          }
10082          case ReplaceMethod:
10083          {
10084            PixelPacket
10085              pixel,
10086              target;
10087
10088            /*
10089              Update matte information using replace algorithm.
10090            */
10091            (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
10092              (ssize_t) y_offset,&target,exception);
10093            for (y=0; y < (int) (*image)->rows; y++)
10094            {
10095              q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10096                (*image)->columns,1,exception);
10097              if (q == (Quantum *) NULL)
10098                break;
10099              for (x=0; x < (int) (*image)->columns; x++)
10100              {
10101                GetPixelPacket(*image,q,&pixel);
10102                if (IsFuzzyEquivalencePixelPacket(*image,&pixel,&target))
10103                  SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10104                q+=GetPixelChannels(*image);
10105              }
10106              if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10107                break;
10108            }
10109            break;
10110          }
10111          case FloodfillMethod:
10112          case FillToBorderMethod:
10113          {
10114            ChannelType
10115              channel_mask;
10116
10117            DrawInfo
10118              *draw_info;
10119
10120            PixelInfo
10121              target;
10122
10123            /*
10124              Update matte information using floodfill algorithm.
10125            */
10126            (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
10127              (ssize_t) y_offset,&target,exception);
10128            if (method == FillToBorderMethod)
10129              {
10130                target.red=(MagickRealType) ScaleShortToQuantum(
10131                  border_color.red);
10132                target.green=(MagickRealType) ScaleShortToQuantum(
10133                  border_color.green);
10134                target.blue=(MagickRealType) ScaleShortToQuantum(
10135                  border_color.blue);
10136              }
10137            draw_info=CloneDrawInfo(resource_info->image_info,
10138              (DrawInfo *) NULL);
10139            draw_info->fill.alpha=ClampToQuantum(InterpretLocaleValue(matte,
10140              (char **) NULL));
10141            channel_mask=SetPixelChannelMask(*image,AlphaChannel);
10142            (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10143              x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
10144              MagickFalse : MagickTrue,exception);
10145            (void) SetPixelChannelMap(*image,channel_mask);
10146            draw_info=DestroyDrawInfo(draw_info);
10147            break;
10148          }
10149          case ResetMethod:
10150          {
10151            /*
10152              Update matte information using reset algorithm.
10153            */
10154            if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10155              return(MagickFalse);
10156            for (y=0; y < (int) (*image)->rows; y++)
10157            {
10158              q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10159                (*image)->columns,1,exception);
10160              if (q == (Quantum *) NULL)
10161                break;
10162              for (x=0; x < (int) (*image)->columns; x++)
10163              {
10164                SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10165                q+=GetPixelChannels(*image);
10166              }
10167              if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10168                break;
10169            }
10170            if (StringToLong(matte) == (long) OpaqueAlpha)
10171              (*image)->matte=MagickFalse;
10172            break;
10173          }
10174        }
10175        image_view=DestroyCacheView(image_view);
10176        state&=(~UpdateConfigurationState);
10177      }
10178  } while ((state & ExitState) == 0);
10179  (void) XSelectInput(display,windows->image.id,
10180    windows->image.attributes.event_mask);
10181  XSetCursorState(display,windows,MagickFalse);
10182  (void) XFreeCursor(display,cursor);
10183  return(MagickTrue);
10184}
10185
10186/*
10187%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10188%                                                                             %
10189%                                                                             %
10190%                                                                             %
10191+   X O p e n I m a g e                                                       %
10192%                                                                             %
10193%                                                                             %
10194%                                                                             %
10195%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10196%
10197%  XOpenImage() loads an image from a file.
10198%
10199%  The format of the XOpenImage method is:
10200%
10201%     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10202%       XWindows *windows,const unsigned int command)
10203%
10204%  A description of each parameter follows:
10205%
10206%    o display: Specifies a connection to an X server; returned from
10207%      XOpenDisplay.
10208%
10209%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10210%
10211%    o windows: Specifies a pointer to a XWindows structure.
10212%
10213%    o command: A value other than zero indicates that the file is selected
10214%      from the command line argument list.
10215%
10216*/
10217static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10218  XWindows *windows,const MagickBooleanType command)
10219{
10220  const MagickInfo
10221    *magick_info;
10222
10223  ExceptionInfo
10224    *exception;
10225
10226  Image
10227    *nexus;
10228
10229  ImageInfo
10230    *image_info;
10231
10232  static char
10233    filename[MaxTextExtent] = "\0";
10234
10235  /*
10236    Request file name from user.
10237  */
10238  if (command == MagickFalse)
10239    XFileBrowserWidget(display,windows,"Open",filename);
10240  else
10241    {
10242      char
10243        **filelist,
10244        **files;
10245
10246      int
10247        count,
10248        status;
10249
10250      register int
10251        i,
10252        j;
10253
10254      /*
10255        Select next image from the command line.
10256      */
10257      status=XGetCommand(display,windows->image.id,&files,&count);
10258      if (status == 0)
10259        {
10260          ThrowXWindowFatalException(XServerError,"UnableToGetProperty","...");
10261          return((Image *) NULL);
10262        }
10263      filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10264      if (filelist == (char **) NULL)
10265        {
10266          ThrowXWindowFatalException(ResourceLimitError,
10267            "MemoryAllocationFailed","...");
10268          (void) XFreeStringList(files);
10269          return((Image *) NULL);
10270        }
10271      j=0;
10272      for (i=1; i < count; i++)
10273        if (*files[i] != '-')
10274          filelist[j++]=files[i];
10275      filelist[j]=(char *) NULL;
10276      XListBrowserWidget(display,windows,&windows->widget,
10277        (const char **) filelist,"Load","Select Image to Load:",filename);
10278      filelist=(char **) RelinquishMagickMemory(filelist);
10279      (void) XFreeStringList(files);
10280    }
10281  if (*filename == '\0')
10282    return((Image *) NULL);
10283  image_info=CloneImageInfo(resource_info->image_info);
10284  (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10285    (void *) NULL);
10286  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10287  exception=AcquireExceptionInfo();
10288  (void) SetImageInfo(image_info,0,exception);
10289  if (LocaleCompare(image_info->magick,"X") == 0)
10290    {
10291      char
10292        seconds[MaxTextExtent];
10293
10294      /*
10295        User may want to delay the X server screen grab.
10296      */
10297      (void) CopyMagickString(seconds,"0",MaxTextExtent);
10298      (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10299        seconds);
10300      if (*seconds == '\0')
10301        return((Image *) NULL);
10302      XDelay(display,(size_t) (1000*StringToLong(seconds)));
10303    }
10304  magick_info=GetMagickInfo(image_info->magick,exception);
10305  if ((magick_info != (const MagickInfo *) NULL) &&
10306      (magick_info->raw != MagickFalse))
10307    {
10308      char
10309        geometry[MaxTextExtent];
10310
10311      /*
10312        Request image size from the user.
10313      */
10314      (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10315      if (image_info->size != (char *) NULL)
10316        (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10317      (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10318        geometry);
10319      (void) CloneString(&image_info->size,geometry);
10320    }
10321  /*
10322    Load the image.
10323  */
10324  XSetCursorState(display,windows,MagickTrue);
10325  XCheckRefreshWindows(display,windows);
10326  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10327  nexus=ReadImage(image_info,exception);
10328  CatchException(exception);
10329  XSetCursorState(display,windows,MagickFalse);
10330  if (nexus != (Image *) NULL)
10331    XClientMessage(display,windows->image.id,windows->im_protocols,
10332      windows->im_next_image,CurrentTime);
10333  else
10334    {
10335      char
10336        *text,
10337        **textlist;
10338
10339      /*
10340        Unknown image format.
10341      */
10342      text=FileToString(filename,~0,exception);
10343      if (text == (char *) NULL)
10344        return((Image *) NULL);
10345      textlist=StringToList(text);
10346      if (textlist != (char **) NULL)
10347        {
10348          char
10349            title[MaxTextExtent];
10350
10351          register int
10352            i;
10353
10354          (void) FormatLocaleString(title,MaxTextExtent,
10355            "Unknown format: %s",filename);
10356          XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10357            (const char **) textlist);
10358          for (i=0; textlist[i] != (char *) NULL; i++)
10359            textlist[i]=DestroyString(textlist[i]);
10360          textlist=(char **) RelinquishMagickMemory(textlist);
10361        }
10362      text=DestroyString(text);
10363    }
10364  exception=DestroyExceptionInfo(exception);
10365  image_info=DestroyImageInfo(image_info);
10366  return(nexus);
10367}
10368
10369/*
10370%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10371%                                                                             %
10372%                                                                             %
10373%                                                                             %
10374+   X P a n I m a g e                                                         %
10375%                                                                             %
10376%                                                                             %
10377%                                                                             %
10378%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10379%
10380%  XPanImage() pans the image until the mouse button is released.
10381%
10382%  The format of the XPanImage method is:
10383%
10384%      void XPanImage(Display *display,XWindows *windows,XEvent *event)
10385%
10386%  A description of each parameter follows:
10387%
10388%    o display: Specifies a connection to an X server;  returned from
10389%      XOpenDisplay.
10390%
10391%    o windows: Specifies a pointer to a XWindows structure.
10392%
10393%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10394%      the entire image is refreshed.
10395%
10396*/
10397static void XPanImage(Display *display,XWindows *windows,XEvent *event)
10398{
10399  char
10400    text[MaxTextExtent];
10401
10402  Cursor
10403    cursor;
10404
10405  MagickRealType
10406    x_factor,
10407    y_factor;
10408
10409  RectangleInfo
10410    pan_info;
10411
10412  size_t
10413    state;
10414
10415  /*
10416    Define cursor.
10417  */
10418  if ((windows->image.ximage->width > (int) windows->image.width) &&
10419      (windows->image.ximage->height > (int) windows->image.height))
10420    cursor=XCreateFontCursor(display,XC_fleur);
10421  else
10422    if (windows->image.ximage->width > (int) windows->image.width)
10423      cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10424    else
10425      if (windows->image.ximage->height > (int) windows->image.height)
10426        cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10427      else
10428        cursor=XCreateFontCursor(display,XC_arrow);
10429  (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10430  /*
10431    Pan image as pointer moves until the mouse button is released.
10432  */
10433  x_factor=(MagickRealType) windows->image.ximage->width/windows->pan.width;
10434  y_factor=(MagickRealType) windows->image.ximage->height/windows->pan.height;
10435  pan_info.width=windows->pan.width*windows->image.width/
10436    windows->image.ximage->width;
10437  pan_info.height=windows->pan.height*windows->image.height/
10438    windows->image.ximage->height;
10439  pan_info.x=0;
10440  pan_info.y=0;
10441  state=UpdateConfigurationState;
10442  do
10443  {
10444    switch (event->type)
10445    {
10446      case ButtonPress:
10447      {
10448        /*
10449          User choose an initial pan location.
10450        */
10451        pan_info.x=(ssize_t) event->xbutton.x;
10452        pan_info.y=(ssize_t) event->xbutton.y;
10453        state|=UpdateConfigurationState;
10454        break;
10455      }
10456      case ButtonRelease:
10457      {
10458        /*
10459          User has finished panning the image.
10460        */
10461        pan_info.x=(ssize_t) event->xbutton.x;
10462        pan_info.y=(ssize_t) event->xbutton.y;
10463        state|=UpdateConfigurationState | ExitState;
10464        break;
10465      }
10466      case MotionNotify:
10467      {
10468        pan_info.x=(ssize_t) event->xmotion.x;
10469        pan_info.y=(ssize_t) event->xmotion.y;
10470        state|=UpdateConfigurationState;
10471      }
10472      default:
10473        break;
10474    }
10475    if ((state & UpdateConfigurationState) != 0)
10476      {
10477        /*
10478          Check boundary conditions.
10479        */
10480        if (pan_info.x < (ssize_t) (pan_info.width/2))
10481          pan_info.x=0;
10482        else
10483          pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10484        if (pan_info.x < 0)
10485          pan_info.x=0;
10486        else
10487          if ((int) (pan_info.x+windows->image.width) >
10488              windows->image.ximage->width)
10489            pan_info.x=(ssize_t)
10490              (windows->image.ximage->width-windows->image.width);
10491        if (pan_info.y < (ssize_t) (pan_info.height/2))
10492          pan_info.y=0;
10493        else
10494          pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10495        if (pan_info.y < 0)
10496          pan_info.y=0;
10497        else
10498          if ((int) (pan_info.y+windows->image.height) >
10499              windows->image.ximage->height)
10500            pan_info.y=(ssize_t)
10501              (windows->image.ximage->height-windows->image.height);
10502        if ((windows->image.x != (int) pan_info.x) ||
10503            (windows->image.y != (int) pan_info.y))
10504          {
10505            /*
10506              Display image pan offset.
10507            */
10508            windows->image.x=(int) pan_info.x;
10509            windows->image.y=(int) pan_info.y;
10510            (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
10511              windows->image.width,windows->image.height,windows->image.x,
10512              windows->image.y);
10513            XInfoWidget(display,windows,text);
10514            /*
10515              Refresh Image window.
10516            */
10517            XDrawPanRectangle(display,windows);
10518            XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10519          }
10520        state&=(~UpdateConfigurationState);
10521      }
10522    /*
10523      Wait for next event.
10524    */
10525    if ((state & ExitState) == 0)
10526      XScreenEvent(display,windows,event);
10527  } while ((state & ExitState) == 0);
10528  /*
10529    Restore cursor.
10530  */
10531  (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10532  (void) XFreeCursor(display,cursor);
10533  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10534}
10535
10536/*
10537%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10538%                                                                             %
10539%                                                                             %
10540%                                                                             %
10541+   X P a s t e I m a g e                                                     %
10542%                                                                             %
10543%                                                                             %
10544%                                                                             %
10545%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10546%
10547%  XPasteImage() pastes an image previously saved with XCropImage in the X
10548%  window image at a location the user chooses with the pointer.
10549%
10550%  The format of the XPasteImage method is:
10551%
10552%      MagickBooleanType XPasteImage(Display *display,
10553%        XResourceInfo *resource_info,XWindows *windows,Image *image,
10554%        ExceptionInfo *exception)
10555%
10556%  A description of each parameter follows:
10557%
10558%    o display: Specifies a connection to an X server;  returned from
10559%      XOpenDisplay.
10560%
10561%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10562%
10563%    o windows: Specifies a pointer to a XWindows structure.
10564%
10565%    o image: the image; returned from ReadImage.
10566%
10567%    o exception: return any errors or warnings in this structure.
10568%
10569*/
10570static MagickBooleanType XPasteImage(Display *display,
10571  XResourceInfo *resource_info,XWindows *windows,Image *image,
10572  ExceptionInfo *exception)
10573{
10574  static const char
10575    *PasteMenu[] =
10576    {
10577      "Operator",
10578      "Help",
10579      "Dismiss",
10580      (char *) NULL
10581    };
10582
10583  static const ModeType
10584    PasteCommands[] =
10585    {
10586      PasteOperatorsCommand,
10587      PasteHelpCommand,
10588      PasteDismissCommand
10589    };
10590
10591  static CompositeOperator
10592    compose = CopyCompositeOp;
10593
10594  char
10595    text[MaxTextExtent];
10596
10597  Cursor
10598    cursor;
10599
10600  Image
10601    *paste_image;
10602
10603  int
10604    entry,
10605    id,
10606    x,
10607    y;
10608
10609  MagickRealType
10610    scale_factor;
10611
10612  RectangleInfo
10613    highlight_info,
10614    paste_info;
10615
10616  unsigned int
10617    height,
10618    width;
10619
10620  size_t
10621    state;
10622
10623  XEvent
10624    event;
10625
10626  /*
10627    Copy image.
10628  */
10629  if (resource_info->copy_image == (Image *) NULL)
10630    return(MagickFalse);
10631  paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,exception);
10632  /*
10633    Map Command widget.
10634  */
10635  (void) CloneString(&windows->command.name,"Paste");
10636  windows->command.data=1;
10637  (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10638  (void) XMapRaised(display,windows->command.id);
10639  XClientMessage(display,windows->image.id,windows->im_protocols,
10640    windows->im_update_widget,CurrentTime);
10641  /*
10642    Track pointer until button 1 is pressed.
10643  */
10644  XSetCursorState(display,windows,MagickFalse);
10645  XQueryPosition(display,windows->image.id,&x,&y);
10646  (void) XSelectInput(display,windows->image.id,
10647    windows->image.attributes.event_mask | PointerMotionMask);
10648  paste_info.x=(ssize_t) windows->image.x+x;
10649  paste_info.y=(ssize_t) windows->image.y+y;
10650  paste_info.width=0;
10651  paste_info.height=0;
10652  cursor=XCreateFontCursor(display,XC_ul_angle);
10653  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10654  state=DefaultState;
10655  do
10656  {
10657    if (windows->info.mapped != MagickFalse)
10658      {
10659        /*
10660          Display pointer position.
10661        */
10662        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
10663          (long) paste_info.x,(long) paste_info.y);
10664        XInfoWidget(display,windows,text);
10665      }
10666    highlight_info=paste_info;
10667    highlight_info.x=paste_info.x-windows->image.x;
10668    highlight_info.y=paste_info.y-windows->image.y;
10669    XHighlightRectangle(display,windows->image.id,
10670      windows->image.highlight_context,&highlight_info);
10671    /*
10672      Wait for next event.
10673    */
10674    XScreenEvent(display,windows,&event);
10675    XHighlightRectangle(display,windows->image.id,
10676      windows->image.highlight_context,&highlight_info);
10677    if (event.xany.window == windows->command.id)
10678      {
10679        /*
10680          Select a command from the Command widget.
10681        */
10682        id=XCommandWidget(display,windows,PasteMenu,&event);
10683        if (id < 0)
10684          continue;
10685        switch (PasteCommands[id])
10686        {
10687          case PasteOperatorsCommand:
10688          {
10689            char
10690              command[MaxTextExtent],
10691              **operators;
10692
10693            /*
10694              Select a command from the pop-up menu.
10695            */
10696            operators=GetCommandOptions(MagickComposeOptions);
10697            if (operators == (char **) NULL)
10698              break;
10699            entry=XMenuWidget(display,windows,PasteMenu[id],
10700              (const char **) operators,command);
10701            if (entry >= 0)
10702              compose=(CompositeOperator) ParseCommandOption(
10703                MagickComposeOptions,MagickFalse,operators[entry]);
10704            operators=DestroyStringList(operators);
10705            break;
10706          }
10707          case PasteHelpCommand:
10708          {
10709            XTextViewWidget(display,resource_info,windows,MagickFalse,
10710              "Help Viewer - Image Composite",ImagePasteHelp);
10711            break;
10712          }
10713          case PasteDismissCommand:
10714          {
10715            /*
10716              Prematurely exit.
10717            */
10718            state|=EscapeState;
10719            state|=ExitState;
10720            break;
10721          }
10722          default:
10723            break;
10724        }
10725        continue;
10726      }
10727    switch (event.type)
10728    {
10729      case ButtonPress:
10730      {
10731        if (image->debug != MagickFalse)
10732          (void) LogMagickEvent(X11Event,GetMagickModule(),
10733            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10734            event.xbutton.button,event.xbutton.x,event.xbutton.y);
10735        if (event.xbutton.button != Button1)
10736          break;
10737        if (event.xbutton.window != windows->image.id)
10738          break;
10739        /*
10740          Paste rectangle is relative to image configuration.
10741        */
10742        width=(unsigned int) image->columns;
10743        height=(unsigned int) image->rows;
10744        x=0;
10745        y=0;
10746        if (windows->image.crop_geometry != (char *) NULL)
10747          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10748            &width,&height);
10749        scale_factor=(MagickRealType) windows->image.ximage->width/width;
10750        paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10751        scale_factor=(MagickRealType) windows->image.ximage->height/height;
10752        paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10753        (void) XCheckDefineCursor(display,windows->image.id,cursor);
10754        paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10755        paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10756        break;
10757      }
10758      case ButtonRelease:
10759      {
10760        if (image->debug != MagickFalse)
10761          (void) LogMagickEvent(X11Event,GetMagickModule(),
10762            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10763            event.xbutton.button,event.xbutton.x,event.xbutton.y);
10764        if (event.xbutton.button != Button1)
10765          break;
10766        if (event.xbutton.window != windows->image.id)
10767          break;
10768        if ((paste_info.width != 0) && (paste_info.height != 0))
10769          {
10770            /*
10771              User has selected the location of the paste image.
10772            */
10773            paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10774            paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10775            state|=ExitState;
10776          }
10777        break;
10778      }
10779      case Expose:
10780        break;
10781      case KeyPress:
10782      {
10783        char
10784          command[MaxTextExtent];
10785
10786        KeySym
10787          key_symbol;
10788
10789        int
10790          length;
10791
10792        if (event.xkey.window != windows->image.id)
10793          break;
10794        /*
10795          Respond to a user key press.
10796        */
10797        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10798          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10799        *(command+length)='\0';
10800        if (image->debug != MagickFalse)
10801          (void) LogMagickEvent(X11Event,GetMagickModule(),
10802            "Key press: 0x%lx (%s)",(long) key_symbol,command);
10803        switch ((int) key_symbol)
10804        {
10805          case XK_Escape:
10806          case XK_F20:
10807          {
10808            /*
10809              Prematurely exit.
10810            */
10811            paste_image=DestroyImage(paste_image);
10812            state|=EscapeState;
10813            state|=ExitState;
10814            break;
10815          }
10816          case XK_F1:
10817          case XK_Help:
10818          {
10819            (void) XSetFunction(display,windows->image.highlight_context,
10820              GXcopy);
10821            XTextViewWidget(display,resource_info,windows,MagickFalse,
10822              "Help Viewer - Image Composite",ImagePasteHelp);
10823            (void) XSetFunction(display,windows->image.highlight_context,
10824              GXinvert);
10825            break;
10826          }
10827          default:
10828          {
10829            (void) XBell(display,0);
10830            break;
10831          }
10832        }
10833        break;
10834      }
10835      case MotionNotify:
10836      {
10837        /*
10838          Map and unmap Info widget as text cursor crosses its boundaries.
10839        */
10840        x=event.xmotion.x;
10841        y=event.xmotion.y;
10842        if (windows->info.mapped != MagickFalse)
10843          {
10844            if ((x < (int) (windows->info.x+windows->info.width)) &&
10845                (y < (int) (windows->info.y+windows->info.height)))
10846              (void) XWithdrawWindow(display,windows->info.id,
10847                windows->info.screen);
10848          }
10849        else
10850          if ((x > (int) (windows->info.x+windows->info.width)) ||
10851              (y > (int) (windows->info.y+windows->info.height)))
10852            (void) XMapWindow(display,windows->info.id);
10853        paste_info.x=(ssize_t) windows->image.x+x;
10854        paste_info.y=(ssize_t) windows->image.y+y;
10855        break;
10856      }
10857      default:
10858      {
10859        if (image->debug != MagickFalse)
10860          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10861            event.type);
10862        break;
10863      }
10864    }
10865  } while ((state & ExitState) == 0);
10866  (void) XSelectInput(display,windows->image.id,
10867    windows->image.attributes.event_mask);
10868  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10869  XSetCursorState(display,windows,MagickFalse);
10870  (void) XFreeCursor(display,cursor);
10871  if ((state & EscapeState) != 0)
10872    return(MagickTrue);
10873  /*
10874    Image pasting is relative to image configuration.
10875  */
10876  XSetCursorState(display,windows,MagickTrue);
10877  XCheckRefreshWindows(display,windows);
10878  width=(unsigned int) image->columns;
10879  height=(unsigned int) image->rows;
10880  x=0;
10881  y=0;
10882  if (windows->image.crop_geometry != (char *) NULL)
10883    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10884  scale_factor=(MagickRealType) width/windows->image.ximage->width;
10885  paste_info.x+=x;
10886  paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10887  paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10888  scale_factor=(MagickRealType) height/windows->image.ximage->height;
10889  paste_info.y+=y;
10890  paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10891  paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10892  /*
10893    Paste image with X Image window.
10894  */
10895  (void) CompositeImage(image,compose,paste_image,paste_info.x,paste_info.y);
10896  paste_image=DestroyImage(paste_image);
10897  XSetCursorState(display,windows,MagickFalse);
10898  /*
10899    Update image colormap.
10900  */
10901  XConfigureImageColormap(display,resource_info,windows,image);
10902  (void) XConfigureImage(display,resource_info,windows,image,exception);
10903  return(MagickTrue);
10904}
10905
10906/*
10907%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10908%                                                                             %
10909%                                                                             %
10910%                                                                             %
10911+   X P r i n t I m a g e                                                     %
10912%                                                                             %
10913%                                                                             %
10914%                                                                             %
10915%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10916%
10917%  XPrintImage() prints an image to a Postscript printer.
10918%
10919%  The format of the XPrintImage method is:
10920%
10921%      MagickBooleanType XPrintImage(Display *display,
10922%        XResourceInfo *resource_info,XWindows *windows,Image *image,
10923%        ExceptionInfo *exception)
10924%
10925%  A description of each parameter follows:
10926%
10927%    o display: Specifies a connection to an X server; returned from
10928%      XOpenDisplay.
10929%
10930%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10931%
10932%    o windows: Specifies a pointer to a XWindows structure.
10933%
10934%    o image: the image.
10935%
10936%    o exception: return any errors or warnings in this structure.
10937%
10938*/
10939static MagickBooleanType XPrintImage(Display *display,
10940  XResourceInfo *resource_info,XWindows *windows,Image *image,
10941  ExceptionInfo *exception)
10942{
10943  char
10944    filename[MaxTextExtent],
10945    geometry[MaxTextExtent];
10946
10947  Image
10948    *print_image;
10949
10950  ImageInfo
10951    *image_info;
10952
10953  MagickStatusType
10954    status;
10955
10956  /*
10957    Request Postscript page geometry from user.
10958  */
10959  image_info=CloneImageInfo(resource_info->image_info);
10960  (void) FormatLocaleString(geometry,MaxTextExtent,"Letter");
10961  if (image_info->page != (char *) NULL)
10962    (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
10963  XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
10964    "Select Postscript Page Geometry:",geometry);
10965  if (*geometry == '\0')
10966    return(MagickTrue);
10967  image_info->page=GetPageGeometry(geometry);
10968  /*
10969    Apply image transforms.
10970  */
10971  XSetCursorState(display,windows,MagickTrue);
10972  XCheckRefreshWindows(display,windows);
10973  print_image=CloneImage(image,0,0,MagickTrue,exception);
10974  if (print_image == (Image *) NULL)
10975    return(MagickFalse);
10976  (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
10977    windows->image.ximage->width,windows->image.ximage->height);
10978  (void) TransformImage(&print_image,windows->image.crop_geometry,geometry);
10979  /*
10980    Print image.
10981  */
10982  (void) AcquireUniqueFilename(filename);
10983  (void) FormatLocaleString(print_image->filename,MaxTextExtent,"print:%s",
10984    filename);
10985  status=WriteImage(image_info,print_image,exception);
10986  (void) RelinquishUniqueFileResource(filename);
10987  print_image=DestroyImage(print_image);
10988  image_info=DestroyImageInfo(image_info);
10989  XSetCursorState(display,windows,MagickFalse);
10990  return(status != 0 ? MagickTrue : MagickFalse);
10991}
10992
10993/*
10994%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10995%                                                                             %
10996%                                                                             %
10997%                                                                             %
10998+   X R O I I m a g e                                                         %
10999%                                                                             %
11000%                                                                             %
11001%                                                                             %
11002%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11003%
11004%  XROIImage() applies an image processing technique to a region of interest.
11005%
11006%  The format of the XROIImage method is:
11007%
11008%      MagickBooleanType XROIImage(Display *display,
11009%        XResourceInfo *resource_info,XWindows *windows,Image **image,
11010%        ExceptionInfo *exception)
11011%
11012%  A description of each parameter follows:
11013%
11014%    o display: Specifies a connection to an X server; returned from
11015%      XOpenDisplay.
11016%
11017%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11018%
11019%    o windows: Specifies a pointer to a XWindows structure.
11020%
11021%    o image: the image; returned from ReadImage.
11022%
11023%    o exception: return any errors or warnings in this structure.
11024%
11025*/
11026static MagickBooleanType XROIImage(Display *display,
11027  XResourceInfo *resource_info,XWindows *windows,Image **image,
11028  ExceptionInfo *exception)
11029{
11030#define ApplyMenus  7
11031
11032  static const char
11033    *ROIMenu[] =
11034    {
11035      "Help",
11036      "Dismiss",
11037      (char *) NULL
11038    },
11039    *ApplyMenu[] =
11040    {
11041      "File",
11042      "Edit",
11043      "Transform",
11044      "Enhance",
11045      "Effects",
11046      "F/X",
11047      "Miscellany",
11048      "Help",
11049      "Dismiss",
11050      (char *) NULL
11051    },
11052    *FileMenu[] =
11053    {
11054      "Save...",
11055      "Print...",
11056      (char *) NULL
11057    },
11058    *EditMenu[] =
11059    {
11060      "Undo",
11061      "Redo",
11062      (char *) NULL
11063    },
11064    *TransformMenu[] =
11065    {
11066      "Flop",
11067      "Flip",
11068      "Rotate Right",
11069      "Rotate Left",
11070      (char *) NULL
11071    },
11072    *EnhanceMenu[] =
11073    {
11074      "Hue...",
11075      "Saturation...",
11076      "Brightness...",
11077      "Gamma...",
11078      "Spiff",
11079      "Dull",
11080      "Contrast Stretch...",
11081      "Sigmoidal Contrast...",
11082      "Normalize",
11083      "Equalize",
11084      "Negate",
11085      "Grayscale",
11086      "Map...",
11087      "Quantize...",
11088      (char *) NULL
11089    },
11090    *EffectsMenu[] =
11091    {
11092      "Despeckle",
11093      "Emboss",
11094      "Reduce Noise",
11095      "Add Noise",
11096      "Sharpen...",
11097      "Blur...",
11098      "Threshold...",
11099      "Edge Detect...",
11100      "Spread...",
11101      "Shade...",
11102      "Raise...",
11103      "Segment...",
11104      (char *) NULL
11105    },
11106    *FXMenu[] =
11107    {
11108      "Solarize...",
11109      "Sepia Tone...",
11110      "Swirl...",
11111      "Implode...",
11112      "Vignette...",
11113      "Wave...",
11114      "Oil Paint...",
11115      "Charcoal Draw...",
11116      (char *) NULL
11117    },
11118    *MiscellanyMenu[] =
11119    {
11120      "Image Info",
11121      "Zoom Image",
11122      "Show Preview...",
11123      "Show Histogram",
11124      "Show Matte",
11125      (char *) NULL
11126    };
11127
11128  static const char
11129    **Menus[ApplyMenus] =
11130    {
11131      FileMenu,
11132      EditMenu,
11133      TransformMenu,
11134      EnhanceMenu,
11135      EffectsMenu,
11136      FXMenu,
11137      MiscellanyMenu
11138    };
11139
11140  static const CommandType
11141    ApplyCommands[] =
11142    {
11143      NullCommand,
11144      NullCommand,
11145      NullCommand,
11146      NullCommand,
11147      NullCommand,
11148      NullCommand,
11149      NullCommand,
11150      HelpCommand,
11151      QuitCommand
11152    },
11153    FileCommands[] =
11154    {
11155      SaveCommand,
11156      PrintCommand
11157    },
11158    EditCommands[] =
11159    {
11160      UndoCommand,
11161      RedoCommand
11162    },
11163    TransformCommands[] =
11164    {
11165      FlopCommand,
11166      FlipCommand,
11167      RotateRightCommand,
11168      RotateLeftCommand
11169    },
11170    EnhanceCommands[] =
11171    {
11172      HueCommand,
11173      SaturationCommand,
11174      BrightnessCommand,
11175      GammaCommand,
11176      SpiffCommand,
11177      DullCommand,
11178      ContrastStretchCommand,
11179      SigmoidalContrastCommand,
11180      NormalizeCommand,
11181      EqualizeCommand,
11182      NegateCommand,
11183      GrayscaleCommand,
11184      MapCommand,
11185      QuantizeCommand
11186    },
11187    EffectsCommands[] =
11188    {
11189      DespeckleCommand,
11190      EmbossCommand,
11191      ReduceNoiseCommand,
11192      AddNoiseCommand,
11193      SharpenCommand,
11194      BlurCommand,
11195      EdgeDetectCommand,
11196      SpreadCommand,
11197      ShadeCommand,
11198      RaiseCommand,
11199      SegmentCommand
11200    },
11201    FXCommands[] =
11202    {
11203      SolarizeCommand,
11204      SepiaToneCommand,
11205      SwirlCommand,
11206      ImplodeCommand,
11207      VignetteCommand,
11208      WaveCommand,
11209      OilPaintCommand,
11210      CharcoalDrawCommand
11211    },
11212    MiscellanyCommands[] =
11213    {
11214      InfoCommand,
11215      ZoomCommand,
11216      ShowPreviewCommand,
11217      ShowHistogramCommand,
11218      ShowMatteCommand
11219    },
11220    ROICommands[] =
11221    {
11222      ROIHelpCommand,
11223      ROIDismissCommand
11224    };
11225
11226  static const CommandType
11227    *Commands[ApplyMenus] =
11228    {
11229      FileCommands,
11230      EditCommands,
11231      TransformCommands,
11232      EnhanceCommands,
11233      EffectsCommands,
11234      FXCommands,
11235      MiscellanyCommands
11236    };
11237
11238  char
11239    command[MaxTextExtent],
11240    text[MaxTextExtent];
11241
11242  CommandType
11243    command_type;
11244
11245  Cursor
11246    cursor;
11247
11248  Image
11249    *roi_image;
11250
11251  int
11252    entry,
11253    id,
11254    x,
11255    y;
11256
11257  MagickRealType
11258    scale_factor;
11259
11260  MagickProgressMonitor
11261    progress_monitor;
11262
11263  RectangleInfo
11264    crop_info,
11265    highlight_info,
11266    roi_info;
11267
11268  unsigned int
11269    height,
11270    width;
11271
11272  size_t
11273    state;
11274
11275  XEvent
11276    event;
11277
11278  /*
11279    Map Command widget.
11280  */
11281  (void) CloneString(&windows->command.name,"ROI");
11282  windows->command.data=0;
11283  (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11284  (void) XMapRaised(display,windows->command.id);
11285  XClientMessage(display,windows->image.id,windows->im_protocols,
11286    windows->im_update_widget,CurrentTime);
11287  /*
11288    Track pointer until button 1 is pressed.
11289  */
11290  XQueryPosition(display,windows->image.id,&x,&y);
11291  (void) XSelectInput(display,windows->image.id,
11292    windows->image.attributes.event_mask | PointerMotionMask);
11293  roi_info.x=(ssize_t) windows->image.x+x;
11294  roi_info.y=(ssize_t) windows->image.y+y;
11295  roi_info.width=0;
11296  roi_info.height=0;
11297  cursor=XCreateFontCursor(display,XC_fleur);
11298  state=DefaultState;
11299  do
11300  {
11301    if (windows->info.mapped != MagickFalse)
11302      {
11303        /*
11304          Display pointer position.
11305        */
11306        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
11307          (long) roi_info.x,(long) roi_info.y);
11308        XInfoWidget(display,windows,text);
11309      }
11310    /*
11311      Wait for next event.
11312    */
11313    XScreenEvent(display,windows,&event);
11314    if (event.xany.window == windows->command.id)
11315      {
11316        /*
11317          Select a command from the Command widget.
11318        */
11319        id=XCommandWidget(display,windows,ROIMenu,&event);
11320        if (id < 0)
11321          continue;
11322        switch (ROICommands[id])
11323        {
11324          case ROIHelpCommand:
11325          {
11326            XTextViewWidget(display,resource_info,windows,MagickFalse,
11327              "Help Viewer - Region of Interest",ImageROIHelp);
11328            break;
11329          }
11330          case ROIDismissCommand:
11331          {
11332            /*
11333              Prematurely exit.
11334            */
11335            state|=EscapeState;
11336            state|=ExitState;
11337            break;
11338          }
11339          default:
11340            break;
11341        }
11342        continue;
11343      }
11344    switch (event.type)
11345    {
11346      case ButtonPress:
11347      {
11348        if (event.xbutton.button != Button1)
11349          break;
11350        if (event.xbutton.window != windows->image.id)
11351          break;
11352        /*
11353          Note first corner of region of interest rectangle-- exit loop.
11354        */
11355        (void) XCheckDefineCursor(display,windows->image.id,cursor);
11356        roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11357        roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11358        state|=ExitState;
11359        break;
11360      }
11361      case ButtonRelease:
11362        break;
11363      case Expose:
11364        break;
11365      case KeyPress:
11366      {
11367        KeySym
11368          key_symbol;
11369
11370        if (event.xkey.window != windows->image.id)
11371          break;
11372        /*
11373          Respond to a user key press.
11374        */
11375        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11376          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11377        switch ((int) key_symbol)
11378        {
11379          case XK_Escape:
11380          case XK_F20:
11381          {
11382            /*
11383              Prematurely exit.
11384            */
11385            state|=EscapeState;
11386            state|=ExitState;
11387            break;
11388          }
11389          case XK_F1:
11390          case XK_Help:
11391          {
11392            XTextViewWidget(display,resource_info,windows,MagickFalse,
11393              "Help Viewer - Region of Interest",ImageROIHelp);
11394            break;
11395          }
11396          default:
11397          {
11398            (void) XBell(display,0);
11399            break;
11400          }
11401        }
11402        break;
11403      }
11404      case MotionNotify:
11405      {
11406        /*
11407          Map and unmap Info widget as text cursor crosses its boundaries.
11408        */
11409        x=event.xmotion.x;
11410        y=event.xmotion.y;
11411        if (windows->info.mapped != MagickFalse)
11412          {
11413            if ((x < (int) (windows->info.x+windows->info.width)) &&
11414                (y < (int) (windows->info.y+windows->info.height)))
11415              (void) XWithdrawWindow(display,windows->info.id,
11416                windows->info.screen);
11417          }
11418        else
11419          if ((x > (int) (windows->info.x+windows->info.width)) ||
11420              (y > (int) (windows->info.y+windows->info.height)))
11421            (void) XMapWindow(display,windows->info.id);
11422        roi_info.x=(ssize_t) windows->image.x+x;
11423        roi_info.y=(ssize_t) windows->image.y+y;
11424        break;
11425      }
11426      default:
11427        break;
11428    }
11429  } while ((state & ExitState) == 0);
11430  (void) XSelectInput(display,windows->image.id,
11431    windows->image.attributes.event_mask);
11432  if ((state & EscapeState) != 0)
11433    {
11434      /*
11435        User want to exit without region of interest.
11436      */
11437      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11438      (void) XFreeCursor(display,cursor);
11439      return(MagickTrue);
11440    }
11441  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11442  do
11443  {
11444    /*
11445      Size rectangle as pointer moves until the mouse button is released.
11446    */
11447    x=(int) roi_info.x;
11448    y=(int) roi_info.y;
11449    roi_info.width=0;
11450    roi_info.height=0;
11451    state=DefaultState;
11452    do
11453    {
11454      highlight_info=roi_info;
11455      highlight_info.x=roi_info.x-windows->image.x;
11456      highlight_info.y=roi_info.y-windows->image.y;
11457      if ((highlight_info.width > 3) && (highlight_info.height > 3))
11458        {
11459          /*
11460            Display info and draw region of interest rectangle.
11461          */
11462          if (windows->info.mapped == MagickFalse)
11463            (void) XMapWindow(display,windows->info.id);
11464          (void) FormatLocaleString(text,MaxTextExtent,
11465            " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11466            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11467          XInfoWidget(display,windows,text);
11468          XHighlightRectangle(display,windows->image.id,
11469            windows->image.highlight_context,&highlight_info);
11470        }
11471      else
11472        if (windows->info.mapped != MagickFalse)
11473          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11474      /*
11475        Wait for next event.
11476      */
11477      XScreenEvent(display,windows,&event);
11478      if ((highlight_info.width > 3) && (highlight_info.height > 3))
11479        XHighlightRectangle(display,windows->image.id,
11480          windows->image.highlight_context,&highlight_info);
11481      switch (event.type)
11482      {
11483        case ButtonPress:
11484        {
11485          roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11486          roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11487          break;
11488        }
11489        case ButtonRelease:
11490        {
11491          /*
11492            User has committed to region of interest rectangle.
11493          */
11494          roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11495          roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11496          XSetCursorState(display,windows,MagickFalse);
11497          state|=ExitState;
11498          if (LocaleCompare(windows->command.name,"Apply") == 0)
11499            break;
11500          (void) CloneString(&windows->command.name,"Apply");
11501          windows->command.data=ApplyMenus;
11502          (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11503          break;
11504        }
11505        case Expose:
11506          break;
11507        case MotionNotify:
11508        {
11509          roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11510          roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11511        }
11512        default:
11513          break;
11514      }
11515      if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11516          ((state & ExitState) != 0))
11517        {
11518          /*
11519            Check boundary conditions.
11520          */
11521          if (roi_info.x < 0)
11522            roi_info.x=0;
11523          else
11524            if (roi_info.x > (ssize_t) windows->image.ximage->width)
11525              roi_info.x=(ssize_t) windows->image.ximage->width;
11526          if ((int) roi_info.x < x)
11527            roi_info.width=(unsigned int) (x-roi_info.x);
11528          else
11529            {
11530              roi_info.width=(unsigned int) (roi_info.x-x);
11531              roi_info.x=(ssize_t) x;
11532            }
11533          if (roi_info.y < 0)
11534            roi_info.y=0;
11535          else
11536            if (roi_info.y > (ssize_t) windows->image.ximage->height)
11537              roi_info.y=(ssize_t) windows->image.ximage->height;
11538          if ((int) roi_info.y < y)
11539            roi_info.height=(unsigned int) (y-roi_info.y);
11540          else
11541            {
11542              roi_info.height=(unsigned int) (roi_info.y-y);
11543              roi_info.y=(ssize_t) y;
11544            }
11545        }
11546    } while ((state & ExitState) == 0);
11547    /*
11548      Wait for user to grab a corner of the rectangle or press return.
11549    */
11550    state=DefaultState;
11551    command_type=NullCommand;
11552    (void) XMapWindow(display,windows->info.id);
11553    do
11554    {
11555      if (windows->info.mapped != MagickFalse)
11556        {
11557          /*
11558            Display pointer position.
11559          */
11560          (void) FormatLocaleString(text,MaxTextExtent,
11561            " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11562            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11563          XInfoWidget(display,windows,text);
11564        }
11565      highlight_info=roi_info;
11566      highlight_info.x=roi_info.x-windows->image.x;
11567      highlight_info.y=roi_info.y-windows->image.y;
11568      if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11569        {
11570          state|=EscapeState;
11571          state|=ExitState;
11572          break;
11573        }
11574      if ((state & UpdateRegionState) != 0)
11575        {
11576          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11577          switch (command_type)
11578          {
11579            case UndoCommand:
11580            case RedoCommand:
11581            {
11582              (void) XMagickCommand(display,resource_info,windows,command_type,
11583                image,exception);
11584              break;
11585            }
11586            default:
11587            {
11588              /*
11589                Region of interest is relative to image configuration.
11590              */
11591              progress_monitor=SetImageProgressMonitor(*image,
11592                (MagickProgressMonitor) NULL,(*image)->client_data);
11593              crop_info=roi_info;
11594              width=(unsigned int) (*image)->columns;
11595              height=(unsigned int) (*image)->rows;
11596              x=0;
11597              y=0;
11598              if (windows->image.crop_geometry != (char *) NULL)
11599                (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11600                  &width,&height);
11601              scale_factor=(MagickRealType) width/windows->image.ximage->width;
11602              crop_info.x+=x;
11603              crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11604              crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11605              scale_factor=(MagickRealType)
11606                height/windows->image.ximage->height;
11607              crop_info.y+=y;
11608              crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11609              crop_info.height=(unsigned int)
11610                (scale_factor*crop_info.height+0.5);
11611              roi_image=CropImage(*image,&crop_info,exception);
11612              (void) SetImageProgressMonitor(*image,progress_monitor,
11613                (*image)->client_data);
11614              if (roi_image == (Image *) NULL)
11615                continue;
11616              /*
11617                Apply image processing technique to the region of interest.
11618              */
11619              windows->image.orphan=MagickTrue;
11620              (void) XMagickCommand(display,resource_info,windows,command_type,
11621                &roi_image,exception);
11622              progress_monitor=SetImageProgressMonitor(*image,
11623                (MagickProgressMonitor) NULL,(*image)->client_data);
11624              (void) XMagickCommand(display,resource_info,windows,
11625                SaveToUndoBufferCommand,image,exception);
11626              windows->image.orphan=MagickFalse;
11627              (void) CompositeImage(*image,CopyCompositeOp,roi_image,
11628                crop_info.x,crop_info.y);
11629              roi_image=DestroyImage(roi_image);
11630              (void) SetImageProgressMonitor(*image,progress_monitor,
11631                (*image)->client_data);
11632              break;
11633            }
11634          }
11635          if (command_type != InfoCommand)
11636            {
11637              XConfigureImageColormap(display,resource_info,windows,*image);
11638              (void) XConfigureImage(display,resource_info,windows,*image,exception);
11639            }
11640          XCheckRefreshWindows(display,windows);
11641          XInfoWidget(display,windows,text);
11642          (void) XSetFunction(display,windows->image.highlight_context,
11643            GXinvert);
11644          state&=(~UpdateRegionState);
11645        }
11646      XHighlightRectangle(display,windows->image.id,
11647        windows->image.highlight_context,&highlight_info);
11648      XScreenEvent(display,windows,&event);
11649      if (event.xany.window == windows->command.id)
11650        {
11651          /*
11652            Select a command from the Command widget.
11653          */
11654          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11655          command_type=NullCommand;
11656          id=XCommandWidget(display,windows,ApplyMenu,&event);
11657          if (id >= 0)
11658            {
11659              (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11660              command_type=ApplyCommands[id];
11661              if (id < ApplyMenus)
11662                {
11663                  /*
11664                    Select a command from a pop-up menu.
11665                  */
11666                  entry=XMenuWidget(display,windows,ApplyMenu[id],
11667                    (const char **) Menus[id],command);
11668                  if (entry >= 0)
11669                    {
11670                      (void) CopyMagickString(command,Menus[id][entry],
11671                        MaxTextExtent);
11672                      command_type=Commands[id][entry];
11673                    }
11674                }
11675            }
11676          (void) XSetFunction(display,windows->image.highlight_context,
11677            GXinvert);
11678          XHighlightRectangle(display,windows->image.id,
11679            windows->image.highlight_context,&highlight_info);
11680          if (command_type == HelpCommand)
11681            {
11682              (void) XSetFunction(display,windows->image.highlight_context,
11683                GXcopy);
11684              XTextViewWidget(display,resource_info,windows,MagickFalse,
11685                "Help Viewer - Region of Interest",ImageROIHelp);
11686              (void) XSetFunction(display,windows->image.highlight_context,
11687                GXinvert);
11688              continue;
11689            }
11690          if (command_type == QuitCommand)
11691            {
11692              /*
11693                exit.
11694              */
11695              state|=EscapeState;
11696              state|=ExitState;
11697              continue;
11698            }
11699          if (command_type != NullCommand)
11700            state|=UpdateRegionState;
11701          continue;
11702        }
11703      XHighlightRectangle(display,windows->image.id,
11704        windows->image.highlight_context,&highlight_info);
11705      switch (event.type)
11706      {
11707        case ButtonPress:
11708        {
11709          x=windows->image.x;
11710          y=windows->image.y;
11711          if (event.xbutton.button != Button1)
11712            break;
11713          if (event.xbutton.window != windows->image.id)
11714            break;
11715          x=windows->image.x+event.xbutton.x;
11716          y=windows->image.y+event.xbutton.y;
11717          if ((x < (int) (roi_info.x+RoiDelta)) &&
11718              (x > (int) (roi_info.x-RoiDelta)) &&
11719              (y < (int) (roi_info.y+RoiDelta)) &&
11720              (y > (int) (roi_info.y-RoiDelta)))
11721            {
11722              roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11723              roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11724              state|=UpdateConfigurationState;
11725              break;
11726            }
11727          if ((x < (int) (roi_info.x+RoiDelta)) &&
11728              (x > (int) (roi_info.x-RoiDelta)) &&
11729              (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11730              (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11731            {
11732              roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11733              state|=UpdateConfigurationState;
11734              break;
11735            }
11736          if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11737              (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11738              (y < (int) (roi_info.y+RoiDelta)) &&
11739              (y > (int) (roi_info.y-RoiDelta)))
11740            {
11741              roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11742              state|=UpdateConfigurationState;
11743              break;
11744            }
11745          if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11746              (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11747              (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11748              (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11749            {
11750              state|=UpdateConfigurationState;
11751              break;
11752            }
11753        }
11754        case ButtonRelease:
11755        {
11756          if (event.xbutton.window == windows->pan.id)
11757            if ((highlight_info.x != crop_info.x-windows->image.x) ||
11758                (highlight_info.y != crop_info.y-windows->image.y))
11759              XHighlightRectangle(display,windows->image.id,
11760                windows->image.highlight_context,&highlight_info);
11761          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11762            event.xbutton.time);
11763          break;
11764        }
11765        case Expose:
11766        {
11767          if (event.xexpose.window == windows->image.id)
11768            if (event.xexpose.count == 0)
11769              {
11770                event.xexpose.x=(int) highlight_info.x;
11771                event.xexpose.y=(int) highlight_info.y;
11772                event.xexpose.width=(int) highlight_info.width;
11773                event.xexpose.height=(int) highlight_info.height;
11774                XRefreshWindow(display,&windows->image,&event);
11775              }
11776          if (event.xexpose.window == windows->info.id)
11777            if (event.xexpose.count == 0)
11778              XInfoWidget(display,windows,text);
11779          break;
11780        }
11781        case KeyPress:
11782        {
11783          KeySym
11784            key_symbol;
11785
11786          if (event.xkey.window != windows->image.id)
11787            break;
11788          /*
11789            Respond to a user key press.
11790          */
11791          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11792            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11793          switch ((int) key_symbol)
11794          {
11795            case XK_Shift_L:
11796            case XK_Shift_R:
11797              break;
11798            case XK_Escape:
11799            case XK_F20:
11800              state|=EscapeState;
11801            case XK_Return:
11802            {
11803              state|=ExitState;
11804              break;
11805            }
11806            case XK_Home:
11807            case XK_KP_Home:
11808            {
11809              roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11810              roi_info.y=(ssize_t) (windows->image.height/2L-
11811                roi_info.height/2L);
11812              break;
11813            }
11814            case XK_Left:
11815            case XK_KP_Left:
11816            {
11817              roi_info.x--;
11818              break;
11819            }
11820            case XK_Up:
11821            case XK_KP_Up:
11822            case XK_Next:
11823            {
11824              roi_info.y--;
11825              break;
11826            }
11827            case XK_Right:
11828            case XK_KP_Right:
11829            {
11830              roi_info.x++;
11831              break;
11832            }
11833            case XK_Prior:
11834            case XK_Down:
11835            case XK_KP_Down:
11836            {
11837              roi_info.y++;
11838              break;
11839            }
11840            case XK_F1:
11841            case XK_Help:
11842            {
11843              (void) XSetFunction(display,windows->image.highlight_context,
11844                GXcopy);
11845              XTextViewWidget(display,resource_info,windows,MagickFalse,
11846                "Help Viewer - Region of Interest",ImageROIHelp);
11847              (void) XSetFunction(display,windows->image.highlight_context,
11848                GXinvert);
11849              break;
11850            }
11851            default:
11852            {
11853              command_type=XImageWindowCommand(display,resource_info,windows,
11854                event.xkey.state,key_symbol,image,exception);
11855              if (command_type != NullCommand)
11856                state|=UpdateRegionState;
11857              break;
11858            }
11859          }
11860          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11861            event.xkey.time);
11862          break;
11863        }
11864        case KeyRelease:
11865          break;
11866        case MotionNotify:
11867        {
11868          if (event.xbutton.window != windows->image.id)
11869            break;
11870          /*
11871            Map and unmap Info widget as text cursor crosses its boundaries.
11872          */
11873          x=event.xmotion.x;
11874          y=event.xmotion.y;
11875          if (windows->info.mapped != MagickFalse)
11876            {
11877              if ((x < (int) (windows->info.x+windows->info.width)) &&
11878                  (y < (int) (windows->info.y+windows->info.height)))
11879                (void) XWithdrawWindow(display,windows->info.id,
11880                  windows->info.screen);
11881            }
11882          else
11883            if ((x > (int) (windows->info.x+windows->info.width)) ||
11884                (y > (int) (windows->info.y+windows->info.height)))
11885              (void) XMapWindow(display,windows->info.id);
11886          roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11887          roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11888          break;
11889        }
11890        case SelectionRequest:
11891        {
11892          XSelectionEvent
11893            notify;
11894
11895          XSelectionRequestEvent
11896            *request;
11897
11898          /*
11899            Set primary selection.
11900          */
11901          (void) FormatLocaleString(text,MaxTextExtent,
11902            "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11903            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11904          request=(&(event.xselectionrequest));
11905          (void) XChangeProperty(request->display,request->requestor,
11906            request->property,request->target,8,PropModeReplace,
11907            (unsigned char *) text,(int) strlen(text));
11908          notify.type=SelectionNotify;
11909          notify.display=request->display;
11910          notify.requestor=request->requestor;
11911          notify.selection=request->selection;
11912          notify.target=request->target;
11913          notify.time=request->time;
11914          if (request->property == None)
11915            notify.property=request->target;
11916          else
11917            notify.property=request->property;
11918          (void) XSendEvent(request->display,request->requestor,False,0,
11919            (XEvent *) &notify);
11920        }
11921        default:
11922          break;
11923      }
11924      if ((state & UpdateConfigurationState) != 0)
11925        {
11926          (void) XPutBackEvent(display,&event);
11927          (void) XCheckDefineCursor(display,windows->image.id,cursor);
11928          break;
11929        }
11930    } while ((state & ExitState) == 0);
11931  } while ((state & ExitState) == 0);
11932  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11933  XSetCursorState(display,windows,MagickFalse);
11934  if ((state & EscapeState) != 0)
11935    return(MagickTrue);
11936  return(MagickTrue);
11937}
11938
11939/*
11940%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11941%                                                                             %
11942%                                                                             %
11943%                                                                             %
11944+   X R o t a t e I m a g e                                                   %
11945%                                                                             %
11946%                                                                             %
11947%                                                                             %
11948%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11949%
11950%  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
11951%  rotation angle is computed from the slope of a line drawn by the user.
11952%
11953%  The format of the XRotateImage method is:
11954%
11955%      MagickBooleanType XRotateImage(Display *display,
11956%        XResourceInfo *resource_info,XWindows *windows,double degrees,
11957%        Image **image,ExceptionInfo *exception)
11958%
11959%  A description of each parameter follows:
11960%
11961%    o display: Specifies a connection to an X server; returned from
11962%      XOpenDisplay.
11963%
11964%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11965%
11966%    o windows: Specifies a pointer to a XWindows structure.
11967%
11968%    o degrees: Specifies the number of degrees to rotate the image.
11969%
11970%    o image: the image.
11971%
11972%    o exception: return any errors or warnings in this structure.
11973%
11974*/
11975static MagickBooleanType XRotateImage(Display *display,
11976  XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
11977  ExceptionInfo *exception)
11978{
11979  static const char
11980    *RotateMenu[] =
11981    {
11982      "Pixel Color",
11983      "Direction",
11984      "Help",
11985      "Dismiss",
11986      (char *) NULL
11987    };
11988
11989  static ModeType
11990    direction = HorizontalRotateCommand;
11991
11992  static const ModeType
11993    DirectionCommands[] =
11994    {
11995      HorizontalRotateCommand,
11996      VerticalRotateCommand
11997    },
11998    RotateCommands[] =
11999    {
12000      RotateColorCommand,
12001      RotateDirectionCommand,
12002      RotateHelpCommand,
12003      RotateDismissCommand
12004    };
12005
12006  static unsigned int
12007    pen_id = 0;
12008
12009  char
12010    command[MaxTextExtent],
12011    text[MaxTextExtent];
12012
12013  Image
12014    *rotate_image;
12015
12016  int
12017    id,
12018    x,
12019    y;
12020
12021  MagickRealType
12022    normalized_degrees;
12023
12024  register int
12025    i;
12026
12027  unsigned int
12028    height,
12029    rotations,
12030    width;
12031
12032  if (degrees == 0.0)
12033    {
12034      unsigned int
12035        distance;
12036
12037      size_t
12038        state;
12039
12040      XEvent
12041        event;
12042
12043      XSegment
12044        rotate_info;
12045
12046      /*
12047        Map Command widget.
12048      */
12049      (void) CloneString(&windows->command.name,"Rotate");
12050      windows->command.data=2;
12051      (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12052      (void) XMapRaised(display,windows->command.id);
12053      XClientMessage(display,windows->image.id,windows->im_protocols,
12054        windows->im_update_widget,CurrentTime);
12055      /*
12056        Wait for first button press.
12057      */
12058      (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12059      XQueryPosition(display,windows->image.id,&x,&y);
12060      rotate_info.x1=x;
12061      rotate_info.y1=y;
12062      rotate_info.x2=x;
12063      rotate_info.y2=y;
12064      state=DefaultState;
12065      do
12066      {
12067        XHighlightLine(display,windows->image.id,
12068          windows->image.highlight_context,&rotate_info);
12069        /*
12070          Wait for next event.
12071        */
12072        XScreenEvent(display,windows,&event);
12073        XHighlightLine(display,windows->image.id,
12074          windows->image.highlight_context,&rotate_info);
12075        if (event.xany.window == windows->command.id)
12076          {
12077            /*
12078              Select a command from the Command widget.
12079            */
12080            id=XCommandWidget(display,windows,RotateMenu,&event);
12081            if (id < 0)
12082              continue;
12083            (void) XSetFunction(display,windows->image.highlight_context,
12084              GXcopy);
12085            switch (RotateCommands[id])
12086            {
12087              case RotateColorCommand:
12088              {
12089                const char
12090                  *ColorMenu[MaxNumberPens];
12091
12092                int
12093                  pen_number;
12094
12095                XColor
12096                  color;
12097
12098                /*
12099                  Initialize menu selections.
12100                */
12101                for (i=0; i < (int) (MaxNumberPens-2); i++)
12102                  ColorMenu[i]=resource_info->pen_colors[i];
12103                ColorMenu[MaxNumberPens-2]="Browser...";
12104                ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12105                /*
12106                  Select a pen color from the pop-up menu.
12107                */
12108                pen_number=XMenuWidget(display,windows,RotateMenu[id],
12109                  (const char **) ColorMenu,command);
12110                if (pen_number < 0)
12111                  break;
12112                if (pen_number == (MaxNumberPens-2))
12113                  {
12114                    static char
12115                      color_name[MaxTextExtent] = "gray";
12116
12117                    /*
12118                      Select a pen color from a dialog.
12119                    */
12120                    resource_info->pen_colors[pen_number]=color_name;
12121                    XColorBrowserWidget(display,windows,"Select",color_name);
12122                    if (*color_name == '\0')
12123                      break;
12124                  }
12125                /*
12126                  Set pen color.
12127                */
12128                (void) XParseColor(display,windows->map_info->colormap,
12129                  resource_info->pen_colors[pen_number],&color);
12130                XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12131                  (unsigned int) MaxColors,&color);
12132                windows->pixel_info->pen_colors[pen_number]=color;
12133                pen_id=(unsigned int) pen_number;
12134                break;
12135              }
12136              case RotateDirectionCommand:
12137              {
12138                static const char
12139                  *Directions[] =
12140                  {
12141                    "horizontal",
12142                    "vertical",
12143                    (char *) NULL,
12144                  };
12145
12146                /*
12147                  Select a command from the pop-up menu.
12148                */
12149                id=XMenuWidget(display,windows,RotateMenu[id],
12150                  Directions,command);
12151                if (id >= 0)
12152                  direction=DirectionCommands[id];
12153                break;
12154              }
12155              case RotateHelpCommand:
12156              {
12157                XTextViewWidget(display,resource_info,windows,MagickFalse,
12158                  "Help Viewer - Image Rotation",ImageRotateHelp);
12159                break;
12160              }
12161              case RotateDismissCommand:
12162              {
12163                /*
12164                  Prematurely exit.
12165                */
12166                state|=EscapeState;
12167                state|=ExitState;
12168                break;
12169              }
12170              default:
12171                break;
12172            }
12173            (void) XSetFunction(display,windows->image.highlight_context,
12174              GXinvert);
12175            continue;
12176          }
12177        switch (event.type)
12178        {
12179          case ButtonPress:
12180          {
12181            if (event.xbutton.button != Button1)
12182              break;
12183            if (event.xbutton.window != windows->image.id)
12184              break;
12185            /*
12186              exit loop.
12187            */
12188            (void) XSetFunction(display,windows->image.highlight_context,
12189              GXcopy);
12190            rotate_info.x1=event.xbutton.x;
12191            rotate_info.y1=event.xbutton.y;
12192            state|=ExitState;
12193            break;
12194          }
12195          case ButtonRelease:
12196            break;
12197          case Expose:
12198            break;
12199          case KeyPress:
12200          {
12201            char
12202              command[MaxTextExtent];
12203
12204            KeySym
12205              key_symbol;
12206
12207            if (event.xkey.window != windows->image.id)
12208              break;
12209            /*
12210              Respond to a user key press.
12211            */
12212            (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12213              sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12214            switch ((int) key_symbol)
12215            {
12216              case XK_Escape:
12217              case XK_F20:
12218              {
12219                /*
12220                  Prematurely exit.
12221                */
12222                state|=EscapeState;
12223                state|=ExitState;
12224                break;
12225              }
12226              case XK_F1:
12227              case XK_Help:
12228              {
12229                (void) XSetFunction(display,windows->image.highlight_context,
12230                  GXcopy);
12231                XTextViewWidget(display,resource_info,windows,MagickFalse,
12232                  "Help Viewer - Image Rotation",ImageRotateHelp);
12233                (void) XSetFunction(display,windows->image.highlight_context,
12234                  GXinvert);
12235                break;
12236              }
12237              default:
12238              {
12239                (void) XBell(display,0);
12240                break;
12241              }
12242            }
12243            break;
12244          }
12245          case MotionNotify:
12246          {
12247            rotate_info.x1=event.xmotion.x;
12248            rotate_info.y1=event.xmotion.y;
12249          }
12250        }
12251        rotate_info.x2=rotate_info.x1;
12252        rotate_info.y2=rotate_info.y1;
12253        if (direction == HorizontalRotateCommand)
12254          rotate_info.x2+=32;
12255        else
12256          rotate_info.y2-=32;
12257      } while ((state & ExitState) == 0);
12258      (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12259      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12260      if ((state & EscapeState) != 0)
12261        return(MagickTrue);
12262      /*
12263        Draw line as pointer moves until the mouse button is released.
12264      */
12265      distance=0;
12266      (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12267      state=DefaultState;
12268      do
12269      {
12270        if (distance > 9)
12271          {
12272            /*
12273              Display info and draw rotation line.
12274            */
12275            if (windows->info.mapped == MagickFalse)
12276              (void) XMapWindow(display,windows->info.id);
12277            (void) FormatLocaleString(text,MaxTextExtent," %g",
12278              direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12279            XInfoWidget(display,windows,text);
12280            XHighlightLine(display,windows->image.id,
12281              windows->image.highlight_context,&rotate_info);
12282          }
12283        else
12284          if (windows->info.mapped != MagickFalse)
12285            (void) XWithdrawWindow(display,windows->info.id,
12286              windows->info.screen);
12287        /*
12288          Wait for next event.
12289        */
12290        XScreenEvent(display,windows,&event);
12291        if (distance > 9)
12292          XHighlightLine(display,windows->image.id,
12293            windows->image.highlight_context,&rotate_info);
12294        switch (event.type)
12295        {
12296          case ButtonPress:
12297            break;
12298          case ButtonRelease:
12299          {
12300            /*
12301              User has committed to rotation line.
12302            */
12303            rotate_info.x2=event.xbutton.x;
12304            rotate_info.y2=event.xbutton.y;
12305            state|=ExitState;
12306            break;
12307          }
12308          case Expose:
12309            break;
12310          case MotionNotify:
12311          {
12312            rotate_info.x2=event.xmotion.x;
12313            rotate_info.y2=event.xmotion.y;
12314          }
12315          default:
12316            break;
12317        }
12318        /*
12319          Check boundary conditions.
12320        */
12321        if (rotate_info.x2 < 0)
12322          rotate_info.x2=0;
12323        else
12324          if (rotate_info.x2 > (int) windows->image.width)
12325            rotate_info.x2=(short) windows->image.width;
12326        if (rotate_info.y2 < 0)
12327          rotate_info.y2=0;
12328        else
12329          if (rotate_info.y2 > (int) windows->image.height)
12330            rotate_info.y2=(short) windows->image.height;
12331        /*
12332          Compute rotation angle from the slope of the line.
12333        */
12334        degrees=0.0;
12335        distance=(unsigned int)
12336          ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12337          ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12338        if (distance > 9)
12339          degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12340            rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12341      } while ((state & ExitState) == 0);
12342      (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12343      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12344      if (distance <= 9)
12345        return(MagickTrue);
12346    }
12347  if (direction == VerticalRotateCommand)
12348    degrees-=90.0;
12349  if (degrees == 0.0)
12350    return(MagickTrue);
12351  /*
12352    Rotate image.
12353  */
12354  normalized_degrees=degrees;
12355  while (normalized_degrees < -45.0)
12356    normalized_degrees+=360.0;
12357  for (rotations=0; normalized_degrees > 45.0; rotations++)
12358    normalized_degrees-=90.0;
12359  if (normalized_degrees != 0.0)
12360    (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12361      exception);
12362  XSetCursorState(display,windows,MagickTrue);
12363  XCheckRefreshWindows(display,windows);
12364  (*image)->background_color.red=ScaleShortToQuantum(
12365    windows->pixel_info->pen_colors[pen_id].red);
12366  (*image)->background_color.green=ScaleShortToQuantum(
12367    windows->pixel_info->pen_colors[pen_id].green);
12368  (*image)->background_color.blue=ScaleShortToQuantum(
12369    windows->pixel_info->pen_colors[pen_id].blue);
12370  rotate_image=RotateImage(*image,degrees,exception);
12371  XSetCursorState(display,windows,MagickFalse);
12372  if (rotate_image == (Image *) NULL)
12373    return(MagickFalse);
12374  *image=DestroyImage(*image);
12375  *image=rotate_image;
12376  if (windows->image.crop_geometry != (char *) NULL)
12377    {
12378      /*
12379        Rotate crop geometry.
12380      */
12381      width=(unsigned int) (*image)->columns;
12382      height=(unsigned int) (*image)->rows;
12383      (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12384      switch (rotations % 4)
12385      {
12386        default:
12387        case 0:
12388          break;
12389        case 1:
12390        {
12391          /*
12392            Rotate 90 degrees.
12393          */
12394          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12395            "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12396            (int) height-y,x);
12397          break;
12398        }
12399        case 2:
12400        {
12401          /*
12402            Rotate 180 degrees.
12403          */
12404          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12405            "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12406          break;
12407        }
12408        case 3:
12409        {
12410          /*
12411            Rotate 270 degrees.
12412          */
12413          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12414            "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12415          break;
12416        }
12417      }
12418    }
12419  if (windows->image.orphan != MagickFalse)
12420    return(MagickTrue);
12421  if (normalized_degrees != 0.0)
12422    {
12423      /*
12424        Update image colormap.
12425      */
12426      windows->image.window_changes.width=(int) (*image)->columns;
12427      windows->image.window_changes.height=(int) (*image)->rows;
12428      if (windows->image.crop_geometry != (char *) NULL)
12429        {
12430          /*
12431            Obtain dimensions of image from crop geometry.
12432          */
12433          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12434            &width,&height);
12435          windows->image.window_changes.width=(int) width;
12436          windows->image.window_changes.height=(int) height;
12437        }
12438      XConfigureImageColormap(display,resource_info,windows,*image);
12439    }
12440  else
12441    if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12442      {
12443        windows->image.window_changes.width=windows->image.ximage->height;
12444        windows->image.window_changes.height=windows->image.ximage->width;
12445      }
12446  /*
12447    Update image configuration.
12448  */
12449  (void) XConfigureImage(display,resource_info,windows,*image,exception);
12450  return(MagickTrue);
12451}
12452
12453/*
12454%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12455%                                                                             %
12456%                                                                             %
12457%                                                                             %
12458+   X S a v e I m a g e                                                       %
12459%                                                                             %
12460%                                                                             %
12461%                                                                             %
12462%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12463%
12464%  XSaveImage() saves an image to a file.
12465%
12466%  The format of the XSaveImage method is:
12467%
12468%      MagickBooleanType XSaveImage(Display *display,
12469%        XResourceInfo *resource_info,XWindows *windows,Image *image,
12470%        ExceptionInfo *exception)
12471%
12472%  A description of each parameter follows:
12473%
12474%    o display: Specifies a connection to an X server; returned from
12475%      XOpenDisplay.
12476%
12477%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12478%
12479%    o windows: Specifies a pointer to a XWindows structure.
12480%
12481%    o image: the image.
12482%
12483%    o exception: return any errors or warnings in this structure.
12484%
12485*/
12486static MagickBooleanType XSaveImage(Display *display,
12487  XResourceInfo *resource_info,XWindows *windows,Image *image,
12488  ExceptionInfo *exception)
12489{
12490  char
12491    filename[MaxTextExtent],
12492    geometry[MaxTextExtent];
12493
12494  Image
12495    *save_image;
12496
12497  ImageInfo
12498    *image_info;
12499
12500  MagickStatusType
12501    status;
12502
12503  /*
12504    Request file name from user.
12505  */
12506  if (resource_info->write_filename != (char *) NULL)
12507    (void) CopyMagickString(filename,resource_info->write_filename,
12508      MaxTextExtent);
12509  else
12510    {
12511      char
12512        path[MaxTextExtent];
12513
12514      int
12515        status;
12516
12517      GetPathComponent(image->filename,HeadPath,path);
12518      GetPathComponent(image->filename,TailPath,filename);
12519      if (*path != '\0')
12520        {
12521          status=chdir(path);
12522          if (status == -1)
12523            (void) ThrowMagickException(exception,GetMagickModule(),
12524              FileOpenError,"UnableToOpenFile","%s",path);
12525        }
12526    }
12527  XFileBrowserWidget(display,windows,"Save",filename);
12528  if (*filename == '\0')
12529    return(MagickTrue);
12530  if (IsPathAccessible(filename) != MagickFalse)
12531    {
12532      int
12533        status;
12534
12535      /*
12536        File exists-- seek user's permission before overwriting.
12537      */
12538      status=XConfirmWidget(display,windows,"Overwrite",filename);
12539      if (status <= 0)
12540        return(MagickTrue);
12541    }
12542  image_info=CloneImageInfo(resource_info->image_info);
12543  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12544  (void) SetImageInfo(image_info,1,exception);
12545  if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12546      (LocaleCompare(image_info->magick,"JPG") == 0))
12547    {
12548      char
12549        quality[MaxTextExtent];
12550
12551      int
12552        status;
12553
12554      /*
12555        Request JPEG quality from user.
12556      */
12557      (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
12558        image->quality);
12559      status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12560        quality);
12561      if (*quality == '\0')
12562        return(MagickTrue);
12563      image->quality=StringToUnsignedLong(quality);
12564      image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12565    }
12566  if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12567      (LocaleCompare(image_info->magick,"PDF") == 0) ||
12568      (LocaleCompare(image_info->magick,"PS") == 0) ||
12569      (LocaleCompare(image_info->magick,"PS2") == 0))
12570    {
12571      char
12572        geometry[MaxTextExtent];
12573
12574      /*
12575        Request page geometry from user.
12576      */
12577      (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12578      if (LocaleCompare(image_info->magick,"PDF") == 0)
12579        (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12580      if (image_info->page != (char *) NULL)
12581        (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12582      XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12583        "Select page geometry:",geometry);
12584      if (*geometry != '\0')
12585        image_info->page=GetPageGeometry(geometry);
12586    }
12587  /*
12588    Apply image transforms.
12589  */
12590  XSetCursorState(display,windows,MagickTrue);
12591  XCheckRefreshWindows(display,windows);
12592  save_image=CloneImage(image,0,0,MagickTrue,exception);
12593  if (save_image == (Image *) NULL)
12594    return(MagickFalse);
12595  (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
12596    windows->image.ximage->width,windows->image.ximage->height);
12597  (void) TransformImage(&save_image,windows->image.crop_geometry,geometry);
12598  /*
12599    Write image.
12600  */
12601  (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12602  status=WriteImage(image_info,save_image,exception);
12603  if (status != MagickFalse)
12604    image->taint=MagickFalse;
12605  save_image=DestroyImage(save_image);
12606  image_info=DestroyImageInfo(image_info);
12607  XSetCursorState(display,windows,MagickFalse);
12608  return(status != 0 ? MagickTrue : MagickFalse);
12609}
12610
12611/*
12612%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12613%                                                                             %
12614%                                                                             %
12615%                                                                             %
12616+   X S c r e e n E v e n t                                                   %
12617%                                                                             %
12618%                                                                             %
12619%                                                                             %
12620%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12621%
12622%  XScreenEvent() handles global events associated with the Pan and Magnify
12623%  windows.
12624%
12625%  The format of the XScreenEvent function is:
12626%
12627%      void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12628%
12629%  A description of each parameter follows:
12630%
12631%    o display: Specifies a pointer to the Display structure;  returned from
12632%      XOpenDisplay.
12633%
12634%    o windows: Specifies a pointer to a XWindows structure.
12635%
12636%    o event: Specifies a pointer to a X11 XEvent structure.
12637%
12638%
12639*/
12640
12641#if defined(__cplusplus) || defined(c_plusplus)
12642extern "C" {
12643#endif
12644
12645static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12646{
12647  register XWindows
12648    *windows;
12649
12650  windows=(XWindows *) data;
12651  if ((event->type == ClientMessage) &&
12652      (event->xclient.window == windows->image.id))
12653    return(MagickFalse);
12654  return(MagickTrue);
12655}
12656
12657#if defined(__cplusplus) || defined(c_plusplus)
12658}
12659#endif
12660
12661static void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12662{
12663  register int
12664    x,
12665    y;
12666
12667  (void) XIfEvent(display,event,XPredicate,(char *) windows);
12668  if (event->xany.window == windows->command.id)
12669    return;
12670  switch (event->type)
12671  {
12672    case ButtonPress:
12673    case ButtonRelease:
12674    {
12675      if ((event->xbutton.button == Button3) &&
12676          (event->xbutton.state & Mod1Mask))
12677        {
12678          /*
12679            Convert Alt-Button3 to Button2.
12680          */
12681          event->xbutton.button=Button2;
12682          event->xbutton.state&=(~Mod1Mask);
12683        }
12684      if (event->xbutton.window == windows->backdrop.id)
12685        {
12686          (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12687            event->xbutton.time);
12688          break;
12689        }
12690      if (event->xbutton.window == windows->pan.id)
12691        {
12692          XPanImage(display,windows,event);
12693          break;
12694        }
12695      if (event->xbutton.window == windows->image.id)
12696        if (event->xbutton.button == Button2)
12697          {
12698            /*
12699              Update magnified image.
12700            */
12701            x=event->xbutton.x;
12702            y=event->xbutton.y;
12703            if (x < 0)
12704              x=0;
12705            else
12706              if (x >= (int) windows->image.width)
12707                x=(int) (windows->image.width-1);
12708            windows->magnify.x=(int) windows->image.x+x;
12709            if (y < 0)
12710              y=0;
12711            else
12712             if (y >= (int) windows->image.height)
12713               y=(int) (windows->image.height-1);
12714            windows->magnify.y=windows->image.y+y;
12715            if (windows->magnify.mapped == MagickFalse)
12716              (void) XMapRaised(display,windows->magnify.id);
12717            XMakeMagnifyImage(display,windows);
12718            if (event->type == ButtonRelease)
12719              (void) XWithdrawWindow(display,windows->info.id,
12720                windows->info.screen);
12721            break;
12722          }
12723      break;
12724    }
12725    case ClientMessage:
12726    {
12727      /*
12728        If client window delete message, exit.
12729      */
12730      if (event->xclient.message_type != windows->wm_protocols)
12731        break;
12732      if (*event->xclient.data.l != (long) windows->wm_delete_window)
12733        break;
12734      if (event->xclient.window == windows->magnify.id)
12735        {
12736          (void) XWithdrawWindow(display,windows->magnify.id,
12737            windows->magnify.screen);
12738          break;
12739        }
12740      break;
12741    }
12742    case ConfigureNotify:
12743    {
12744      if (event->xconfigure.window == windows->magnify.id)
12745        {
12746          unsigned int
12747            magnify;
12748
12749          /*
12750            Magnify window has a new configuration.
12751          */
12752          windows->magnify.width=(unsigned int) event->xconfigure.width;
12753          windows->magnify.height=(unsigned int) event->xconfigure.height;
12754          if (windows->magnify.mapped == MagickFalse)
12755            break;
12756          magnify=1;
12757          while ((int) magnify <= event->xconfigure.width)
12758            magnify<<=1;
12759          while ((int) magnify <= event->xconfigure.height)
12760            magnify<<=1;
12761          magnify>>=1;
12762          if (((int) magnify != event->xconfigure.width) ||
12763              ((int) magnify != event->xconfigure.height))
12764            {
12765              XWindowChanges
12766                window_changes;
12767
12768              window_changes.width=(int) magnify;
12769              window_changes.height=(int) magnify;
12770              (void) XReconfigureWMWindow(display,windows->magnify.id,
12771                windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12772                &window_changes);
12773              break;
12774            }
12775          XMakeMagnifyImage(display,windows);
12776          break;
12777        }
12778      break;
12779    }
12780    case Expose:
12781    {
12782      if (event->xexpose.window == windows->image.id)
12783        {
12784          XRefreshWindow(display,&windows->image,event);
12785          break;
12786        }
12787      if (event->xexpose.window == windows->pan.id)
12788        if (event->xexpose.count == 0)
12789          {
12790            XDrawPanRectangle(display,windows);
12791            break;
12792          }
12793      if (event->xexpose.window == windows->magnify.id)
12794        if (event->xexpose.count == 0)
12795          {
12796            XMakeMagnifyImage(display,windows);
12797            break;
12798          }
12799      break;
12800    }
12801    case KeyPress:
12802    {
12803      char
12804        command[MaxTextExtent];
12805
12806      KeySym
12807        key_symbol;
12808
12809      if (event->xkey.window != windows->magnify.id)
12810        break;
12811      /*
12812        Respond to a user key press.
12813      */
12814      (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12815        sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12816      XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol);
12817      break;
12818    }
12819    case MapNotify:
12820    {
12821      if (event->xmap.window == windows->magnify.id)
12822        {
12823          windows->magnify.mapped=MagickTrue;
12824          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12825          break;
12826        }
12827      if (event->xmap.window == windows->info.id)
12828        {
12829          windows->info.mapped=MagickTrue;
12830          break;
12831        }
12832      break;
12833    }
12834    case MotionNotify:
12835    {
12836      while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12837      if (event->xmotion.window == windows->image.id)
12838        if (windows->magnify.mapped != MagickFalse)
12839          {
12840            /*
12841              Update magnified image.
12842            */
12843            x=event->xmotion.x;
12844            y=event->xmotion.y;
12845            if (x < 0)
12846              x=0;
12847            else
12848              if (x >= (int) windows->image.width)
12849                x=(int) (windows->image.width-1);
12850            windows->magnify.x=(int) windows->image.x+x;
12851            if (y < 0)
12852              y=0;
12853            else
12854             if (y >= (int) windows->image.height)
12855               y=(int) (windows->image.height-1);
12856            windows->magnify.y=windows->image.y+y;
12857            XMakeMagnifyImage(display,windows);
12858          }
12859      break;
12860    }
12861    case UnmapNotify:
12862    {
12863      if (event->xunmap.window == windows->magnify.id)
12864        {
12865          windows->magnify.mapped=MagickFalse;
12866          break;
12867        }
12868      if (event->xunmap.window == windows->info.id)
12869        {
12870          windows->info.mapped=MagickFalse;
12871          break;
12872        }
12873      break;
12874    }
12875    default:
12876      break;
12877  }
12878}
12879
12880/*
12881%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12882%                                                                             %
12883%                                                                             %
12884%                                                                             %
12885+   X S e t C r o p G e o m e t r y                                           %
12886%                                                                             %
12887%                                                                             %
12888%                                                                             %
12889%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12890%
12891%  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12892%  and translates it to a cropping geometry relative to the image.
12893%
12894%  The format of the XSetCropGeometry method is:
12895%
12896%      void XSetCropGeometry(Display *display,XWindows *windows,
12897%        RectangleInfo *crop_info,Image *image)
12898%
12899%  A description of each parameter follows:
12900%
12901%    o display: Specifies a connection to an X server; returned from
12902%      XOpenDisplay.
12903%
12904%    o windows: Specifies a pointer to a XWindows structure.
12905%
12906%    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12907%      Image window to crop.
12908%
12909%    o image: the image.
12910%
12911*/
12912static void XSetCropGeometry(Display *display,XWindows *windows,
12913  RectangleInfo *crop_info,Image *image)
12914{
12915  char
12916    text[MaxTextExtent];
12917
12918  int
12919    x,
12920    y;
12921
12922  MagickRealType
12923    scale_factor;
12924
12925  unsigned int
12926    height,
12927    width;
12928
12929  if (windows->info.mapped != MagickFalse)
12930    {
12931      /*
12932        Display info on cropping rectangle.
12933      */
12934      (void) FormatLocaleString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
12935        (double) crop_info->width,(double) crop_info->height,(double)
12936        crop_info->x,(double) crop_info->y);
12937      XInfoWidget(display,windows,text);
12938    }
12939  /*
12940    Cropping geometry is relative to any previous crop geometry.
12941  */
12942  x=0;
12943  y=0;
12944  width=(unsigned int) image->columns;
12945  height=(unsigned int) image->rows;
12946  if (windows->image.crop_geometry != (char *) NULL)
12947    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12948  else
12949    windows->image.crop_geometry=AcquireString((char *) NULL);
12950  /*
12951    Define the crop geometry string from the cropping rectangle.
12952  */
12953  scale_factor=(MagickRealType) width/windows->image.ximage->width;
12954  if (crop_info->x > 0)
12955    x+=(int) (scale_factor*crop_info->x+0.5);
12956  width=(unsigned int) (scale_factor*crop_info->width+0.5);
12957  if (width == 0)
12958    width=1;
12959  scale_factor=(MagickRealType) height/windows->image.ximage->height;
12960  if (crop_info->y > 0)
12961    y+=(int) (scale_factor*crop_info->y+0.5);
12962  height=(unsigned int) (scale_factor*crop_info->height+0.5);
12963  if (height == 0)
12964    height=1;
12965  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12966    "%ux%u%+d%+d",width,height,x,y);
12967}
12968
12969/*
12970%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12971%                                                                             %
12972%                                                                             %
12973%                                                                             %
12974+   X T i l e I m a g e                                                       %
12975%                                                                             %
12976%                                                                             %
12977%                                                                             %
12978%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12979%
12980%  XTileImage() loads or deletes a selected tile from a visual image directory.
12981%  The load or delete command is chosen from a menu.
12982%
12983%  The format of the XTileImage method is:
12984%
12985%      Image *XTileImage(Display *display,XResourceInfo *resource_info,
12986%        XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
12987%
12988%  A description of each parameter follows:
12989%
12990%    o tile_image:  XTileImage reads or deletes the tile image
12991%      and returns it.  A null image is returned if an error occurs.
12992%
12993%    o display: Specifies a connection to an X server;  returned from
12994%      XOpenDisplay.
12995%
12996%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12997%
12998%    o windows: Specifies a pointer to a XWindows structure.
12999%
13000%    o image: the image; returned from ReadImage.
13001%
13002%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
13003%      the entire image is refreshed.
13004%
13005%    o exception: return any errors or warnings in this structure.
13006%
13007*/
13008static Image *XTileImage(Display *display,XResourceInfo *resource_info,
13009  XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13010{
13011  static const char
13012    *VerbMenu[] =
13013    {
13014      "Load",
13015      "Next",
13016      "Former",
13017      "Delete",
13018      "Update",
13019      (char *) NULL,
13020    };
13021
13022  static const ModeType
13023    TileCommands[] =
13024    {
13025      TileLoadCommand,
13026      TileNextCommand,
13027      TileFormerCommand,
13028      TileDeleteCommand,
13029      TileUpdateCommand
13030    };
13031
13032  char
13033    command[MaxTextExtent],
13034    filename[MaxTextExtent];
13035
13036  Image
13037    *tile_image;
13038
13039  int
13040    id,
13041    status,
13042    tile,
13043    x,
13044    y;
13045
13046  MagickRealType
13047    scale_factor;
13048
13049  register char
13050    *p,
13051    *q;
13052
13053  register int
13054    i;
13055
13056  unsigned int
13057    height,
13058    width;
13059
13060  /*
13061    Tile image is relative to montage image configuration.
13062  */
13063  x=0;
13064  y=0;
13065  width=(unsigned int) image->columns;
13066  height=(unsigned int) image->rows;
13067  if (windows->image.crop_geometry != (char *) NULL)
13068    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13069  scale_factor=(MagickRealType) width/windows->image.ximage->width;
13070  event->xbutton.x+=windows->image.x;
13071  event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13072  scale_factor=(MagickRealType) height/windows->image.ximage->height;
13073  event->xbutton.y+=windows->image.y;
13074  event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13075  /*
13076    Determine size and location of each tile in the visual image directory.
13077  */
13078  width=(unsigned int) image->columns;
13079  height=(unsigned int) image->rows;
13080  x=0;
13081  y=0;
13082  (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13083  tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13084    (event->xbutton.x-x)/width;
13085  if (tile < 0)
13086    {
13087      /*
13088        Button press is outside any tile.
13089      */
13090      (void) XBell(display,0);
13091      return((Image *) NULL);
13092    }
13093  /*
13094    Determine file name from the tile directory.
13095  */
13096  p=image->directory;
13097  for (i=tile; (i != 0) && (*p != '\0'); )
13098  {
13099    if (*p == '\n')
13100      i--;
13101    p++;
13102  }
13103  if (*p == '\0')
13104    {
13105      /*
13106        Button press is outside any tile.
13107      */
13108      (void) XBell(display,0);
13109      return((Image *) NULL);
13110    }
13111  /*
13112    Select a command from the pop-up menu.
13113  */
13114  id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13115  if (id < 0)
13116    return((Image *) NULL);
13117  q=p;
13118  while ((*q != '\n') && (*q != '\0'))
13119    q++;
13120  (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13121  /*
13122    Perform command for the selected tile.
13123  */
13124  XSetCursorState(display,windows,MagickTrue);
13125  XCheckRefreshWindows(display,windows);
13126  tile_image=NewImageList();
13127  switch (TileCommands[id])
13128  {
13129    case TileLoadCommand:
13130    {
13131      /*
13132        Load tile image.
13133      */
13134      XCheckRefreshWindows(display,windows);
13135      (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13136        MaxTextExtent);
13137      (void) CopyMagickString(resource_info->image_info->filename,filename,
13138        MaxTextExtent);
13139      tile_image=ReadImage(resource_info->image_info,exception);
13140      CatchException(exception);
13141      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13142      break;
13143    }
13144    case TileNextCommand:
13145    {
13146      /*
13147        Display next image.
13148      */
13149      XClientMessage(display,windows->image.id,windows->im_protocols,
13150        windows->im_next_image,CurrentTime);
13151      break;
13152    }
13153    case TileFormerCommand:
13154    {
13155      /*
13156        Display former image.
13157      */
13158      XClientMessage(display,windows->image.id,windows->im_protocols,
13159        windows->im_former_image,CurrentTime);
13160      break;
13161    }
13162    case TileDeleteCommand:
13163    {
13164      /*
13165        Delete tile image.
13166      */
13167      if (IsPathAccessible(filename) == MagickFalse)
13168        {
13169          XNoticeWidget(display,windows,"Image file does not exist:",filename);
13170          break;
13171        }
13172      status=XConfirmWidget(display,windows,"Really delete tile",filename);
13173      if (status <= 0)
13174        break;
13175      status=remove(filename) != 0 ? MagickTrue : MagickFalse;
13176      if (status != MagickFalse)
13177        {
13178          XNoticeWidget(display,windows,"Unable to delete image file:",
13179            filename);
13180          break;
13181        }
13182    }
13183    case TileUpdateCommand:
13184    {
13185      int
13186        x_offset,
13187        y_offset;
13188
13189      PixelPacket
13190        pixel;
13191
13192      register int
13193        j;
13194
13195      register Quantum
13196        *s;
13197
13198      /*
13199        Ensure all the images exist.
13200      */
13201      tile=0;
13202      for (p=image->directory; *p != '\0'; p++)
13203      {
13204        CacheView
13205          *image_view;
13206
13207        q=p;
13208        while ((*q != '\n') && (*q != '\0'))
13209          q++;
13210        (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13211        p=q;
13212        if (IsPathAccessible(filename) != MagickFalse)
13213          {
13214            tile++;
13215            continue;
13216          }
13217        /*
13218          Overwrite tile with background color.
13219        */
13220        x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13221        y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13222        image_view=AcquireCacheView(image);
13223        (void) GetOneCacheViewVirtualPixel(image_view,0,0,&pixel,exception);
13224        for (i=0; i < (int) height; i++)
13225        {
13226          s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13227            y_offset+i,width,1,exception);
13228          if (s == (Quantum *) NULL)
13229            break;
13230          for (j=0; j < (int) width; j++)
13231          {
13232            SetPixelPacket(image,&pixel,s);
13233            s+=GetPixelChannels(image);
13234          }
13235          if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13236            break;
13237        }
13238        image_view=DestroyCacheView(image_view);
13239        tile++;
13240      }
13241      windows->image.window_changes.width=(int) image->columns;
13242      windows->image.window_changes.height=(int) image->rows;
13243      XConfigureImageColormap(display,resource_info,windows,image);
13244      (void) XConfigureImage(display,resource_info,windows,image,exception);
13245      break;
13246    }
13247    default:
13248      break;
13249  }
13250  XSetCursorState(display,windows,MagickFalse);
13251  return(tile_image);
13252}
13253
13254/*
13255%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13256%                                                                             %
13257%                                                                             %
13258%                                                                             %
13259+   X T r a n s l a t e I m a g e                                             %
13260%                                                                             %
13261%                                                                             %
13262%                                                                             %
13263%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13264%
13265%  XTranslateImage() translates the image within an Image window by one pixel
13266%  as specified by the key symbol.  If the image has a `montage string the
13267%  translation is respect to the width and height contained within the string.
13268%
13269%  The format of the XTranslateImage method is:
13270%
13271%      void XTranslateImage(Display *display,XWindows *windows,
13272%        Image *image,const KeySym key_symbol)
13273%
13274%  A description of each parameter follows:
13275%
13276%    o display: Specifies a connection to an X server; returned from
13277%      XOpenDisplay.
13278%
13279%    o windows: Specifies a pointer to a XWindows structure.
13280%
13281%    o image: the image.
13282%
13283%    o key_symbol: Specifies a KeySym which indicates which side of the image
13284%      to trim.
13285%
13286*/
13287static void XTranslateImage(Display *display,XWindows *windows,
13288  Image *image,const KeySym key_symbol)
13289{
13290  char
13291    text[MaxTextExtent];
13292
13293  int
13294    x,
13295    y;
13296
13297  unsigned int
13298    x_offset,
13299    y_offset;
13300
13301  /*
13302    User specified a pan position offset.
13303  */
13304  x_offset=windows->image.width;
13305  y_offset=windows->image.height;
13306  if (image->montage != (char *) NULL)
13307    (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13308  switch ((int) key_symbol)
13309  {
13310    case XK_Home:
13311    case XK_KP_Home:
13312    {
13313      windows->image.x=(int) windows->image.width/2;
13314      windows->image.y=(int) windows->image.height/2;
13315      break;
13316    }
13317    case XK_Left:
13318    case XK_KP_Left:
13319    {
13320      windows->image.x-=x_offset;
13321      break;
13322    }
13323    case XK_Next:
13324    case XK_Up:
13325    case XK_KP_Up:
13326    {
13327      windows->image.y-=y_offset;
13328      break;
13329    }
13330    case XK_Right:
13331    case XK_KP_Right:
13332    {
13333      windows->image.x+=x_offset;
13334      break;
13335    }
13336    case XK_Prior:
13337    case XK_Down:
13338    case XK_KP_Down:
13339    {
13340      windows->image.y+=y_offset;
13341      break;
13342    }
13343    default:
13344      return;
13345  }
13346  /*
13347    Check boundary conditions.
13348  */
13349  if (windows->image.x < 0)
13350    windows->image.x=0;
13351  else
13352    if ((int) (windows->image.x+windows->image.width) >
13353        windows->image.ximage->width)
13354      windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13355  if (windows->image.y < 0)
13356    windows->image.y=0;
13357  else
13358    if ((int) (windows->image.y+windows->image.height) >
13359        windows->image.ximage->height)
13360      windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13361  /*
13362    Refresh Image window.
13363  */
13364  (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
13365    windows->image.width,windows->image.height,windows->image.x,
13366    windows->image.y);
13367  XInfoWidget(display,windows,text);
13368  XCheckRefreshWindows(display,windows);
13369  XDrawPanRectangle(display,windows);
13370  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13371  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13372}
13373
13374/*
13375%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13376%                                                                             %
13377%                                                                             %
13378%                                                                             %
13379+   X T r i m I m a g e                                                       %
13380%                                                                             %
13381%                                                                             %
13382%                                                                             %
13383%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13384%
13385%  XTrimImage() trims the edges from the Image window.
13386%
13387%  The format of the XTrimImage method is:
13388%
13389%      MagickBooleanType XTrimImage(Display *display,
13390%        XResourceInfo *resource_info,XWindows *windows,Image *image,
13391%        ExceptionInfo *exception)
13392%
13393%  A description of each parameter follows:
13394%
13395%    o display: Specifies a connection to an X server; returned from
13396%      XOpenDisplay.
13397%
13398%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13399%
13400%    o windows: Specifies a pointer to a XWindows structure.
13401%
13402%    o image: the image.
13403%
13404%    o exception: return any errors or warnings in this structure.
13405%
13406*/
13407static MagickBooleanType XTrimImage(Display *display,
13408  XResourceInfo *resource_info,XWindows *windows,Image *image,
13409  ExceptionInfo *exception)
13410{
13411  RectangleInfo
13412    trim_info;
13413
13414  register int
13415    x,
13416    y;
13417
13418  size_t
13419    background,
13420    pixel;
13421
13422  /*
13423    Trim edges from image.
13424  */
13425  XSetCursorState(display,windows,MagickTrue);
13426  XCheckRefreshWindows(display,windows);
13427  /*
13428    Crop the left edge.
13429  */
13430  background=XGetPixel(windows->image.ximage,0,0);
13431  trim_info.width=(size_t) windows->image.ximage->width;
13432  for (x=0; x < windows->image.ximage->width; x++)
13433  {
13434    for (y=0; y < windows->image.ximage->height; y++)
13435    {
13436      pixel=XGetPixel(windows->image.ximage,x,y);
13437      if (pixel != background)
13438        break;
13439    }
13440    if (y < windows->image.ximage->height)
13441      break;
13442  }
13443  trim_info.x=(ssize_t) x;
13444  if (trim_info.x == (ssize_t) windows->image.ximage->width)
13445    {
13446      XSetCursorState(display,windows,MagickFalse);
13447      return(MagickFalse);
13448    }
13449  /*
13450    Crop the right edge.
13451  */
13452  background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13453  for (x=windows->image.ximage->width-1; x != 0; x--)
13454  {
13455    for (y=0; y < windows->image.ximage->height; y++)
13456    {
13457      pixel=XGetPixel(windows->image.ximage,x,y);
13458      if (pixel != background)
13459        break;
13460    }
13461    if (y < windows->image.ximage->height)
13462      break;
13463  }
13464  trim_info.width=(size_t) (x-trim_info.x+1);
13465  /*
13466    Crop the top edge.
13467  */
13468  background=XGetPixel(windows->image.ximage,0,0);
13469  trim_info.height=(size_t) windows->image.ximage->height;
13470  for (y=0; y < windows->image.ximage->height; y++)
13471  {
13472    for (x=0; x < windows->image.ximage->width; x++)
13473    {
13474      pixel=XGetPixel(windows->image.ximage,x,y);
13475      if (pixel != background)
13476        break;
13477    }
13478    if (x < windows->image.ximage->width)
13479      break;
13480  }
13481  trim_info.y=(ssize_t) y;
13482  /*
13483    Crop the bottom edge.
13484  */
13485  background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13486  for (y=windows->image.ximage->height-1; y != 0; y--)
13487  {
13488    for (x=0; x < windows->image.ximage->width; x++)
13489    {
13490      pixel=XGetPixel(windows->image.ximage,x,y);
13491      if (pixel != background)
13492        break;
13493    }
13494    if (x < windows->image.ximage->width)
13495      break;
13496  }
13497  trim_info.height=(size_t) y-trim_info.y+1;
13498  if (((unsigned int) trim_info.width != windows->image.width) ||
13499      ((unsigned int) trim_info.height != windows->image.height))
13500    {
13501      /*
13502        Reconfigure Image window as defined by the trimming rectangle.
13503      */
13504      XSetCropGeometry(display,windows,&trim_info,image);
13505      windows->image.window_changes.width=(int) trim_info.width;
13506      windows->image.window_changes.height=(int) trim_info.height;
13507      (void) XConfigureImage(display,resource_info,windows,image,exception);
13508    }
13509  XSetCursorState(display,windows,MagickFalse);
13510  return(MagickTrue);
13511}
13512
13513/*
13514%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13515%                                                                             %
13516%                                                                             %
13517%                                                                             %
13518+   X V i s u a l D i r e c t o r y I m a g e                                 %
13519%                                                                             %
13520%                                                                             %
13521%                                                                             %
13522%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13523%
13524%  XVisualDirectoryImage() creates a Visual Image Directory.
13525%
13526%  The format of the XVisualDirectoryImage method is:
13527%
13528%      Image *XVisualDirectoryImage(Display *display,
13529%        XResourceInfo *resource_info,XWindows *windows,
13530%        ExceptionInfo *exception)
13531%
13532%  A description of each parameter follows:
13533%
13534%    o display: Specifies a connection to an X server; returned from
13535%      XOpenDisplay.
13536%
13537%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13538%
13539%    o windows: Specifies a pointer to a XWindows structure.
13540%
13541%    o exception: return any errors or warnings in this structure.
13542%
13543*/
13544static Image *XVisualDirectoryImage(Display *display,
13545  XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
13546{
13547#define TileImageTag  "Scale/Image"
13548#define XClientName  "montage"
13549
13550  char
13551    **filelist;
13552
13553  Image
13554    *images,
13555    *montage_image,
13556    *next_image,
13557    *thumbnail_image;
13558
13559  ImageInfo
13560    *read_info;
13561
13562  int
13563    number_files;
13564
13565  MagickBooleanType
13566    backdrop;
13567
13568  MagickStatusType
13569    status;
13570
13571  MontageInfo
13572    *montage_info;
13573
13574  RectangleInfo
13575    geometry;
13576
13577  register int
13578    i;
13579
13580  static char
13581    filename[MaxTextExtent] = "\0",
13582    filenames[MaxTextExtent] = "*";
13583
13584  XResourceInfo
13585    background_resources;
13586
13587  /*
13588    Request file name from user.
13589  */
13590  XFileBrowserWidget(display,windows,"Directory",filenames);
13591  if (*filenames == '\0')
13592    return((Image *) NULL);
13593  /*
13594    Expand the filenames.
13595  */
13596  filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13597  if (filelist == (char **) NULL)
13598    {
13599      ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13600        filenames);
13601      return((Image *) NULL);
13602    }
13603  number_files=1;
13604  filelist[0]=filenames;
13605  status=ExpandFilenames(&number_files,&filelist);
13606  if ((status == MagickFalse) || (number_files == 0))
13607    {
13608      if (number_files == 0)
13609        ThrowXWindowFatalException(ImageError,"NoImagesWereFound",filenames)
13610      else
13611        ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13612          filenames);
13613      return((Image *) NULL);
13614    }
13615  /*
13616    Set image background resources.
13617  */
13618  background_resources=(*resource_info);
13619  background_resources.window_id=AcquireString("");
13620  (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
13621    "0x%lx",windows->image.id);
13622  background_resources.backdrop=MagickTrue;
13623  /*
13624    Read each image and convert them to a tile.
13625  */
13626  backdrop=(windows->visual_info->klass == TrueColor) ||
13627    (windows->visual_info->klass == DirectColor) ? MagickTrue : MagickFalse;
13628  read_info=CloneImageInfo(resource_info->image_info);
13629  (void) SetImageOption(read_info,"jpeg:size","120x120");
13630  (void) CloneString(&read_info->size,DefaultTileGeometry);
13631  (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13632    (void *) NULL);
13633  images=NewImageList();
13634  XSetCursorState(display,windows,MagickTrue);
13635  XCheckRefreshWindows(display,windows);
13636  for (i=0; i < (int) number_files; i++)
13637  {
13638    (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13639    filelist[i]=DestroyString(filelist[i]);
13640    *read_info->magick='\0';
13641    next_image=ReadImage(read_info,exception);
13642    CatchException(exception);
13643    if (next_image != (Image *) NULL)
13644      {
13645        (void) DeleteImageProperty(next_image,"label");
13646        (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13647          read_info,next_image,DefaultTileLabel,exception));
13648        (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13649          exception);
13650        thumbnail_image=ThumbnailImage(next_image,geometry.width,
13651          geometry.height,exception);
13652        if (thumbnail_image != (Image *) NULL)
13653          {
13654            next_image=DestroyImage(next_image);
13655            next_image=thumbnail_image;
13656          }
13657        if (backdrop)
13658          {
13659            (void) XDisplayBackgroundImage(display,&background_resources,
13660              next_image,exception);
13661            XSetCursorState(display,windows,MagickTrue);
13662          }
13663        AppendImageToList(&images,next_image);
13664        if (images->progress_monitor != (MagickProgressMonitor) NULL)
13665          {
13666            MagickBooleanType
13667              proceed;
13668
13669            proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13670              (MagickSizeType) number_files);
13671            if (proceed == MagickFalse)
13672              break;
13673          }
13674      }
13675  }
13676  filelist=(char **) RelinquishMagickMemory(filelist);
13677  if (images == (Image *) NULL)
13678    {
13679      read_info=DestroyImageInfo(read_info);
13680      XSetCursorState(display,windows,MagickFalse);
13681      ThrowXWindowFatalException(ImageError,"NoImagesWereLoaded",filenames);
13682      return((Image *) NULL);
13683    }
13684  /*
13685    Create the Visual Image Directory.
13686  */
13687  montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13688  montage_info->pointsize=10;
13689  if (resource_info->font != (char *) NULL)
13690    (void) CloneString(&montage_info->font,resource_info->font);
13691  (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13692  montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13693    images),exception);
13694  images=DestroyImageList(images);
13695  montage_info=DestroyMontageInfo(montage_info);
13696  read_info=DestroyImageInfo(read_info);
13697  XSetCursorState(display,windows,MagickFalse);
13698  if (montage_image == (Image *) NULL)
13699    return(montage_image);
13700  XClientMessage(display,windows->image.id,windows->im_protocols,
13701    windows->im_next_image,CurrentTime);
13702  return(montage_image);
13703}
13704
13705/*
13706%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13707%                                                                             %
13708%                                                                             %
13709%                                                                             %
13710%   X D i s p l a y B a c k g r o u n d I m a g e                             %
13711%                                                                             %
13712%                                                                             %
13713%                                                                             %
13714%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13715%
13716%  XDisplayBackgroundImage() displays an image in the background of a window.
13717%
13718%  The format of the XDisplayBackgroundImage method is:
13719%
13720%      MagickBooleanType XDisplayBackgroundImage(Display *display,
13721%        XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13722%
13723%  A description of each parameter follows:
13724%
13725%    o display: Specifies a connection to an X server;  returned from
13726%      XOpenDisplay.
13727%
13728%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13729%
13730%    o image: the image.
13731%
13732%    o exception: return any errors or warnings in this structure.
13733%
13734*/
13735MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13736  XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13737{
13738  char
13739    geometry[MaxTextExtent],
13740    visual_type[MaxTextExtent];
13741
13742  int
13743    height,
13744    status,
13745    width;
13746
13747  RectangleInfo
13748    geometry_info;
13749
13750  static XPixelInfo
13751    pixel;
13752
13753  static XStandardColormap
13754    *map_info;
13755
13756  static XVisualInfo
13757    *visual_info = (XVisualInfo *) NULL;
13758
13759  static XWindowInfo
13760    window_info;
13761
13762  size_t
13763    delay;
13764
13765  Window
13766    root_window;
13767
13768  XGCValues
13769    context_values;
13770
13771  XResourceInfo
13772    resources;
13773
13774  XWindowAttributes
13775    window_attributes;
13776
13777  /*
13778    Determine target window.
13779  */
13780  assert(image != (Image *) NULL);
13781  assert(image->signature == MagickSignature);
13782  if (image->debug != MagickFalse)
13783    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13784  resources=(*resource_info);
13785  window_info.id=(Window) NULL;
13786  root_window=XRootWindow(display,XDefaultScreen(display));
13787  if (LocaleCompare(resources.window_id,"root") == 0)
13788    window_info.id=root_window;
13789  else
13790    {
13791      if (isdigit((unsigned char) *resources.window_id) != 0)
13792        window_info.id=XWindowByID(display,root_window,
13793          (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13794      if (window_info.id == (Window) NULL)
13795        window_info.id=XWindowByName(display,root_window,resources.window_id);
13796    }
13797  if (window_info.id == (Window) NULL)
13798    {
13799      ThrowXWindowFatalException(XServerError,"NoWindowWithSpecifiedIDExists",
13800        resources.window_id);
13801      return(MagickFalse);
13802    }
13803  /*
13804    Determine window visual id.
13805  */
13806  window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13807  window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13808  (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13809  status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13810  if (status != 0)
13811    (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
13812      XVisualIDFromVisual(window_attributes.visual));
13813  if (visual_info == (XVisualInfo *) NULL)
13814    {
13815      /*
13816        Allocate standard colormap.
13817      */
13818      map_info=XAllocStandardColormap();
13819      if (map_info == (XStandardColormap *) NULL)
13820        ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13821          image->filename);
13822      map_info->colormap=(Colormap) NULL;
13823      pixel.pixels=(unsigned long *) NULL;
13824      /*
13825        Initialize visual info.
13826      */
13827      resources.map_type=(char *) NULL;
13828      resources.visual_type=visual_type;
13829      visual_info=XBestVisualInfo(display,map_info,&resources);
13830      if (visual_info == (XVisualInfo *) NULL)
13831        ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13832          resources.visual_type);
13833      /*
13834        Initialize window info.
13835      */
13836      window_info.ximage=(XImage *) NULL;
13837      window_info.matte_image=(XImage *) NULL;
13838      window_info.pixmap=(Pixmap) NULL;
13839      window_info.matte_pixmap=(Pixmap) NULL;
13840    }
13841  /*
13842    Free previous root colors.
13843  */
13844  if (window_info.id == root_window)
13845    (void) XDestroyWindowColors(display,root_window);
13846  /*
13847    Initialize Standard Colormap.
13848  */
13849  resources.colormap=SharedColormap;
13850  XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel);
13851  /*
13852    Graphic context superclass.
13853  */
13854  context_values.background=pixel.background_color.pixel;
13855  context_values.foreground=pixel.foreground_color.pixel;
13856  pixel.annotate_context=XCreateGC(display,window_info.id,
13857    (size_t) (GCBackground | GCForeground),&context_values);
13858  if (pixel.annotate_context == (GC) NULL)
13859    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13860      image->filename);
13861  /*
13862    Initialize Image window attributes.
13863  */
13864  window_info.name=AcquireString("\0");
13865  window_info.icon_name=AcquireString("\0");
13866  XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13867    &resources,&window_info);
13868  /*
13869    Create the X image.
13870  */
13871  window_info.width=(unsigned int) image->columns;
13872  window_info.height=(unsigned int) image->rows;
13873  if ((image->columns != window_info.width) ||
13874      (image->rows != window_info.height))
13875    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13876      image->filename);
13877  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
13878    window_attributes.width,window_attributes.height);
13879  geometry_info.width=window_info.width;
13880  geometry_info.height=window_info.height;
13881  geometry_info.x=(ssize_t) window_info.x;
13882  geometry_info.y=(ssize_t) window_info.y;
13883  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13884    &geometry_info.width,&geometry_info.height);
13885  window_info.width=(unsigned int) geometry_info.width;
13886  window_info.height=(unsigned int) geometry_info.height;
13887  window_info.x=(int) geometry_info.x;
13888  window_info.y=(int) geometry_info.y;
13889  status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13890    window_info.height,exception);
13891  if (status == MagickFalse)
13892    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13893      image->filename);
13894  window_info.x=0;
13895  window_info.y=0;
13896  if (image->debug != MagickFalse)
13897    {
13898      (void) LogMagickEvent(X11Event,GetMagickModule(),
13899        "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13900        (double) image->columns,(double) image->rows);
13901      if (image->colors != 0)
13902        (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13903          image->colors);
13904      (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13905    }
13906  /*
13907    Adjust image dimensions as specified by backdrop or geometry options.
13908  */
13909  width=(int) window_info.width;
13910  height=(int) window_info.height;
13911  if (resources.backdrop != MagickFalse)
13912    {
13913      /*
13914        Center image on window.
13915      */
13916      window_info.x=(window_attributes.width/2)-
13917        (window_info.ximage->width/2);
13918      window_info.y=(window_attributes.height/2)-
13919        (window_info.ximage->height/2);
13920      width=window_attributes.width;
13921      height=window_attributes.height;
13922    }
13923  if ((resources.image_geometry != (char *) NULL) &&
13924      (*resources.image_geometry != '\0'))
13925    {
13926      char
13927        default_geometry[MaxTextExtent];
13928
13929      int
13930        flags,
13931        gravity;
13932
13933      XSizeHints
13934        *size_hints;
13935
13936      /*
13937        User specified geometry.
13938      */
13939      size_hints=XAllocSizeHints();
13940      if (size_hints == (XSizeHints *) NULL)
13941        ThrowXWindowFatalException(ResourceLimitFatalError,
13942          "MemoryAllocationFailed",image->filename);
13943      size_hints->flags=0L;
13944      (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
13945        width,height);
13946      flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
13947        default_geometry,window_info.border_width,size_hints,&window_info.x,
13948        &window_info.y,&width,&height,&gravity);
13949      if (flags & (XValue | YValue))
13950        {
13951          width=window_attributes.width;
13952          height=window_attributes.height;
13953        }
13954      (void) XFree((void *) size_hints);
13955    }
13956  /*
13957    Create the X pixmap.
13958  */
13959  window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
13960    (unsigned int) height,window_info.depth);
13961  if (window_info.pixmap == (Pixmap) NULL)
13962    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
13963      image->filename);
13964  /*
13965    Display pixmap on the window.
13966  */
13967  if (((unsigned int) width > window_info.width) ||
13968      ((unsigned int) height > window_info.height))
13969    (void) XFillRectangle(display,window_info.pixmap,
13970      window_info.annotate_context,0,0,(unsigned int) width,
13971      (unsigned int) height);
13972  (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
13973    window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
13974    window_info.width,(unsigned int) window_info.height);
13975  (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
13976  (void) XClearWindow(display,window_info.id);
13977  delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
13978  XDelay(display,delay == 0UL ? 10UL : delay);
13979  (void) XSync(display,MagickFalse);
13980  return(window_info.id == root_window ? MagickTrue : MagickFalse);
13981}
13982
13983/*
13984%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13985%                                                                             %
13986%                                                                             %
13987%                                                                             %
13988+   X D i s p l a y I m a g e                                                 %
13989%                                                                             %
13990%                                                                             %
13991%                                                                             %
13992%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13993%
13994%  XDisplayImage() displays an image via X11.  A new image is created and
13995%  returned if the user interactively transforms the displayed image.
13996%
13997%  The format of the XDisplayImage method is:
13998%
13999%      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14000%        char **argv,int argc,Image **image,size_t *state,
14001%        ExceptionInfo *exception)
14002%
14003%  A description of each parameter follows:
14004%
14005%    o nexus:  Method XDisplayImage returns an image when the
14006%      user chooses 'Open Image' from the command menu or picks a tile
14007%      from the image directory.  Otherwise a null image is returned.
14008%
14009%    o display: Specifies a connection to an X server;  returned from
14010%      XOpenDisplay.
14011%
14012%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14013%
14014%    o argv: Specifies the application's argument list.
14015%
14016%    o argc: Specifies the number of arguments.
14017%
14018%    o image: Specifies an address to an address of an Image structure;
14019%
14020%    o exception: return any errors or warnings in this structure.
14021%
14022*/
14023MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14024  char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
14025{
14026#define MagnifySize  256  /* must be a power of 2 */
14027#define MagickMenus  10
14028#define MagickTitle  "Commands"
14029
14030  static const char
14031    *CommandMenu[] =
14032    {
14033      "File",
14034      "Edit",
14035      "View",
14036      "Transform",
14037      "Enhance",
14038      "Effects",
14039      "F/X",
14040      "Image Edit",
14041      "Miscellany",
14042      "Help",
14043      (char *) NULL
14044    },
14045    *FileMenu[] =
14046    {
14047      "Open...",
14048      "Next",
14049      "Former",
14050      "Select...",
14051      "Save...",
14052      "Print...",
14053      "Delete...",
14054      "New...",
14055      "Visual Directory...",
14056      "Quit",
14057      (char *) NULL
14058    },
14059    *EditMenu[] =
14060    {
14061      "Undo",
14062      "Redo",
14063      "Cut",
14064      "Copy",
14065      "Paste",
14066      (char *) NULL
14067    },
14068    *ViewMenu[] =
14069    {
14070      "Half Size",
14071      "Original Size",
14072      "Double Size",
14073      "Resize...",
14074      "Apply",
14075      "Refresh",
14076      "Restore",
14077      (char *) NULL
14078    },
14079    *TransformMenu[] =
14080    {
14081      "Crop",
14082      "Chop",
14083      "Flop",
14084      "Flip",
14085      "Rotate Right",
14086      "Rotate Left",
14087      "Rotate...",
14088      "Shear...",
14089      "Roll...",
14090      "Trim Edges",
14091      (char *) NULL
14092    },
14093    *EnhanceMenu[] =
14094    {
14095      "Hue...",
14096      "Saturation...",
14097      "Brightness...",
14098      "Gamma...",
14099      "Spiff",
14100      "Dull",
14101      "Contrast Stretch...",
14102      "Sigmoidal Contrast...",
14103      "Normalize",
14104      "Equalize",
14105      "Negate",
14106      "Grayscale",
14107      "Map...",
14108      "Quantize...",
14109      (char *) NULL
14110    },
14111    *EffectsMenu[] =
14112    {
14113      "Despeckle",
14114      "Emboss",
14115      "Reduce Noise",
14116      "Add Noise...",
14117      "Sharpen...",
14118      "Blur...",
14119      "Threshold...",
14120      "Edge Detect...",
14121      "Spread...",
14122      "Shade...",
14123      "Raise...",
14124      "Segment...",
14125      (char *) NULL
14126    },
14127    *FXMenu[] =
14128    {
14129      "Solarize...",
14130      "Sepia Tone...",
14131      "Swirl...",
14132      "Implode...",
14133      "Vignette...",
14134      "Wave...",
14135      "Oil Paint...",
14136      "Charcoal Draw...",
14137      (char *) NULL
14138    },
14139    *ImageEditMenu[] =
14140    {
14141      "Annotate...",
14142      "Draw...",
14143      "Color...",
14144      "Matte...",
14145      "Composite...",
14146      "Add Border...",
14147      "Add Frame...",
14148      "Comment...",
14149      "Launch...",
14150      "Region of Interest...",
14151      (char *) NULL
14152    },
14153    *MiscellanyMenu[] =
14154    {
14155      "Image Info",
14156      "Zoom Image",
14157      "Show Preview...",
14158      "Show Histogram",
14159      "Show Matte",
14160      "Background...",
14161      "Slide Show...",
14162      "Preferences...",
14163      (char *) NULL
14164    },
14165    *HelpMenu[] =
14166    {
14167      "Overview",
14168      "Browse Documentation",
14169      "About Display",
14170      (char *) NULL
14171    },
14172    *ShortCutsMenu[] =
14173    {
14174      "Next",
14175      "Former",
14176      "Open...",
14177      "Save...",
14178      "Print...",
14179      "Undo",
14180      "Restore",
14181      "Image Info",
14182      "Quit",
14183      (char *) NULL
14184    },
14185    *VirtualMenu[] =
14186    {
14187      "Image Info",
14188      "Print",
14189      "Next",
14190      "Quit",
14191      (char *) NULL
14192    };
14193
14194  static const char
14195    **Menus[MagickMenus] =
14196    {
14197      FileMenu,
14198      EditMenu,
14199      ViewMenu,
14200      TransformMenu,
14201      EnhanceMenu,
14202      EffectsMenu,
14203      FXMenu,
14204      ImageEditMenu,
14205      MiscellanyMenu,
14206      HelpMenu
14207    };
14208
14209  static CommandType
14210    CommandMenus[] =
14211    {
14212      NullCommand,
14213      NullCommand,
14214      NullCommand,
14215      NullCommand,
14216      NullCommand,
14217      NullCommand,
14218      NullCommand,
14219      NullCommand,
14220      NullCommand,
14221      NullCommand,
14222    },
14223    FileCommands[] =
14224    {
14225      OpenCommand,
14226      NextCommand,
14227      FormerCommand,
14228      SelectCommand,
14229      SaveCommand,
14230      PrintCommand,
14231      DeleteCommand,
14232      NewCommand,
14233      VisualDirectoryCommand,
14234      QuitCommand
14235    },
14236    EditCommands[] =
14237    {
14238      UndoCommand,
14239      RedoCommand,
14240      CutCommand,
14241      CopyCommand,
14242      PasteCommand
14243    },
14244    ViewCommands[] =
14245    {
14246      HalfSizeCommand,
14247      OriginalSizeCommand,
14248      DoubleSizeCommand,
14249      ResizeCommand,
14250      ApplyCommand,
14251      RefreshCommand,
14252      RestoreCommand
14253    },
14254    TransformCommands[] =
14255    {
14256      CropCommand,
14257      ChopCommand,
14258      FlopCommand,
14259      FlipCommand,
14260      RotateRightCommand,
14261      RotateLeftCommand,
14262      RotateCommand,
14263      ShearCommand,
14264      RollCommand,
14265      TrimCommand
14266    },
14267    EnhanceCommands[] =
14268    {
14269      HueCommand,
14270      SaturationCommand,
14271      BrightnessCommand,
14272      GammaCommand,
14273      SpiffCommand,
14274      DullCommand,
14275      ContrastStretchCommand,
14276      SigmoidalContrastCommand,
14277      NormalizeCommand,
14278      EqualizeCommand,
14279      NegateCommand,
14280      GrayscaleCommand,
14281      MapCommand,
14282      QuantizeCommand
14283    },
14284    EffectsCommands[] =
14285    {
14286      DespeckleCommand,
14287      EmbossCommand,
14288      ReduceNoiseCommand,
14289      AddNoiseCommand,
14290      SharpenCommand,
14291      BlurCommand,
14292      ThresholdCommand,
14293      EdgeDetectCommand,
14294      SpreadCommand,
14295      ShadeCommand,
14296      RaiseCommand,
14297      SegmentCommand
14298    },
14299    FXCommands[] =
14300    {
14301      SolarizeCommand,
14302      SepiaToneCommand,
14303      SwirlCommand,
14304      ImplodeCommand,
14305      VignetteCommand,
14306      WaveCommand,
14307      OilPaintCommand,
14308      CharcoalDrawCommand
14309    },
14310    ImageEditCommands[] =
14311    {
14312      AnnotateCommand,
14313      DrawCommand,
14314      ColorCommand,
14315      MatteCommand,
14316      CompositeCommand,
14317      AddBorderCommand,
14318      AddFrameCommand,
14319      CommentCommand,
14320      LaunchCommand,
14321      RegionofInterestCommand
14322    },
14323    MiscellanyCommands[] =
14324    {
14325      InfoCommand,
14326      ZoomCommand,
14327      ShowPreviewCommand,
14328      ShowHistogramCommand,
14329      ShowMatteCommand,
14330      BackgroundCommand,
14331      SlideShowCommand,
14332      PreferencesCommand
14333    },
14334    HelpCommands[] =
14335    {
14336      HelpCommand,
14337      BrowseDocumentationCommand,
14338      VersionCommand
14339    },
14340    ShortCutsCommands[] =
14341    {
14342      NextCommand,
14343      FormerCommand,
14344      OpenCommand,
14345      SaveCommand,
14346      PrintCommand,
14347      UndoCommand,
14348      RestoreCommand,
14349      InfoCommand,
14350      QuitCommand
14351    },
14352    VirtualCommands[] =
14353    {
14354      InfoCommand,
14355      PrintCommand,
14356      NextCommand,
14357      QuitCommand
14358    };
14359
14360  static CommandType
14361    *Commands[MagickMenus] =
14362    {
14363      FileCommands,
14364      EditCommands,
14365      ViewCommands,
14366      TransformCommands,
14367      EnhanceCommands,
14368      EffectsCommands,
14369      FXCommands,
14370      ImageEditCommands,
14371      MiscellanyCommands,
14372      HelpCommands
14373    };
14374
14375  char
14376    command[MaxTextExtent],
14377    *directory,
14378    geometry[MaxTextExtent],
14379    resource_name[MaxTextExtent];
14380
14381  CommandType
14382    command_type;
14383
14384  Image
14385    *display_image,
14386    *nexus;
14387
14388  int
14389    entry,
14390    id;
14391
14392  KeySym
14393    key_symbol;
14394
14395  MagickStatusType
14396    context_mask,
14397    status;
14398
14399  RectangleInfo
14400    geometry_info;
14401
14402  register int
14403    i;
14404
14405  static char
14406    working_directory[MaxTextExtent];
14407
14408  static XPoint
14409    vid_info;
14410
14411  static XWindowInfo
14412    *magick_windows[MaxXWindows];
14413
14414  static unsigned int
14415    number_windows;
14416
14417  struct stat
14418    attributes;
14419
14420  time_t
14421    timer,
14422    timestamp,
14423    update_time;
14424
14425  unsigned int
14426    height,
14427    width;
14428
14429  size_t
14430    delay;
14431
14432  WarningHandler
14433    warning_handler;
14434
14435  Window
14436    root_window;
14437
14438  XClassHint
14439    *class_hints;
14440
14441  XEvent
14442    event;
14443
14444  XFontStruct
14445    *font_info;
14446
14447  XGCValues
14448    context_values;
14449
14450  XPixelInfo
14451    *icon_pixel,
14452    *pixel;
14453
14454  XResourceInfo
14455    *icon_resources;
14456
14457  XStandardColormap
14458    *icon_map,
14459    *map_info;
14460
14461  XVisualInfo
14462    *icon_visual,
14463    *visual_info;
14464
14465  XWindowChanges
14466    window_changes;
14467
14468  XWindows
14469    *windows;
14470
14471  XWMHints
14472    *manager_hints;
14473
14474  assert(image != (Image **) NULL);
14475  assert((*image)->signature == MagickSignature);
14476  if ((*image)->debug != MagickFalse)
14477    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14478  display_image=(*image);
14479  warning_handler=(WarningHandler) NULL;
14480  windows=XSetWindows((XWindows *) ~0);
14481  if (windows != (XWindows *) NULL)
14482    {
14483      int
14484        status;
14485
14486      status=chdir(working_directory);
14487      if (status == -1)
14488        (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14489          "UnableToOpenFile","%s",working_directory);
14490      warning_handler=resource_info->display_warnings ?
14491        SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14492      warning_handler=resource_info->display_warnings ?
14493        SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14494    }
14495  else
14496    {
14497      /*
14498        Allocate windows structure.
14499      */
14500      resource_info->colors=display_image->colors;
14501      windows=XSetWindows(XInitializeWindows(display,resource_info));
14502      if (windows == (XWindows *) NULL)
14503        ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14504          (*image)->filename);
14505      /*
14506        Initialize window id's.
14507      */
14508      number_windows=0;
14509      magick_windows[number_windows++]=(&windows->icon);
14510      magick_windows[number_windows++]=(&windows->backdrop);
14511      magick_windows[number_windows++]=(&windows->image);
14512      magick_windows[number_windows++]=(&windows->info);
14513      magick_windows[number_windows++]=(&windows->command);
14514      magick_windows[number_windows++]=(&windows->widget);
14515      magick_windows[number_windows++]=(&windows->popup);
14516      magick_windows[number_windows++]=(&windows->magnify);
14517      magick_windows[number_windows++]=(&windows->pan);
14518      for (i=0; i < (int) number_windows; i++)
14519        magick_windows[i]->id=(Window) NULL;
14520      vid_info.x=0;
14521      vid_info.y=0;
14522    }
14523  /*
14524    Initialize font info.
14525  */
14526  if (windows->font_info != (XFontStruct *) NULL)
14527    (void) XFreeFont(display,windows->font_info);
14528  windows->font_info=XBestFont(display,resource_info,MagickFalse);
14529  if (windows->font_info == (XFontStruct *) NULL)
14530    ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14531      resource_info->font);
14532  /*
14533    Initialize Standard Colormap.
14534  */
14535  map_info=windows->map_info;
14536  icon_map=windows->icon_map;
14537  visual_info=windows->visual_info;
14538  icon_visual=windows->icon_visual;
14539  pixel=windows->pixel_info;
14540  icon_pixel=windows->icon_pixel;
14541  font_info=windows->font_info;
14542  icon_resources=windows->icon_resources;
14543  class_hints=windows->class_hints;
14544  manager_hints=windows->manager_hints;
14545  root_window=XRootWindow(display,visual_info->screen);
14546  nexus=NewImageList();
14547  if (display_image->debug != MagickFalse)
14548    {
14549      (void) LogMagickEvent(X11Event,GetMagickModule(),
14550        "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14551        (double) display_image->scene,(double) display_image->columns,
14552        (double) display_image->rows);
14553      if (display_image->colors != 0)
14554        (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14555          display_image->colors);
14556      (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14557        display_image->magick);
14558    }
14559  XMakeStandardColormap(display,visual_info,resource_info,display_image,
14560    map_info,pixel);
14561  display_image->taint=MagickFalse;
14562  /*
14563    Initialize graphic context.
14564  */
14565  windows->context.id=(Window) NULL;
14566  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14567    resource_info,&windows->context);
14568  (void) CloneString(&class_hints->res_name,resource_info->client_name);
14569  (void) CloneString(&class_hints->res_class,resource_info->client_name);
14570  class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14571  manager_hints->flags=InputHint | StateHint;
14572  manager_hints->input=MagickFalse;
14573  manager_hints->initial_state=WithdrawnState;
14574  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14575    &windows->context);
14576  if (display_image->debug != MagickFalse)
14577    (void) LogMagickEvent(X11Event,GetMagickModule(),
14578      "Window id: 0x%lx (context)",windows->context.id);
14579  context_values.background=pixel->background_color.pixel;
14580  context_values.font=font_info->fid;
14581  context_values.foreground=pixel->foreground_color.pixel;
14582  context_values.graphics_exposures=MagickFalse;
14583  context_mask=(MagickStatusType)
14584    (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14585  if (pixel->annotate_context != (GC) NULL)
14586    (void) XFreeGC(display,pixel->annotate_context);
14587  pixel->annotate_context=XCreateGC(display,windows->context.id,
14588    context_mask,&context_values);
14589  if (pixel->annotate_context == (GC) NULL)
14590    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14591      display_image->filename);
14592  context_values.background=pixel->depth_color.pixel;
14593  if (pixel->widget_context != (GC) NULL)
14594    (void) XFreeGC(display,pixel->widget_context);
14595  pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14596    &context_values);
14597  if (pixel->widget_context == (GC) NULL)
14598    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14599      display_image->filename);
14600  context_values.background=pixel->foreground_color.pixel;
14601  context_values.foreground=pixel->background_color.pixel;
14602  context_values.plane_mask=context_values.background ^
14603    context_values.foreground;
14604  if (pixel->highlight_context != (GC) NULL)
14605    (void) XFreeGC(display,pixel->highlight_context);
14606  pixel->highlight_context=XCreateGC(display,windows->context.id,
14607    (size_t) (context_mask | GCPlaneMask),&context_values);
14608  if (pixel->highlight_context == (GC) NULL)
14609    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14610      display_image->filename);
14611  (void) XDestroyWindow(display,windows->context.id);
14612  /*
14613    Initialize icon window.
14614  */
14615  XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14616    icon_resources,&windows->icon);
14617  windows->icon.geometry=resource_info->icon_geometry;
14618  XBestIconSize(display,&windows->icon,display_image);
14619  windows->icon.attributes.colormap=XDefaultColormap(display,
14620    icon_visual->screen);
14621  windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14622  manager_hints->flags=InputHint | StateHint;
14623  manager_hints->input=MagickFalse;
14624  manager_hints->initial_state=IconicState;
14625  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14626    &windows->icon);
14627  if (display_image->debug != MagickFalse)
14628    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14629      windows->icon.id);
14630  /*
14631    Initialize graphic context for icon window.
14632  */
14633  if (icon_pixel->annotate_context != (GC) NULL)
14634    (void) XFreeGC(display,icon_pixel->annotate_context);
14635  context_values.background=icon_pixel->background_color.pixel;
14636  context_values.foreground=icon_pixel->foreground_color.pixel;
14637  icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14638    (size_t) (GCBackground | GCForeground),&context_values);
14639  if (icon_pixel->annotate_context == (GC) NULL)
14640    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14641      display_image->filename);
14642  windows->icon.annotate_context=icon_pixel->annotate_context;
14643  /*
14644    Initialize Image window.
14645  */
14646  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14647    &windows->image);
14648  windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14649  if (resource_info->use_shared_memory == MagickFalse)
14650    windows->image.shared_memory=MagickFalse;
14651  if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14652    {
14653      char
14654        *title;
14655
14656      title=InterpretImageProperties(resource_info->image_info,display_image,
14657        resource_info->title,exception);
14658      (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
14659      (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
14660      title=DestroyString(title);
14661    }
14662  else
14663    {
14664      char
14665        filename[MaxTextExtent];
14666
14667      /*
14668        Window name is the base of the filename.
14669      */
14670      GetPathComponent(display_image->magick_filename,TailPath,filename);
14671      if (GetImageListLength(display_image) == 1)
14672        (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14673          "%s: %s",MagickPackageName,filename);
14674      else
14675        (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14676          "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14677          (double) display_image->scene,(double) GetImageListLength(
14678          display_image));
14679      (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
14680    }
14681  if (resource_info->immutable)
14682    windows->image.immutable=MagickTrue;
14683  windows->image.use_pixmap=resource_info->use_pixmap;
14684  windows->image.geometry=resource_info->image_geometry;
14685  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14686    XDisplayWidth(display,visual_info->screen),
14687    XDisplayHeight(display,visual_info->screen));
14688  geometry_info.width=display_image->columns;
14689  geometry_info.height=display_image->rows;
14690  geometry_info.x=0;
14691  geometry_info.y=0;
14692  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14693    &geometry_info.width,&geometry_info.height);
14694  windows->image.width=(unsigned int) geometry_info.width;
14695  windows->image.height=(unsigned int) geometry_info.height;
14696  windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14697    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14698    KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14699    PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14700  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14701    resource_info,&windows->backdrop);
14702  if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14703    {
14704      /*
14705        Initialize backdrop window.
14706      */
14707      windows->backdrop.x=0;
14708      windows->backdrop.y=0;
14709      (void) CloneString(&windows->backdrop.name,"Backdrop");
14710      windows->backdrop.flags=(size_t) (USSize | USPosition);
14711      windows->backdrop.width=(unsigned int)
14712        XDisplayWidth(display,visual_info->screen);
14713      windows->backdrop.height=(unsigned int)
14714        XDisplayHeight(display,visual_info->screen);
14715      windows->backdrop.border_width=0;
14716      windows->backdrop.immutable=MagickTrue;
14717      windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14718        ButtonReleaseMask;
14719      windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14720        StructureNotifyMask;
14721      manager_hints->flags=IconWindowHint | InputHint | StateHint;
14722      manager_hints->icon_window=windows->icon.id;
14723      manager_hints->input=MagickTrue;
14724      manager_hints->initial_state=resource_info->iconic ? IconicState :
14725        NormalState;
14726      XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14727        &windows->backdrop);
14728      if (display_image->debug != MagickFalse)
14729        (void) LogMagickEvent(X11Event,GetMagickModule(),
14730          "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14731      (void) XMapWindow(display,windows->backdrop.id);
14732      (void) XClearWindow(display,windows->backdrop.id);
14733      if (windows->image.id != (Window) NULL)
14734        {
14735          (void) XDestroyWindow(display,windows->image.id);
14736          windows->image.id=(Window) NULL;
14737        }
14738      /*
14739        Position image in the center the backdrop.
14740      */
14741      windows->image.flags|=USPosition;
14742      windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14743        (windows->image.width/2);
14744      windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14745        (windows->image.height/2);
14746    }
14747  manager_hints->flags=IconWindowHint | InputHint | StateHint;
14748  manager_hints->icon_window=windows->icon.id;
14749  manager_hints->input=MagickTrue;
14750  manager_hints->initial_state=resource_info->iconic ? IconicState :
14751    NormalState;
14752  if (windows->group_leader.id != (Window) NULL)
14753    {
14754      /*
14755        Follow the leader.
14756      */
14757      manager_hints->flags|=WindowGroupHint;
14758      manager_hints->window_group=windows->group_leader.id;
14759      (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14760      if (display_image->debug != MagickFalse)
14761        (void) LogMagickEvent(X11Event,GetMagickModule(),
14762          "Window id: 0x%lx (group leader)",windows->group_leader.id);
14763    }
14764  XMakeWindow(display,
14765    (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14766    argv,argc,class_hints,manager_hints,&windows->image);
14767  (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14768    XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14769  if (windows->group_leader.id != (Window) NULL)
14770    (void) XSetTransientForHint(display,windows->image.id,
14771      windows->group_leader.id);
14772  if (display_image->debug != MagickFalse)
14773    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14774      windows->image.id);
14775  /*
14776    Initialize Info widget.
14777  */
14778  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14779    &windows->info);
14780  (void) CloneString(&windows->info.name,"Info");
14781  (void) CloneString(&windows->info.icon_name,"Info");
14782  windows->info.border_width=1;
14783  windows->info.x=2;
14784  windows->info.y=2;
14785  windows->info.flags|=PPosition;
14786  windows->info.attributes.win_gravity=UnmapGravity;
14787  windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14788    StructureNotifyMask;
14789  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14790  manager_hints->input=MagickFalse;
14791  manager_hints->initial_state=NormalState;
14792  manager_hints->window_group=windows->image.id;
14793  XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14794    &windows->info);
14795  windows->info.highlight_stipple=XCreateBitmapFromData(display,
14796    windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14797  windows->info.shadow_stipple=XCreateBitmapFromData(display,
14798    windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14799  (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14800  if (windows->image.mapped != MagickFalse)
14801    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14802  if (display_image->debug != MagickFalse)
14803    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14804      windows->info.id);
14805  /*
14806    Initialize Command widget.
14807  */
14808  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14809    resource_info,&windows->command);
14810  windows->command.data=MagickMenus;
14811  (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14812  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
14813    resource_info->client_name);
14814  windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14815    resource_name,"geometry",(char *) NULL);
14816  (void) CloneString(&windows->command.name,MagickTitle);
14817  windows->command.border_width=0;
14818  windows->command.flags|=PPosition;
14819  windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14820    ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14821    OwnerGrabButtonMask | StructureNotifyMask;
14822  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14823  manager_hints->input=MagickTrue;
14824  manager_hints->initial_state=NormalState;
14825  manager_hints->window_group=windows->image.id;
14826  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14827    &windows->command);
14828  windows->command.highlight_stipple=XCreateBitmapFromData(display,
14829    windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14830    HighlightHeight);
14831  windows->command.shadow_stipple=XCreateBitmapFromData(display,
14832    windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14833  (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14834  if (windows->command.mapped != MagickFalse)
14835    (void) XMapRaised(display,windows->command.id);
14836  if (display_image->debug != MagickFalse)
14837    (void) LogMagickEvent(X11Event,GetMagickModule(),
14838      "Window id: 0x%lx (command)",windows->command.id);
14839  /*
14840    Initialize Widget window.
14841  */
14842  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14843    resource_info,&windows->widget);
14844  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
14845    resource_info->client_name);
14846  windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14847    resource_name,"geometry",(char *) NULL);
14848  windows->widget.border_width=0;
14849  windows->widget.flags|=PPosition;
14850  windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14851    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14852    KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14853    StructureNotifyMask;
14854  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14855  manager_hints->input=MagickTrue;
14856  manager_hints->initial_state=NormalState;
14857  manager_hints->window_group=windows->image.id;
14858  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14859    &windows->widget);
14860  windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14861    windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14862  windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14863    windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14864  (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14865  if (display_image->debug != MagickFalse)
14866    (void) LogMagickEvent(X11Event,GetMagickModule(),
14867      "Window id: 0x%lx (widget)",windows->widget.id);
14868  /*
14869    Initialize popup window.
14870  */
14871  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14872    resource_info,&windows->popup);
14873  windows->popup.border_width=0;
14874  windows->popup.flags|=PPosition;
14875  windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14876    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14877    KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14878  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14879  manager_hints->input=MagickTrue;
14880  manager_hints->initial_state=NormalState;
14881  manager_hints->window_group=windows->image.id;
14882  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14883    &windows->popup);
14884  windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14885    windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14886  windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14887    windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14888  (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14889  if (display_image->debug != MagickFalse)
14890    (void) LogMagickEvent(X11Event,GetMagickModule(),
14891      "Window id: 0x%lx (pop up)",windows->popup.id);
14892  /*
14893    Initialize Magnify window and cursor.
14894  */
14895  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14896    resource_info,&windows->magnify);
14897  if (resource_info->use_shared_memory == MagickFalse)
14898    windows->magnify.shared_memory=MagickFalse;
14899  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
14900    resource_info->client_name);
14901  windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14902    resource_name,"geometry",(char *) NULL);
14903  (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14904    resource_info->magnify);
14905  if (windows->magnify.cursor != (Cursor) NULL)
14906    (void) XFreeCursor(display,windows->magnify.cursor);
14907  windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14908    map_info->colormap,resource_info->background_color,
14909    resource_info->foreground_color);
14910  if (windows->magnify.cursor == (Cursor) NULL)
14911    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14912      display_image->filename);
14913  windows->magnify.width=MagnifySize;
14914  windows->magnify.height=MagnifySize;
14915  windows->magnify.flags|=PPosition;
14916  windows->magnify.min_width=MagnifySize;
14917  windows->magnify.min_height=MagnifySize;
14918  windows->magnify.width_inc=MagnifySize;
14919  windows->magnify.height_inc=MagnifySize;
14920  windows->magnify.data=resource_info->magnify;
14921  windows->magnify.attributes.cursor=windows->magnify.cursor;
14922  windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14923    ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14924    StructureNotifyMask;
14925  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14926  manager_hints->input=MagickTrue;
14927  manager_hints->initial_state=NormalState;
14928  manager_hints->window_group=windows->image.id;
14929  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14930    &windows->magnify);
14931  if (display_image->debug != MagickFalse)
14932    (void) LogMagickEvent(X11Event,GetMagickModule(),
14933      "Window id: 0x%lx (magnify)",windows->magnify.id);
14934  (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14935  /*
14936    Initialize panning window.
14937  */
14938  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14939    resource_info,&windows->pan);
14940  (void) CloneString(&windows->pan.name,"Pan Icon");
14941  windows->pan.width=windows->icon.width;
14942  windows->pan.height=windows->icon.height;
14943  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
14944    resource_info->client_name);
14945  windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
14946    resource_name,"geometry",(char *) NULL);
14947  (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
14948    &windows->pan.width,&windows->pan.height);
14949  windows->pan.flags|=PPosition;
14950  windows->pan.immutable=MagickTrue;
14951  windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14952    ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
14953    StructureNotifyMask;
14954  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14955  manager_hints->input=MagickFalse;
14956  manager_hints->initial_state=NormalState;
14957  manager_hints->window_group=windows->image.id;
14958  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14959    &windows->pan);
14960  if (display_image->debug != MagickFalse)
14961    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
14962      windows->pan.id);
14963  (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
14964  if (windows->info.mapped != MagickFalse)
14965    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14966  if ((windows->image.mapped == MagickFalse) ||
14967      (windows->backdrop.id != (Window) NULL))
14968    (void) XMapWindow(display,windows->image.id);
14969  /*
14970    Set our progress monitor and warning handlers.
14971  */
14972  if (warning_handler == (WarningHandler) NULL)
14973    {
14974      warning_handler=resource_info->display_warnings ?
14975        SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14976      warning_handler=resource_info->display_warnings ?
14977        SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14978    }
14979  /*
14980    Initialize Image and Magnify X images.
14981  */
14982  windows->image.x=0;
14983  windows->image.y=0;
14984  windows->magnify.shape=MagickFalse;
14985  width=(unsigned int) display_image->columns;
14986  height=(unsigned int) display_image->rows;
14987  if ((display_image->columns != width) || (display_image->rows != height))
14988    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14989      display_image->filename);
14990  status=XMakeImage(display,resource_info,&windows->image,display_image,
14991    width,height,exception);
14992  if (status == MagickFalse)
14993    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14994      display_image->filename);
14995  status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
14996    windows->magnify.width,windows->magnify.height,exception);
14997  if (status == MagickFalse)
14998    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14999      display_image->filename);
15000  if (windows->magnify.mapped != MagickFalse)
15001    (void) XMapRaised(display,windows->magnify.id);
15002  if (windows->pan.mapped != MagickFalse)
15003    (void) XMapRaised(display,windows->pan.id);
15004  windows->image.window_changes.width=(int) display_image->columns;
15005  windows->image.window_changes.height=(int) display_image->rows;
15006  (void) XConfigureImage(display,resource_info,windows,display_image,exception);
15007  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15008  (void) XSync(display,MagickFalse);
15009  /*
15010    Respond to events.
15011  */
15012  delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
15013  timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15014  update_time=0;
15015  if (resource_info->update != MagickFalse)
15016    {
15017      MagickBooleanType
15018        status;
15019
15020      /*
15021        Determine when file data was last modified.
15022      */
15023      status=GetPathAttributes(display_image->filename,&attributes);
15024      if (status != MagickFalse)
15025        update_time=attributes.st_mtime;
15026    }
15027  *state&=(~FormerImageState);
15028  *state&=(~MontageImageState);
15029  *state&=(~NextImageState);
15030  do
15031  {
15032    /*
15033      Handle a window event.
15034    */
15035    if (windows->image.mapped != MagickFalse)
15036      if ((display_image->delay != 0) || (resource_info->update != 0))
15037        {
15038          if (timer < time((time_t *) NULL))
15039            {
15040              if (resource_info->update == MagickFalse)
15041                *state|=NextImageState | ExitState;
15042              else
15043                {
15044                  MagickBooleanType
15045                    status;
15046
15047                  /*
15048                    Determine if image file was modified.
15049                  */
15050                  status=GetPathAttributes(display_image->filename,&attributes);
15051                  if (status != MagickFalse)
15052                    if (update_time != attributes.st_mtime)
15053                      {
15054                        /*
15055                          Redisplay image.
15056                        */
15057                        (void) FormatLocaleString(
15058                          resource_info->image_info->filename,MaxTextExtent,
15059                          "%s:%s",display_image->magick,
15060                          display_image->filename);
15061                        nexus=ReadImage(resource_info->image_info,
15062                          &display_image->exception);
15063                        if (nexus != (Image *) NULL)
15064                          {
15065                            nexus=DestroyImage(nexus);
15066                            *state|=NextImageState | ExitState;
15067                          }
15068                      }
15069                  delay=display_image->delay/MagickMax(
15070                    display_image->ticks_per_second,1L);
15071                  timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15072                }
15073            }
15074          if (XEventsQueued(display,QueuedAfterFlush) == 0)
15075            {
15076              /*
15077                Do not block if delay > 0.
15078              */
15079              XDelay(display,SuspendTime << 2);
15080              continue;
15081            }
15082        }
15083    timestamp=time((time_t *) NULL);
15084    (void) XNextEvent(display,&event);
15085    if (windows->image.stasis == MagickFalse)
15086      windows->image.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15087        MagickTrue : MagickFalse;
15088    if (windows->magnify.stasis == MagickFalse)
15089      windows->magnify.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15090        MagickTrue : MagickFalse;
15091    if (event.xany.window == windows->command.id)
15092      {
15093        /*
15094          Select a command from the Command widget.
15095        */
15096        id=XCommandWidget(display,windows,CommandMenu,&event);
15097        if (id < 0)
15098          continue;
15099        (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15100        command_type=CommandMenus[id];
15101        if (id < MagickMenus)
15102          {
15103            /*
15104              Select a command from a pop-up menu.
15105            */
15106            entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15107              command);
15108            if (entry < 0)
15109              continue;
15110            (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15111            command_type=Commands[id][entry];
15112          }
15113        if (command_type != NullCommand)
15114          nexus=XMagickCommand(display,resource_info,windows,command_type,
15115            &display_image,exception);
15116        continue;
15117      }
15118    switch (event.type)
15119    {
15120      case ButtonPress:
15121      {
15122        if (display_image->debug != MagickFalse)
15123          (void) LogMagickEvent(X11Event,GetMagickModule(),
15124            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15125            event.xbutton.button,event.xbutton.x,event.xbutton.y);
15126        if ((event.xbutton.button == Button3) &&
15127            (event.xbutton.state & Mod1Mask))
15128          {
15129            /*
15130              Convert Alt-Button3 to Button2.
15131            */
15132            event.xbutton.button=Button2;
15133            event.xbutton.state&=(~Mod1Mask);
15134          }
15135        if (event.xbutton.window == windows->backdrop.id)
15136          {
15137            (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15138              event.xbutton.time);
15139            break;
15140          }
15141        if (event.xbutton.window == windows->image.id)
15142          {
15143            switch (event.xbutton.button)
15144            {
15145              case Button1:
15146              {
15147                if (resource_info->immutable)
15148                  {
15149                    /*
15150                      Select a command from the Virtual menu.
15151                    */
15152                    entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15153                      command);
15154                    if (entry >= 0)
15155                      nexus=XMagickCommand(display,resource_info,windows,
15156                        VirtualCommands[entry],&display_image,exception);
15157                    break;
15158                  }
15159                /*
15160                  Map/unmap Command widget.
15161                */
15162                if (windows->command.mapped != MagickFalse)
15163                  (void) XWithdrawWindow(display,windows->command.id,
15164                    windows->command.screen);
15165                else
15166                  {
15167                    (void) XCommandWidget(display,windows,CommandMenu,
15168                      (XEvent *) NULL);
15169                    (void) XMapRaised(display,windows->command.id);
15170                  }
15171                break;
15172              }
15173              case Button2:
15174              {
15175                /*
15176                  User pressed the image magnify button.
15177                */
15178                (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15179                  &display_image,exception);
15180                XMagnifyImage(display,windows,&event);
15181                break;
15182              }
15183              case Button3:
15184              {
15185                if (resource_info->immutable)
15186                  {
15187                    /*
15188                      Select a command from the Virtual menu.
15189                    */
15190                    entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15191                      command);
15192                    if (entry >= 0)
15193                      nexus=XMagickCommand(display,resource_info,windows,
15194                        VirtualCommands[entry],&display_image,exception);
15195                    break;
15196                  }
15197                if (display_image->montage != (char *) NULL)
15198                  {
15199                    /*
15200                      Open or delete a tile from a visual image directory.
15201                    */
15202                    nexus=XTileImage(display,resource_info,windows,
15203                      display_image,&event,exception);
15204                    if (nexus != (Image *) NULL)
15205                      *state|=MontageImageState | NextImageState | ExitState;
15206                    vid_info.x=(short int) windows->image.x;
15207                    vid_info.y=(short int) windows->image.y;
15208                    break;
15209                  }
15210                /*
15211                  Select a command from the Short Cuts menu.
15212                */
15213                entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15214                  command);
15215                if (entry >= 0)
15216                  nexus=XMagickCommand(display,resource_info,windows,
15217                    ShortCutsCommands[entry],&display_image,exception);
15218                break;
15219              }
15220              case Button4:
15221              {
15222                /*
15223                  Wheel up.
15224                */
15225                XTranslateImage(display,windows,*image,XK_Up);
15226                break;
15227              }
15228              case Button5:
15229              {
15230                /*
15231                  Wheel down.
15232                */
15233                XTranslateImage(display,windows,*image,XK_Down);
15234                break;
15235              }
15236              default:
15237                break;
15238            }
15239            break;
15240          }
15241        if (event.xbutton.window == windows->magnify.id)
15242          {
15243            int
15244              factor;
15245
15246            static const char
15247              *MagnifyMenu[] =
15248              {
15249                "2",
15250                "4",
15251                "5",
15252                "6",
15253                "7",
15254                "8",
15255                "9",
15256                "3",
15257                (char *) NULL,
15258              };
15259
15260            static KeySym
15261              MagnifyCommands[] =
15262              {
15263                XK_2,
15264                XK_4,
15265                XK_5,
15266                XK_6,
15267                XK_7,
15268                XK_8,
15269                XK_9,
15270                XK_3
15271              };
15272
15273            /*
15274              Select a magnify factor from the pop-up menu.
15275            */
15276            factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15277            if (factor >= 0)
15278              XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor]);
15279            break;
15280          }
15281        if (event.xbutton.window == windows->pan.id)
15282          {
15283            switch (event.xbutton.button)
15284            {
15285              case Button4:
15286              {
15287                /*
15288                  Wheel up.
15289                */
15290                XTranslateImage(display,windows,*image,XK_Up);
15291                break;
15292              }
15293              case Button5:
15294              {
15295                /*
15296                  Wheel down.
15297                */
15298                XTranslateImage(display,windows,*image,XK_Down);
15299                break;
15300              }
15301              default:
15302              {
15303                XPanImage(display,windows,&event);
15304                break;
15305              }
15306            }
15307            break;
15308          }
15309        delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15310          1L);
15311        timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15312        break;
15313      }
15314      case ButtonRelease:
15315      {
15316        if (display_image->debug != MagickFalse)
15317          (void) LogMagickEvent(X11Event,GetMagickModule(),
15318            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15319            event.xbutton.button,event.xbutton.x,event.xbutton.y);
15320        break;
15321      }
15322      case ClientMessage:
15323      {
15324        if (display_image->debug != MagickFalse)
15325          (void) LogMagickEvent(X11Event,GetMagickModule(),
15326            "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15327            event.xclient.message_type,event.xclient.format,(unsigned long)
15328            event.xclient.data.l[0]);
15329        if (event.xclient.message_type == windows->im_protocols)
15330          {
15331            if (*event.xclient.data.l == (long) windows->im_update_widget)
15332              {
15333                (void) CloneString(&windows->command.name,MagickTitle);
15334                windows->command.data=MagickMenus;
15335                (void) XCommandWidget(display,windows,CommandMenu,
15336                  (XEvent *) NULL);
15337                break;
15338              }
15339            if (*event.xclient.data.l == (long) windows->im_update_colormap)
15340              {
15341                /*
15342                  Update graphic context and window colormap.
15343                */
15344                for (i=0; i < (int) number_windows; i++)
15345                {
15346                  if (magick_windows[i]->id == windows->icon.id)
15347                    continue;
15348                  context_values.background=pixel->background_color.pixel;
15349                  context_values.foreground=pixel->foreground_color.pixel;
15350                  (void) XChangeGC(display,magick_windows[i]->annotate_context,
15351                    context_mask,&context_values);
15352                  (void) XChangeGC(display,magick_windows[i]->widget_context,
15353                    context_mask,&context_values);
15354                  context_values.background=pixel->foreground_color.pixel;
15355                  context_values.foreground=pixel->background_color.pixel;
15356                  context_values.plane_mask=context_values.background ^
15357                    context_values.foreground;
15358                  (void) XChangeGC(display,magick_windows[i]->highlight_context,
15359                    (size_t) (context_mask | GCPlaneMask),
15360                    &context_values);
15361                  magick_windows[i]->attributes.background_pixel=
15362                    pixel->background_color.pixel;
15363                  magick_windows[i]->attributes.border_pixel=
15364                    pixel->border_color.pixel;
15365                  magick_windows[i]->attributes.colormap=map_info->colormap;
15366                  (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15367                    (unsigned long) magick_windows[i]->mask,
15368                    &magick_windows[i]->attributes);
15369                }
15370                if (windows->pan.mapped != MagickFalse)
15371                  {
15372                    (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15373                      windows->pan.pixmap);
15374                    (void) XClearWindow(display,windows->pan.id);
15375                    XDrawPanRectangle(display,windows);
15376                  }
15377                if (windows->backdrop.id != (Window) NULL)
15378                  (void) XInstallColormap(display,map_info->colormap);
15379                break;
15380              }
15381            if (*event.xclient.data.l == (long) windows->im_former_image)
15382              {
15383                *state|=FormerImageState | ExitState;
15384                break;
15385              }
15386            if (*event.xclient.data.l == (long) windows->im_next_image)
15387              {
15388                *state|=NextImageState | ExitState;
15389                break;
15390              }
15391            if (*event.xclient.data.l == (long) windows->im_retain_colors)
15392              {
15393                *state|=RetainColorsState;
15394                break;
15395              }
15396            if (*event.xclient.data.l == (long) windows->im_exit)
15397              {
15398                *state|=ExitState;
15399                break;
15400              }
15401            break;
15402          }
15403        if (event.xclient.message_type == windows->dnd_protocols)
15404          {
15405            Atom
15406              selection,
15407              type;
15408
15409            int
15410              format,
15411              status;
15412
15413            unsigned char
15414              *data;
15415
15416            unsigned long
15417              after,
15418              length;
15419
15420            /*
15421              Display image named by the Drag-and-Drop selection.
15422            */
15423            if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15424              break;
15425            selection=XInternAtom(display,"DndSelection",MagickFalse);
15426            status=XGetWindowProperty(display,root_window,selection,0L,(long)
15427              MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15428              &length,&after,&data);
15429            if ((status != Success) || (length == 0))
15430              break;
15431            if (*event.xclient.data.l == 2)
15432              {
15433                /*
15434                  Offix DND.
15435                */
15436                (void) CopyMagickString(resource_info->image_info->filename,
15437                  (char *) data,MaxTextExtent);
15438              }
15439            else
15440              {
15441                /*
15442                  XDND.
15443                */
15444                if (strncmp((char *) data, "file:", 5) != 0)
15445                  {
15446                    (void) XFree((void *) data);
15447                    break;
15448                  }
15449                (void) CopyMagickString(resource_info->image_info->filename,
15450                  ((char *) data)+5,MaxTextExtent);
15451              }
15452            nexus=ReadImage(resource_info->image_info,
15453              &display_image->exception);
15454            CatchException(&display_image->exception);
15455            if (nexus != (Image *) NULL)
15456              *state|=NextImageState | ExitState;
15457            (void) XFree((void *) data);
15458            break;
15459          }
15460        /*
15461          If client window delete message, exit.
15462        */
15463        if (event.xclient.message_type != windows->wm_protocols)
15464          break;
15465        if (*event.xclient.data.l != (long) windows->wm_delete_window)
15466          break;
15467        (void) XWithdrawWindow(display,event.xclient.window,
15468          visual_info->screen);
15469        if (event.xclient.window == windows->image.id)
15470          {
15471            *state|=ExitState;
15472            break;
15473          }
15474        if (event.xclient.window == windows->pan.id)
15475          {
15476            /*
15477              Restore original image size when pan window is deleted.
15478            */
15479            windows->image.window_changes.width=windows->image.ximage->width;
15480            windows->image.window_changes.height=windows->image.ximage->height;
15481            (void) XConfigureImage(display,resource_info,windows,
15482              display_image,exception);
15483          }
15484        break;
15485      }
15486      case ConfigureNotify:
15487      {
15488        if (display_image->debug != MagickFalse)
15489          (void) LogMagickEvent(X11Event,GetMagickModule(),
15490            "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15491            event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15492            event.xconfigure.y,event.xconfigure.send_event);
15493        if (event.xconfigure.window == windows->image.id)
15494          {
15495            /*
15496              Image window has a new configuration.
15497            */
15498            if (event.xconfigure.send_event != 0)
15499              {
15500                XWindowChanges
15501                  window_changes;
15502
15503                /*
15504                  Position the transient windows relative of the Image window.
15505                */
15506                if (windows->command.geometry == (char *) NULL)
15507                  if (windows->command.mapped == MagickFalse)
15508                    {
15509                      windows->command.x=event.xconfigure.x-
15510                        windows->command.width-25;
15511                      windows->command.y=event.xconfigure.y;
15512                      XConstrainWindowPosition(display,&windows->command);
15513                      window_changes.x=windows->command.x;
15514                      window_changes.y=windows->command.y;
15515                      (void) XReconfigureWMWindow(display,windows->command.id,
15516                        windows->command.screen,(unsigned int) (CWX | CWY),
15517                        &window_changes);
15518                    }
15519                if (windows->widget.geometry == (char *) NULL)
15520                  if (windows->widget.mapped == MagickFalse)
15521                    {
15522                      windows->widget.x=event.xconfigure.x+
15523                        event.xconfigure.width/10;
15524                      windows->widget.y=event.xconfigure.y+
15525                        event.xconfigure.height/10;
15526                      XConstrainWindowPosition(display,&windows->widget);
15527                      window_changes.x=windows->widget.x;
15528                      window_changes.y=windows->widget.y;
15529                      (void) XReconfigureWMWindow(display,windows->widget.id,
15530                        windows->widget.screen,(unsigned int) (CWX | CWY),
15531                        &window_changes);
15532                    }
15533                if (windows->magnify.geometry == (char *) NULL)
15534                  if (windows->magnify.mapped == MagickFalse)
15535                    {
15536                      windows->magnify.x=event.xconfigure.x+
15537                        event.xconfigure.width+25;
15538                      windows->magnify.y=event.xconfigure.y;
15539                      XConstrainWindowPosition(display,&windows->magnify);
15540                      window_changes.x=windows->magnify.x;
15541                      window_changes.y=windows->magnify.y;
15542                      (void) XReconfigureWMWindow(display,windows->magnify.id,
15543                        windows->magnify.screen,(unsigned int) (CWX | CWY),
15544                        &window_changes);
15545                    }
15546                if (windows->pan.geometry == (char *) NULL)
15547                  if (windows->pan.mapped == MagickFalse)
15548                    {
15549                      windows->pan.x=event.xconfigure.x+
15550                        event.xconfigure.width+25;
15551                      windows->pan.y=event.xconfigure.y+
15552                        windows->magnify.height+50;
15553                      XConstrainWindowPosition(display,&windows->pan);
15554                      window_changes.x=windows->pan.x;
15555                      window_changes.y=windows->pan.y;
15556                      (void) XReconfigureWMWindow(display,windows->pan.id,
15557                        windows->pan.screen,(unsigned int) (CWX | CWY),
15558                        &window_changes);
15559                    }
15560              }
15561            if ((event.xconfigure.width == (int) windows->image.width) &&
15562                (event.xconfigure.height == (int) windows->image.height))
15563              break;
15564            windows->image.width=(unsigned int) event.xconfigure.width;
15565            windows->image.height=(unsigned int) event.xconfigure.height;
15566            windows->image.x=0;
15567            windows->image.y=0;
15568            if (display_image->montage != (char *) NULL)
15569              {
15570                windows->image.x=vid_info.x;
15571                windows->image.y=vid_info.y;
15572              }
15573            if ((windows->image.mapped != MagickFalse) &&
15574                (windows->image.stasis != MagickFalse))
15575              {
15576                /*
15577                  Update image window configuration.
15578                */
15579                windows->image.window_changes.width=event.xconfigure.width;
15580                windows->image.window_changes.height=event.xconfigure.height;
15581                (void) XConfigureImage(display,resource_info,windows,
15582                  display_image,exception);
15583              }
15584            /*
15585              Update pan window configuration.
15586            */
15587            if ((event.xconfigure.width < windows->image.ximage->width) ||
15588                (event.xconfigure.height < windows->image.ximage->height))
15589              {
15590                (void) XMapRaised(display,windows->pan.id);
15591                XDrawPanRectangle(display,windows);
15592              }
15593            else
15594              if (windows->pan.mapped != MagickFalse)
15595                (void) XWithdrawWindow(display,windows->pan.id,
15596                  windows->pan.screen);
15597            break;
15598          }
15599        if (event.xconfigure.window == windows->magnify.id)
15600          {
15601            unsigned int
15602              magnify;
15603
15604            /*
15605              Magnify window has a new configuration.
15606            */
15607            windows->magnify.width=(unsigned int) event.xconfigure.width;
15608            windows->magnify.height=(unsigned int) event.xconfigure.height;
15609            if (windows->magnify.mapped == MagickFalse)
15610              break;
15611            magnify=1;
15612            while ((int) magnify <= event.xconfigure.width)
15613              magnify<<=1;
15614            while ((int) magnify <= event.xconfigure.height)
15615              magnify<<=1;
15616            magnify>>=1;
15617            if (((int) magnify != event.xconfigure.width) ||
15618                ((int) magnify != event.xconfigure.height))
15619              {
15620                window_changes.width=(int) magnify;
15621                window_changes.height=(int) magnify;
15622                (void) XReconfigureWMWindow(display,windows->magnify.id,
15623                  windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15624                  &window_changes);
15625                break;
15626              }
15627            if ((windows->magnify.mapped != MagickFalse) &&
15628                (windows->magnify.stasis != MagickFalse))
15629              {
15630                status=XMakeImage(display,resource_info,&windows->magnify,
15631                  display_image,windows->magnify.width,windows->magnify.height,
15632                  exception);
15633                XMakeMagnifyImage(display,windows);
15634              }
15635            break;
15636          }
15637        if ((windows->magnify.mapped != MagickFalse) &&
15638            (event.xconfigure.window == windows->pan.id))
15639          {
15640            /*
15641              Pan icon window has a new configuration.
15642            */
15643            if (event.xconfigure.send_event != 0)
15644              {
15645                windows->pan.x=event.xconfigure.x;
15646                windows->pan.y=event.xconfigure.y;
15647              }
15648            windows->pan.width=(unsigned int) event.xconfigure.width;
15649            windows->pan.height=(unsigned int) event.xconfigure.height;
15650            break;
15651          }
15652        if (event.xconfigure.window == windows->icon.id)
15653          {
15654            /*
15655              Icon window has a new configuration.
15656            */
15657            windows->icon.width=(unsigned int) event.xconfigure.width;
15658            windows->icon.height=(unsigned int) event.xconfigure.height;
15659            break;
15660          }
15661        break;
15662      }
15663      case DestroyNotify:
15664      {
15665        /*
15666          Group leader has exited.
15667        */
15668        if (display_image->debug != MagickFalse)
15669          (void) LogMagickEvent(X11Event,GetMagickModule(),
15670            "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15671        if (event.xdestroywindow.window == windows->group_leader.id)
15672          {
15673            *state|=ExitState;
15674            break;
15675          }
15676        break;
15677      }
15678      case EnterNotify:
15679      {
15680        /*
15681          Selectively install colormap.
15682        */
15683        if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15684          if (event.xcrossing.mode != NotifyUngrab)
15685            XInstallColormap(display,map_info->colormap);
15686        break;
15687      }
15688      case Expose:
15689      {
15690        if (display_image->debug != MagickFalse)
15691          (void) LogMagickEvent(X11Event,GetMagickModule(),
15692            "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15693            event.xexpose.width,event.xexpose.height,event.xexpose.x,
15694            event.xexpose.y);
15695        /*
15696          Refresh windows that are now exposed.
15697        */
15698        if ((event.xexpose.window == windows->image.id) &&
15699            (windows->image.mapped != MagickFalse))
15700          {
15701            XRefreshWindow(display,&windows->image,&event);
15702            delay=display_image->delay/MagickMax(
15703              display_image->ticks_per_second,1L);
15704            timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15705            break;
15706          }
15707        if ((event.xexpose.window == windows->magnify.id) &&
15708            (windows->magnify.mapped != MagickFalse))
15709          {
15710            XMakeMagnifyImage(display,windows);
15711            break;
15712          }
15713        if (event.xexpose.window == windows->pan.id)
15714          {
15715            XDrawPanRectangle(display,windows);
15716            break;
15717          }
15718        if (event.xexpose.window == windows->icon.id)
15719          {
15720            XRefreshWindow(display,&windows->icon,&event);
15721            break;
15722          }
15723        break;
15724      }
15725      case KeyPress:
15726      {
15727        int
15728          length;
15729
15730        /*
15731          Respond to a user key press.
15732        */
15733        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15734          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15735        *(command+length)='\0';
15736        if (display_image->debug != MagickFalse)
15737          (void) LogMagickEvent(X11Event,GetMagickModule(),
15738            "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15739            key_symbol,command);
15740        if (event.xkey.window == windows->image.id)
15741          {
15742            command_type=XImageWindowCommand(display,resource_info,windows,
15743              event.xkey.state,key_symbol,&display_image,exception);
15744            if (command_type != NullCommand)
15745              nexus=XMagickCommand(display,resource_info,windows,command_type,
15746                &display_image,exception);
15747          }
15748        if (event.xkey.window == windows->magnify.id)
15749          XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol);
15750        if (event.xkey.window == windows->pan.id)
15751          {
15752            if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15753              (void) XWithdrawWindow(display,windows->pan.id,
15754                windows->pan.screen);
15755            else
15756              if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15757                XTextViewWidget(display,resource_info,windows,MagickFalse,
15758                  "Help Viewer - Image Pan",ImagePanHelp);
15759              else
15760                XTranslateImage(display,windows,*image,key_symbol);
15761          }
15762        delay=display_image->delay/MagickMax(
15763          display_image->ticks_per_second,1L);
15764        timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15765        break;
15766      }
15767      case KeyRelease:
15768      {
15769        /*
15770          Respond to a user key release.
15771        */
15772        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15773          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15774        if (display_image->debug != MagickFalse)
15775          (void) LogMagickEvent(X11Event,GetMagickModule(),
15776            "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15777        break;
15778      }
15779      case LeaveNotify:
15780      {
15781        /*
15782          Selectively uninstall colormap.
15783        */
15784        if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15785          if (event.xcrossing.mode != NotifyUngrab)
15786            XUninstallColormap(display,map_info->colormap);
15787        break;
15788      }
15789      case MapNotify:
15790      {
15791        if (display_image->debug != MagickFalse)
15792          (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15793            event.xmap.window);
15794        if (event.xmap.window == windows->backdrop.id)
15795          {
15796            (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15797              CurrentTime);
15798            windows->backdrop.mapped=MagickTrue;
15799            break;
15800          }
15801        if (event.xmap.window == windows->image.id)
15802          {
15803            if (windows->backdrop.id != (Window) NULL)
15804              (void) XInstallColormap(display,map_info->colormap);
15805            if (LocaleCompare(display_image->magick,"LOGO") == 0)
15806              {
15807                if (LocaleCompare(display_image->filename,"LOGO") == 0)
15808                  nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15809              }
15810            if (((int) windows->image.width < windows->image.ximage->width) ||
15811                ((int) windows->image.height < windows->image.ximage->height))
15812              (void) XMapRaised(display,windows->pan.id);
15813            windows->image.mapped=MagickTrue;
15814            break;
15815          }
15816        if (event.xmap.window == windows->magnify.id)
15817          {
15818            XMakeMagnifyImage(display,windows);
15819            windows->magnify.mapped=MagickTrue;
15820            (void) XWithdrawWindow(display,windows->info.id,
15821              windows->info.screen);
15822            break;
15823          }
15824        if (event.xmap.window == windows->pan.id)
15825          {
15826            XMakePanImage(display,resource_info,windows,display_image,
15827              exception);
15828            windows->pan.mapped=MagickTrue;
15829            break;
15830          }
15831        if (event.xmap.window == windows->info.id)
15832          {
15833            windows->info.mapped=MagickTrue;
15834            break;
15835          }
15836        if (event.xmap.window == windows->icon.id)
15837          {
15838            MagickBooleanType
15839              taint;
15840
15841            /*
15842              Create an icon image.
15843            */
15844            taint=display_image->taint;
15845            XMakeStandardColormap(display,icon_visual,icon_resources,
15846              display_image,icon_map,icon_pixel);
15847            (void) XMakeImage(display,icon_resources,&windows->icon,
15848              display_image,windows->icon.width,windows->icon.height,
15849              exception);
15850            display_image->taint=taint;
15851            (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15852              windows->icon.pixmap);
15853            (void) XClearWindow(display,windows->icon.id);
15854            (void) XWithdrawWindow(display,windows->info.id,
15855              windows->info.screen);
15856            windows->icon.mapped=MagickTrue;
15857            break;
15858          }
15859        if (event.xmap.window == windows->command.id)
15860          {
15861            windows->command.mapped=MagickTrue;
15862            break;
15863          }
15864        if (event.xmap.window == windows->popup.id)
15865          {
15866            windows->popup.mapped=MagickTrue;
15867            break;
15868          }
15869        if (event.xmap.window == windows->widget.id)
15870          {
15871            windows->widget.mapped=MagickTrue;
15872            break;
15873          }
15874        break;
15875      }
15876      case MappingNotify:
15877      {
15878        (void) XRefreshKeyboardMapping(&event.xmapping);
15879        break;
15880      }
15881      case NoExpose:
15882        break;
15883      case PropertyNotify:
15884      {
15885        Atom
15886          type;
15887
15888        int
15889          format,
15890          status;
15891
15892        unsigned char
15893          *data;
15894
15895        unsigned long
15896          after,
15897          length;
15898
15899        if (display_image->debug != MagickFalse)
15900          (void) LogMagickEvent(X11Event,GetMagickModule(),
15901            "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15902            event.xproperty.atom,event.xproperty.state);
15903        if (event.xproperty.atom != windows->im_remote_command)
15904          break;
15905        /*
15906          Display image named by the remote command protocol.
15907        */
15908        status=XGetWindowProperty(display,event.xproperty.window,
15909          event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15910          AnyPropertyType,&type,&format,&length,&after,&data);
15911        if ((status != Success) || (length == 0))
15912          break;
15913        if (LocaleCompare((char *) data,"-quit") == 0)
15914          {
15915            XClientMessage(display,windows->image.id,windows->im_protocols,
15916              windows->im_exit,CurrentTime);
15917            (void) XFree((void *) data);
15918            break;
15919          }
15920        (void) CopyMagickString(resource_info->image_info->filename,
15921          (char *) data,MaxTextExtent);
15922        (void) XFree((void *) data);
15923        nexus=ReadImage(resource_info->image_info,&display_image->exception);
15924        CatchException(&display_image->exception);
15925        if (nexus != (Image *) NULL)
15926          *state|=NextImageState | ExitState;
15927        break;
15928      }
15929      case ReparentNotify:
15930      {
15931        if (display_image->debug != MagickFalse)
15932          (void) LogMagickEvent(X11Event,GetMagickModule(),
15933            "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15934            event.xreparent.window);
15935        break;
15936      }
15937      case UnmapNotify:
15938      {
15939        if (display_image->debug != MagickFalse)
15940          (void) LogMagickEvent(X11Event,GetMagickModule(),
15941            "Unmap Notify: 0x%lx",event.xunmap.window);
15942        if (event.xunmap.window == windows->backdrop.id)
15943          {
15944            windows->backdrop.mapped=MagickFalse;
15945            break;
15946          }
15947        if (event.xunmap.window == windows->image.id)
15948          {
15949            windows->image.mapped=MagickFalse;
15950            break;
15951          }
15952        if (event.xunmap.window == windows->magnify.id)
15953          {
15954            windows->magnify.mapped=MagickFalse;
15955            break;
15956          }
15957        if (event.xunmap.window == windows->pan.id)
15958          {
15959            windows->pan.mapped=MagickFalse;
15960            break;
15961          }
15962        if (event.xunmap.window == windows->info.id)
15963          {
15964            windows->info.mapped=MagickFalse;
15965            break;
15966          }
15967        if (event.xunmap.window == windows->icon.id)
15968          {
15969            if (map_info->colormap == icon_map->colormap)
15970              XConfigureImageColormap(display,resource_info,windows,
15971                display_image);
15972            (void) XFreeStandardColormap(display,icon_visual,icon_map,
15973              icon_pixel);
15974            windows->icon.mapped=MagickFalse;
15975            break;
15976          }
15977        if (event.xunmap.window == windows->command.id)
15978          {
15979            windows->command.mapped=MagickFalse;
15980            break;
15981          }
15982        if (event.xunmap.window == windows->popup.id)
15983          {
15984            if (windows->backdrop.id != (Window) NULL)
15985              (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15986                CurrentTime);
15987            windows->popup.mapped=MagickFalse;
15988            break;
15989          }
15990        if (event.xunmap.window == windows->widget.id)
15991          {
15992            if (windows->backdrop.id != (Window) NULL)
15993              (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15994                CurrentTime);
15995            windows->widget.mapped=MagickFalse;
15996            break;
15997          }
15998        break;
15999      }
16000      default:
16001      {
16002        if (display_image->debug != MagickFalse)
16003          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
16004            event.type);
16005        break;
16006      }
16007    }
16008  } while (!(*state & ExitState));
16009  if ((*state & ExitState) == 0)
16010    (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
16011      &display_image,exception);
16012  else
16013    if (resource_info->confirm_edit != MagickFalse)
16014      {
16015        /*
16016          Query user if image has changed.
16017        */
16018        if ((resource_info->immutable == MagickFalse) &&
16019            (display_image->taint != MagickFalse))
16020          {
16021            int
16022              status;
16023
16024            status=XConfirmWidget(display,windows,"Your image changed.",
16025              "Do you want to save it");
16026            if (status == 0)
16027              *state&=(~ExitState);
16028            else
16029              if (status > 0)
16030                (void) XMagickCommand(display,resource_info,windows,SaveCommand,
16031                  &display_image,exception);
16032          }
16033      }
16034  if ((windows->visual_info->klass == GrayScale) ||
16035      (windows->visual_info->klass == PseudoColor) ||
16036      (windows->visual_info->klass == DirectColor))
16037    {
16038      /*
16039        Withdraw pan and Magnify window.
16040      */
16041      if (windows->info.mapped != MagickFalse)
16042        (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16043      if (windows->magnify.mapped != MagickFalse)
16044        (void) XWithdrawWindow(display,windows->magnify.id,
16045          windows->magnify.screen);
16046      if (windows->command.mapped != MagickFalse)
16047        (void) XWithdrawWindow(display,windows->command.id,
16048          windows->command.screen);
16049    }
16050  if (windows->pan.mapped != MagickFalse)
16051    (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16052  if (resource_info->backdrop == MagickFalse)
16053    if (windows->backdrop.mapped)
16054      {
16055        (void) XWithdrawWindow(display,windows->backdrop.id,
16056          windows->backdrop.screen);
16057        (void) XDestroyWindow(display,windows->backdrop.id);
16058        windows->backdrop.id=(Window) NULL;
16059        (void) XWithdrawWindow(display,windows->image.id,
16060          windows->image.screen);
16061        (void) XDestroyWindow(display,windows->image.id);
16062        windows->image.id=(Window) NULL;
16063      }
16064  XSetCursorState(display,windows,MagickTrue);
16065  XCheckRefreshWindows(display,windows);
16066  if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16067    *state&=(~ExitState);
16068  if (*state & ExitState)
16069    {
16070      /*
16071        Free Standard Colormap.
16072      */
16073      (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16074      if (resource_info->map_type == (char *) NULL)
16075        (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16076      /*
16077        Free X resources.
16078      */
16079      if (resource_info->copy_image != (Image *) NULL)
16080        {
16081          resource_info->copy_image=DestroyImage(resource_info->copy_image);
16082          resource_info->copy_image=NewImageList();
16083        }
16084      DestroyXResources();
16085    }
16086  (void) XSync(display,MagickFalse);
16087  /*
16088    Restore our progress monitor and warning handlers.
16089  */
16090  (void) SetErrorHandler(warning_handler);
16091  (void) SetWarningHandler(warning_handler);
16092  /*
16093    Change to home directory.
16094  */
16095  directory=getcwd(working_directory,MaxTextExtent);
16096  (void) directory;
16097  {
16098    int
16099      status;
16100
16101    status=chdir(resource_info->home_directory);
16102    if (status == -1)
16103      (void) ThrowMagickException(&display_image->exception,GetMagickModule(),
16104        FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
16105  }
16106  *image=display_image;
16107  return(nexus);
16108}
16109#else
16110
16111/*
16112%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16113%                                                                             %
16114%                                                                             %
16115%                                                                             %
16116+   D i s p l a y I m a g e s                                                 %
16117%                                                                             %
16118%                                                                             %
16119%                                                                             %
16120%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16121%
16122%  DisplayImages() displays an image sequence to any X window screen.  It
16123%  returns a value other than 0 if successful.  Check the exception member
16124%  of image to determine the reason for any failure.
16125%
16126%  The format of the DisplayImages method is:
16127%
16128%      MagickBooleanType DisplayImages(const ImageInfo *image_info,
16129%        Image *images,ExceptionInfo *exception)
16130%
16131%  A description of each parameter follows:
16132%
16133%    o image_info: the image info.
16134%
16135%    o image: the image.
16136%
16137%    o exception: return any errors or warnings in this structure.
16138%
16139*/
16140MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16141  Image *image,ExceptionInfo *exception)
16142{
16143  assert(image_info != (const ImageInfo *) NULL);
16144  assert(image_info->signature == MagickSignature);
16145  assert(image != (Image *) NULL);
16146  assert(image->signature == MagickSignature);
16147  if (image->debug != MagickFalse)
16148    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16149  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16150    "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image->filename);
16151  return(MagickFalse);
16152}
16153
16154/*
16155%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16156%                                                                             %
16157%                                                                             %
16158%                                                                             %
16159+   R e m o t e D i s p l a y C o m m a n d                                   %
16160%                                                                             %
16161%                                                                             %
16162%                                                                             %
16163%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16164%
16165%  RemoteDisplayCommand() encourages a remote display program to display the
16166%  specified image filename.
16167%
16168%  The format of the RemoteDisplayCommand method is:
16169%
16170%      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16171%        const char *window,const char *filename,ExceptionInfo *exception)
16172%
16173%  A description of each parameter follows:
16174%
16175%    o image_info: the image info.
16176%
16177%    o window: Specifies the name or id of an X window.
16178%
16179%    o filename: the name of the image filename to display.
16180%
16181%    o exception: return any errors or warnings in this structure.
16182%
16183*/
16184MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16185  const char *window,const char *filename,ExceptionInfo *exception)
16186{
16187  assert(image_info != (const ImageInfo *) NULL);
16188  assert(image_info->signature == MagickSignature);
16189  assert(filename != (char *) NULL);
16190  (void) window;
16191  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16192  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16193    "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image_info->filename);
16194  return(MagickFalse);
16195}
16196#endif
16197