display.c revision 9a48b17ace21f903496c0f5d0870f997e1bc3ba8
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          char
7213            message[MaxTextExtent];
7214
7215          (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7216            exception->reason != (char *) NULL ? exception->reason : "",
7217            exception->description != (char *) NULL ? exception->description :
7218            "");
7219          XNoticeWidget(display,windows,"Unable to save file:",message);
7220          break;
7221        }
7222      break;
7223    }
7224    case PrintCommand:
7225    {
7226      /*
7227        Print image.
7228      */
7229      status=XPrintImage(display,resource_info,windows,*image,exception);
7230      if (status == MagickFalse)
7231        {
7232          char
7233            message[MaxTextExtent];
7234
7235          (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7236            exception->reason != (char *) NULL ? exception->reason : "",
7237            exception->description != (char *) NULL ? exception->description :
7238            "");
7239          XNoticeWidget(display,windows,"Unable to print file:",message);
7240          break;
7241        }
7242      break;
7243    }
7244    case DeleteCommand:
7245    {
7246      static char
7247        filename[MaxTextExtent] = "\0";
7248
7249      /*
7250        Delete image file.
7251      */
7252      XFileBrowserWidget(display,windows,"Delete",filename);
7253      if (*filename == '\0')
7254        break;
7255      status=remove(filename) != 0 ? MagickTrue : MagickFalse;
7256      if (status != MagickFalse)
7257        XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7258      break;
7259    }
7260    case NewCommand:
7261    {
7262      int
7263        status;
7264
7265      static char
7266        color[MaxTextExtent] = "gray",
7267        geometry[MaxTextExtent] = "640x480";
7268
7269      static const char
7270        *format = "gradient";
7271
7272      /*
7273        Query user for canvas geometry.
7274      */
7275      status=XDialogWidget(display,windows,"New","Enter image geometry:",
7276        geometry);
7277      if (*geometry == '\0')
7278        break;
7279      if (status == 0)
7280        format="xc";
7281      XColorBrowserWidget(display,windows,"Select",color);
7282      if (*color == '\0')
7283        break;
7284      /*
7285        Create canvas.
7286      */
7287      (void) FormatLocaleString(image_info->filename,MaxTextExtent,
7288        "%s:%s",format,color);
7289      (void) CloneString(&image_info->size,geometry);
7290      nexus=ReadImage(image_info,exception);
7291      CatchException(exception);
7292      XClientMessage(display,windows->image.id,windows->im_protocols,
7293        windows->im_next_image,CurrentTime);
7294      break;
7295    }
7296    case VisualDirectoryCommand:
7297    {
7298      /*
7299        Visual Image directory.
7300      */
7301      nexus=XVisualDirectoryImage(display,resource_info,windows,exception);
7302      break;
7303    }
7304    case QuitCommand:
7305    {
7306      /*
7307        exit program.
7308      */
7309      if (resource_info->confirm_exit == MagickFalse)
7310        XClientMessage(display,windows->image.id,windows->im_protocols,
7311          windows->im_exit,CurrentTime);
7312      else
7313        {
7314          int
7315            status;
7316
7317          /*
7318            Confirm program exit.
7319          */
7320          status=XConfirmWidget(display,windows,"Do you really want to exit",
7321            resource_info->client_name);
7322          if (status > 0)
7323            XClientMessage(display,windows->image.id,windows->im_protocols,
7324              windows->im_exit,CurrentTime);
7325        }
7326      break;
7327    }
7328    case CutCommand:
7329    {
7330      /*
7331        Cut image.
7332      */
7333      (void) XCropImage(display,resource_info,windows,*image,CutMode,exception);
7334      break;
7335    }
7336    case CopyCommand:
7337    {
7338      /*
7339        Copy image.
7340      */
7341      (void) XCropImage(display,resource_info,windows,*image,CopyMode,
7342        exception);
7343      break;
7344    }
7345    case PasteCommand:
7346    {
7347      /*
7348        Paste image.
7349      */
7350      status=XPasteImage(display,resource_info,windows,*image,exception);
7351      if (status == MagickFalse)
7352        {
7353          XNoticeWidget(display,windows,"Unable to paste X image",
7354            (*image)->filename);
7355          break;
7356        }
7357      break;
7358    }
7359    case HalfSizeCommand:
7360    {
7361      /*
7362        Half image size.
7363      */
7364      windows->image.window_changes.width=windows->image.ximage->width/2;
7365      windows->image.window_changes.height=windows->image.ximage->height/2;
7366      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7367      break;
7368    }
7369    case OriginalSizeCommand:
7370    {
7371      /*
7372        Original image size.
7373      */
7374      windows->image.window_changes.width=(int) (*image)->columns;
7375      windows->image.window_changes.height=(int) (*image)->rows;
7376      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7377      break;
7378    }
7379    case DoubleSizeCommand:
7380    {
7381      /*
7382        Double the image size.
7383      */
7384      windows->image.window_changes.width=windows->image.ximage->width << 1;
7385      windows->image.window_changes.height=windows->image.ximage->height << 1;
7386      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7387      break;
7388    }
7389    case ResizeCommand:
7390    {
7391      int
7392        status;
7393
7394      size_t
7395        height,
7396        width;
7397
7398      ssize_t
7399        x,
7400        y;
7401
7402      /*
7403        Resize image.
7404      */
7405      width=(size_t) windows->image.ximage->width;
7406      height=(size_t) windows->image.ximage->height;
7407      x=0;
7408      y=0;
7409      (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g+0+0",
7410        (double) width,(double) height);
7411      status=XDialogWidget(display,windows,"Resize",
7412        "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7413      if (*geometry == '\0')
7414        break;
7415      if (status == 0)
7416        (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7417      (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7418      windows->image.window_changes.width=(int) width;
7419      windows->image.window_changes.height=(int) height;
7420      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7421      break;
7422    }
7423    case ApplyCommand:
7424    {
7425      char
7426        image_geometry[MaxTextExtent];
7427
7428      if ((windows->image.crop_geometry == (char *) NULL) &&
7429          ((int) (*image)->columns == windows->image.ximage->width) &&
7430          ((int) (*image)->rows == windows->image.ximage->height))
7431        break;
7432      /*
7433        Apply size transforms to image.
7434      */
7435      XSetCursorState(display,windows,MagickTrue);
7436      XCheckRefreshWindows(display,windows);
7437      /*
7438        Crop and/or scale displayed image.
7439      */
7440      (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
7441        windows->image.ximage->width,windows->image.ximage->height);
7442      (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
7443      if (windows->image.crop_geometry != (char *) NULL)
7444        windows->image.crop_geometry=(char *)
7445          RelinquishMagickMemory(windows->image.crop_geometry);
7446      windows->image.x=0;
7447      windows->image.y=0;
7448      XConfigureImageColormap(display,resource_info,windows,*image);
7449      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7450      break;
7451    }
7452    case RefreshCommand:
7453    {
7454      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7455      break;
7456    }
7457    case RestoreCommand:
7458    {
7459      /*
7460        Restore Image window to its original size.
7461      */
7462      if ((windows->image.width == (unsigned int) (*image)->columns) &&
7463          (windows->image.height == (unsigned int) (*image)->rows) &&
7464          (windows->image.crop_geometry == (char *) NULL))
7465        {
7466          (void) XBell(display,0);
7467          break;
7468        }
7469      windows->image.window_changes.width=(int) (*image)->columns;
7470      windows->image.window_changes.height=(int) (*image)->rows;
7471      if (windows->image.crop_geometry != (char *) NULL)
7472        {
7473          windows->image.crop_geometry=(char *)
7474            RelinquishMagickMemory(windows->image.crop_geometry);
7475          windows->image.crop_geometry=(char *) NULL;
7476          windows->image.x=0;
7477          windows->image.y=0;
7478        }
7479      XConfigureImageColormap(display,resource_info,windows,*image);
7480      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7481      break;
7482    }
7483    case CropCommand:
7484    {
7485      /*
7486        Crop image.
7487      */
7488      (void) XCropImage(display,resource_info,windows,*image,CropMode,
7489        exception);
7490      break;
7491    }
7492    case ChopCommand:
7493    {
7494      /*
7495        Chop image.
7496      */
7497      status=XChopImage(display,resource_info,windows,image,exception);
7498      if (status == MagickFalse)
7499        {
7500          XNoticeWidget(display,windows,"Unable to cut X image",
7501            (*image)->filename);
7502          break;
7503        }
7504      break;
7505    }
7506    case FlopCommand:
7507    {
7508      Image
7509        *flop_image;
7510
7511      /*
7512        Flop image scanlines.
7513      */
7514      XSetCursorState(display,windows,MagickTrue);
7515      XCheckRefreshWindows(display,windows);
7516      flop_image=FlopImage(*image,exception);
7517      if (flop_image != (Image *) NULL)
7518        {
7519          *image=DestroyImage(*image);
7520          *image=flop_image;
7521        }
7522      CatchException(exception);
7523      XSetCursorState(display,windows,MagickFalse);
7524      if (windows->image.crop_geometry != (char *) NULL)
7525        {
7526          /*
7527            Flop crop geometry.
7528          */
7529          width=(unsigned int) (*image)->columns;
7530          height=(unsigned int) (*image)->rows;
7531          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7532            &width,&height);
7533          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7534            "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7535        }
7536      if (windows->image.orphan != MagickFalse)
7537        break;
7538      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7539      break;
7540    }
7541    case FlipCommand:
7542    {
7543      Image
7544        *flip_image;
7545
7546      /*
7547        Flip image scanlines.
7548      */
7549      XSetCursorState(display,windows,MagickTrue);
7550      XCheckRefreshWindows(display,windows);
7551      flip_image=FlipImage(*image,exception);
7552      if (flip_image != (Image *) NULL)
7553        {
7554          *image=DestroyImage(*image);
7555          *image=flip_image;
7556        }
7557      CatchException(exception);
7558      XSetCursorState(display,windows,MagickFalse);
7559      if (windows->image.crop_geometry != (char *) NULL)
7560        {
7561          /*
7562            Flip crop geometry.
7563          */
7564          width=(unsigned int) (*image)->columns;
7565          height=(unsigned int) (*image)->rows;
7566          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7567            &width,&height);
7568          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7569            "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7570        }
7571      if (windows->image.orphan != MagickFalse)
7572        break;
7573      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7574      break;
7575    }
7576    case RotateRightCommand:
7577    {
7578      /*
7579        Rotate image 90 degrees clockwise.
7580      */
7581      status=XRotateImage(display,resource_info,windows,90.0,image,exception);
7582      if (status == MagickFalse)
7583        {
7584          XNoticeWidget(display,windows,"Unable to rotate X image",
7585            (*image)->filename);
7586          break;
7587        }
7588      break;
7589    }
7590    case RotateLeftCommand:
7591    {
7592      /*
7593        Rotate image 90 degrees counter-clockwise.
7594      */
7595      status=XRotateImage(display,resource_info,windows,-90.0,image,exception);
7596      if (status == MagickFalse)
7597        {
7598          XNoticeWidget(display,windows,"Unable to rotate X image",
7599            (*image)->filename);
7600          break;
7601        }
7602      break;
7603    }
7604    case RotateCommand:
7605    {
7606      /*
7607        Rotate image.
7608      */
7609      status=XRotateImage(display,resource_info,windows,0.0,image,exception);
7610      if (status == MagickFalse)
7611        {
7612          XNoticeWidget(display,windows,"Unable to rotate X image",
7613            (*image)->filename);
7614          break;
7615        }
7616      break;
7617    }
7618    case ShearCommand:
7619    {
7620      Image
7621        *shear_image;
7622
7623      static char
7624        geometry[MaxTextExtent] = "45.0x45.0";
7625
7626      /*
7627        Query user for shear color and geometry.
7628      */
7629      XColorBrowserWidget(display,windows,"Select",color);
7630      if (*color == '\0')
7631        break;
7632      (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7633        geometry);
7634      if (*geometry == '\0')
7635        break;
7636      /*
7637        Shear image.
7638      */
7639      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7640        exception);
7641      XSetCursorState(display,windows,MagickTrue);
7642      XCheckRefreshWindows(display,windows);
7643      (void) QueryColorDatabase(color,&(*image)->background_color,
7644        exception);
7645      flags=ParseGeometry(geometry,&geometry_info);
7646      if ((flags & SigmaValue) == 0)
7647        geometry_info.sigma=geometry_info.rho;
7648      shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7649        exception);
7650      if (shear_image != (Image *) NULL)
7651        {
7652          *image=DestroyImage(*image);
7653          *image=shear_image;
7654        }
7655      CatchException(exception);
7656      XSetCursorState(display,windows,MagickFalse);
7657      if (windows->image.orphan != MagickFalse)
7658        break;
7659      windows->image.window_changes.width=(int) (*image)->columns;
7660      windows->image.window_changes.height=(int) (*image)->rows;
7661      XConfigureImageColormap(display,resource_info,windows,*image);
7662      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7663      break;
7664    }
7665    case RollCommand:
7666    {
7667      Image
7668        *roll_image;
7669
7670      static char
7671        geometry[MaxTextExtent] = "+2+2";
7672
7673      /*
7674        Query user for the roll geometry.
7675      */
7676      (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7677        geometry);
7678      if (*geometry == '\0')
7679        break;
7680      /*
7681        Roll image.
7682      */
7683      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7684        exception);
7685      XSetCursorState(display,windows,MagickTrue);
7686      XCheckRefreshWindows(display,windows);
7687      (void) ParsePageGeometry(*image,geometry,&page_geometry,
7688        exception);
7689      roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7690        exception);
7691      if (roll_image != (Image *) NULL)
7692        {
7693          *image=DestroyImage(*image);
7694          *image=roll_image;
7695        }
7696      CatchException(exception);
7697      XSetCursorState(display,windows,MagickFalse);
7698      if (windows->image.orphan != MagickFalse)
7699        break;
7700      windows->image.window_changes.width=(int) (*image)->columns;
7701      windows->image.window_changes.height=(int) (*image)->rows;
7702      XConfigureImageColormap(display,resource_info,windows,*image);
7703      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7704      break;
7705    }
7706    case TrimCommand:
7707    {
7708      static char
7709        fuzz[MaxTextExtent];
7710
7711      /*
7712        Query user for the fuzz factor.
7713      */
7714      (void) FormatLocaleString(fuzz,MaxTextExtent,"%g%%",100.0*
7715        (*image)->fuzz/(QuantumRange+1.0));
7716      (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7717      if (*fuzz == '\0')
7718        break;
7719      (*image)->fuzz=SiPrefixToDouble(fuzz,(double) QuantumRange+1.0);
7720      /*
7721        Trim image.
7722      */
7723      status=XTrimImage(display,resource_info,windows,*image,exception);
7724      if (status == MagickFalse)
7725        {
7726          XNoticeWidget(display,windows,"Unable to trim X image",
7727            (*image)->filename);
7728          break;
7729        }
7730      break;
7731    }
7732    case HueCommand:
7733    {
7734      static char
7735        hue_percent[MaxTextExtent] = "110";
7736
7737      /*
7738        Query user for percent hue change.
7739      */
7740      (void) XDialogWidget(display,windows,"Apply",
7741        "Enter percent change in image hue (0-200):",hue_percent);
7742      if (*hue_percent == '\0')
7743        break;
7744      /*
7745        Vary the image hue.
7746      */
7747      XSetCursorState(display,windows,MagickTrue);
7748      XCheckRefreshWindows(display,windows);
7749      (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7750      (void) ConcatenateMagickString(modulate_factors,hue_percent,
7751        MaxTextExtent);
7752      (void) ModulateImage(*image,modulate_factors,exception);
7753      XSetCursorState(display,windows,MagickFalse);
7754      if (windows->image.orphan != MagickFalse)
7755        break;
7756      XConfigureImageColormap(display,resource_info,windows,*image);
7757      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7758      break;
7759    }
7760    case SaturationCommand:
7761    {
7762      static char
7763        saturation_percent[MaxTextExtent] = "110";
7764
7765      /*
7766        Query user for percent saturation change.
7767      */
7768      (void) XDialogWidget(display,windows,"Apply",
7769        "Enter percent change in color saturation (0-200):",saturation_percent);
7770      if (*saturation_percent == '\0')
7771        break;
7772      /*
7773        Vary color saturation.
7774      */
7775      XSetCursorState(display,windows,MagickTrue);
7776      XCheckRefreshWindows(display,windows);
7777      (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7778      (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7779        MaxTextExtent);
7780      (void) ModulateImage(*image,modulate_factors,exception);
7781      XSetCursorState(display,windows,MagickFalse);
7782      if (windows->image.orphan != MagickFalse)
7783        break;
7784      XConfigureImageColormap(display,resource_info,windows,*image);
7785      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7786      break;
7787    }
7788    case BrightnessCommand:
7789    {
7790      static char
7791        brightness_percent[MaxTextExtent] = "110";
7792
7793      /*
7794        Query user for percent brightness change.
7795      */
7796      (void) XDialogWidget(display,windows,"Apply",
7797        "Enter percent change in color brightness (0-200):",brightness_percent);
7798      if (*brightness_percent == '\0')
7799        break;
7800      /*
7801        Vary the color brightness.
7802      */
7803      XSetCursorState(display,windows,MagickTrue);
7804      XCheckRefreshWindows(display,windows);
7805      (void) CopyMagickString(modulate_factors,brightness_percent,
7806        MaxTextExtent);
7807      (void) ModulateImage(*image,modulate_factors,exception);
7808      XSetCursorState(display,windows,MagickFalse);
7809      if (windows->image.orphan != MagickFalse)
7810        break;
7811      XConfigureImageColormap(display,resource_info,windows,*image);
7812      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7813      break;
7814    }
7815    case GammaCommand:
7816    {
7817      static char
7818        factor[MaxTextExtent] = "1.6";
7819
7820      /*
7821        Query user for gamma value.
7822      */
7823      (void) XDialogWidget(display,windows,"Gamma",
7824        "Enter gamma value (e.g. 1.2):",factor);
7825      if (*factor == '\0')
7826        break;
7827      /*
7828        Gamma correct image.
7829      */
7830      XSetCursorState(display,windows,MagickTrue);
7831      XCheckRefreshWindows(display,windows);
7832      (void) GammaImage(*image,atof(factor),exception);
7833      XSetCursorState(display,windows,MagickFalse);
7834      if (windows->image.orphan != MagickFalse)
7835        break;
7836      XConfigureImageColormap(display,resource_info,windows,*image);
7837      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7838      break;
7839    }
7840    case SpiffCommand:
7841    {
7842      /*
7843        Sharpen the image contrast.
7844      */
7845      XSetCursorState(display,windows,MagickTrue);
7846      XCheckRefreshWindows(display,windows);
7847      (void) ContrastImage(*image,MagickTrue,exception);
7848      XSetCursorState(display,windows,MagickFalse);
7849      if (windows->image.orphan != MagickFalse)
7850        break;
7851      XConfigureImageColormap(display,resource_info,windows,*image);
7852      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7853      break;
7854    }
7855    case DullCommand:
7856    {
7857      /*
7858        Dull the image contrast.
7859      */
7860      XSetCursorState(display,windows,MagickTrue);
7861      XCheckRefreshWindows(display,windows);
7862      (void) ContrastImage(*image,MagickFalse,exception);
7863      XSetCursorState(display,windows,MagickFalse);
7864      if (windows->image.orphan != MagickFalse)
7865        break;
7866      XConfigureImageColormap(display,resource_info,windows,*image);
7867      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7868      break;
7869    }
7870    case ContrastStretchCommand:
7871    {
7872      double
7873        black_point,
7874        white_point;
7875
7876      static char
7877        levels[MaxTextExtent] = "1%";
7878
7879      /*
7880        Query user for gamma value.
7881      */
7882      (void) XDialogWidget(display,windows,"Contrast Stretch",
7883        "Enter black and white points:",levels);
7884      if (*levels == '\0')
7885        break;
7886      /*
7887        Contrast stretch image.
7888      */
7889      XSetCursorState(display,windows,MagickTrue);
7890      XCheckRefreshWindows(display,windows);
7891      flags=ParseGeometry(levels,&geometry_info);
7892      black_point=geometry_info.rho;
7893      white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7894      if ((flags & PercentValue) != 0)
7895        {
7896          black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7897          white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7898        }
7899      white_point=(MagickRealType) (*image)->columns*(*image)->rows-white_point;
7900      (void) ContrastStretchImage(*image,black_point,white_point,
7901        exception);
7902      XSetCursorState(display,windows,MagickFalse);
7903      if (windows->image.orphan != MagickFalse)
7904        break;
7905      XConfigureImageColormap(display,resource_info,windows,*image);
7906      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7907      break;
7908    }
7909    case SigmoidalContrastCommand:
7910    {
7911      GeometryInfo
7912        geometry_info;
7913
7914      MagickStatusType
7915        flags;
7916
7917      static char
7918        levels[MaxTextExtent] = "3x50%";
7919
7920      /*
7921        Query user for gamma value.
7922      */
7923      (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7924        "Enter contrast and midpoint:",levels);
7925      if (*levels == '\0')
7926        break;
7927      /*
7928        Contrast stretch image.
7929      */
7930      XSetCursorState(display,windows,MagickTrue);
7931      XCheckRefreshWindows(display,windows);
7932      flags=ParseGeometry(levels,&geometry_info);
7933      if ((flags & SigmaValue) == 0)
7934        geometry_info.sigma=1.0*QuantumRange/2.0;
7935      if ((flags & PercentValue) != 0)
7936        geometry_info.sigma=1.0*QuantumRange*geometry_info.sigma/100.0;
7937      (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
7938        geometry_info.sigma,exception);
7939      XSetCursorState(display,windows,MagickFalse);
7940      if (windows->image.orphan != MagickFalse)
7941        break;
7942      XConfigureImageColormap(display,resource_info,windows,*image);
7943      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7944      break;
7945    }
7946    case NormalizeCommand:
7947    {
7948      /*
7949        Perform histogram normalization on the image.
7950      */
7951      XSetCursorState(display,windows,MagickTrue);
7952      XCheckRefreshWindows(display,windows);
7953      (void) NormalizeImage(*image,exception);
7954      XSetCursorState(display,windows,MagickFalse);
7955      if (windows->image.orphan != MagickFalse)
7956        break;
7957      XConfigureImageColormap(display,resource_info,windows,*image);
7958      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7959      break;
7960    }
7961    case EqualizeCommand:
7962    {
7963      /*
7964        Perform histogram equalization on the image.
7965      */
7966      XSetCursorState(display,windows,MagickTrue);
7967      XCheckRefreshWindows(display,windows);
7968      (void) EqualizeImage(*image,exception);
7969      XSetCursorState(display,windows,MagickFalse);
7970      if (windows->image.orphan != MagickFalse)
7971        break;
7972      XConfigureImageColormap(display,resource_info,windows,*image);
7973      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7974      break;
7975    }
7976    case NegateCommand:
7977    {
7978      /*
7979        Negate colors in image.
7980      */
7981      XSetCursorState(display,windows,MagickTrue);
7982      XCheckRefreshWindows(display,windows);
7983      (void) NegateImage(*image,MagickFalse,exception);
7984      XSetCursorState(display,windows,MagickFalse);
7985      if (windows->image.orphan != MagickFalse)
7986        break;
7987      XConfigureImageColormap(display,resource_info,windows,*image);
7988      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7989      break;
7990    }
7991    case GrayscaleCommand:
7992    {
7993      /*
7994        Convert image to grayscale.
7995      */
7996      XSetCursorState(display,windows,MagickTrue);
7997      XCheckRefreshWindows(display,windows);
7998      (void) SetImageType(*image,(*image)->matte == MagickFalse ?
7999        GrayscaleType : GrayscaleMatteType,exception);
8000      XSetCursorState(display,windows,MagickFalse);
8001      if (windows->image.orphan != MagickFalse)
8002        break;
8003      XConfigureImageColormap(display,resource_info,windows,*image);
8004      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8005      break;
8006    }
8007    case MapCommand:
8008    {
8009      Image
8010        *affinity_image;
8011
8012      static char
8013        filename[MaxTextExtent] = "\0";
8014
8015      /*
8016        Request image file name from user.
8017      */
8018      XFileBrowserWidget(display,windows,"Map",filename);
8019      if (*filename == '\0')
8020        break;
8021      /*
8022        Map image.
8023      */
8024      XSetCursorState(display,windows,MagickTrue);
8025      XCheckRefreshWindows(display,windows);
8026      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
8027      affinity_image=ReadImage(image_info,exception);
8028      if (affinity_image != (Image *) NULL)
8029        {
8030          (void) RemapImage(&quantize_info,*image,affinity_image,exception);
8031          affinity_image=DestroyImage(affinity_image);
8032        }
8033      CatchException(exception);
8034      XSetCursorState(display,windows,MagickFalse);
8035      if (windows->image.orphan != MagickFalse)
8036        break;
8037      XConfigureImageColormap(display,resource_info,windows,*image);
8038      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8039      break;
8040    }
8041    case QuantizeCommand:
8042    {
8043      int
8044        status;
8045
8046      static char
8047        colors[MaxTextExtent] = "256";
8048
8049      /*
8050        Query user for maximum number of colors.
8051      */
8052      status=XDialogWidget(display,windows,"Quantize",
8053        "Maximum number of colors:",colors);
8054      if (*colors == '\0')
8055        break;
8056      /*
8057        Color reduce the image.
8058      */
8059      XSetCursorState(display,windows,MagickTrue);
8060      XCheckRefreshWindows(display,windows);
8061      quantize_info.number_colors=StringToUnsignedLong(colors);
8062      quantize_info.dither=status != 0 ? MagickTrue : MagickFalse;
8063      (void) QuantizeImage(&quantize_info,*image,exception);
8064      XSetCursorState(display,windows,MagickFalse);
8065      if (windows->image.orphan != MagickFalse)
8066        break;
8067      XConfigureImageColormap(display,resource_info,windows,*image);
8068      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8069      break;
8070    }
8071    case DespeckleCommand:
8072    {
8073      Image
8074        *despeckle_image;
8075
8076      /*
8077        Despeckle image.
8078      */
8079      XSetCursorState(display,windows,MagickTrue);
8080      XCheckRefreshWindows(display,windows);
8081      despeckle_image=DespeckleImage(*image,exception);
8082      if (despeckle_image != (Image *) NULL)
8083        {
8084          *image=DestroyImage(*image);
8085          *image=despeckle_image;
8086        }
8087      CatchException(exception);
8088      XSetCursorState(display,windows,MagickFalse);
8089      if (windows->image.orphan != MagickFalse)
8090        break;
8091      XConfigureImageColormap(display,resource_info,windows,*image);
8092      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8093      break;
8094    }
8095    case EmbossCommand:
8096    {
8097      Image
8098        *emboss_image;
8099
8100      static char
8101        radius[MaxTextExtent] = "0.0x1.0";
8102
8103      /*
8104        Query user for emboss radius.
8105      */
8106      (void) XDialogWidget(display,windows,"Emboss",
8107        "Enter the emboss radius and standard deviation:",radius);
8108      if (*radius == '\0')
8109        break;
8110      /*
8111        Reduce noise in the image.
8112      */
8113      XSetCursorState(display,windows,MagickTrue);
8114      XCheckRefreshWindows(display,windows);
8115      flags=ParseGeometry(radius,&geometry_info);
8116      if ((flags & SigmaValue) == 0)
8117        geometry_info.sigma=1.0;
8118      emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8119        exception);
8120      if (emboss_image != (Image *) NULL)
8121        {
8122          *image=DestroyImage(*image);
8123          *image=emboss_image;
8124        }
8125      CatchException(exception);
8126      XSetCursorState(display,windows,MagickFalse);
8127      if (windows->image.orphan != MagickFalse)
8128        break;
8129      XConfigureImageColormap(display,resource_info,windows,*image);
8130      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8131      break;
8132    }
8133    case ReduceNoiseCommand:
8134    {
8135      Image
8136        *noise_image;
8137
8138      static char
8139        radius[MaxTextExtent] = "0";
8140
8141      /*
8142        Query user for noise radius.
8143      */
8144      (void) XDialogWidget(display,windows,"Reduce Noise",
8145        "Enter the noise radius:",radius);
8146      if (*radius == '\0')
8147        break;
8148      /*
8149        Reduce noise in the image.
8150      */
8151      XSetCursorState(display,windows,MagickTrue);
8152      XCheckRefreshWindows(display,windows);
8153      flags=ParseGeometry(radius,&geometry_info);
8154      noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8155        geometry_info.rho,(size_t) geometry_info.rho,exception);
8156      if (noise_image != (Image *) NULL)
8157        {
8158          *image=DestroyImage(*image);
8159          *image=noise_image;
8160        }
8161      CatchException(exception);
8162      XSetCursorState(display,windows,MagickFalse);
8163      if (windows->image.orphan != MagickFalse)
8164        break;
8165      XConfigureImageColormap(display,resource_info,windows,*image);
8166      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8167      break;
8168    }
8169    case AddNoiseCommand:
8170    {
8171      char
8172        **noises;
8173
8174      Image
8175        *noise_image;
8176
8177      static char
8178        noise_type[MaxTextExtent] = "Gaussian";
8179
8180      /*
8181        Add noise to the image.
8182      */
8183      noises=GetCommandOptions(MagickNoiseOptions);
8184      if (noises == (char **) NULL)
8185        break;
8186      XListBrowserWidget(display,windows,&windows->widget,
8187        (const char **) noises,"Add Noise",
8188        "Select a type of noise to add to your image:",noise_type);
8189      noises=DestroyStringList(noises);
8190      if (*noise_type == '\0')
8191        break;
8192      XSetCursorState(display,windows,MagickTrue);
8193      XCheckRefreshWindows(display,windows);
8194      noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8195        MagickNoiseOptions,MagickFalse,noise_type),exception);
8196      if (noise_image != (Image *) NULL)
8197        {
8198          *image=DestroyImage(*image);
8199          *image=noise_image;
8200        }
8201      CatchException(exception);
8202      XSetCursorState(display,windows,MagickFalse);
8203      if (windows->image.orphan != MagickFalse)
8204        break;
8205      XConfigureImageColormap(display,resource_info,windows,*image);
8206      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8207      break;
8208    }
8209    case SharpenCommand:
8210    {
8211      Image
8212        *sharp_image;
8213
8214      static char
8215        radius[MaxTextExtent] = "0.0x1.0";
8216
8217      /*
8218        Query user for sharpen radius.
8219      */
8220      (void) XDialogWidget(display,windows,"Sharpen",
8221        "Enter the sharpen radius and standard deviation:",radius);
8222      if (*radius == '\0')
8223        break;
8224      /*
8225        Sharpen image scanlines.
8226      */
8227      XSetCursorState(display,windows,MagickTrue);
8228      XCheckRefreshWindows(display,windows);
8229      flags=ParseGeometry(radius,&geometry_info);
8230      sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8231        geometry_info.xi,exception);
8232      if (sharp_image != (Image *) NULL)
8233        {
8234          *image=DestroyImage(*image);
8235          *image=sharp_image;
8236        }
8237      CatchException(exception);
8238      XSetCursorState(display,windows,MagickFalse);
8239      if (windows->image.orphan != MagickFalse)
8240        break;
8241      XConfigureImageColormap(display,resource_info,windows,*image);
8242      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8243      break;
8244    }
8245    case BlurCommand:
8246    {
8247      Image
8248        *blur_image;
8249
8250      static char
8251        radius[MaxTextExtent] = "0.0x1.0";
8252
8253      /*
8254        Query user for blur radius.
8255      */
8256      (void) XDialogWidget(display,windows,"Blur",
8257        "Enter the blur radius and standard deviation:",radius);
8258      if (*radius == '\0')
8259        break;
8260      /*
8261        Blur an image.
8262      */
8263      XSetCursorState(display,windows,MagickTrue);
8264      XCheckRefreshWindows(display,windows);
8265      flags=ParseGeometry(radius,&geometry_info);
8266      blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8267        geometry_info.xi,exception);
8268      if (blur_image != (Image *) NULL)
8269        {
8270          *image=DestroyImage(*image);
8271          *image=blur_image;
8272        }
8273      CatchException(exception);
8274      XSetCursorState(display,windows,MagickFalse);
8275      if (windows->image.orphan != MagickFalse)
8276        break;
8277      XConfigureImageColormap(display,resource_info,windows,*image);
8278      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8279      break;
8280    }
8281    case ThresholdCommand:
8282    {
8283      double
8284        threshold;
8285
8286      static char
8287        factor[MaxTextExtent] = "128";
8288
8289      /*
8290        Query user for threshold value.
8291      */
8292      (void) XDialogWidget(display,windows,"Threshold",
8293        "Enter threshold value:",factor);
8294      if (*factor == '\0')
8295        break;
8296      /*
8297        Gamma correct image.
8298      */
8299      XSetCursorState(display,windows,MagickTrue);
8300      XCheckRefreshWindows(display,windows);
8301      threshold=SiPrefixToDouble(factor,QuantumRange);
8302      (void) BilevelImage(*image,threshold);
8303      XSetCursorState(display,windows,MagickFalse);
8304      if (windows->image.orphan != MagickFalse)
8305        break;
8306      XConfigureImageColormap(display,resource_info,windows,*image);
8307      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8308      break;
8309    }
8310    case EdgeDetectCommand:
8311    {
8312      Image
8313        *edge_image;
8314
8315      static char
8316        radius[MaxTextExtent] = "0";
8317
8318      /*
8319        Query user for edge factor.
8320      */
8321      (void) XDialogWidget(display,windows,"Detect Edges",
8322        "Enter the edge detect radius:",radius);
8323      if (*radius == '\0')
8324        break;
8325      /*
8326        Detect edge in image.
8327      */
8328      XSetCursorState(display,windows,MagickTrue);
8329      XCheckRefreshWindows(display,windows);
8330      flags=ParseGeometry(radius,&geometry_info);
8331      edge_image=EdgeImage(*image,geometry_info.rho,geometry_info.sigma,
8332        exception);
8333      if (edge_image != (Image *) NULL)
8334        {
8335          *image=DestroyImage(*image);
8336          *image=edge_image;
8337        }
8338      CatchException(exception);
8339      XSetCursorState(display,windows,MagickFalse);
8340      if (windows->image.orphan != MagickFalse)
8341        break;
8342      XConfigureImageColormap(display,resource_info,windows,*image);
8343      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8344      break;
8345    }
8346    case SpreadCommand:
8347    {
8348      Image
8349        *spread_image;
8350
8351      static char
8352        amount[MaxTextExtent] = "2";
8353
8354      /*
8355        Query user for spread amount.
8356      */
8357      (void) XDialogWidget(display,windows,"Spread",
8358        "Enter the displacement amount:",amount);
8359      if (*amount == '\0')
8360        break;
8361      /*
8362        Displace image pixels by a random amount.
8363      */
8364      XSetCursorState(display,windows,MagickTrue);
8365      XCheckRefreshWindows(display,windows);
8366      flags=ParseGeometry(amount,&geometry_info);
8367      spread_image=EdgeImage(*image,geometry_info.rho,geometry_info.sigma,
8368        exception);
8369      if (spread_image != (Image *) NULL)
8370        {
8371          *image=DestroyImage(*image);
8372          *image=spread_image;
8373        }
8374      CatchException(exception);
8375      XSetCursorState(display,windows,MagickFalse);
8376      if (windows->image.orphan != MagickFalse)
8377        break;
8378      XConfigureImageColormap(display,resource_info,windows,*image);
8379      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8380      break;
8381    }
8382    case ShadeCommand:
8383    {
8384      Image
8385        *shade_image;
8386
8387      int
8388        status;
8389
8390      static char
8391        geometry[MaxTextExtent] = "30x30";
8392
8393      /*
8394        Query user for the shade geometry.
8395      */
8396      status=XDialogWidget(display,windows,"Shade",
8397        "Enter the azimuth and elevation of the light source:",geometry);
8398      if (*geometry == '\0')
8399        break;
8400      /*
8401        Shade image pixels.
8402      */
8403      XSetCursorState(display,windows,MagickTrue);
8404      XCheckRefreshWindows(display,windows);
8405      flags=ParseGeometry(geometry,&geometry_info);
8406      if ((flags & SigmaValue) == 0)
8407        geometry_info.sigma=1.0;
8408      shade_image=ShadeImage(*image,status != 0 ? MagickFalse : MagickTrue,
8409        geometry_info.rho,geometry_info.sigma,exception);
8410      if (shade_image != (Image *) NULL)
8411        {
8412          *image=DestroyImage(*image);
8413          *image=shade_image;
8414        }
8415      CatchException(exception);
8416      XSetCursorState(display,windows,MagickFalse);
8417      if (windows->image.orphan != MagickFalse)
8418        break;
8419      XConfigureImageColormap(display,resource_info,windows,*image);
8420      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8421      break;
8422    }
8423    case RaiseCommand:
8424    {
8425      static char
8426        bevel_width[MaxTextExtent] = "10";
8427
8428      /*
8429        Query user for bevel width.
8430      */
8431      (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8432      if (*bevel_width == '\0')
8433        break;
8434      /*
8435        Raise an image.
8436      */
8437      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8438        exception);
8439      XSetCursorState(display,windows,MagickTrue);
8440      XCheckRefreshWindows(display,windows);
8441      (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8442        exception);
8443      (void) RaiseImage(*image,&page_geometry,MagickTrue,exception);
8444      XSetCursorState(display,windows,MagickFalse);
8445      if (windows->image.orphan != MagickFalse)
8446        break;
8447      XConfigureImageColormap(display,resource_info,windows,*image);
8448      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8449      break;
8450    }
8451    case SegmentCommand:
8452    {
8453      static char
8454        threshold[MaxTextExtent] = "1.0x1.5";
8455
8456      /*
8457        Query user for smoothing threshold.
8458      */
8459      (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8460        threshold);
8461      if (*threshold == '\0')
8462        break;
8463      /*
8464        Segment an image.
8465      */
8466      XSetCursorState(display,windows,MagickTrue);
8467      XCheckRefreshWindows(display,windows);
8468      flags=ParseGeometry(threshold,&geometry_info);
8469      if ((flags & SigmaValue) == 0)
8470        geometry_info.sigma=1.0;
8471      (void) SegmentImage(*image,RGBColorspace,MagickFalse,geometry_info.rho,
8472        geometry_info.sigma,exception);
8473      XSetCursorState(display,windows,MagickFalse);
8474      if (windows->image.orphan != MagickFalse)
8475        break;
8476      XConfigureImageColormap(display,resource_info,windows,*image);
8477      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8478      break;
8479    }
8480    case SepiaToneCommand:
8481    {
8482      double
8483        threshold;
8484
8485      Image
8486        *sepia_image;
8487
8488      static char
8489        factor[MaxTextExtent] = "80%";
8490
8491      /*
8492        Query user for sepia-tone factor.
8493      */
8494      (void) XDialogWidget(display,windows,"Sepia Tone",
8495        "Enter the sepia tone factor (0 - 99.9%):",factor);
8496      if (*factor == '\0')
8497        break;
8498      /*
8499        Sepia tone image pixels.
8500      */
8501      XSetCursorState(display,windows,MagickTrue);
8502      XCheckRefreshWindows(display,windows);
8503      threshold=SiPrefixToDouble(factor,QuantumRange);
8504      sepia_image=SepiaToneImage(*image,threshold,exception);
8505      if (sepia_image != (Image *) NULL)
8506        {
8507          *image=DestroyImage(*image);
8508          *image=sepia_image;
8509        }
8510      CatchException(exception);
8511      XSetCursorState(display,windows,MagickFalse);
8512      if (windows->image.orphan != MagickFalse)
8513        break;
8514      XConfigureImageColormap(display,resource_info,windows,*image);
8515      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8516      break;
8517    }
8518    case SolarizeCommand:
8519    {
8520      double
8521        threshold;
8522
8523      static char
8524        factor[MaxTextExtent] = "60%";
8525
8526      /*
8527        Query user for solarize factor.
8528      */
8529      (void) XDialogWidget(display,windows,"Solarize",
8530        "Enter the solarize factor (0 - 99.9%):",factor);
8531      if (*factor == '\0')
8532        break;
8533      /*
8534        Solarize image pixels.
8535      */
8536      XSetCursorState(display,windows,MagickTrue);
8537      XCheckRefreshWindows(display,windows);
8538      threshold=SiPrefixToDouble(factor,QuantumRange);
8539      (void) SolarizeImage(*image,threshold,exception);
8540      XSetCursorState(display,windows,MagickFalse);
8541      if (windows->image.orphan != MagickFalse)
8542        break;
8543      XConfigureImageColormap(display,resource_info,windows,*image);
8544      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8545      break;
8546    }
8547    case SwirlCommand:
8548    {
8549      Image
8550        *swirl_image;
8551
8552      static char
8553        degrees[MaxTextExtent] = "60";
8554
8555      /*
8556        Query user for swirl angle.
8557      */
8558      (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8559        degrees);
8560      if (*degrees == '\0')
8561        break;
8562      /*
8563        Swirl image pixels about the center.
8564      */
8565      XSetCursorState(display,windows,MagickTrue);
8566      XCheckRefreshWindows(display,windows);
8567      flags=ParseGeometry(degrees,&geometry_info);
8568      swirl_image=SwirlImage(*image,geometry_info.rho,(*image)->interpolate,
8569        exception);
8570      if (swirl_image != (Image *) NULL)
8571        {
8572          *image=DestroyImage(*image);
8573          *image=swirl_image;
8574        }
8575      CatchException(exception);
8576      XSetCursorState(display,windows,MagickFalse);
8577      if (windows->image.orphan != MagickFalse)
8578        break;
8579      XConfigureImageColormap(display,resource_info,windows,*image);
8580      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8581      break;
8582    }
8583    case ImplodeCommand:
8584    {
8585      Image
8586        *implode_image;
8587
8588      static char
8589        factor[MaxTextExtent] = "0.3";
8590
8591      /*
8592        Query user for implode factor.
8593      */
8594      (void) XDialogWidget(display,windows,"Implode",
8595        "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8596      if (*factor == '\0')
8597        break;
8598      /*
8599        Implode image pixels about the center.
8600      */
8601      XSetCursorState(display,windows,MagickTrue);
8602      XCheckRefreshWindows(display,windows);
8603      flags=ParseGeometry(factor,&geometry_info);
8604      implode_image=ImplodeImage(*image,geometry_info.rho,(*image)->interpolate,
8605        exception);
8606      if (implode_image != (Image *) NULL)
8607        {
8608          *image=DestroyImage(*image);
8609          *image=implode_image;
8610        }
8611      CatchException(exception);
8612      XSetCursorState(display,windows,MagickFalse);
8613      if (windows->image.orphan != MagickFalse)
8614        break;
8615      XConfigureImageColormap(display,resource_info,windows,*image);
8616      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8617      break;
8618    }
8619    case VignetteCommand:
8620    {
8621      Image
8622        *vignette_image;
8623
8624      static char
8625        geometry[MaxTextExtent] = "0x20";
8626
8627      /*
8628        Query user for the vignette geometry.
8629      */
8630      (void) XDialogWidget(display,windows,"Vignette",
8631        "Enter the radius, sigma, and x and y offsets:",geometry);
8632      if (*geometry == '\0')
8633        break;
8634      /*
8635        Soften the edges of the image in vignette style
8636      */
8637      XSetCursorState(display,windows,MagickTrue);
8638      XCheckRefreshWindows(display,windows);
8639      flags=ParseGeometry(geometry,&geometry_info);
8640      if ((flags & SigmaValue) == 0)
8641        geometry_info.sigma=1.0;
8642      if ((flags & XiValue) == 0)
8643        geometry_info.xi=0.1*(*image)->columns;
8644      if ((flags & PsiValue) == 0)
8645        geometry_info.psi=0.1*(*image)->rows;
8646      vignette_image=VignetteImage(*image,geometry_info.rho,geometry_info.sigma,
8647        (ssize_t) ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-
8648        0.5),exception);
8649      if (vignette_image != (Image *) NULL)
8650        {
8651          *image=DestroyImage(*image);
8652          *image=vignette_image;
8653        }
8654      CatchException(exception);
8655      XSetCursorState(display,windows,MagickFalse);
8656      if (windows->image.orphan != MagickFalse)
8657        break;
8658      XConfigureImageColormap(display,resource_info,windows,*image);
8659      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8660      break;
8661    }
8662    case WaveCommand:
8663    {
8664      Image
8665        *wave_image;
8666
8667      static char
8668        geometry[MaxTextExtent] = "25x150";
8669
8670      /*
8671        Query user for the wave geometry.
8672      */
8673      (void) XDialogWidget(display,windows,"Wave",
8674        "Enter the amplitude and length of the wave:",geometry);
8675      if (*geometry == '\0')
8676        break;
8677      /*
8678        Alter an image along a sine wave.
8679      */
8680      XSetCursorState(display,windows,MagickTrue);
8681      XCheckRefreshWindows(display,windows);
8682      flags=ParseGeometry(geometry,&geometry_info);
8683      if ((flags & SigmaValue) == 0)
8684        geometry_info.sigma=1.0;
8685      wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8686        (*image)->interpolate,exception);
8687      if (wave_image != (Image *) NULL)
8688        {
8689          *image=DestroyImage(*image);
8690          *image=wave_image;
8691        }
8692      CatchException(exception);
8693      XSetCursorState(display,windows,MagickFalse);
8694      if (windows->image.orphan != MagickFalse)
8695        break;
8696      XConfigureImageColormap(display,resource_info,windows,*image);
8697      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8698      break;
8699    }
8700    case OilPaintCommand:
8701    {
8702      Image
8703        *paint_image;
8704
8705      static char
8706        radius[MaxTextExtent] = "0";
8707
8708      /*
8709        Query user for circular neighborhood radius.
8710      */
8711      (void) XDialogWidget(display,windows,"Oil Paint",
8712        "Enter the mask radius:",radius);
8713      if (*radius == '\0')
8714        break;
8715      /*
8716        OilPaint image scanlines.
8717      */
8718      XSetCursorState(display,windows,MagickTrue);
8719      XCheckRefreshWindows(display,windows);
8720      flags=ParseGeometry(radius,&geometry_info);
8721      paint_image=OilPaintImage(*image,geometry_info.rho,geometry_info.sigma,
8722        exception);
8723      if (paint_image != (Image *) NULL)
8724        {
8725          *image=DestroyImage(*image);
8726          *image=paint_image;
8727        }
8728      CatchException(exception);
8729      XSetCursorState(display,windows,MagickFalse);
8730      if (windows->image.orphan != MagickFalse)
8731        break;
8732      XConfigureImageColormap(display,resource_info,windows,*image);
8733      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8734      break;
8735    }
8736    case CharcoalDrawCommand:
8737    {
8738      Image
8739        *charcoal_image;
8740
8741      static char
8742        radius[MaxTextExtent] = "0x1";
8743
8744      /*
8745        Query user for charcoal radius.
8746      */
8747      (void) XDialogWidget(display,windows,"Charcoal Draw",
8748        "Enter the charcoal radius and sigma:",radius);
8749      if (*radius == '\0')
8750        break;
8751      /*
8752        Charcoal the image.
8753      */
8754      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8755        exception);
8756      XSetCursorState(display,windows,MagickTrue);
8757      XCheckRefreshWindows(display,windows);
8758      flags=ParseGeometry(radius,&geometry_info);
8759      if ((flags & SigmaValue) == 0)
8760        geometry_info.sigma=geometry_info.rho;
8761      charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8762        geometry_info.xi,exception);
8763      if (charcoal_image != (Image *) NULL)
8764        {
8765          *image=DestroyImage(*image);
8766          *image=charcoal_image;
8767        }
8768      CatchException(exception);
8769      XSetCursorState(display,windows,MagickFalse);
8770      if (windows->image.orphan != MagickFalse)
8771        break;
8772      XConfigureImageColormap(display,resource_info,windows,*image);
8773      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8774      break;
8775    }
8776    case AnnotateCommand:
8777    {
8778      /*
8779        Annotate the image with text.
8780      */
8781      status=XAnnotateEditImage(display,resource_info,windows,*image,exception);
8782      if (status == MagickFalse)
8783        {
8784          XNoticeWidget(display,windows,"Unable to annotate X image",
8785            (*image)->filename);
8786          break;
8787        }
8788      break;
8789    }
8790    case DrawCommand:
8791    {
8792      /*
8793        Draw image.
8794      */
8795      status=XDrawEditImage(display,resource_info,windows,image,exception);
8796      if (status == MagickFalse)
8797        {
8798          XNoticeWidget(display,windows,"Unable to draw on the X image",
8799            (*image)->filename);
8800          break;
8801        }
8802      break;
8803    }
8804    case ColorCommand:
8805    {
8806      /*
8807        Color edit.
8808      */
8809      status=XColorEditImage(display,resource_info,windows,image,exception);
8810      if (status == MagickFalse)
8811        {
8812          XNoticeWidget(display,windows,"Unable to pixel edit X image",
8813            (*image)->filename);
8814          break;
8815        }
8816      break;
8817    }
8818    case MatteCommand:
8819    {
8820      /*
8821        Matte edit.
8822      */
8823      status=XMatteEditImage(display,resource_info,windows,image,exception);
8824      if (status == MagickFalse)
8825        {
8826          XNoticeWidget(display,windows,"Unable to matte edit X image",
8827            (*image)->filename);
8828          break;
8829        }
8830      break;
8831    }
8832    case CompositeCommand:
8833    {
8834      /*
8835        Composite image.
8836      */
8837      status=XCompositeImage(display,resource_info,windows,*image,
8838        exception);
8839      if (status == MagickFalse)
8840        {
8841          XNoticeWidget(display,windows,"Unable to composite X image",
8842            (*image)->filename);
8843          break;
8844        }
8845      break;
8846    }
8847    case AddBorderCommand:
8848    {
8849      Image
8850        *border_image;
8851
8852      static char
8853        geometry[MaxTextExtent] = "6x6";
8854
8855      /*
8856        Query user for border color and geometry.
8857      */
8858      XColorBrowserWidget(display,windows,"Select",color);
8859      if (*color == '\0')
8860        break;
8861      (void) XDialogWidget(display,windows,"Add Border",
8862        "Enter border geometry:",geometry);
8863      if (*geometry == '\0')
8864        break;
8865      /*
8866        Add a border to the image.
8867      */
8868      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8869        exception);
8870      XSetCursorState(display,windows,MagickTrue);
8871      XCheckRefreshWindows(display,windows);
8872      (void) QueryColorDatabase(color,&(*image)->border_color,
8873        exception);
8874      (void) ParsePageGeometry(*image,geometry,&page_geometry,
8875        exception);
8876      border_image=BorderImage(*image,&page_geometry,(*image)->compose,
8877        exception);
8878      if (border_image != (Image *) NULL)
8879        {
8880          *image=DestroyImage(*image);
8881          *image=border_image;
8882        }
8883      CatchException(exception);
8884      XSetCursorState(display,windows,MagickFalse);
8885      if (windows->image.orphan != MagickFalse)
8886        break;
8887      windows->image.window_changes.width=(int) (*image)->columns;
8888      windows->image.window_changes.height=(int) (*image)->rows;
8889      XConfigureImageColormap(display,resource_info,windows,*image);
8890      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8891      break;
8892    }
8893    case AddFrameCommand:
8894    {
8895      FrameInfo
8896        frame_info;
8897
8898      Image
8899        *frame_image;
8900
8901      static char
8902        geometry[MaxTextExtent] = "6x6";
8903
8904      /*
8905        Query user for frame color and geometry.
8906      */
8907      XColorBrowserWidget(display,windows,"Select",color);
8908      if (*color == '\0')
8909        break;
8910      (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8911        geometry);
8912      if (*geometry == '\0')
8913        break;
8914      /*
8915        Surround image with an ornamental border.
8916      */
8917      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8918        exception);
8919      XSetCursorState(display,windows,MagickTrue);
8920      XCheckRefreshWindows(display,windows);
8921      (void) QueryColorDatabase(color,&(*image)->matte_color,
8922        exception);
8923      (void) ParsePageGeometry(*image,geometry,&page_geometry,
8924        exception);
8925      frame_info.width=page_geometry.width;
8926      frame_info.height=page_geometry.height;
8927      frame_info.outer_bevel=page_geometry.x;
8928      frame_info.inner_bevel=page_geometry.y;
8929      frame_info.x=(ssize_t) frame_info.width;
8930      frame_info.y=(ssize_t) frame_info.height;
8931      frame_info.width=(*image)->columns+2*frame_info.width;
8932      frame_info.height=(*image)->rows+2*frame_info.height;
8933      frame_image=FrameImage(*image,&frame_info,(*image)->compose,exception);
8934      if (frame_image != (Image *) NULL)
8935        {
8936          *image=DestroyImage(*image);
8937          *image=frame_image;
8938        }
8939      CatchException(exception);
8940      XSetCursorState(display,windows,MagickFalse);
8941      if (windows->image.orphan != MagickFalse)
8942        break;
8943      windows->image.window_changes.width=(int) (*image)->columns;
8944      windows->image.window_changes.height=(int) (*image)->rows;
8945      XConfigureImageColormap(display,resource_info,windows,*image);
8946      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8947      break;
8948    }
8949    case CommentCommand:
8950    {
8951      const char
8952        *value;
8953
8954      FILE
8955        *file;
8956
8957      int
8958        unique_file;
8959
8960      /*
8961        Edit image comment.
8962      */
8963      unique_file=AcquireUniqueFileResource(image_info->filename);
8964      if (unique_file == -1)
8965        XNoticeWidget(display,windows,"Unable to edit image comment",
8966          image_info->filename);
8967      value=GetImageProperty(*image,"comment");
8968      if (value == (char *) NULL)
8969        unique_file=close(unique_file)-1;
8970      else
8971        {
8972          register const char
8973            *p;
8974
8975          file=fdopen(unique_file,"w");
8976          if (file == (FILE *) NULL)
8977            {
8978              XNoticeWidget(display,windows,"Unable to edit image comment",
8979                image_info->filename);
8980              break;
8981            }
8982          for (p=value; *p != '\0'; p++)
8983            (void) fputc((int) *p,file);
8984          (void) fputc('\n',file);
8985          (void) fclose(file);
8986        }
8987      XSetCursorState(display,windows,MagickTrue);
8988      XCheckRefreshWindows(display,windows);
8989      status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
8990        exception);
8991      if (status == MagickFalse)
8992        XNoticeWidget(display,windows,"Unable to edit image comment",
8993          (char *) NULL);
8994      else
8995        {
8996          char
8997            *comment;
8998
8999          comment=FileToString(image_info->filename,~0UL,exception);
9000          if (comment != (char *) NULL)
9001            {
9002              (void) SetImageProperty(*image,"comment",comment);
9003              (*image)->taint=MagickTrue;
9004            }
9005        }
9006      (void) RelinquishUniqueFileResource(image_info->filename);
9007      XSetCursorState(display,windows,MagickFalse);
9008      break;
9009    }
9010    case LaunchCommand:
9011    {
9012      /*
9013        Launch program.
9014      */
9015      XSetCursorState(display,windows,MagickTrue);
9016      XCheckRefreshWindows(display,windows);
9017      (void) AcquireUniqueFilename(filename);
9018      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"launch:%s",
9019        filename);
9020      status=WriteImage(image_info,*image,exception);
9021      if (status == MagickFalse)
9022        XNoticeWidget(display,windows,"Unable to launch image editor",
9023          (char *) NULL);
9024      else
9025        {
9026          nexus=ReadImage(resource_info->image_info,exception);
9027          CatchException(exception);
9028          XClientMessage(display,windows->image.id,windows->im_protocols,
9029            windows->im_next_image,CurrentTime);
9030        }
9031      (void) RelinquishUniqueFileResource(filename);
9032      XSetCursorState(display,windows,MagickFalse);
9033      break;
9034    }
9035    case RegionofInterestCommand:
9036    {
9037      /*
9038        Apply an image processing technique to a region of interest.
9039      */
9040      (void) XROIImage(display,resource_info,windows,image,exception);
9041      break;
9042    }
9043    case InfoCommand:
9044      break;
9045    case ZoomCommand:
9046    {
9047      /*
9048        Zoom image.
9049      */
9050      if (windows->magnify.mapped != MagickFalse)
9051        (void) XRaiseWindow(display,windows->magnify.id);
9052      else
9053        {
9054          /*
9055            Make magnify image.
9056          */
9057          XSetCursorState(display,windows,MagickTrue);
9058          (void) XMapRaised(display,windows->magnify.id);
9059          XSetCursorState(display,windows,MagickFalse);
9060        }
9061      break;
9062    }
9063    case ShowPreviewCommand:
9064    {
9065      char
9066        **previews;
9067
9068      Image
9069        *preview_image;
9070
9071      static char
9072        preview_type[MaxTextExtent] = "Gamma";
9073
9074      /*
9075        Select preview type from menu.
9076      */
9077      previews=GetCommandOptions(MagickPreviewOptions);
9078      if (previews == (char **) NULL)
9079        break;
9080      XListBrowserWidget(display,windows,&windows->widget,
9081        (const char **) previews,"Preview",
9082        "Select an enhancement, effect, or F/X:",preview_type);
9083      previews=DestroyStringList(previews);
9084      if (*preview_type == '\0')
9085        break;
9086      /*
9087        Show image preview.
9088      */
9089      XSetCursorState(display,windows,MagickTrue);
9090      XCheckRefreshWindows(display,windows);
9091      image_info->preview_type=(PreviewType)
9092        ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
9093      image_info->group=(ssize_t) windows->image.id;
9094      (void) DeleteImageProperty(*image,"label");
9095      (void) SetImageProperty(*image,"label","Preview");
9096      (void) AcquireUniqueFilename(filename);
9097      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"preview:%s",
9098        filename);
9099      status=WriteImage(image_info,*image,exception);
9100      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9101      preview_image=ReadImage(image_info,exception);
9102      (void) RelinquishUniqueFileResource(filename);
9103      if (preview_image == (Image *) NULL)
9104        break;
9105      (void) FormatLocaleString(preview_image->filename,MaxTextExtent,"show:%s",
9106        filename);
9107      status=WriteImage(image_info,preview_image,exception);
9108      preview_image=DestroyImage(preview_image);
9109      if (status == MagickFalse)
9110        XNoticeWidget(display,windows,"Unable to show image preview",
9111          (*image)->filename);
9112      XDelay(display,1500);
9113      XSetCursorState(display,windows,MagickFalse);
9114      break;
9115    }
9116    case ShowHistogramCommand:
9117    {
9118      Image
9119        *histogram_image;
9120
9121      /*
9122        Show image histogram.
9123      */
9124      XSetCursorState(display,windows,MagickTrue);
9125      XCheckRefreshWindows(display,windows);
9126      image_info->group=(ssize_t) windows->image.id;
9127      (void) DeleteImageProperty(*image,"label");
9128      (void) SetImageProperty(*image,"label","Histogram");
9129      (void) AcquireUniqueFilename(filename);
9130      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"histogram:%s",
9131        filename);
9132      status=WriteImage(image_info,*image,exception);
9133      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9134      histogram_image=ReadImage(image_info,exception);
9135      (void) RelinquishUniqueFileResource(filename);
9136      if (histogram_image == (Image *) NULL)
9137        break;
9138      (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
9139        "show:%s",filename);
9140      status=WriteImage(image_info,histogram_image,exception);
9141      histogram_image=DestroyImage(histogram_image);
9142      if (status == MagickFalse)
9143        XNoticeWidget(display,windows,"Unable to show histogram",
9144          (*image)->filename);
9145      XDelay(display,1500);
9146      XSetCursorState(display,windows,MagickFalse);
9147      break;
9148    }
9149    case ShowMatteCommand:
9150    {
9151      Image
9152        *matte_image;
9153
9154      if ((*image)->matte == MagickFalse)
9155        {
9156          XNoticeWidget(display,windows,
9157            "Image does not have any matte information",(*image)->filename);
9158          break;
9159        }
9160      /*
9161        Show image matte.
9162      */
9163      XSetCursorState(display,windows,MagickTrue);
9164      XCheckRefreshWindows(display,windows);
9165      image_info->group=(ssize_t) windows->image.id;
9166      (void) DeleteImageProperty(*image,"label");
9167      (void) SetImageProperty(*image,"label","Matte");
9168      (void) AcquireUniqueFilename(filename);
9169      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"matte:%s",
9170        filename);
9171      status=WriteImage(image_info,*image,exception);
9172      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9173      matte_image=ReadImage(image_info,exception);
9174      (void) RelinquishUniqueFileResource(filename);
9175      if (matte_image == (Image *) NULL)
9176        break;
9177      (void) FormatLocaleString(matte_image->filename,MaxTextExtent,"show:%s",
9178        filename);
9179      status=WriteImage(image_info,matte_image,exception);
9180      matte_image=DestroyImage(matte_image);
9181      if (status == MagickFalse)
9182        XNoticeWidget(display,windows,"Unable to show matte",
9183          (*image)->filename);
9184      XDelay(display,1500);
9185      XSetCursorState(display,windows,MagickFalse);
9186      break;
9187    }
9188    case BackgroundCommand:
9189    {
9190      /*
9191        Background image.
9192      */
9193      status=XBackgroundImage(display,resource_info,windows,image,exception);
9194      if (status == MagickFalse)
9195        break;
9196      nexus=CloneImage(*image,0,0,MagickTrue,exception);
9197      if (nexus != (Image *) NULL)
9198        XClientMessage(display,windows->image.id,windows->im_protocols,
9199          windows->im_next_image,CurrentTime);
9200      break;
9201    }
9202    case SlideShowCommand:
9203    {
9204      static char
9205        delay[MaxTextExtent] = "5";
9206
9207      /*
9208        Display next image after pausing.
9209      */
9210      (void) XDialogWidget(display,windows,"Slide Show",
9211        "Pause how many 1/100ths of a second between images:",delay);
9212      if (*delay == '\0')
9213        break;
9214      resource_info->delay=StringToUnsignedLong(delay);
9215      XClientMessage(display,windows->image.id,windows->im_protocols,
9216        windows->im_next_image,CurrentTime);
9217      break;
9218    }
9219    case PreferencesCommand:
9220    {
9221      /*
9222        Set user preferences.
9223      */
9224      status=XPreferencesWidget(display,resource_info,windows);
9225      if (status == MagickFalse)
9226        break;
9227      nexus=CloneImage(*image,0,0,MagickTrue,exception);
9228      if (nexus != (Image *) NULL)
9229        XClientMessage(display,windows->image.id,windows->im_protocols,
9230          windows->im_next_image,CurrentTime);
9231      break;
9232    }
9233    case HelpCommand:
9234    {
9235      /*
9236        User requested help.
9237      */
9238      XTextViewWidget(display,resource_info,windows,MagickFalse,
9239        "Help Viewer - Display",DisplayHelp);
9240      break;
9241    }
9242    case BrowseDocumentationCommand:
9243    {
9244      Atom
9245        mozilla_atom;
9246
9247      Window
9248        mozilla_window,
9249        root_window;
9250
9251      /*
9252        Browse the ImageMagick documentation.
9253      */
9254      root_window=XRootWindow(display,XDefaultScreen(display));
9255      mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9256      mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9257      if (mozilla_window != (Window) NULL)
9258        {
9259          char
9260            command[MaxTextExtent],
9261            *url;
9262
9263          /*
9264            Display documentation using Netscape remote control.
9265          */
9266          url=GetMagickHomeURL();
9267          (void) FormatLocaleString(command,MaxTextExtent,
9268            "openurl(%s,new-tab)",url);
9269          url=DestroyString(url);
9270          mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9271          (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9272            8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9273          XSetCursorState(display,windows,MagickFalse);
9274          break;
9275        }
9276      XSetCursorState(display,windows,MagickTrue);
9277      XCheckRefreshWindows(display,windows);
9278      status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9279        exception);
9280      if (status == MagickFalse)
9281        XNoticeWidget(display,windows,"Unable to browse documentation",
9282          (char *) NULL);
9283      XDelay(display,1500);
9284      XSetCursorState(display,windows,MagickFalse);
9285      break;
9286    }
9287    case VersionCommand:
9288    {
9289      XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9290        GetMagickCopyright());
9291      break;
9292    }
9293    case SaveToUndoBufferCommand:
9294      break;
9295    default:
9296    {
9297      (void) XBell(display,0);
9298      break;
9299    }
9300  }
9301  image_info=DestroyImageInfo(image_info);
9302  return(nexus);
9303}
9304
9305/*
9306%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9307%                                                                             %
9308%                                                                             %
9309%                                                                             %
9310+   X M a g n i f y I m a g e                                                 %
9311%                                                                             %
9312%                                                                             %
9313%                                                                             %
9314%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9315%
9316%  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9317%  The magnified portion is displayed in a separate window.
9318%
9319%  The format of the XMagnifyImage method is:
9320%
9321%      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9322%
9323%  A description of each parameter follows:
9324%
9325%    o display: Specifies a connection to an X server;  returned from
9326%      XOpenDisplay.
9327%
9328%    o windows: Specifies a pointer to a XWindows structure.
9329%
9330%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
9331%      the entire image is refreshed.
9332%
9333*/
9334static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9335{
9336  char
9337    text[MaxTextExtent];
9338
9339  register int
9340    x,
9341    y;
9342
9343  size_t
9344    state;
9345
9346  /*
9347    Update magnified image until the mouse button is released.
9348  */
9349  (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9350  state=DefaultState;
9351  x=event->xbutton.x;
9352  y=event->xbutton.y;
9353  windows->magnify.x=(int) windows->image.x+x;
9354  windows->magnify.y=(int) windows->image.y+y;
9355  do
9356  {
9357    /*
9358      Map and unmap Info widget as text cursor crosses its boundaries.
9359    */
9360    if (windows->info.mapped != MagickFalse)
9361      {
9362        if ((x < (int) (windows->info.x+windows->info.width)) &&
9363            (y < (int) (windows->info.y+windows->info.height)))
9364          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9365      }
9366    else
9367      if ((x > (int) (windows->info.x+windows->info.width)) ||
9368          (y > (int) (windows->info.y+windows->info.height)))
9369        (void) XMapWindow(display,windows->info.id);
9370    if (windows->info.mapped != MagickFalse)
9371      {
9372        /*
9373          Display pointer position.
9374        */
9375        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9376          windows->magnify.x,windows->magnify.y);
9377        XInfoWidget(display,windows,text);
9378      }
9379    /*
9380      Wait for next event.
9381    */
9382    XScreenEvent(display,windows,event);
9383    switch (event->type)
9384    {
9385      case ButtonPress:
9386        break;
9387      case ButtonRelease:
9388      {
9389        /*
9390          User has finished magnifying image.
9391        */
9392        x=event->xbutton.x;
9393        y=event->xbutton.y;
9394        state|=ExitState;
9395        break;
9396      }
9397      case Expose:
9398        break;
9399      case MotionNotify:
9400      {
9401        x=event->xmotion.x;
9402        y=event->xmotion.y;
9403        break;
9404      }
9405      default:
9406        break;
9407    }
9408    /*
9409      Check boundary conditions.
9410    */
9411    if (x < 0)
9412      x=0;
9413    else
9414      if (x >= (int) windows->image.width)
9415        x=(int) windows->image.width-1;
9416    if (y < 0)
9417      y=0;
9418    else
9419     if (y >= (int) windows->image.height)
9420       y=(int) windows->image.height-1;
9421  } while ((state & ExitState) == 0);
9422  /*
9423    Display magnified image.
9424  */
9425  XSetCursorState(display,windows,MagickFalse);
9426}
9427
9428/*
9429%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9430%                                                                             %
9431%                                                                             %
9432%                                                                             %
9433+   X M a g n i f y W i n d o w C o m m a n d                                 %
9434%                                                                             %
9435%                                                                             %
9436%                                                                             %
9437%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9438%
9439%  XMagnifyWindowCommand() moves the image within an Magnify window by one
9440%  pixel as specified by the key symbol.
9441%
9442%  The format of the XMagnifyWindowCommand method is:
9443%
9444%      void XMagnifyWindowCommand(Display *display,XWindows *windows,
9445%        const MagickStatusType state,const KeySym key_symbol)
9446%
9447%  A description of each parameter follows:
9448%
9449%    o display: Specifies a connection to an X server; returned from
9450%      XOpenDisplay.
9451%
9452%    o windows: Specifies a pointer to a XWindows structure.
9453%
9454%    o state: key mask.
9455%
9456%    o key_symbol: Specifies a KeySym which indicates which side of the image
9457%      to trim.
9458%
9459*/
9460static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9461  const MagickStatusType state,const KeySym key_symbol)
9462{
9463  unsigned int
9464    quantum;
9465
9466  /*
9467    User specified a magnify factor or position.
9468  */
9469  quantum=1;
9470  if ((state & Mod1Mask) != 0)
9471    quantum=10;
9472  switch ((int) key_symbol)
9473  {
9474    case QuitCommand:
9475    {
9476      (void) XWithdrawWindow(display,windows->magnify.id,
9477        windows->magnify.screen);
9478      break;
9479    }
9480    case XK_Home:
9481    case XK_KP_Home:
9482    {
9483      windows->magnify.x=(int) windows->image.width/2;
9484      windows->magnify.y=(int) windows->image.height/2;
9485      break;
9486    }
9487    case XK_Left:
9488    case XK_KP_Left:
9489    {
9490      if (windows->magnify.x > 0)
9491        windows->magnify.x-=quantum;
9492      break;
9493    }
9494    case XK_Up:
9495    case XK_KP_Up:
9496    {
9497      if (windows->magnify.y > 0)
9498        windows->magnify.y-=quantum;
9499      break;
9500    }
9501    case XK_Right:
9502    case XK_KP_Right:
9503    {
9504      if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9505        windows->magnify.x+=quantum;
9506      break;
9507    }
9508    case XK_Down:
9509    case XK_KP_Down:
9510    {
9511      if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9512        windows->magnify.y+=quantum;
9513      break;
9514    }
9515    case XK_0:
9516    case XK_1:
9517    case XK_2:
9518    case XK_3:
9519    case XK_4:
9520    case XK_5:
9521    case XK_6:
9522    case XK_7:
9523    case XK_8:
9524    case XK_9:
9525    {
9526      windows->magnify.data=(key_symbol-XK_0);
9527      break;
9528    }
9529    case XK_KP_0:
9530    case XK_KP_1:
9531    case XK_KP_2:
9532    case XK_KP_3:
9533    case XK_KP_4:
9534    case XK_KP_5:
9535    case XK_KP_6:
9536    case XK_KP_7:
9537    case XK_KP_8:
9538    case XK_KP_9:
9539    {
9540      windows->magnify.data=(key_symbol-XK_KP_0);
9541      break;
9542    }
9543    default:
9544      break;
9545  }
9546  XMakeMagnifyImage(display,windows);
9547}
9548
9549/*
9550%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9551%                                                                             %
9552%                                                                             %
9553%                                                                             %
9554+   X M a k e P a n I m a g e                                                 %
9555%                                                                             %
9556%                                                                             %
9557%                                                                             %
9558%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9559%
9560%  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9561%  icon window.
9562%
9563%  The format of the XMakePanImage method is:
9564%
9565%        void XMakePanImage(Display *display,XResourceInfo *resource_info,
9566%          XWindows *windows,Image *image,ExceptionInfo *exception)
9567%
9568%  A description of each parameter follows:
9569%
9570%    o display: Specifies a connection to an X server;  returned from
9571%      XOpenDisplay.
9572%
9573%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9574%
9575%    o windows: Specifies a pointer to a XWindows structure.
9576%
9577%    o image: the image.
9578%
9579%    o exception: return any errors or warnings in this structure.
9580%
9581*/
9582static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9583  XWindows *windows,Image *image,ExceptionInfo *exception)
9584{
9585  MagickStatusType
9586    status;
9587
9588  /*
9589    Create and display image for panning icon.
9590  */
9591  XSetCursorState(display,windows,MagickTrue);
9592  XCheckRefreshWindows(display,windows);
9593  windows->pan.x=(int) windows->image.x;
9594  windows->pan.y=(int) windows->image.y;
9595  status=XMakeImage(display,resource_info,&windows->pan,image,
9596    windows->pan.width,windows->pan.height,exception);
9597  if (status == MagickFalse)
9598    ThrowXWindowFatalException(ResourceLimitError,
9599     "MemoryAllocationFailed",image->filename);
9600  (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9601    windows->pan.pixmap);
9602  (void) XClearWindow(display,windows->pan.id);
9603  XDrawPanRectangle(display,windows);
9604  XSetCursorState(display,windows,MagickFalse);
9605}
9606
9607/*
9608%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9609%                                                                             %
9610%                                                                             %
9611%                                                                             %
9612+   X M a t t a E d i t I m a g e                                             %
9613%                                                                             %
9614%                                                                             %
9615%                                                                             %
9616%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9617%
9618%  XMatteEditImage() allows the user to interactively change the Matte channel
9619%  of an image.  If the image is PseudoClass it is promoted to DirectClass
9620%  before the matte information is stored.
9621%
9622%  The format of the XMatteEditImage method is:
9623%
9624%      MagickBooleanType XMatteEditImage(Display *display,
9625%        XResourceInfo *resource_info,XWindows *windows,Image **image,
9626%        ExceptionInfo *exception)
9627%
9628%  A description of each parameter follows:
9629%
9630%    o display: Specifies a connection to an X server;  returned from
9631%      XOpenDisplay.
9632%
9633%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9634%
9635%    o windows: Specifies a pointer to a XWindows structure.
9636%
9637%    o image: the image; returned from ReadImage.
9638%
9639%    o exception: return any errors or warnings in this structure.
9640%
9641*/
9642static MagickBooleanType XMatteEditImage(Display *display,
9643  XResourceInfo *resource_info,XWindows *windows,Image **image,
9644  ExceptionInfo *exception)
9645{
9646  static char
9647    matte[MaxTextExtent] = "0";
9648
9649  static const char
9650    *MatteEditMenu[] =
9651    {
9652      "Method",
9653      "Border Color",
9654      "Fuzz",
9655      "Matte Value",
9656      "Undo",
9657      "Help",
9658      "Dismiss",
9659      (char *) NULL
9660    };
9661
9662  static const ModeType
9663    MatteEditCommands[] =
9664    {
9665      MatteEditMethod,
9666      MatteEditBorderCommand,
9667      MatteEditFuzzCommand,
9668      MatteEditValueCommand,
9669      MatteEditUndoCommand,
9670      MatteEditHelpCommand,
9671      MatteEditDismissCommand
9672    };
9673
9674  static PaintMethod
9675    method = PointMethod;
9676
9677  static XColor
9678    border_color = { 0, 0, 0, 0, 0, 0 };
9679
9680  char
9681    command[MaxTextExtent],
9682    text[MaxTextExtent];
9683
9684  Cursor
9685    cursor;
9686
9687  int
9688    entry,
9689    id,
9690    x,
9691    x_offset,
9692    y,
9693    y_offset;
9694
9695  register int
9696    i;
9697
9698  register Quantum
9699    *q;
9700
9701  unsigned int
9702    height,
9703    width;
9704
9705  size_t
9706    state;
9707
9708  XEvent
9709    event;
9710
9711  /*
9712    Map Command widget.
9713  */
9714  (void) CloneString(&windows->command.name,"Matte Edit");
9715  windows->command.data=4;
9716  (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9717  (void) XMapRaised(display,windows->command.id);
9718  XClientMessage(display,windows->image.id,windows->im_protocols,
9719    windows->im_update_widget,CurrentTime);
9720  /*
9721    Make cursor.
9722  */
9723  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9724    resource_info->background_color,resource_info->foreground_color);
9725  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9726  /*
9727    Track pointer until button 1 is pressed.
9728  */
9729  XQueryPosition(display,windows->image.id,&x,&y);
9730  (void) XSelectInput(display,windows->image.id,
9731    windows->image.attributes.event_mask | PointerMotionMask);
9732  state=DefaultState;
9733  do
9734  {
9735    if (windows->info.mapped != MagickFalse)
9736      {
9737        /*
9738          Display pointer position.
9739        */
9740        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9741          x+windows->image.x,y+windows->image.y);
9742        XInfoWidget(display,windows,text);
9743      }
9744    /*
9745      Wait for next event.
9746    */
9747    XScreenEvent(display,windows,&event);
9748    if (event.xany.window == windows->command.id)
9749      {
9750        /*
9751          Select a command from the Command widget.
9752        */
9753        id=XCommandWidget(display,windows,MatteEditMenu,&event);
9754        if (id < 0)
9755          {
9756            (void) XCheckDefineCursor(display,windows->image.id,cursor);
9757            continue;
9758          }
9759        switch (MatteEditCommands[id])
9760        {
9761          case MatteEditMethod:
9762          {
9763            char
9764              **methods;
9765
9766            /*
9767              Select a method from the pop-up menu.
9768            */
9769            methods=GetCommandOptions(MagickMethodOptions);
9770            if (methods == (char **) NULL)
9771              break;
9772            entry=XMenuWidget(display,windows,MatteEditMenu[id],
9773              (const char **) methods,command);
9774            if (entry >= 0)
9775              method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9776                MagickFalse,methods[entry]);
9777            methods=DestroyStringList(methods);
9778            break;
9779          }
9780          case MatteEditBorderCommand:
9781          {
9782            const char
9783              *ColorMenu[MaxNumberPens];
9784
9785            int
9786              pen_number;
9787
9788            /*
9789              Initialize menu selections.
9790            */
9791            for (i=0; i < (int) (MaxNumberPens-2); i++)
9792              ColorMenu[i]=resource_info->pen_colors[i];
9793            ColorMenu[MaxNumberPens-2]="Browser...";
9794            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9795            /*
9796              Select a pen color from the pop-up menu.
9797            */
9798            pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9799              (const char **) ColorMenu,command);
9800            if (pen_number < 0)
9801              break;
9802            if (pen_number == (MaxNumberPens-2))
9803              {
9804                static char
9805                  color_name[MaxTextExtent] = "gray";
9806
9807                /*
9808                  Select a pen color from a dialog.
9809                */
9810                resource_info->pen_colors[pen_number]=color_name;
9811                XColorBrowserWidget(display,windows,"Select",color_name);
9812                if (*color_name == '\0')
9813                  break;
9814              }
9815            /*
9816              Set border color.
9817            */
9818            (void) XParseColor(display,windows->map_info->colormap,
9819              resource_info->pen_colors[pen_number],&border_color);
9820            break;
9821          }
9822          case MatteEditFuzzCommand:
9823          {
9824            static char
9825              fuzz[MaxTextExtent];
9826
9827            static const char
9828              *FuzzMenu[] =
9829              {
9830                "0%",
9831                "2%",
9832                "5%",
9833                "10%",
9834                "15%",
9835                "Dialog...",
9836                (char *) NULL,
9837              };
9838
9839            /*
9840              Select a command from the pop-up menu.
9841            */
9842            entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9843              command);
9844            if (entry < 0)
9845              break;
9846            if (entry != 5)
9847              {
9848                (*image)->fuzz=SiPrefixToDouble(FuzzMenu[entry],1.0*
9849                  QuantumRange+1.0);
9850                break;
9851              }
9852            (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9853            (void) XDialogWidget(display,windows,"Ok",
9854              "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9855            if (*fuzz == '\0')
9856              break;
9857            (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
9858            (*image)->fuzz=SiPrefixToDouble(fuzz,1.0*QuantumRange+1.0);
9859            break;
9860          }
9861          case MatteEditValueCommand:
9862          {
9863            static char
9864              message[MaxTextExtent];
9865
9866            static const char
9867              *MatteMenu[] =
9868              {
9869                "Opaque",
9870                "Transparent",
9871                "Dialog...",
9872                (char *) NULL,
9873              };
9874
9875            /*
9876              Select a command from the pop-up menu.
9877            */
9878            entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9879              command);
9880            if (entry < 0)
9881              break;
9882            if (entry != 2)
9883              {
9884                (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9885                  OpaqueAlpha);
9886                if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9887                  (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9888                    (Quantum) TransparentAlpha);
9889                break;
9890              }
9891            (void) FormatLocaleString(message,MaxTextExtent,
9892              "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9893              QuantumRange);
9894            (void) XDialogWidget(display,windows,"Matte",message,matte);
9895            if (*matte == '\0')
9896              break;
9897            break;
9898          }
9899          case MatteEditUndoCommand:
9900          {
9901            (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9902              image,exception);
9903            break;
9904          }
9905          case MatteEditHelpCommand:
9906          {
9907            XTextViewWidget(display,resource_info,windows,MagickFalse,
9908              "Help Viewer - Matte Edit",ImageMatteEditHelp);
9909            break;
9910          }
9911          case MatteEditDismissCommand:
9912          {
9913            /*
9914              Prematurely exit.
9915            */
9916            state|=EscapeState;
9917            state|=ExitState;
9918            break;
9919          }
9920          default:
9921            break;
9922        }
9923        (void) XCheckDefineCursor(display,windows->image.id,cursor);
9924        continue;
9925      }
9926    switch (event.type)
9927    {
9928      case ButtonPress:
9929      {
9930        if (event.xbutton.button != Button1)
9931          break;
9932        if ((event.xbutton.window != windows->image.id) &&
9933            (event.xbutton.window != windows->magnify.id))
9934          break;
9935        /*
9936          Update matte data.
9937        */
9938        x=event.xbutton.x;
9939        y=event.xbutton.y;
9940        (void) XMagickCommand(display,resource_info,windows,
9941          SaveToUndoBufferCommand,image,exception);
9942        state|=UpdateConfigurationState;
9943        break;
9944      }
9945      case ButtonRelease:
9946      {
9947        if (event.xbutton.button != Button1)
9948          break;
9949        if ((event.xbutton.window != windows->image.id) &&
9950            (event.xbutton.window != windows->magnify.id))
9951          break;
9952        /*
9953          Update colormap information.
9954        */
9955        x=event.xbutton.x;
9956        y=event.xbutton.y;
9957        XConfigureImageColormap(display,resource_info,windows,*image);
9958        (void) XConfigureImage(display,resource_info,windows,*image,exception);
9959        XInfoWidget(display,windows,text);
9960        (void) XCheckDefineCursor(display,windows->image.id,cursor);
9961        state&=(~UpdateConfigurationState);
9962        break;
9963      }
9964      case Expose:
9965        break;
9966      case KeyPress:
9967      {
9968        char
9969          command[MaxTextExtent];
9970
9971        KeySym
9972          key_symbol;
9973
9974        if (event.xkey.window == windows->magnify.id)
9975          {
9976            Window
9977              window;
9978
9979            window=windows->magnify.id;
9980            while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9981          }
9982        if (event.xkey.window != windows->image.id)
9983          break;
9984        /*
9985          Respond to a user key press.
9986        */
9987        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
9988          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
9989        switch ((int) key_symbol)
9990        {
9991          case XK_Escape:
9992          case XK_F20:
9993          {
9994            /*
9995              Prematurely exit.
9996            */
9997            state|=ExitState;
9998            break;
9999          }
10000          case XK_F1:
10001          case XK_Help:
10002          {
10003            XTextViewWidget(display,resource_info,windows,MagickFalse,
10004              "Help Viewer - Matte Edit",ImageMatteEditHelp);
10005            break;
10006          }
10007          default:
10008          {
10009            (void) XBell(display,0);
10010            break;
10011          }
10012        }
10013        break;
10014      }
10015      case MotionNotify:
10016      {
10017        /*
10018          Map and unmap Info widget as cursor crosses its boundaries.
10019        */
10020        x=event.xmotion.x;
10021        y=event.xmotion.y;
10022        if (windows->info.mapped != MagickFalse)
10023          {
10024            if ((x < (int) (windows->info.x+windows->info.width)) &&
10025                (y < (int) (windows->info.y+windows->info.height)))
10026              (void) XWithdrawWindow(display,windows->info.id,
10027                windows->info.screen);
10028          }
10029        else
10030          if ((x > (int) (windows->info.x+windows->info.width)) ||
10031              (y > (int) (windows->info.y+windows->info.height)))
10032            (void) XMapWindow(display,windows->info.id);
10033        break;
10034      }
10035      default:
10036        break;
10037    }
10038    if (event.xany.window == windows->magnify.id)
10039      {
10040        x=windows->magnify.x-windows->image.x;
10041        y=windows->magnify.y-windows->image.y;
10042      }
10043    x_offset=x;
10044    y_offset=y;
10045    if ((state & UpdateConfigurationState) != 0)
10046      {
10047        CacheView
10048          *image_view;
10049
10050        int
10051          x,
10052          y;
10053
10054        /*
10055          Matte edit is relative to image configuration.
10056        */
10057        (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
10058          MagickTrue);
10059        XPutPixel(windows->image.ximage,x_offset,y_offset,
10060          windows->pixel_info->background_color.pixel);
10061        width=(unsigned int) (*image)->columns;
10062        height=(unsigned int) (*image)->rows;
10063        x=0;
10064        y=0;
10065        if (windows->image.crop_geometry != (char *) NULL)
10066          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
10067            &height);
10068        x_offset=(int) (width*(windows->image.x+x_offset)/
10069          windows->image.ximage->width+x);
10070        y_offset=(int) (height*(windows->image.y+y_offset)/
10071          windows->image.ximage->height+y);
10072        if ((x_offset < 0) || (y_offset < 0))
10073          continue;
10074        if ((x_offset >= (int) (*image)->columns) ||
10075            (y_offset >= (int) (*image)->rows))
10076          continue;
10077        if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10078          return(MagickFalse);
10079        (*image)->matte=MagickTrue;
10080        image_view=AcquireCacheView(*image);
10081        switch (method)
10082        {
10083          case PointMethod:
10084          default:
10085          {
10086            /*
10087              Update matte information using point algorithm.
10088            */
10089            q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10090              (ssize_t) y_offset,1,1,exception);
10091            if (q == (Quantum *) NULL)
10092              break;
10093            SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10094            (void) SyncCacheViewAuthenticPixels(image_view,exception);
10095            break;
10096          }
10097          case ReplaceMethod:
10098          {
10099            PixelPacket
10100              pixel,
10101              target;
10102
10103            /*
10104              Update matte information using replace algorithm.
10105            */
10106            (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
10107              (ssize_t) y_offset,&target,exception);
10108            for (y=0; y < (int) (*image)->rows; y++)
10109            {
10110              q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10111                (*image)->columns,1,exception);
10112              if (q == (Quantum *) NULL)
10113                break;
10114              for (x=0; x < (int) (*image)->columns; x++)
10115              {
10116                GetPixelPacket(*image,q,&pixel);
10117                if (IsFuzzyEquivalencePixelPacket(*image,&pixel,&target))
10118                  SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10119                q+=GetPixelChannels(*image);
10120              }
10121              if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10122                break;
10123            }
10124            break;
10125          }
10126          case FloodfillMethod:
10127          case FillToBorderMethod:
10128          {
10129            ChannelType
10130              channel_mask;
10131
10132            DrawInfo
10133              *draw_info;
10134
10135            PixelInfo
10136              target;
10137
10138            /*
10139              Update matte information using floodfill algorithm.
10140            */
10141            (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
10142              (ssize_t) y_offset,&target,exception);
10143            if (method == FillToBorderMethod)
10144              {
10145                target.red=(MagickRealType) ScaleShortToQuantum(
10146                  border_color.red);
10147                target.green=(MagickRealType) ScaleShortToQuantum(
10148                  border_color.green);
10149                target.blue=(MagickRealType) ScaleShortToQuantum(
10150                  border_color.blue);
10151              }
10152            draw_info=CloneDrawInfo(resource_info->image_info,
10153              (DrawInfo *) NULL);
10154            draw_info->fill.alpha=ClampToQuantum(InterpretLocaleValue(matte,
10155              (char **) NULL));
10156            channel_mask=SetPixelChannelMask(*image,AlphaChannel);
10157            (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10158              x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
10159              MagickFalse : MagickTrue,exception);
10160            (void) SetPixelChannelMap(*image,channel_mask);
10161            draw_info=DestroyDrawInfo(draw_info);
10162            break;
10163          }
10164          case ResetMethod:
10165          {
10166            /*
10167              Update matte information using reset algorithm.
10168            */
10169            if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10170              return(MagickFalse);
10171            for (y=0; y < (int) (*image)->rows; y++)
10172            {
10173              q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10174                (*image)->columns,1,exception);
10175              if (q == (Quantum *) NULL)
10176                break;
10177              for (x=0; x < (int) (*image)->columns; x++)
10178              {
10179                SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10180                q+=GetPixelChannels(*image);
10181              }
10182              if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10183                break;
10184            }
10185            if (StringToLong(matte) == (long) OpaqueAlpha)
10186              (*image)->matte=MagickFalse;
10187            break;
10188          }
10189        }
10190        image_view=DestroyCacheView(image_view);
10191        state&=(~UpdateConfigurationState);
10192      }
10193  } while ((state & ExitState) == 0);
10194  (void) XSelectInput(display,windows->image.id,
10195    windows->image.attributes.event_mask);
10196  XSetCursorState(display,windows,MagickFalse);
10197  (void) XFreeCursor(display,cursor);
10198  return(MagickTrue);
10199}
10200
10201/*
10202%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10203%                                                                             %
10204%                                                                             %
10205%                                                                             %
10206+   X O p e n I m a g e                                                       %
10207%                                                                             %
10208%                                                                             %
10209%                                                                             %
10210%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10211%
10212%  XOpenImage() loads an image from a file.
10213%
10214%  The format of the XOpenImage method is:
10215%
10216%     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10217%       XWindows *windows,const unsigned int command)
10218%
10219%  A description of each parameter follows:
10220%
10221%    o display: Specifies a connection to an X server; returned from
10222%      XOpenDisplay.
10223%
10224%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10225%
10226%    o windows: Specifies a pointer to a XWindows structure.
10227%
10228%    o command: A value other than zero indicates that the file is selected
10229%      from the command line argument list.
10230%
10231*/
10232static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10233  XWindows *windows,const MagickBooleanType command)
10234{
10235  const MagickInfo
10236    *magick_info;
10237
10238  ExceptionInfo
10239    *exception;
10240
10241  Image
10242    *nexus;
10243
10244  ImageInfo
10245    *image_info;
10246
10247  static char
10248    filename[MaxTextExtent] = "\0";
10249
10250  /*
10251    Request file name from user.
10252  */
10253  if (command == MagickFalse)
10254    XFileBrowserWidget(display,windows,"Open",filename);
10255  else
10256    {
10257      char
10258        **filelist,
10259        **files;
10260
10261      int
10262        count,
10263        status;
10264
10265      register int
10266        i,
10267        j;
10268
10269      /*
10270        Select next image from the command line.
10271      */
10272      status=XGetCommand(display,windows->image.id,&files,&count);
10273      if (status == 0)
10274        {
10275          ThrowXWindowFatalException(XServerError,"UnableToGetProperty","...");
10276          return((Image *) NULL);
10277        }
10278      filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10279      if (filelist == (char **) NULL)
10280        {
10281          ThrowXWindowFatalException(ResourceLimitError,
10282            "MemoryAllocationFailed","...");
10283          (void) XFreeStringList(files);
10284          return((Image *) NULL);
10285        }
10286      j=0;
10287      for (i=1; i < count; i++)
10288        if (*files[i] != '-')
10289          filelist[j++]=files[i];
10290      filelist[j]=(char *) NULL;
10291      XListBrowserWidget(display,windows,&windows->widget,
10292        (const char **) filelist,"Load","Select Image to Load:",filename);
10293      filelist=(char **) RelinquishMagickMemory(filelist);
10294      (void) XFreeStringList(files);
10295    }
10296  if (*filename == '\0')
10297    return((Image *) NULL);
10298  image_info=CloneImageInfo(resource_info->image_info);
10299  (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10300    (void *) NULL);
10301  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10302  exception=AcquireExceptionInfo();
10303  (void) SetImageInfo(image_info,0,exception);
10304  if (LocaleCompare(image_info->magick,"X") == 0)
10305    {
10306      char
10307        seconds[MaxTextExtent];
10308
10309      /*
10310        User may want to delay the X server screen grab.
10311      */
10312      (void) CopyMagickString(seconds,"0",MaxTextExtent);
10313      (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10314        seconds);
10315      if (*seconds == '\0')
10316        return((Image *) NULL);
10317      XDelay(display,(size_t) (1000*StringToLong(seconds)));
10318    }
10319  magick_info=GetMagickInfo(image_info->magick,exception);
10320  if ((magick_info != (const MagickInfo *) NULL) &&
10321      (magick_info->raw != MagickFalse))
10322    {
10323      char
10324        geometry[MaxTextExtent];
10325
10326      /*
10327        Request image size from the user.
10328      */
10329      (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10330      if (image_info->size != (char *) NULL)
10331        (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10332      (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10333        geometry);
10334      (void) CloneString(&image_info->size,geometry);
10335    }
10336  /*
10337    Load the image.
10338  */
10339  XSetCursorState(display,windows,MagickTrue);
10340  XCheckRefreshWindows(display,windows);
10341  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10342  nexus=ReadImage(image_info,exception);
10343  CatchException(exception);
10344  XSetCursorState(display,windows,MagickFalse);
10345  if (nexus != (Image *) NULL)
10346    XClientMessage(display,windows->image.id,windows->im_protocols,
10347      windows->im_next_image,CurrentTime);
10348  else
10349    {
10350      char
10351        *text,
10352        **textlist;
10353
10354      /*
10355        Unknown image format.
10356      */
10357      text=FileToString(filename,~0,exception);
10358      if (text == (char *) NULL)
10359        return((Image *) NULL);
10360      textlist=StringToList(text);
10361      if (textlist != (char **) NULL)
10362        {
10363          char
10364            title[MaxTextExtent];
10365
10366          register int
10367            i;
10368
10369          (void) FormatLocaleString(title,MaxTextExtent,
10370            "Unknown format: %s",filename);
10371          XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10372            (const char **) textlist);
10373          for (i=0; textlist[i] != (char *) NULL; i++)
10374            textlist[i]=DestroyString(textlist[i]);
10375          textlist=(char **) RelinquishMagickMemory(textlist);
10376        }
10377      text=DestroyString(text);
10378    }
10379  exception=DestroyExceptionInfo(exception);
10380  image_info=DestroyImageInfo(image_info);
10381  return(nexus);
10382}
10383
10384/*
10385%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10386%                                                                             %
10387%                                                                             %
10388%                                                                             %
10389+   X P a n I m a g e                                                         %
10390%                                                                             %
10391%                                                                             %
10392%                                                                             %
10393%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10394%
10395%  XPanImage() pans the image until the mouse button is released.
10396%
10397%  The format of the XPanImage method is:
10398%
10399%      void XPanImage(Display *display,XWindows *windows,XEvent *event)
10400%
10401%  A description of each parameter follows:
10402%
10403%    o display: Specifies a connection to an X server;  returned from
10404%      XOpenDisplay.
10405%
10406%    o windows: Specifies a pointer to a XWindows structure.
10407%
10408%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10409%      the entire image is refreshed.
10410%
10411*/
10412static void XPanImage(Display *display,XWindows *windows,XEvent *event)
10413{
10414  char
10415    text[MaxTextExtent];
10416
10417  Cursor
10418    cursor;
10419
10420  MagickRealType
10421    x_factor,
10422    y_factor;
10423
10424  RectangleInfo
10425    pan_info;
10426
10427  size_t
10428    state;
10429
10430  /*
10431    Define cursor.
10432  */
10433  if ((windows->image.ximage->width > (int) windows->image.width) &&
10434      (windows->image.ximage->height > (int) windows->image.height))
10435    cursor=XCreateFontCursor(display,XC_fleur);
10436  else
10437    if (windows->image.ximage->width > (int) windows->image.width)
10438      cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10439    else
10440      if (windows->image.ximage->height > (int) windows->image.height)
10441        cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10442      else
10443        cursor=XCreateFontCursor(display,XC_arrow);
10444  (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10445  /*
10446    Pan image as pointer moves until the mouse button is released.
10447  */
10448  x_factor=(MagickRealType) windows->image.ximage->width/windows->pan.width;
10449  y_factor=(MagickRealType) windows->image.ximage->height/windows->pan.height;
10450  pan_info.width=windows->pan.width*windows->image.width/
10451    windows->image.ximage->width;
10452  pan_info.height=windows->pan.height*windows->image.height/
10453    windows->image.ximage->height;
10454  pan_info.x=0;
10455  pan_info.y=0;
10456  state=UpdateConfigurationState;
10457  do
10458  {
10459    switch (event->type)
10460    {
10461      case ButtonPress:
10462      {
10463        /*
10464          User choose an initial pan location.
10465        */
10466        pan_info.x=(ssize_t) event->xbutton.x;
10467        pan_info.y=(ssize_t) event->xbutton.y;
10468        state|=UpdateConfigurationState;
10469        break;
10470      }
10471      case ButtonRelease:
10472      {
10473        /*
10474          User has finished panning the image.
10475        */
10476        pan_info.x=(ssize_t) event->xbutton.x;
10477        pan_info.y=(ssize_t) event->xbutton.y;
10478        state|=UpdateConfigurationState | ExitState;
10479        break;
10480      }
10481      case MotionNotify:
10482      {
10483        pan_info.x=(ssize_t) event->xmotion.x;
10484        pan_info.y=(ssize_t) event->xmotion.y;
10485        state|=UpdateConfigurationState;
10486      }
10487      default:
10488        break;
10489    }
10490    if ((state & UpdateConfigurationState) != 0)
10491      {
10492        /*
10493          Check boundary conditions.
10494        */
10495        if (pan_info.x < (ssize_t) (pan_info.width/2))
10496          pan_info.x=0;
10497        else
10498          pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10499        if (pan_info.x < 0)
10500          pan_info.x=0;
10501        else
10502          if ((int) (pan_info.x+windows->image.width) >
10503              windows->image.ximage->width)
10504            pan_info.x=(ssize_t)
10505              (windows->image.ximage->width-windows->image.width);
10506        if (pan_info.y < (ssize_t) (pan_info.height/2))
10507          pan_info.y=0;
10508        else
10509          pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10510        if (pan_info.y < 0)
10511          pan_info.y=0;
10512        else
10513          if ((int) (pan_info.y+windows->image.height) >
10514              windows->image.ximage->height)
10515            pan_info.y=(ssize_t)
10516              (windows->image.ximage->height-windows->image.height);
10517        if ((windows->image.x != (int) pan_info.x) ||
10518            (windows->image.y != (int) pan_info.y))
10519          {
10520            /*
10521              Display image pan offset.
10522            */
10523            windows->image.x=(int) pan_info.x;
10524            windows->image.y=(int) pan_info.y;
10525            (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
10526              windows->image.width,windows->image.height,windows->image.x,
10527              windows->image.y);
10528            XInfoWidget(display,windows,text);
10529            /*
10530              Refresh Image window.
10531            */
10532            XDrawPanRectangle(display,windows);
10533            XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10534          }
10535        state&=(~UpdateConfigurationState);
10536      }
10537    /*
10538      Wait for next event.
10539    */
10540    if ((state & ExitState) == 0)
10541      XScreenEvent(display,windows,event);
10542  } while ((state & ExitState) == 0);
10543  /*
10544    Restore cursor.
10545  */
10546  (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10547  (void) XFreeCursor(display,cursor);
10548  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10549}
10550
10551/*
10552%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10553%                                                                             %
10554%                                                                             %
10555%                                                                             %
10556+   X P a s t e I m a g e                                                     %
10557%                                                                             %
10558%                                                                             %
10559%                                                                             %
10560%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10561%
10562%  XPasteImage() pastes an image previously saved with XCropImage in the X
10563%  window image at a location the user chooses with the pointer.
10564%
10565%  The format of the XPasteImage method is:
10566%
10567%      MagickBooleanType XPasteImage(Display *display,
10568%        XResourceInfo *resource_info,XWindows *windows,Image *image,
10569%        ExceptionInfo *exception)
10570%
10571%  A description of each parameter follows:
10572%
10573%    o display: Specifies a connection to an X server;  returned from
10574%      XOpenDisplay.
10575%
10576%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10577%
10578%    o windows: Specifies a pointer to a XWindows structure.
10579%
10580%    o image: the image; returned from ReadImage.
10581%
10582%    o exception: return any errors or warnings in this structure.
10583%
10584*/
10585static MagickBooleanType XPasteImage(Display *display,
10586  XResourceInfo *resource_info,XWindows *windows,Image *image,
10587  ExceptionInfo *exception)
10588{
10589  static const char
10590    *PasteMenu[] =
10591    {
10592      "Operator",
10593      "Help",
10594      "Dismiss",
10595      (char *) NULL
10596    };
10597
10598  static const ModeType
10599    PasteCommands[] =
10600    {
10601      PasteOperatorsCommand,
10602      PasteHelpCommand,
10603      PasteDismissCommand
10604    };
10605
10606  static CompositeOperator
10607    compose = CopyCompositeOp;
10608
10609  char
10610    text[MaxTextExtent];
10611
10612  Cursor
10613    cursor;
10614
10615  Image
10616    *paste_image;
10617
10618  int
10619    entry,
10620    id,
10621    x,
10622    y;
10623
10624  MagickRealType
10625    scale_factor;
10626
10627  RectangleInfo
10628    highlight_info,
10629    paste_info;
10630
10631  unsigned int
10632    height,
10633    width;
10634
10635  size_t
10636    state;
10637
10638  XEvent
10639    event;
10640
10641  /*
10642    Copy image.
10643  */
10644  if (resource_info->copy_image == (Image *) NULL)
10645    return(MagickFalse);
10646  paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,exception);
10647  /*
10648    Map Command widget.
10649  */
10650  (void) CloneString(&windows->command.name,"Paste");
10651  windows->command.data=1;
10652  (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10653  (void) XMapRaised(display,windows->command.id);
10654  XClientMessage(display,windows->image.id,windows->im_protocols,
10655    windows->im_update_widget,CurrentTime);
10656  /*
10657    Track pointer until button 1 is pressed.
10658  */
10659  XSetCursorState(display,windows,MagickFalse);
10660  XQueryPosition(display,windows->image.id,&x,&y);
10661  (void) XSelectInput(display,windows->image.id,
10662    windows->image.attributes.event_mask | PointerMotionMask);
10663  paste_info.x=(ssize_t) windows->image.x+x;
10664  paste_info.y=(ssize_t) windows->image.y+y;
10665  paste_info.width=0;
10666  paste_info.height=0;
10667  cursor=XCreateFontCursor(display,XC_ul_angle);
10668  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10669  state=DefaultState;
10670  do
10671  {
10672    if (windows->info.mapped != MagickFalse)
10673      {
10674        /*
10675          Display pointer position.
10676        */
10677        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
10678          (long) paste_info.x,(long) paste_info.y);
10679        XInfoWidget(display,windows,text);
10680      }
10681    highlight_info=paste_info;
10682    highlight_info.x=paste_info.x-windows->image.x;
10683    highlight_info.y=paste_info.y-windows->image.y;
10684    XHighlightRectangle(display,windows->image.id,
10685      windows->image.highlight_context,&highlight_info);
10686    /*
10687      Wait for next event.
10688    */
10689    XScreenEvent(display,windows,&event);
10690    XHighlightRectangle(display,windows->image.id,
10691      windows->image.highlight_context,&highlight_info);
10692    if (event.xany.window == windows->command.id)
10693      {
10694        /*
10695          Select a command from the Command widget.
10696        */
10697        id=XCommandWidget(display,windows,PasteMenu,&event);
10698        if (id < 0)
10699          continue;
10700        switch (PasteCommands[id])
10701        {
10702          case PasteOperatorsCommand:
10703          {
10704            char
10705              command[MaxTextExtent],
10706              **operators;
10707
10708            /*
10709              Select a command from the pop-up menu.
10710            */
10711            operators=GetCommandOptions(MagickComposeOptions);
10712            if (operators == (char **) NULL)
10713              break;
10714            entry=XMenuWidget(display,windows,PasteMenu[id],
10715              (const char **) operators,command);
10716            if (entry >= 0)
10717              compose=(CompositeOperator) ParseCommandOption(
10718                MagickComposeOptions,MagickFalse,operators[entry]);
10719            operators=DestroyStringList(operators);
10720            break;
10721          }
10722          case PasteHelpCommand:
10723          {
10724            XTextViewWidget(display,resource_info,windows,MagickFalse,
10725              "Help Viewer - Image Composite",ImagePasteHelp);
10726            break;
10727          }
10728          case PasteDismissCommand:
10729          {
10730            /*
10731              Prematurely exit.
10732            */
10733            state|=EscapeState;
10734            state|=ExitState;
10735            break;
10736          }
10737          default:
10738            break;
10739        }
10740        continue;
10741      }
10742    switch (event.type)
10743    {
10744      case ButtonPress:
10745      {
10746        if (image->debug != MagickFalse)
10747          (void) LogMagickEvent(X11Event,GetMagickModule(),
10748            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10749            event.xbutton.button,event.xbutton.x,event.xbutton.y);
10750        if (event.xbutton.button != Button1)
10751          break;
10752        if (event.xbutton.window != windows->image.id)
10753          break;
10754        /*
10755          Paste rectangle is relative to image configuration.
10756        */
10757        width=(unsigned int) image->columns;
10758        height=(unsigned int) image->rows;
10759        x=0;
10760        y=0;
10761        if (windows->image.crop_geometry != (char *) NULL)
10762          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10763            &width,&height);
10764        scale_factor=(MagickRealType) windows->image.ximage->width/width;
10765        paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10766        scale_factor=(MagickRealType) windows->image.ximage->height/height;
10767        paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10768        (void) XCheckDefineCursor(display,windows->image.id,cursor);
10769        paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10770        paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10771        break;
10772      }
10773      case ButtonRelease:
10774      {
10775        if (image->debug != MagickFalse)
10776          (void) LogMagickEvent(X11Event,GetMagickModule(),
10777            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10778            event.xbutton.button,event.xbutton.x,event.xbutton.y);
10779        if (event.xbutton.button != Button1)
10780          break;
10781        if (event.xbutton.window != windows->image.id)
10782          break;
10783        if ((paste_info.width != 0) && (paste_info.height != 0))
10784          {
10785            /*
10786              User has selected the location of the paste image.
10787            */
10788            paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10789            paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10790            state|=ExitState;
10791          }
10792        break;
10793      }
10794      case Expose:
10795        break;
10796      case KeyPress:
10797      {
10798        char
10799          command[MaxTextExtent];
10800
10801        KeySym
10802          key_symbol;
10803
10804        int
10805          length;
10806
10807        if (event.xkey.window != windows->image.id)
10808          break;
10809        /*
10810          Respond to a user key press.
10811        */
10812        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10813          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10814        *(command+length)='\0';
10815        if (image->debug != MagickFalse)
10816          (void) LogMagickEvent(X11Event,GetMagickModule(),
10817            "Key press: 0x%lx (%s)",(long) key_symbol,command);
10818        switch ((int) key_symbol)
10819        {
10820          case XK_Escape:
10821          case XK_F20:
10822          {
10823            /*
10824              Prematurely exit.
10825            */
10826            paste_image=DestroyImage(paste_image);
10827            state|=EscapeState;
10828            state|=ExitState;
10829            break;
10830          }
10831          case XK_F1:
10832          case XK_Help:
10833          {
10834            (void) XSetFunction(display,windows->image.highlight_context,
10835              GXcopy);
10836            XTextViewWidget(display,resource_info,windows,MagickFalse,
10837              "Help Viewer - Image Composite",ImagePasteHelp);
10838            (void) XSetFunction(display,windows->image.highlight_context,
10839              GXinvert);
10840            break;
10841          }
10842          default:
10843          {
10844            (void) XBell(display,0);
10845            break;
10846          }
10847        }
10848        break;
10849      }
10850      case MotionNotify:
10851      {
10852        /*
10853          Map and unmap Info widget as text cursor crosses its boundaries.
10854        */
10855        x=event.xmotion.x;
10856        y=event.xmotion.y;
10857        if (windows->info.mapped != MagickFalse)
10858          {
10859            if ((x < (int) (windows->info.x+windows->info.width)) &&
10860                (y < (int) (windows->info.y+windows->info.height)))
10861              (void) XWithdrawWindow(display,windows->info.id,
10862                windows->info.screen);
10863          }
10864        else
10865          if ((x > (int) (windows->info.x+windows->info.width)) ||
10866              (y > (int) (windows->info.y+windows->info.height)))
10867            (void) XMapWindow(display,windows->info.id);
10868        paste_info.x=(ssize_t) windows->image.x+x;
10869        paste_info.y=(ssize_t) windows->image.y+y;
10870        break;
10871      }
10872      default:
10873      {
10874        if (image->debug != MagickFalse)
10875          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10876            event.type);
10877        break;
10878      }
10879    }
10880  } while ((state & ExitState) == 0);
10881  (void) XSelectInput(display,windows->image.id,
10882    windows->image.attributes.event_mask);
10883  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10884  XSetCursorState(display,windows,MagickFalse);
10885  (void) XFreeCursor(display,cursor);
10886  if ((state & EscapeState) != 0)
10887    return(MagickTrue);
10888  /*
10889    Image pasting is relative to image configuration.
10890  */
10891  XSetCursorState(display,windows,MagickTrue);
10892  XCheckRefreshWindows(display,windows);
10893  width=(unsigned int) image->columns;
10894  height=(unsigned int) image->rows;
10895  x=0;
10896  y=0;
10897  if (windows->image.crop_geometry != (char *) NULL)
10898    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10899  scale_factor=(MagickRealType) width/windows->image.ximage->width;
10900  paste_info.x+=x;
10901  paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10902  paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10903  scale_factor=(MagickRealType) height/windows->image.ximage->height;
10904  paste_info.y+=y;
10905  paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10906  paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10907  /*
10908    Paste image with X Image window.
10909  */
10910  (void) CompositeImage(image,compose,paste_image,paste_info.x,paste_info.y);
10911  paste_image=DestroyImage(paste_image);
10912  XSetCursorState(display,windows,MagickFalse);
10913  /*
10914    Update image colormap.
10915  */
10916  XConfigureImageColormap(display,resource_info,windows,image);
10917  (void) XConfigureImage(display,resource_info,windows,image,exception);
10918  return(MagickTrue);
10919}
10920
10921/*
10922%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10923%                                                                             %
10924%                                                                             %
10925%                                                                             %
10926+   X P r i n t I m a g e                                                     %
10927%                                                                             %
10928%                                                                             %
10929%                                                                             %
10930%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10931%
10932%  XPrintImage() prints an image to a Postscript printer.
10933%
10934%  The format of the XPrintImage method is:
10935%
10936%      MagickBooleanType XPrintImage(Display *display,
10937%        XResourceInfo *resource_info,XWindows *windows,Image *image,
10938%        ExceptionInfo *exception)
10939%
10940%  A description of each parameter follows:
10941%
10942%    o display: Specifies a connection to an X server; returned from
10943%      XOpenDisplay.
10944%
10945%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10946%
10947%    o windows: Specifies a pointer to a XWindows structure.
10948%
10949%    o image: the image.
10950%
10951%    o exception: return any errors or warnings in this structure.
10952%
10953*/
10954static MagickBooleanType XPrintImage(Display *display,
10955  XResourceInfo *resource_info,XWindows *windows,Image *image,
10956  ExceptionInfo *exception)
10957{
10958  char
10959    filename[MaxTextExtent],
10960    geometry[MaxTextExtent];
10961
10962  Image
10963    *print_image;
10964
10965  ImageInfo
10966    *image_info;
10967
10968  MagickStatusType
10969    status;
10970
10971  /*
10972    Request Postscript page geometry from user.
10973  */
10974  image_info=CloneImageInfo(resource_info->image_info);
10975  (void) FormatLocaleString(geometry,MaxTextExtent,"Letter");
10976  if (image_info->page != (char *) NULL)
10977    (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
10978  XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
10979    "Select Postscript Page Geometry:",geometry);
10980  if (*geometry == '\0')
10981    return(MagickTrue);
10982  image_info->page=GetPageGeometry(geometry);
10983  /*
10984    Apply image transforms.
10985  */
10986  XSetCursorState(display,windows,MagickTrue);
10987  XCheckRefreshWindows(display,windows);
10988  print_image=CloneImage(image,0,0,MagickTrue,exception);
10989  if (print_image == (Image *) NULL)
10990    return(MagickFalse);
10991  (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
10992    windows->image.ximage->width,windows->image.ximage->height);
10993  (void) TransformImage(&print_image,windows->image.crop_geometry,geometry);
10994  /*
10995    Print image.
10996  */
10997  (void) AcquireUniqueFilename(filename);
10998  (void) FormatLocaleString(print_image->filename,MaxTextExtent,"print:%s",
10999    filename);
11000  status=WriteImage(image_info,print_image,exception);
11001  (void) RelinquishUniqueFileResource(filename);
11002  print_image=DestroyImage(print_image);
11003  image_info=DestroyImageInfo(image_info);
11004  XSetCursorState(display,windows,MagickFalse);
11005  return(status != 0 ? MagickTrue : MagickFalse);
11006}
11007
11008/*
11009%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11010%                                                                             %
11011%                                                                             %
11012%                                                                             %
11013+   X R O I I m a g e                                                         %
11014%                                                                             %
11015%                                                                             %
11016%                                                                             %
11017%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11018%
11019%  XROIImage() applies an image processing technique to a region of interest.
11020%
11021%  The format of the XROIImage method is:
11022%
11023%      MagickBooleanType XROIImage(Display *display,
11024%        XResourceInfo *resource_info,XWindows *windows,Image **image,
11025%        ExceptionInfo *exception)
11026%
11027%  A description of each parameter follows:
11028%
11029%    o display: Specifies a connection to an X server; returned from
11030%      XOpenDisplay.
11031%
11032%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11033%
11034%    o windows: Specifies a pointer to a XWindows structure.
11035%
11036%    o image: the image; returned from ReadImage.
11037%
11038%    o exception: return any errors or warnings in this structure.
11039%
11040*/
11041static MagickBooleanType XROIImage(Display *display,
11042  XResourceInfo *resource_info,XWindows *windows,Image **image,
11043  ExceptionInfo *exception)
11044{
11045#define ApplyMenus  7
11046
11047  static const char
11048    *ROIMenu[] =
11049    {
11050      "Help",
11051      "Dismiss",
11052      (char *) NULL
11053    },
11054    *ApplyMenu[] =
11055    {
11056      "File",
11057      "Edit",
11058      "Transform",
11059      "Enhance",
11060      "Effects",
11061      "F/X",
11062      "Miscellany",
11063      "Help",
11064      "Dismiss",
11065      (char *) NULL
11066    },
11067    *FileMenu[] =
11068    {
11069      "Save...",
11070      "Print...",
11071      (char *) NULL
11072    },
11073    *EditMenu[] =
11074    {
11075      "Undo",
11076      "Redo",
11077      (char *) NULL
11078    },
11079    *TransformMenu[] =
11080    {
11081      "Flop",
11082      "Flip",
11083      "Rotate Right",
11084      "Rotate Left",
11085      (char *) NULL
11086    },
11087    *EnhanceMenu[] =
11088    {
11089      "Hue...",
11090      "Saturation...",
11091      "Brightness...",
11092      "Gamma...",
11093      "Spiff",
11094      "Dull",
11095      "Contrast Stretch...",
11096      "Sigmoidal Contrast...",
11097      "Normalize",
11098      "Equalize",
11099      "Negate",
11100      "Grayscale",
11101      "Map...",
11102      "Quantize...",
11103      (char *) NULL
11104    },
11105    *EffectsMenu[] =
11106    {
11107      "Despeckle",
11108      "Emboss",
11109      "Reduce Noise",
11110      "Add Noise",
11111      "Sharpen...",
11112      "Blur...",
11113      "Threshold...",
11114      "Edge Detect...",
11115      "Spread...",
11116      "Shade...",
11117      "Raise...",
11118      "Segment...",
11119      (char *) NULL
11120    },
11121    *FXMenu[] =
11122    {
11123      "Solarize...",
11124      "Sepia Tone...",
11125      "Swirl...",
11126      "Implode...",
11127      "Vignette...",
11128      "Wave...",
11129      "Oil Paint...",
11130      "Charcoal Draw...",
11131      (char *) NULL
11132    },
11133    *MiscellanyMenu[] =
11134    {
11135      "Image Info",
11136      "Zoom Image",
11137      "Show Preview...",
11138      "Show Histogram",
11139      "Show Matte",
11140      (char *) NULL
11141    };
11142
11143  static const char
11144    **Menus[ApplyMenus] =
11145    {
11146      FileMenu,
11147      EditMenu,
11148      TransformMenu,
11149      EnhanceMenu,
11150      EffectsMenu,
11151      FXMenu,
11152      MiscellanyMenu
11153    };
11154
11155  static const CommandType
11156    ApplyCommands[] =
11157    {
11158      NullCommand,
11159      NullCommand,
11160      NullCommand,
11161      NullCommand,
11162      NullCommand,
11163      NullCommand,
11164      NullCommand,
11165      HelpCommand,
11166      QuitCommand
11167    },
11168    FileCommands[] =
11169    {
11170      SaveCommand,
11171      PrintCommand
11172    },
11173    EditCommands[] =
11174    {
11175      UndoCommand,
11176      RedoCommand
11177    },
11178    TransformCommands[] =
11179    {
11180      FlopCommand,
11181      FlipCommand,
11182      RotateRightCommand,
11183      RotateLeftCommand
11184    },
11185    EnhanceCommands[] =
11186    {
11187      HueCommand,
11188      SaturationCommand,
11189      BrightnessCommand,
11190      GammaCommand,
11191      SpiffCommand,
11192      DullCommand,
11193      ContrastStretchCommand,
11194      SigmoidalContrastCommand,
11195      NormalizeCommand,
11196      EqualizeCommand,
11197      NegateCommand,
11198      GrayscaleCommand,
11199      MapCommand,
11200      QuantizeCommand
11201    },
11202    EffectsCommands[] =
11203    {
11204      DespeckleCommand,
11205      EmbossCommand,
11206      ReduceNoiseCommand,
11207      AddNoiseCommand,
11208      SharpenCommand,
11209      BlurCommand,
11210      EdgeDetectCommand,
11211      SpreadCommand,
11212      ShadeCommand,
11213      RaiseCommand,
11214      SegmentCommand
11215    },
11216    FXCommands[] =
11217    {
11218      SolarizeCommand,
11219      SepiaToneCommand,
11220      SwirlCommand,
11221      ImplodeCommand,
11222      VignetteCommand,
11223      WaveCommand,
11224      OilPaintCommand,
11225      CharcoalDrawCommand
11226    },
11227    MiscellanyCommands[] =
11228    {
11229      InfoCommand,
11230      ZoomCommand,
11231      ShowPreviewCommand,
11232      ShowHistogramCommand,
11233      ShowMatteCommand
11234    },
11235    ROICommands[] =
11236    {
11237      ROIHelpCommand,
11238      ROIDismissCommand
11239    };
11240
11241  static const CommandType
11242    *Commands[ApplyMenus] =
11243    {
11244      FileCommands,
11245      EditCommands,
11246      TransformCommands,
11247      EnhanceCommands,
11248      EffectsCommands,
11249      FXCommands,
11250      MiscellanyCommands
11251    };
11252
11253  char
11254    command[MaxTextExtent],
11255    text[MaxTextExtent];
11256
11257  CommandType
11258    command_type;
11259
11260  Cursor
11261    cursor;
11262
11263  Image
11264    *roi_image;
11265
11266  int
11267    entry,
11268    id,
11269    x,
11270    y;
11271
11272  MagickRealType
11273    scale_factor;
11274
11275  MagickProgressMonitor
11276    progress_monitor;
11277
11278  RectangleInfo
11279    crop_info,
11280    highlight_info,
11281    roi_info;
11282
11283  unsigned int
11284    height,
11285    width;
11286
11287  size_t
11288    state;
11289
11290  XEvent
11291    event;
11292
11293  /*
11294    Map Command widget.
11295  */
11296  (void) CloneString(&windows->command.name,"ROI");
11297  windows->command.data=0;
11298  (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11299  (void) XMapRaised(display,windows->command.id);
11300  XClientMessage(display,windows->image.id,windows->im_protocols,
11301    windows->im_update_widget,CurrentTime);
11302  /*
11303    Track pointer until button 1 is pressed.
11304  */
11305  XQueryPosition(display,windows->image.id,&x,&y);
11306  (void) XSelectInput(display,windows->image.id,
11307    windows->image.attributes.event_mask | PointerMotionMask);
11308  roi_info.x=(ssize_t) windows->image.x+x;
11309  roi_info.y=(ssize_t) windows->image.y+y;
11310  roi_info.width=0;
11311  roi_info.height=0;
11312  cursor=XCreateFontCursor(display,XC_fleur);
11313  state=DefaultState;
11314  do
11315  {
11316    if (windows->info.mapped != MagickFalse)
11317      {
11318        /*
11319          Display pointer position.
11320        */
11321        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
11322          (long) roi_info.x,(long) roi_info.y);
11323        XInfoWidget(display,windows,text);
11324      }
11325    /*
11326      Wait for next event.
11327    */
11328    XScreenEvent(display,windows,&event);
11329    if (event.xany.window == windows->command.id)
11330      {
11331        /*
11332          Select a command from the Command widget.
11333        */
11334        id=XCommandWidget(display,windows,ROIMenu,&event);
11335        if (id < 0)
11336          continue;
11337        switch (ROICommands[id])
11338        {
11339          case ROIHelpCommand:
11340          {
11341            XTextViewWidget(display,resource_info,windows,MagickFalse,
11342              "Help Viewer - Region of Interest",ImageROIHelp);
11343            break;
11344          }
11345          case ROIDismissCommand:
11346          {
11347            /*
11348              Prematurely exit.
11349            */
11350            state|=EscapeState;
11351            state|=ExitState;
11352            break;
11353          }
11354          default:
11355            break;
11356        }
11357        continue;
11358      }
11359    switch (event.type)
11360    {
11361      case ButtonPress:
11362      {
11363        if (event.xbutton.button != Button1)
11364          break;
11365        if (event.xbutton.window != windows->image.id)
11366          break;
11367        /*
11368          Note first corner of region of interest rectangle-- exit loop.
11369        */
11370        (void) XCheckDefineCursor(display,windows->image.id,cursor);
11371        roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11372        roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11373        state|=ExitState;
11374        break;
11375      }
11376      case ButtonRelease:
11377        break;
11378      case Expose:
11379        break;
11380      case KeyPress:
11381      {
11382        KeySym
11383          key_symbol;
11384
11385        if (event.xkey.window != windows->image.id)
11386          break;
11387        /*
11388          Respond to a user key press.
11389        */
11390        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11391          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11392        switch ((int) key_symbol)
11393        {
11394          case XK_Escape:
11395          case XK_F20:
11396          {
11397            /*
11398              Prematurely exit.
11399            */
11400            state|=EscapeState;
11401            state|=ExitState;
11402            break;
11403          }
11404          case XK_F1:
11405          case XK_Help:
11406          {
11407            XTextViewWidget(display,resource_info,windows,MagickFalse,
11408              "Help Viewer - Region of Interest",ImageROIHelp);
11409            break;
11410          }
11411          default:
11412          {
11413            (void) XBell(display,0);
11414            break;
11415          }
11416        }
11417        break;
11418      }
11419      case MotionNotify:
11420      {
11421        /*
11422          Map and unmap Info widget as text cursor crosses its boundaries.
11423        */
11424        x=event.xmotion.x;
11425        y=event.xmotion.y;
11426        if (windows->info.mapped != MagickFalse)
11427          {
11428            if ((x < (int) (windows->info.x+windows->info.width)) &&
11429                (y < (int) (windows->info.y+windows->info.height)))
11430              (void) XWithdrawWindow(display,windows->info.id,
11431                windows->info.screen);
11432          }
11433        else
11434          if ((x > (int) (windows->info.x+windows->info.width)) ||
11435              (y > (int) (windows->info.y+windows->info.height)))
11436            (void) XMapWindow(display,windows->info.id);
11437        roi_info.x=(ssize_t) windows->image.x+x;
11438        roi_info.y=(ssize_t) windows->image.y+y;
11439        break;
11440      }
11441      default:
11442        break;
11443    }
11444  } while ((state & ExitState) == 0);
11445  (void) XSelectInput(display,windows->image.id,
11446    windows->image.attributes.event_mask);
11447  if ((state & EscapeState) != 0)
11448    {
11449      /*
11450        User want to exit without region of interest.
11451      */
11452      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11453      (void) XFreeCursor(display,cursor);
11454      return(MagickTrue);
11455    }
11456  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11457  do
11458  {
11459    /*
11460      Size rectangle as pointer moves until the mouse button is released.
11461    */
11462    x=(int) roi_info.x;
11463    y=(int) roi_info.y;
11464    roi_info.width=0;
11465    roi_info.height=0;
11466    state=DefaultState;
11467    do
11468    {
11469      highlight_info=roi_info;
11470      highlight_info.x=roi_info.x-windows->image.x;
11471      highlight_info.y=roi_info.y-windows->image.y;
11472      if ((highlight_info.width > 3) && (highlight_info.height > 3))
11473        {
11474          /*
11475            Display info and draw region of interest rectangle.
11476          */
11477          if (windows->info.mapped == MagickFalse)
11478            (void) XMapWindow(display,windows->info.id);
11479          (void) FormatLocaleString(text,MaxTextExtent,
11480            " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11481            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11482          XInfoWidget(display,windows,text);
11483          XHighlightRectangle(display,windows->image.id,
11484            windows->image.highlight_context,&highlight_info);
11485        }
11486      else
11487        if (windows->info.mapped != MagickFalse)
11488          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11489      /*
11490        Wait for next event.
11491      */
11492      XScreenEvent(display,windows,&event);
11493      if ((highlight_info.width > 3) && (highlight_info.height > 3))
11494        XHighlightRectangle(display,windows->image.id,
11495          windows->image.highlight_context,&highlight_info);
11496      switch (event.type)
11497      {
11498        case ButtonPress:
11499        {
11500          roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11501          roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11502          break;
11503        }
11504        case ButtonRelease:
11505        {
11506          /*
11507            User has committed to region of interest rectangle.
11508          */
11509          roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11510          roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11511          XSetCursorState(display,windows,MagickFalse);
11512          state|=ExitState;
11513          if (LocaleCompare(windows->command.name,"Apply") == 0)
11514            break;
11515          (void) CloneString(&windows->command.name,"Apply");
11516          windows->command.data=ApplyMenus;
11517          (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11518          break;
11519        }
11520        case Expose:
11521          break;
11522        case MotionNotify:
11523        {
11524          roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11525          roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11526        }
11527        default:
11528          break;
11529      }
11530      if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11531          ((state & ExitState) != 0))
11532        {
11533          /*
11534            Check boundary conditions.
11535          */
11536          if (roi_info.x < 0)
11537            roi_info.x=0;
11538          else
11539            if (roi_info.x > (ssize_t) windows->image.ximage->width)
11540              roi_info.x=(ssize_t) windows->image.ximage->width;
11541          if ((int) roi_info.x < x)
11542            roi_info.width=(unsigned int) (x-roi_info.x);
11543          else
11544            {
11545              roi_info.width=(unsigned int) (roi_info.x-x);
11546              roi_info.x=(ssize_t) x;
11547            }
11548          if (roi_info.y < 0)
11549            roi_info.y=0;
11550          else
11551            if (roi_info.y > (ssize_t) windows->image.ximage->height)
11552              roi_info.y=(ssize_t) windows->image.ximage->height;
11553          if ((int) roi_info.y < y)
11554            roi_info.height=(unsigned int) (y-roi_info.y);
11555          else
11556            {
11557              roi_info.height=(unsigned int) (roi_info.y-y);
11558              roi_info.y=(ssize_t) y;
11559            }
11560        }
11561    } while ((state & ExitState) == 0);
11562    /*
11563      Wait for user to grab a corner of the rectangle or press return.
11564    */
11565    state=DefaultState;
11566    command_type=NullCommand;
11567    (void) XMapWindow(display,windows->info.id);
11568    do
11569    {
11570      if (windows->info.mapped != MagickFalse)
11571        {
11572          /*
11573            Display pointer position.
11574          */
11575          (void) FormatLocaleString(text,MaxTextExtent,
11576            " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11577            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11578          XInfoWidget(display,windows,text);
11579        }
11580      highlight_info=roi_info;
11581      highlight_info.x=roi_info.x-windows->image.x;
11582      highlight_info.y=roi_info.y-windows->image.y;
11583      if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11584        {
11585          state|=EscapeState;
11586          state|=ExitState;
11587          break;
11588        }
11589      if ((state & UpdateRegionState) != 0)
11590        {
11591          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11592          switch (command_type)
11593          {
11594            case UndoCommand:
11595            case RedoCommand:
11596            {
11597              (void) XMagickCommand(display,resource_info,windows,command_type,
11598                image,exception);
11599              break;
11600            }
11601            default:
11602            {
11603              /*
11604                Region of interest is relative to image configuration.
11605              */
11606              progress_monitor=SetImageProgressMonitor(*image,
11607                (MagickProgressMonitor) NULL,(*image)->client_data);
11608              crop_info=roi_info;
11609              width=(unsigned int) (*image)->columns;
11610              height=(unsigned int) (*image)->rows;
11611              x=0;
11612              y=0;
11613              if (windows->image.crop_geometry != (char *) NULL)
11614                (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11615                  &width,&height);
11616              scale_factor=(MagickRealType) width/windows->image.ximage->width;
11617              crop_info.x+=x;
11618              crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11619              crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11620              scale_factor=(MagickRealType)
11621                height/windows->image.ximage->height;
11622              crop_info.y+=y;
11623              crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11624              crop_info.height=(unsigned int)
11625                (scale_factor*crop_info.height+0.5);
11626              roi_image=CropImage(*image,&crop_info,exception);
11627              (void) SetImageProgressMonitor(*image,progress_monitor,
11628                (*image)->client_data);
11629              if (roi_image == (Image *) NULL)
11630                continue;
11631              /*
11632                Apply image processing technique to the region of interest.
11633              */
11634              windows->image.orphan=MagickTrue;
11635              (void) XMagickCommand(display,resource_info,windows,command_type,
11636                &roi_image,exception);
11637              progress_monitor=SetImageProgressMonitor(*image,
11638                (MagickProgressMonitor) NULL,(*image)->client_data);
11639              (void) XMagickCommand(display,resource_info,windows,
11640                SaveToUndoBufferCommand,image,exception);
11641              windows->image.orphan=MagickFalse;
11642              (void) CompositeImage(*image,CopyCompositeOp,roi_image,
11643                crop_info.x,crop_info.y);
11644              roi_image=DestroyImage(roi_image);
11645              (void) SetImageProgressMonitor(*image,progress_monitor,
11646                (*image)->client_data);
11647              break;
11648            }
11649          }
11650          if (command_type != InfoCommand)
11651            {
11652              XConfigureImageColormap(display,resource_info,windows,*image);
11653              (void) XConfigureImage(display,resource_info,windows,*image,exception);
11654            }
11655          XCheckRefreshWindows(display,windows);
11656          XInfoWidget(display,windows,text);
11657          (void) XSetFunction(display,windows->image.highlight_context,
11658            GXinvert);
11659          state&=(~UpdateRegionState);
11660        }
11661      XHighlightRectangle(display,windows->image.id,
11662        windows->image.highlight_context,&highlight_info);
11663      XScreenEvent(display,windows,&event);
11664      if (event.xany.window == windows->command.id)
11665        {
11666          /*
11667            Select a command from the Command widget.
11668          */
11669          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11670          command_type=NullCommand;
11671          id=XCommandWidget(display,windows,ApplyMenu,&event);
11672          if (id >= 0)
11673            {
11674              (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11675              command_type=ApplyCommands[id];
11676              if (id < ApplyMenus)
11677                {
11678                  /*
11679                    Select a command from a pop-up menu.
11680                  */
11681                  entry=XMenuWidget(display,windows,ApplyMenu[id],
11682                    (const char **) Menus[id],command);
11683                  if (entry >= 0)
11684                    {
11685                      (void) CopyMagickString(command,Menus[id][entry],
11686                        MaxTextExtent);
11687                      command_type=Commands[id][entry];
11688                    }
11689                }
11690            }
11691          (void) XSetFunction(display,windows->image.highlight_context,
11692            GXinvert);
11693          XHighlightRectangle(display,windows->image.id,
11694            windows->image.highlight_context,&highlight_info);
11695          if (command_type == HelpCommand)
11696            {
11697              (void) XSetFunction(display,windows->image.highlight_context,
11698                GXcopy);
11699              XTextViewWidget(display,resource_info,windows,MagickFalse,
11700                "Help Viewer - Region of Interest",ImageROIHelp);
11701              (void) XSetFunction(display,windows->image.highlight_context,
11702                GXinvert);
11703              continue;
11704            }
11705          if (command_type == QuitCommand)
11706            {
11707              /*
11708                exit.
11709              */
11710              state|=EscapeState;
11711              state|=ExitState;
11712              continue;
11713            }
11714          if (command_type != NullCommand)
11715            state|=UpdateRegionState;
11716          continue;
11717        }
11718      XHighlightRectangle(display,windows->image.id,
11719        windows->image.highlight_context,&highlight_info);
11720      switch (event.type)
11721      {
11722        case ButtonPress:
11723        {
11724          x=windows->image.x;
11725          y=windows->image.y;
11726          if (event.xbutton.button != Button1)
11727            break;
11728          if (event.xbutton.window != windows->image.id)
11729            break;
11730          x=windows->image.x+event.xbutton.x;
11731          y=windows->image.y+event.xbutton.y;
11732          if ((x < (int) (roi_info.x+RoiDelta)) &&
11733              (x > (int) (roi_info.x-RoiDelta)) &&
11734              (y < (int) (roi_info.y+RoiDelta)) &&
11735              (y > (int) (roi_info.y-RoiDelta)))
11736            {
11737              roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11738              roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11739              state|=UpdateConfigurationState;
11740              break;
11741            }
11742          if ((x < (int) (roi_info.x+RoiDelta)) &&
11743              (x > (int) (roi_info.x-RoiDelta)) &&
11744              (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11745              (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11746            {
11747              roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11748              state|=UpdateConfigurationState;
11749              break;
11750            }
11751          if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11752              (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11753              (y < (int) (roi_info.y+RoiDelta)) &&
11754              (y > (int) (roi_info.y-RoiDelta)))
11755            {
11756              roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11757              state|=UpdateConfigurationState;
11758              break;
11759            }
11760          if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11761              (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11762              (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11763              (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11764            {
11765              state|=UpdateConfigurationState;
11766              break;
11767            }
11768        }
11769        case ButtonRelease:
11770        {
11771          if (event.xbutton.window == windows->pan.id)
11772            if ((highlight_info.x != crop_info.x-windows->image.x) ||
11773                (highlight_info.y != crop_info.y-windows->image.y))
11774              XHighlightRectangle(display,windows->image.id,
11775                windows->image.highlight_context,&highlight_info);
11776          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11777            event.xbutton.time);
11778          break;
11779        }
11780        case Expose:
11781        {
11782          if (event.xexpose.window == windows->image.id)
11783            if (event.xexpose.count == 0)
11784              {
11785                event.xexpose.x=(int) highlight_info.x;
11786                event.xexpose.y=(int) highlight_info.y;
11787                event.xexpose.width=(int) highlight_info.width;
11788                event.xexpose.height=(int) highlight_info.height;
11789                XRefreshWindow(display,&windows->image,&event);
11790              }
11791          if (event.xexpose.window == windows->info.id)
11792            if (event.xexpose.count == 0)
11793              XInfoWidget(display,windows,text);
11794          break;
11795        }
11796        case KeyPress:
11797        {
11798          KeySym
11799            key_symbol;
11800
11801          if (event.xkey.window != windows->image.id)
11802            break;
11803          /*
11804            Respond to a user key press.
11805          */
11806          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11807            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11808          switch ((int) key_symbol)
11809          {
11810            case XK_Shift_L:
11811            case XK_Shift_R:
11812              break;
11813            case XK_Escape:
11814            case XK_F20:
11815              state|=EscapeState;
11816            case XK_Return:
11817            {
11818              state|=ExitState;
11819              break;
11820            }
11821            case XK_Home:
11822            case XK_KP_Home:
11823            {
11824              roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11825              roi_info.y=(ssize_t) (windows->image.height/2L-
11826                roi_info.height/2L);
11827              break;
11828            }
11829            case XK_Left:
11830            case XK_KP_Left:
11831            {
11832              roi_info.x--;
11833              break;
11834            }
11835            case XK_Up:
11836            case XK_KP_Up:
11837            case XK_Next:
11838            {
11839              roi_info.y--;
11840              break;
11841            }
11842            case XK_Right:
11843            case XK_KP_Right:
11844            {
11845              roi_info.x++;
11846              break;
11847            }
11848            case XK_Prior:
11849            case XK_Down:
11850            case XK_KP_Down:
11851            {
11852              roi_info.y++;
11853              break;
11854            }
11855            case XK_F1:
11856            case XK_Help:
11857            {
11858              (void) XSetFunction(display,windows->image.highlight_context,
11859                GXcopy);
11860              XTextViewWidget(display,resource_info,windows,MagickFalse,
11861                "Help Viewer - Region of Interest",ImageROIHelp);
11862              (void) XSetFunction(display,windows->image.highlight_context,
11863                GXinvert);
11864              break;
11865            }
11866            default:
11867            {
11868              command_type=XImageWindowCommand(display,resource_info,windows,
11869                event.xkey.state,key_symbol,image,exception);
11870              if (command_type != NullCommand)
11871                state|=UpdateRegionState;
11872              break;
11873            }
11874          }
11875          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11876            event.xkey.time);
11877          break;
11878        }
11879        case KeyRelease:
11880          break;
11881        case MotionNotify:
11882        {
11883          if (event.xbutton.window != windows->image.id)
11884            break;
11885          /*
11886            Map and unmap Info widget as text cursor crosses its boundaries.
11887          */
11888          x=event.xmotion.x;
11889          y=event.xmotion.y;
11890          if (windows->info.mapped != MagickFalse)
11891            {
11892              if ((x < (int) (windows->info.x+windows->info.width)) &&
11893                  (y < (int) (windows->info.y+windows->info.height)))
11894                (void) XWithdrawWindow(display,windows->info.id,
11895                  windows->info.screen);
11896            }
11897          else
11898            if ((x > (int) (windows->info.x+windows->info.width)) ||
11899                (y > (int) (windows->info.y+windows->info.height)))
11900              (void) XMapWindow(display,windows->info.id);
11901          roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11902          roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11903          break;
11904        }
11905        case SelectionRequest:
11906        {
11907          XSelectionEvent
11908            notify;
11909
11910          XSelectionRequestEvent
11911            *request;
11912
11913          /*
11914            Set primary selection.
11915          */
11916          (void) FormatLocaleString(text,MaxTextExtent,
11917            "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11918            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11919          request=(&(event.xselectionrequest));
11920          (void) XChangeProperty(request->display,request->requestor,
11921            request->property,request->target,8,PropModeReplace,
11922            (unsigned char *) text,(int) strlen(text));
11923          notify.type=SelectionNotify;
11924          notify.display=request->display;
11925          notify.requestor=request->requestor;
11926          notify.selection=request->selection;
11927          notify.target=request->target;
11928          notify.time=request->time;
11929          if (request->property == None)
11930            notify.property=request->target;
11931          else
11932            notify.property=request->property;
11933          (void) XSendEvent(request->display,request->requestor,False,0,
11934            (XEvent *) &notify);
11935        }
11936        default:
11937          break;
11938      }
11939      if ((state & UpdateConfigurationState) != 0)
11940        {
11941          (void) XPutBackEvent(display,&event);
11942          (void) XCheckDefineCursor(display,windows->image.id,cursor);
11943          break;
11944        }
11945    } while ((state & ExitState) == 0);
11946  } while ((state & ExitState) == 0);
11947  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11948  XSetCursorState(display,windows,MagickFalse);
11949  if ((state & EscapeState) != 0)
11950    return(MagickTrue);
11951  return(MagickTrue);
11952}
11953
11954/*
11955%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11956%                                                                             %
11957%                                                                             %
11958%                                                                             %
11959+   X R o t a t e I m a g e                                                   %
11960%                                                                             %
11961%                                                                             %
11962%                                                                             %
11963%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11964%
11965%  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
11966%  rotation angle is computed from the slope of a line drawn by the user.
11967%
11968%  The format of the XRotateImage method is:
11969%
11970%      MagickBooleanType XRotateImage(Display *display,
11971%        XResourceInfo *resource_info,XWindows *windows,double degrees,
11972%        Image **image,ExceptionInfo *exception)
11973%
11974%  A description of each parameter follows:
11975%
11976%    o display: Specifies a connection to an X server; returned from
11977%      XOpenDisplay.
11978%
11979%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11980%
11981%    o windows: Specifies a pointer to a XWindows structure.
11982%
11983%    o degrees: Specifies the number of degrees to rotate the image.
11984%
11985%    o image: the image.
11986%
11987%    o exception: return any errors or warnings in this structure.
11988%
11989*/
11990static MagickBooleanType XRotateImage(Display *display,
11991  XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
11992  ExceptionInfo *exception)
11993{
11994  static const char
11995    *RotateMenu[] =
11996    {
11997      "Pixel Color",
11998      "Direction",
11999      "Help",
12000      "Dismiss",
12001      (char *) NULL
12002    };
12003
12004  static ModeType
12005    direction = HorizontalRotateCommand;
12006
12007  static const ModeType
12008    DirectionCommands[] =
12009    {
12010      HorizontalRotateCommand,
12011      VerticalRotateCommand
12012    },
12013    RotateCommands[] =
12014    {
12015      RotateColorCommand,
12016      RotateDirectionCommand,
12017      RotateHelpCommand,
12018      RotateDismissCommand
12019    };
12020
12021  static unsigned int
12022    pen_id = 0;
12023
12024  char
12025    command[MaxTextExtent],
12026    text[MaxTextExtent];
12027
12028  Image
12029    *rotate_image;
12030
12031  int
12032    id,
12033    x,
12034    y;
12035
12036  MagickRealType
12037    normalized_degrees;
12038
12039  register int
12040    i;
12041
12042  unsigned int
12043    height,
12044    rotations,
12045    width;
12046
12047  if (degrees == 0.0)
12048    {
12049      unsigned int
12050        distance;
12051
12052      size_t
12053        state;
12054
12055      XEvent
12056        event;
12057
12058      XSegment
12059        rotate_info;
12060
12061      /*
12062        Map Command widget.
12063      */
12064      (void) CloneString(&windows->command.name,"Rotate");
12065      windows->command.data=2;
12066      (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12067      (void) XMapRaised(display,windows->command.id);
12068      XClientMessage(display,windows->image.id,windows->im_protocols,
12069        windows->im_update_widget,CurrentTime);
12070      /*
12071        Wait for first button press.
12072      */
12073      (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12074      XQueryPosition(display,windows->image.id,&x,&y);
12075      rotate_info.x1=x;
12076      rotate_info.y1=y;
12077      rotate_info.x2=x;
12078      rotate_info.y2=y;
12079      state=DefaultState;
12080      do
12081      {
12082        XHighlightLine(display,windows->image.id,
12083          windows->image.highlight_context,&rotate_info);
12084        /*
12085          Wait for next event.
12086        */
12087        XScreenEvent(display,windows,&event);
12088        XHighlightLine(display,windows->image.id,
12089          windows->image.highlight_context,&rotate_info);
12090        if (event.xany.window == windows->command.id)
12091          {
12092            /*
12093              Select a command from the Command widget.
12094            */
12095            id=XCommandWidget(display,windows,RotateMenu,&event);
12096            if (id < 0)
12097              continue;
12098            (void) XSetFunction(display,windows->image.highlight_context,
12099              GXcopy);
12100            switch (RotateCommands[id])
12101            {
12102              case RotateColorCommand:
12103              {
12104                const char
12105                  *ColorMenu[MaxNumberPens];
12106
12107                int
12108                  pen_number;
12109
12110                XColor
12111                  color;
12112
12113                /*
12114                  Initialize menu selections.
12115                */
12116                for (i=0; i < (int) (MaxNumberPens-2); i++)
12117                  ColorMenu[i]=resource_info->pen_colors[i];
12118                ColorMenu[MaxNumberPens-2]="Browser...";
12119                ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12120                /*
12121                  Select a pen color from the pop-up menu.
12122                */
12123                pen_number=XMenuWidget(display,windows,RotateMenu[id],
12124                  (const char **) ColorMenu,command);
12125                if (pen_number < 0)
12126                  break;
12127                if (pen_number == (MaxNumberPens-2))
12128                  {
12129                    static char
12130                      color_name[MaxTextExtent] = "gray";
12131
12132                    /*
12133                      Select a pen color from a dialog.
12134                    */
12135                    resource_info->pen_colors[pen_number]=color_name;
12136                    XColorBrowserWidget(display,windows,"Select",color_name);
12137                    if (*color_name == '\0')
12138                      break;
12139                  }
12140                /*
12141                  Set pen color.
12142                */
12143                (void) XParseColor(display,windows->map_info->colormap,
12144                  resource_info->pen_colors[pen_number],&color);
12145                XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12146                  (unsigned int) MaxColors,&color);
12147                windows->pixel_info->pen_colors[pen_number]=color;
12148                pen_id=(unsigned int) pen_number;
12149                break;
12150              }
12151              case RotateDirectionCommand:
12152              {
12153                static const char
12154                  *Directions[] =
12155                  {
12156                    "horizontal",
12157                    "vertical",
12158                    (char *) NULL,
12159                  };
12160
12161                /*
12162                  Select a command from the pop-up menu.
12163                */
12164                id=XMenuWidget(display,windows,RotateMenu[id],
12165                  Directions,command);
12166                if (id >= 0)
12167                  direction=DirectionCommands[id];
12168                break;
12169              }
12170              case RotateHelpCommand:
12171              {
12172                XTextViewWidget(display,resource_info,windows,MagickFalse,
12173                  "Help Viewer - Image Rotation",ImageRotateHelp);
12174                break;
12175              }
12176              case RotateDismissCommand:
12177              {
12178                /*
12179                  Prematurely exit.
12180                */
12181                state|=EscapeState;
12182                state|=ExitState;
12183                break;
12184              }
12185              default:
12186                break;
12187            }
12188            (void) XSetFunction(display,windows->image.highlight_context,
12189              GXinvert);
12190            continue;
12191          }
12192        switch (event.type)
12193        {
12194          case ButtonPress:
12195          {
12196            if (event.xbutton.button != Button1)
12197              break;
12198            if (event.xbutton.window != windows->image.id)
12199              break;
12200            /*
12201              exit loop.
12202            */
12203            (void) XSetFunction(display,windows->image.highlight_context,
12204              GXcopy);
12205            rotate_info.x1=event.xbutton.x;
12206            rotate_info.y1=event.xbutton.y;
12207            state|=ExitState;
12208            break;
12209          }
12210          case ButtonRelease:
12211            break;
12212          case Expose:
12213            break;
12214          case KeyPress:
12215          {
12216            char
12217              command[MaxTextExtent];
12218
12219            KeySym
12220              key_symbol;
12221
12222            if (event.xkey.window != windows->image.id)
12223              break;
12224            /*
12225              Respond to a user key press.
12226            */
12227            (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12228              sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12229            switch ((int) key_symbol)
12230            {
12231              case XK_Escape:
12232              case XK_F20:
12233              {
12234                /*
12235                  Prematurely exit.
12236                */
12237                state|=EscapeState;
12238                state|=ExitState;
12239                break;
12240              }
12241              case XK_F1:
12242              case XK_Help:
12243              {
12244                (void) XSetFunction(display,windows->image.highlight_context,
12245                  GXcopy);
12246                XTextViewWidget(display,resource_info,windows,MagickFalse,
12247                  "Help Viewer - Image Rotation",ImageRotateHelp);
12248                (void) XSetFunction(display,windows->image.highlight_context,
12249                  GXinvert);
12250                break;
12251              }
12252              default:
12253              {
12254                (void) XBell(display,0);
12255                break;
12256              }
12257            }
12258            break;
12259          }
12260          case MotionNotify:
12261          {
12262            rotate_info.x1=event.xmotion.x;
12263            rotate_info.y1=event.xmotion.y;
12264          }
12265        }
12266        rotate_info.x2=rotate_info.x1;
12267        rotate_info.y2=rotate_info.y1;
12268        if (direction == HorizontalRotateCommand)
12269          rotate_info.x2+=32;
12270        else
12271          rotate_info.y2-=32;
12272      } while ((state & ExitState) == 0);
12273      (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12274      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12275      if ((state & EscapeState) != 0)
12276        return(MagickTrue);
12277      /*
12278        Draw line as pointer moves until the mouse button is released.
12279      */
12280      distance=0;
12281      (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12282      state=DefaultState;
12283      do
12284      {
12285        if (distance > 9)
12286          {
12287            /*
12288              Display info and draw rotation line.
12289            */
12290            if (windows->info.mapped == MagickFalse)
12291              (void) XMapWindow(display,windows->info.id);
12292            (void) FormatLocaleString(text,MaxTextExtent," %g",
12293              direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12294            XInfoWidget(display,windows,text);
12295            XHighlightLine(display,windows->image.id,
12296              windows->image.highlight_context,&rotate_info);
12297          }
12298        else
12299          if (windows->info.mapped != MagickFalse)
12300            (void) XWithdrawWindow(display,windows->info.id,
12301              windows->info.screen);
12302        /*
12303          Wait for next event.
12304        */
12305        XScreenEvent(display,windows,&event);
12306        if (distance > 9)
12307          XHighlightLine(display,windows->image.id,
12308            windows->image.highlight_context,&rotate_info);
12309        switch (event.type)
12310        {
12311          case ButtonPress:
12312            break;
12313          case ButtonRelease:
12314          {
12315            /*
12316              User has committed to rotation line.
12317            */
12318            rotate_info.x2=event.xbutton.x;
12319            rotate_info.y2=event.xbutton.y;
12320            state|=ExitState;
12321            break;
12322          }
12323          case Expose:
12324            break;
12325          case MotionNotify:
12326          {
12327            rotate_info.x2=event.xmotion.x;
12328            rotate_info.y2=event.xmotion.y;
12329          }
12330          default:
12331            break;
12332        }
12333        /*
12334          Check boundary conditions.
12335        */
12336        if (rotate_info.x2 < 0)
12337          rotate_info.x2=0;
12338        else
12339          if (rotate_info.x2 > (int) windows->image.width)
12340            rotate_info.x2=(short) windows->image.width;
12341        if (rotate_info.y2 < 0)
12342          rotate_info.y2=0;
12343        else
12344          if (rotate_info.y2 > (int) windows->image.height)
12345            rotate_info.y2=(short) windows->image.height;
12346        /*
12347          Compute rotation angle from the slope of the line.
12348        */
12349        degrees=0.0;
12350        distance=(unsigned int)
12351          ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12352          ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12353        if (distance > 9)
12354          degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12355            rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12356      } while ((state & ExitState) == 0);
12357      (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12358      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12359      if (distance <= 9)
12360        return(MagickTrue);
12361    }
12362  if (direction == VerticalRotateCommand)
12363    degrees-=90.0;
12364  if (degrees == 0.0)
12365    return(MagickTrue);
12366  /*
12367    Rotate image.
12368  */
12369  normalized_degrees=degrees;
12370  while (normalized_degrees < -45.0)
12371    normalized_degrees+=360.0;
12372  for (rotations=0; normalized_degrees > 45.0; rotations++)
12373    normalized_degrees-=90.0;
12374  if (normalized_degrees != 0.0)
12375    (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12376      exception);
12377  XSetCursorState(display,windows,MagickTrue);
12378  XCheckRefreshWindows(display,windows);
12379  (*image)->background_color.red=ScaleShortToQuantum(
12380    windows->pixel_info->pen_colors[pen_id].red);
12381  (*image)->background_color.green=ScaleShortToQuantum(
12382    windows->pixel_info->pen_colors[pen_id].green);
12383  (*image)->background_color.blue=ScaleShortToQuantum(
12384    windows->pixel_info->pen_colors[pen_id].blue);
12385  rotate_image=RotateImage(*image,degrees,exception);
12386  XSetCursorState(display,windows,MagickFalse);
12387  if (rotate_image == (Image *) NULL)
12388    return(MagickFalse);
12389  *image=DestroyImage(*image);
12390  *image=rotate_image;
12391  if (windows->image.crop_geometry != (char *) NULL)
12392    {
12393      /*
12394        Rotate crop geometry.
12395      */
12396      width=(unsigned int) (*image)->columns;
12397      height=(unsigned int) (*image)->rows;
12398      (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12399      switch (rotations % 4)
12400      {
12401        default:
12402        case 0:
12403          break;
12404        case 1:
12405        {
12406          /*
12407            Rotate 90 degrees.
12408          */
12409          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12410            "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12411            (int) height-y,x);
12412          break;
12413        }
12414        case 2:
12415        {
12416          /*
12417            Rotate 180 degrees.
12418          */
12419          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12420            "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12421          break;
12422        }
12423        case 3:
12424        {
12425          /*
12426            Rotate 270 degrees.
12427          */
12428          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12429            "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12430          break;
12431        }
12432      }
12433    }
12434  if (windows->image.orphan != MagickFalse)
12435    return(MagickTrue);
12436  if (normalized_degrees != 0.0)
12437    {
12438      /*
12439        Update image colormap.
12440      */
12441      windows->image.window_changes.width=(int) (*image)->columns;
12442      windows->image.window_changes.height=(int) (*image)->rows;
12443      if (windows->image.crop_geometry != (char *) NULL)
12444        {
12445          /*
12446            Obtain dimensions of image from crop geometry.
12447          */
12448          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12449            &width,&height);
12450          windows->image.window_changes.width=(int) width;
12451          windows->image.window_changes.height=(int) height;
12452        }
12453      XConfigureImageColormap(display,resource_info,windows,*image);
12454    }
12455  else
12456    if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12457      {
12458        windows->image.window_changes.width=windows->image.ximage->height;
12459        windows->image.window_changes.height=windows->image.ximage->width;
12460      }
12461  /*
12462    Update image configuration.
12463  */
12464  (void) XConfigureImage(display,resource_info,windows,*image,exception);
12465  return(MagickTrue);
12466}
12467
12468/*
12469%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12470%                                                                             %
12471%                                                                             %
12472%                                                                             %
12473+   X S a v e I m a g e                                                       %
12474%                                                                             %
12475%                                                                             %
12476%                                                                             %
12477%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12478%
12479%  XSaveImage() saves an image to a file.
12480%
12481%  The format of the XSaveImage method is:
12482%
12483%      MagickBooleanType XSaveImage(Display *display,
12484%        XResourceInfo *resource_info,XWindows *windows,Image *image,
12485%        ExceptionInfo *exception)
12486%
12487%  A description of each parameter follows:
12488%
12489%    o display: Specifies a connection to an X server; returned from
12490%      XOpenDisplay.
12491%
12492%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12493%
12494%    o windows: Specifies a pointer to a XWindows structure.
12495%
12496%    o image: the image.
12497%
12498%    o exception: return any errors or warnings in this structure.
12499%
12500*/
12501static MagickBooleanType XSaveImage(Display *display,
12502  XResourceInfo *resource_info,XWindows *windows,Image *image,
12503  ExceptionInfo *exception)
12504{
12505  char
12506    filename[MaxTextExtent],
12507    geometry[MaxTextExtent];
12508
12509  Image
12510    *save_image;
12511
12512  ImageInfo
12513    *image_info;
12514
12515  MagickStatusType
12516    status;
12517
12518  /*
12519    Request file name from user.
12520  */
12521  if (resource_info->write_filename != (char *) NULL)
12522    (void) CopyMagickString(filename,resource_info->write_filename,
12523      MaxTextExtent);
12524  else
12525    {
12526      char
12527        path[MaxTextExtent];
12528
12529      int
12530        status;
12531
12532      GetPathComponent(image->filename,HeadPath,path);
12533      GetPathComponent(image->filename,TailPath,filename);
12534      if (*path != '\0')
12535        {
12536          status=chdir(path);
12537          if (status == -1)
12538            (void) ThrowMagickException(exception,GetMagickModule(),
12539              FileOpenError,"UnableToOpenFile","%s",path);
12540        }
12541    }
12542  XFileBrowserWidget(display,windows,"Save",filename);
12543  if (*filename == '\0')
12544    return(MagickTrue);
12545  if (IsPathAccessible(filename) != MagickFalse)
12546    {
12547      int
12548        status;
12549
12550      /*
12551        File exists-- seek user's permission before overwriting.
12552      */
12553      status=XConfirmWidget(display,windows,"Overwrite",filename);
12554      if (status <= 0)
12555        return(MagickTrue);
12556    }
12557  image_info=CloneImageInfo(resource_info->image_info);
12558  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12559  (void) SetImageInfo(image_info,1,exception);
12560  if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12561      (LocaleCompare(image_info->magick,"JPG") == 0))
12562    {
12563      char
12564        quality[MaxTextExtent];
12565
12566      int
12567        status;
12568
12569      /*
12570        Request JPEG quality from user.
12571      */
12572      (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
12573        image->quality);
12574      status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12575        quality);
12576      if (*quality == '\0')
12577        return(MagickTrue);
12578      image->quality=StringToUnsignedLong(quality);
12579      image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12580    }
12581  if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12582      (LocaleCompare(image_info->magick,"PDF") == 0) ||
12583      (LocaleCompare(image_info->magick,"PS") == 0) ||
12584      (LocaleCompare(image_info->magick,"PS2") == 0))
12585    {
12586      char
12587        geometry[MaxTextExtent];
12588
12589      /*
12590        Request page geometry from user.
12591      */
12592      (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12593      if (LocaleCompare(image_info->magick,"PDF") == 0)
12594        (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12595      if (image_info->page != (char *) NULL)
12596        (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12597      XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12598        "Select page geometry:",geometry);
12599      if (*geometry != '\0')
12600        image_info->page=GetPageGeometry(geometry);
12601    }
12602  /*
12603    Apply image transforms.
12604  */
12605  XSetCursorState(display,windows,MagickTrue);
12606  XCheckRefreshWindows(display,windows);
12607  save_image=CloneImage(image,0,0,MagickTrue,exception);
12608  if (save_image == (Image *) NULL)
12609    return(MagickFalse);
12610  (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
12611    windows->image.ximage->width,windows->image.ximage->height);
12612  (void) TransformImage(&save_image,windows->image.crop_geometry,geometry);
12613  /*
12614    Write image.
12615  */
12616  (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12617  status=WriteImage(image_info,save_image,exception);
12618  if (status != MagickFalse)
12619    image->taint=MagickFalse;
12620  save_image=DestroyImage(save_image);
12621  image_info=DestroyImageInfo(image_info);
12622  XSetCursorState(display,windows,MagickFalse);
12623  return(status != 0 ? MagickTrue : MagickFalse);
12624}
12625
12626/*
12627%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12628%                                                                             %
12629%                                                                             %
12630%                                                                             %
12631+   X S c r e e n E v e n t                                                   %
12632%                                                                             %
12633%                                                                             %
12634%                                                                             %
12635%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12636%
12637%  XScreenEvent() handles global events associated with the Pan and Magnify
12638%  windows.
12639%
12640%  The format of the XScreenEvent function is:
12641%
12642%      void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12643%
12644%  A description of each parameter follows:
12645%
12646%    o display: Specifies a pointer to the Display structure;  returned from
12647%      XOpenDisplay.
12648%
12649%    o windows: Specifies a pointer to a XWindows structure.
12650%
12651%    o event: Specifies a pointer to a X11 XEvent structure.
12652%
12653%
12654*/
12655
12656#if defined(__cplusplus) || defined(c_plusplus)
12657extern "C" {
12658#endif
12659
12660static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12661{
12662  register XWindows
12663    *windows;
12664
12665  windows=(XWindows *) data;
12666  if ((event->type == ClientMessage) &&
12667      (event->xclient.window == windows->image.id))
12668    return(MagickFalse);
12669  return(MagickTrue);
12670}
12671
12672#if defined(__cplusplus) || defined(c_plusplus)
12673}
12674#endif
12675
12676static void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12677{
12678  register int
12679    x,
12680    y;
12681
12682  (void) XIfEvent(display,event,XPredicate,(char *) windows);
12683  if (event->xany.window == windows->command.id)
12684    return;
12685  switch (event->type)
12686  {
12687    case ButtonPress:
12688    case ButtonRelease:
12689    {
12690      if ((event->xbutton.button == Button3) &&
12691          (event->xbutton.state & Mod1Mask))
12692        {
12693          /*
12694            Convert Alt-Button3 to Button2.
12695          */
12696          event->xbutton.button=Button2;
12697          event->xbutton.state&=(~Mod1Mask);
12698        }
12699      if (event->xbutton.window == windows->backdrop.id)
12700        {
12701          (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12702            event->xbutton.time);
12703          break;
12704        }
12705      if (event->xbutton.window == windows->pan.id)
12706        {
12707          XPanImage(display,windows,event);
12708          break;
12709        }
12710      if (event->xbutton.window == windows->image.id)
12711        if (event->xbutton.button == Button2)
12712          {
12713            /*
12714              Update magnified image.
12715            */
12716            x=event->xbutton.x;
12717            y=event->xbutton.y;
12718            if (x < 0)
12719              x=0;
12720            else
12721              if (x >= (int) windows->image.width)
12722                x=(int) (windows->image.width-1);
12723            windows->magnify.x=(int) windows->image.x+x;
12724            if (y < 0)
12725              y=0;
12726            else
12727             if (y >= (int) windows->image.height)
12728               y=(int) (windows->image.height-1);
12729            windows->magnify.y=windows->image.y+y;
12730            if (windows->magnify.mapped == MagickFalse)
12731              (void) XMapRaised(display,windows->magnify.id);
12732            XMakeMagnifyImage(display,windows);
12733            if (event->type == ButtonRelease)
12734              (void) XWithdrawWindow(display,windows->info.id,
12735                windows->info.screen);
12736            break;
12737          }
12738      break;
12739    }
12740    case ClientMessage:
12741    {
12742      /*
12743        If client window delete message, exit.
12744      */
12745      if (event->xclient.message_type != windows->wm_protocols)
12746        break;
12747      if (*event->xclient.data.l != (long) windows->wm_delete_window)
12748        break;
12749      if (event->xclient.window == windows->magnify.id)
12750        {
12751          (void) XWithdrawWindow(display,windows->magnify.id,
12752            windows->magnify.screen);
12753          break;
12754        }
12755      break;
12756    }
12757    case ConfigureNotify:
12758    {
12759      if (event->xconfigure.window == windows->magnify.id)
12760        {
12761          unsigned int
12762            magnify;
12763
12764          /*
12765            Magnify window has a new configuration.
12766          */
12767          windows->magnify.width=(unsigned int) event->xconfigure.width;
12768          windows->magnify.height=(unsigned int) event->xconfigure.height;
12769          if (windows->magnify.mapped == MagickFalse)
12770            break;
12771          magnify=1;
12772          while ((int) magnify <= event->xconfigure.width)
12773            magnify<<=1;
12774          while ((int) magnify <= event->xconfigure.height)
12775            magnify<<=1;
12776          magnify>>=1;
12777          if (((int) magnify != event->xconfigure.width) ||
12778              ((int) magnify != event->xconfigure.height))
12779            {
12780              XWindowChanges
12781                window_changes;
12782
12783              window_changes.width=(int) magnify;
12784              window_changes.height=(int) magnify;
12785              (void) XReconfigureWMWindow(display,windows->magnify.id,
12786                windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12787                &window_changes);
12788              break;
12789            }
12790          XMakeMagnifyImage(display,windows);
12791          break;
12792        }
12793      break;
12794    }
12795    case Expose:
12796    {
12797      if (event->xexpose.window == windows->image.id)
12798        {
12799          XRefreshWindow(display,&windows->image,event);
12800          break;
12801        }
12802      if (event->xexpose.window == windows->pan.id)
12803        if (event->xexpose.count == 0)
12804          {
12805            XDrawPanRectangle(display,windows);
12806            break;
12807          }
12808      if (event->xexpose.window == windows->magnify.id)
12809        if (event->xexpose.count == 0)
12810          {
12811            XMakeMagnifyImage(display,windows);
12812            break;
12813          }
12814      break;
12815    }
12816    case KeyPress:
12817    {
12818      char
12819        command[MaxTextExtent];
12820
12821      KeySym
12822        key_symbol;
12823
12824      if (event->xkey.window != windows->magnify.id)
12825        break;
12826      /*
12827        Respond to a user key press.
12828      */
12829      (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12830        sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12831      XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol);
12832      break;
12833    }
12834    case MapNotify:
12835    {
12836      if (event->xmap.window == windows->magnify.id)
12837        {
12838          windows->magnify.mapped=MagickTrue;
12839          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12840          break;
12841        }
12842      if (event->xmap.window == windows->info.id)
12843        {
12844          windows->info.mapped=MagickTrue;
12845          break;
12846        }
12847      break;
12848    }
12849    case MotionNotify:
12850    {
12851      while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12852      if (event->xmotion.window == windows->image.id)
12853        if (windows->magnify.mapped != MagickFalse)
12854          {
12855            /*
12856              Update magnified image.
12857            */
12858            x=event->xmotion.x;
12859            y=event->xmotion.y;
12860            if (x < 0)
12861              x=0;
12862            else
12863              if (x >= (int) windows->image.width)
12864                x=(int) (windows->image.width-1);
12865            windows->magnify.x=(int) windows->image.x+x;
12866            if (y < 0)
12867              y=0;
12868            else
12869             if (y >= (int) windows->image.height)
12870               y=(int) (windows->image.height-1);
12871            windows->magnify.y=windows->image.y+y;
12872            XMakeMagnifyImage(display,windows);
12873          }
12874      break;
12875    }
12876    case UnmapNotify:
12877    {
12878      if (event->xunmap.window == windows->magnify.id)
12879        {
12880          windows->magnify.mapped=MagickFalse;
12881          break;
12882        }
12883      if (event->xunmap.window == windows->info.id)
12884        {
12885          windows->info.mapped=MagickFalse;
12886          break;
12887        }
12888      break;
12889    }
12890    default:
12891      break;
12892  }
12893}
12894
12895/*
12896%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12897%                                                                             %
12898%                                                                             %
12899%                                                                             %
12900+   X S e t C r o p G e o m e t r y                                           %
12901%                                                                             %
12902%                                                                             %
12903%                                                                             %
12904%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12905%
12906%  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12907%  and translates it to a cropping geometry relative to the image.
12908%
12909%  The format of the XSetCropGeometry method is:
12910%
12911%      void XSetCropGeometry(Display *display,XWindows *windows,
12912%        RectangleInfo *crop_info,Image *image)
12913%
12914%  A description of each parameter follows:
12915%
12916%    o display: Specifies a connection to an X server; returned from
12917%      XOpenDisplay.
12918%
12919%    o windows: Specifies a pointer to a XWindows structure.
12920%
12921%    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12922%      Image window to crop.
12923%
12924%    o image: the image.
12925%
12926*/
12927static void XSetCropGeometry(Display *display,XWindows *windows,
12928  RectangleInfo *crop_info,Image *image)
12929{
12930  char
12931    text[MaxTextExtent];
12932
12933  int
12934    x,
12935    y;
12936
12937  MagickRealType
12938    scale_factor;
12939
12940  unsigned int
12941    height,
12942    width;
12943
12944  if (windows->info.mapped != MagickFalse)
12945    {
12946      /*
12947        Display info on cropping rectangle.
12948      */
12949      (void) FormatLocaleString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
12950        (double) crop_info->width,(double) crop_info->height,(double)
12951        crop_info->x,(double) crop_info->y);
12952      XInfoWidget(display,windows,text);
12953    }
12954  /*
12955    Cropping geometry is relative to any previous crop geometry.
12956  */
12957  x=0;
12958  y=0;
12959  width=(unsigned int) image->columns;
12960  height=(unsigned int) image->rows;
12961  if (windows->image.crop_geometry != (char *) NULL)
12962    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12963  else
12964    windows->image.crop_geometry=AcquireString((char *) NULL);
12965  /*
12966    Define the crop geometry string from the cropping rectangle.
12967  */
12968  scale_factor=(MagickRealType) width/windows->image.ximage->width;
12969  if (crop_info->x > 0)
12970    x+=(int) (scale_factor*crop_info->x+0.5);
12971  width=(unsigned int) (scale_factor*crop_info->width+0.5);
12972  if (width == 0)
12973    width=1;
12974  scale_factor=(MagickRealType) height/windows->image.ximage->height;
12975  if (crop_info->y > 0)
12976    y+=(int) (scale_factor*crop_info->y+0.5);
12977  height=(unsigned int) (scale_factor*crop_info->height+0.5);
12978  if (height == 0)
12979    height=1;
12980  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12981    "%ux%u%+d%+d",width,height,x,y);
12982}
12983
12984/*
12985%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12986%                                                                             %
12987%                                                                             %
12988%                                                                             %
12989+   X T i l e I m a g e                                                       %
12990%                                                                             %
12991%                                                                             %
12992%                                                                             %
12993%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12994%
12995%  XTileImage() loads or deletes a selected tile from a visual image directory.
12996%  The load or delete command is chosen from a menu.
12997%
12998%  The format of the XTileImage method is:
12999%
13000%      Image *XTileImage(Display *display,XResourceInfo *resource_info,
13001%        XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13002%
13003%  A description of each parameter follows:
13004%
13005%    o tile_image:  XTileImage reads or deletes the tile image
13006%      and returns it.  A null image is returned if an error occurs.
13007%
13008%    o display: Specifies a connection to an X server;  returned from
13009%      XOpenDisplay.
13010%
13011%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13012%
13013%    o windows: Specifies a pointer to a XWindows structure.
13014%
13015%    o image: the image; returned from ReadImage.
13016%
13017%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
13018%      the entire image is refreshed.
13019%
13020%    o exception: return any errors or warnings in this structure.
13021%
13022*/
13023static Image *XTileImage(Display *display,XResourceInfo *resource_info,
13024  XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13025{
13026  static const char
13027    *VerbMenu[] =
13028    {
13029      "Load",
13030      "Next",
13031      "Former",
13032      "Delete",
13033      "Update",
13034      (char *) NULL,
13035    };
13036
13037  static const ModeType
13038    TileCommands[] =
13039    {
13040      TileLoadCommand,
13041      TileNextCommand,
13042      TileFormerCommand,
13043      TileDeleteCommand,
13044      TileUpdateCommand
13045    };
13046
13047  char
13048    command[MaxTextExtent],
13049    filename[MaxTextExtent];
13050
13051  Image
13052    *tile_image;
13053
13054  int
13055    id,
13056    status,
13057    tile,
13058    x,
13059    y;
13060
13061  MagickRealType
13062    scale_factor;
13063
13064  register char
13065    *p,
13066    *q;
13067
13068  register int
13069    i;
13070
13071  unsigned int
13072    height,
13073    width;
13074
13075  /*
13076    Tile image is relative to montage image configuration.
13077  */
13078  x=0;
13079  y=0;
13080  width=(unsigned int) image->columns;
13081  height=(unsigned int) image->rows;
13082  if (windows->image.crop_geometry != (char *) NULL)
13083    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13084  scale_factor=(MagickRealType) width/windows->image.ximage->width;
13085  event->xbutton.x+=windows->image.x;
13086  event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13087  scale_factor=(MagickRealType) height/windows->image.ximage->height;
13088  event->xbutton.y+=windows->image.y;
13089  event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13090  /*
13091    Determine size and location of each tile in the visual image directory.
13092  */
13093  width=(unsigned int) image->columns;
13094  height=(unsigned int) image->rows;
13095  x=0;
13096  y=0;
13097  (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13098  tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13099    (event->xbutton.x-x)/width;
13100  if (tile < 0)
13101    {
13102      /*
13103        Button press is outside any tile.
13104      */
13105      (void) XBell(display,0);
13106      return((Image *) NULL);
13107    }
13108  /*
13109    Determine file name from the tile directory.
13110  */
13111  p=image->directory;
13112  for (i=tile; (i != 0) && (*p != '\0'); )
13113  {
13114    if (*p == '\n')
13115      i--;
13116    p++;
13117  }
13118  if (*p == '\0')
13119    {
13120      /*
13121        Button press is outside any tile.
13122      */
13123      (void) XBell(display,0);
13124      return((Image *) NULL);
13125    }
13126  /*
13127    Select a command from the pop-up menu.
13128  */
13129  id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13130  if (id < 0)
13131    return((Image *) NULL);
13132  q=p;
13133  while ((*q != '\n') && (*q != '\0'))
13134    q++;
13135  (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13136  /*
13137    Perform command for the selected tile.
13138  */
13139  XSetCursorState(display,windows,MagickTrue);
13140  XCheckRefreshWindows(display,windows);
13141  tile_image=NewImageList();
13142  switch (TileCommands[id])
13143  {
13144    case TileLoadCommand:
13145    {
13146      /*
13147        Load tile image.
13148      */
13149      XCheckRefreshWindows(display,windows);
13150      (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13151        MaxTextExtent);
13152      (void) CopyMagickString(resource_info->image_info->filename,filename,
13153        MaxTextExtent);
13154      tile_image=ReadImage(resource_info->image_info,exception);
13155      CatchException(exception);
13156      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13157      break;
13158    }
13159    case TileNextCommand:
13160    {
13161      /*
13162        Display next image.
13163      */
13164      XClientMessage(display,windows->image.id,windows->im_protocols,
13165        windows->im_next_image,CurrentTime);
13166      break;
13167    }
13168    case TileFormerCommand:
13169    {
13170      /*
13171        Display former image.
13172      */
13173      XClientMessage(display,windows->image.id,windows->im_protocols,
13174        windows->im_former_image,CurrentTime);
13175      break;
13176    }
13177    case TileDeleteCommand:
13178    {
13179      /*
13180        Delete tile image.
13181      */
13182      if (IsPathAccessible(filename) == MagickFalse)
13183        {
13184          XNoticeWidget(display,windows,"Image file does not exist:",filename);
13185          break;
13186        }
13187      status=XConfirmWidget(display,windows,"Really delete tile",filename);
13188      if (status <= 0)
13189        break;
13190      status=remove(filename) != 0 ? MagickTrue : MagickFalse;
13191      if (status != MagickFalse)
13192        {
13193          XNoticeWidget(display,windows,"Unable to delete image file:",
13194            filename);
13195          break;
13196        }
13197    }
13198    case TileUpdateCommand:
13199    {
13200      int
13201        x_offset,
13202        y_offset;
13203
13204      PixelPacket
13205        pixel;
13206
13207      register int
13208        j;
13209
13210      register Quantum
13211        *s;
13212
13213      /*
13214        Ensure all the images exist.
13215      */
13216      tile=0;
13217      for (p=image->directory; *p != '\0'; p++)
13218      {
13219        CacheView
13220          *image_view;
13221
13222        q=p;
13223        while ((*q != '\n') && (*q != '\0'))
13224          q++;
13225        (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13226        p=q;
13227        if (IsPathAccessible(filename) != MagickFalse)
13228          {
13229            tile++;
13230            continue;
13231          }
13232        /*
13233          Overwrite tile with background color.
13234        */
13235        x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13236        y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13237        image_view=AcquireCacheView(image);
13238        (void) GetOneCacheViewVirtualPixel(image_view,0,0,&pixel,exception);
13239        for (i=0; i < (int) height; i++)
13240        {
13241          s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13242            y_offset+i,width,1,exception);
13243          if (s == (Quantum *) NULL)
13244            break;
13245          for (j=0; j < (int) width; j++)
13246          {
13247            SetPixelPacket(image,&pixel,s);
13248            s+=GetPixelChannels(image);
13249          }
13250          if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13251            break;
13252        }
13253        image_view=DestroyCacheView(image_view);
13254        tile++;
13255      }
13256      windows->image.window_changes.width=(int) image->columns;
13257      windows->image.window_changes.height=(int) image->rows;
13258      XConfigureImageColormap(display,resource_info,windows,image);
13259      (void) XConfigureImage(display,resource_info,windows,image,exception);
13260      break;
13261    }
13262    default:
13263      break;
13264  }
13265  XSetCursorState(display,windows,MagickFalse);
13266  return(tile_image);
13267}
13268
13269/*
13270%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13271%                                                                             %
13272%                                                                             %
13273%                                                                             %
13274+   X T r a n s l a t e I m a g e                                             %
13275%                                                                             %
13276%                                                                             %
13277%                                                                             %
13278%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13279%
13280%  XTranslateImage() translates the image within an Image window by one pixel
13281%  as specified by the key symbol.  If the image has a `montage string the
13282%  translation is respect to the width and height contained within the string.
13283%
13284%  The format of the XTranslateImage method is:
13285%
13286%      void XTranslateImage(Display *display,XWindows *windows,
13287%        Image *image,const KeySym key_symbol)
13288%
13289%  A description of each parameter follows:
13290%
13291%    o display: Specifies a connection to an X server; returned from
13292%      XOpenDisplay.
13293%
13294%    o windows: Specifies a pointer to a XWindows structure.
13295%
13296%    o image: the image.
13297%
13298%    o key_symbol: Specifies a KeySym which indicates which side of the image
13299%      to trim.
13300%
13301*/
13302static void XTranslateImage(Display *display,XWindows *windows,
13303  Image *image,const KeySym key_symbol)
13304{
13305  char
13306    text[MaxTextExtent];
13307
13308  int
13309    x,
13310    y;
13311
13312  unsigned int
13313    x_offset,
13314    y_offset;
13315
13316  /*
13317    User specified a pan position offset.
13318  */
13319  x_offset=windows->image.width;
13320  y_offset=windows->image.height;
13321  if (image->montage != (char *) NULL)
13322    (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13323  switch ((int) key_symbol)
13324  {
13325    case XK_Home:
13326    case XK_KP_Home:
13327    {
13328      windows->image.x=(int) windows->image.width/2;
13329      windows->image.y=(int) windows->image.height/2;
13330      break;
13331    }
13332    case XK_Left:
13333    case XK_KP_Left:
13334    {
13335      windows->image.x-=x_offset;
13336      break;
13337    }
13338    case XK_Next:
13339    case XK_Up:
13340    case XK_KP_Up:
13341    {
13342      windows->image.y-=y_offset;
13343      break;
13344    }
13345    case XK_Right:
13346    case XK_KP_Right:
13347    {
13348      windows->image.x+=x_offset;
13349      break;
13350    }
13351    case XK_Prior:
13352    case XK_Down:
13353    case XK_KP_Down:
13354    {
13355      windows->image.y+=y_offset;
13356      break;
13357    }
13358    default:
13359      return;
13360  }
13361  /*
13362    Check boundary conditions.
13363  */
13364  if (windows->image.x < 0)
13365    windows->image.x=0;
13366  else
13367    if ((int) (windows->image.x+windows->image.width) >
13368        windows->image.ximage->width)
13369      windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13370  if (windows->image.y < 0)
13371    windows->image.y=0;
13372  else
13373    if ((int) (windows->image.y+windows->image.height) >
13374        windows->image.ximage->height)
13375      windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13376  /*
13377    Refresh Image window.
13378  */
13379  (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
13380    windows->image.width,windows->image.height,windows->image.x,
13381    windows->image.y);
13382  XInfoWidget(display,windows,text);
13383  XCheckRefreshWindows(display,windows);
13384  XDrawPanRectangle(display,windows);
13385  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13386  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13387}
13388
13389/*
13390%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13391%                                                                             %
13392%                                                                             %
13393%                                                                             %
13394+   X T r i m I m a g e                                                       %
13395%                                                                             %
13396%                                                                             %
13397%                                                                             %
13398%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13399%
13400%  XTrimImage() trims the edges from the Image window.
13401%
13402%  The format of the XTrimImage method is:
13403%
13404%      MagickBooleanType XTrimImage(Display *display,
13405%        XResourceInfo *resource_info,XWindows *windows,Image *image,
13406%        ExceptionInfo *exception)
13407%
13408%  A description of each parameter follows:
13409%
13410%    o display: Specifies a connection to an X server; returned from
13411%      XOpenDisplay.
13412%
13413%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13414%
13415%    o windows: Specifies a pointer to a XWindows structure.
13416%
13417%    o image: the image.
13418%
13419%    o exception: return any errors or warnings in this structure.
13420%
13421*/
13422static MagickBooleanType XTrimImage(Display *display,
13423  XResourceInfo *resource_info,XWindows *windows,Image *image,
13424  ExceptionInfo *exception)
13425{
13426  RectangleInfo
13427    trim_info;
13428
13429  register int
13430    x,
13431    y;
13432
13433  size_t
13434    background,
13435    pixel;
13436
13437  /*
13438    Trim edges from image.
13439  */
13440  XSetCursorState(display,windows,MagickTrue);
13441  XCheckRefreshWindows(display,windows);
13442  /*
13443    Crop the left edge.
13444  */
13445  background=XGetPixel(windows->image.ximage,0,0);
13446  trim_info.width=(size_t) windows->image.ximage->width;
13447  for (x=0; x < windows->image.ximage->width; x++)
13448  {
13449    for (y=0; y < windows->image.ximage->height; y++)
13450    {
13451      pixel=XGetPixel(windows->image.ximage,x,y);
13452      if (pixel != background)
13453        break;
13454    }
13455    if (y < windows->image.ximage->height)
13456      break;
13457  }
13458  trim_info.x=(ssize_t) x;
13459  if (trim_info.x == (ssize_t) windows->image.ximage->width)
13460    {
13461      XSetCursorState(display,windows,MagickFalse);
13462      return(MagickFalse);
13463    }
13464  /*
13465    Crop the right edge.
13466  */
13467  background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13468  for (x=windows->image.ximage->width-1; x != 0; x--)
13469  {
13470    for (y=0; y < windows->image.ximage->height; y++)
13471    {
13472      pixel=XGetPixel(windows->image.ximage,x,y);
13473      if (pixel != background)
13474        break;
13475    }
13476    if (y < windows->image.ximage->height)
13477      break;
13478  }
13479  trim_info.width=(size_t) (x-trim_info.x+1);
13480  /*
13481    Crop the top edge.
13482  */
13483  background=XGetPixel(windows->image.ximage,0,0);
13484  trim_info.height=(size_t) windows->image.ximage->height;
13485  for (y=0; y < windows->image.ximage->height; y++)
13486  {
13487    for (x=0; x < windows->image.ximage->width; x++)
13488    {
13489      pixel=XGetPixel(windows->image.ximage,x,y);
13490      if (pixel != background)
13491        break;
13492    }
13493    if (x < windows->image.ximage->width)
13494      break;
13495  }
13496  trim_info.y=(ssize_t) y;
13497  /*
13498    Crop the bottom edge.
13499  */
13500  background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13501  for (y=windows->image.ximage->height-1; y != 0; y--)
13502  {
13503    for (x=0; x < windows->image.ximage->width; x++)
13504    {
13505      pixel=XGetPixel(windows->image.ximage,x,y);
13506      if (pixel != background)
13507        break;
13508    }
13509    if (x < windows->image.ximage->width)
13510      break;
13511  }
13512  trim_info.height=(size_t) y-trim_info.y+1;
13513  if (((unsigned int) trim_info.width != windows->image.width) ||
13514      ((unsigned int) trim_info.height != windows->image.height))
13515    {
13516      /*
13517        Reconfigure Image window as defined by the trimming rectangle.
13518      */
13519      XSetCropGeometry(display,windows,&trim_info,image);
13520      windows->image.window_changes.width=(int) trim_info.width;
13521      windows->image.window_changes.height=(int) trim_info.height;
13522      (void) XConfigureImage(display,resource_info,windows,image,exception);
13523    }
13524  XSetCursorState(display,windows,MagickFalse);
13525  return(MagickTrue);
13526}
13527
13528/*
13529%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13530%                                                                             %
13531%                                                                             %
13532%                                                                             %
13533+   X V i s u a l D i r e c t o r y I m a g e                                 %
13534%                                                                             %
13535%                                                                             %
13536%                                                                             %
13537%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13538%
13539%  XVisualDirectoryImage() creates a Visual Image Directory.
13540%
13541%  The format of the XVisualDirectoryImage method is:
13542%
13543%      Image *XVisualDirectoryImage(Display *display,
13544%        XResourceInfo *resource_info,XWindows *windows,
13545%        ExceptionInfo *exception)
13546%
13547%  A description of each parameter follows:
13548%
13549%    o display: Specifies a connection to an X server; returned from
13550%      XOpenDisplay.
13551%
13552%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13553%
13554%    o windows: Specifies a pointer to a XWindows structure.
13555%
13556%    o exception: return any errors or warnings in this structure.
13557%
13558*/
13559static Image *XVisualDirectoryImage(Display *display,
13560  XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
13561{
13562#define TileImageTag  "Scale/Image"
13563#define XClientName  "montage"
13564
13565  char
13566    **filelist;
13567
13568  Image
13569    *images,
13570    *montage_image,
13571    *next_image,
13572    *thumbnail_image;
13573
13574  ImageInfo
13575    *read_info;
13576
13577  int
13578    number_files;
13579
13580  MagickBooleanType
13581    backdrop;
13582
13583  MagickStatusType
13584    status;
13585
13586  MontageInfo
13587    *montage_info;
13588
13589  RectangleInfo
13590    geometry;
13591
13592  register int
13593    i;
13594
13595  static char
13596    filename[MaxTextExtent] = "\0",
13597    filenames[MaxTextExtent] = "*";
13598
13599  XResourceInfo
13600    background_resources;
13601
13602  /*
13603    Request file name from user.
13604  */
13605  XFileBrowserWidget(display,windows,"Directory",filenames);
13606  if (*filenames == '\0')
13607    return((Image *) NULL);
13608  /*
13609    Expand the filenames.
13610  */
13611  filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13612  if (filelist == (char **) NULL)
13613    {
13614      ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13615        filenames);
13616      return((Image *) NULL);
13617    }
13618  number_files=1;
13619  filelist[0]=filenames;
13620  status=ExpandFilenames(&number_files,&filelist);
13621  if ((status == MagickFalse) || (number_files == 0))
13622    {
13623      if (number_files == 0)
13624        ThrowXWindowFatalException(ImageError,"NoImagesWereFound",filenames)
13625      else
13626        ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13627          filenames);
13628      return((Image *) NULL);
13629    }
13630  /*
13631    Set image background resources.
13632  */
13633  background_resources=(*resource_info);
13634  background_resources.window_id=AcquireString("");
13635  (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
13636    "0x%lx",windows->image.id);
13637  background_resources.backdrop=MagickTrue;
13638  /*
13639    Read each image and convert them to a tile.
13640  */
13641  backdrop=(windows->visual_info->klass == TrueColor) ||
13642    (windows->visual_info->klass == DirectColor) ? MagickTrue : MagickFalse;
13643  read_info=CloneImageInfo(resource_info->image_info);
13644  (void) SetImageOption(read_info,"jpeg:size","120x120");
13645  (void) CloneString(&read_info->size,DefaultTileGeometry);
13646  (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13647    (void *) NULL);
13648  images=NewImageList();
13649  XSetCursorState(display,windows,MagickTrue);
13650  XCheckRefreshWindows(display,windows);
13651  for (i=0; i < (int) number_files; i++)
13652  {
13653    (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13654    filelist[i]=DestroyString(filelist[i]);
13655    *read_info->magick='\0';
13656    next_image=ReadImage(read_info,exception);
13657    CatchException(exception);
13658    if (next_image != (Image *) NULL)
13659      {
13660        (void) DeleteImageProperty(next_image,"label");
13661        (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13662          read_info,next_image,DefaultTileLabel,exception));
13663        (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13664          exception);
13665        thumbnail_image=ThumbnailImage(next_image,geometry.width,
13666          geometry.height,exception);
13667        if (thumbnail_image != (Image *) NULL)
13668          {
13669            next_image=DestroyImage(next_image);
13670            next_image=thumbnail_image;
13671          }
13672        if (backdrop)
13673          {
13674            (void) XDisplayBackgroundImage(display,&background_resources,
13675              next_image,exception);
13676            XSetCursorState(display,windows,MagickTrue);
13677          }
13678        AppendImageToList(&images,next_image);
13679        if (images->progress_monitor != (MagickProgressMonitor) NULL)
13680          {
13681            MagickBooleanType
13682              proceed;
13683
13684            proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13685              (MagickSizeType) number_files);
13686            if (proceed == MagickFalse)
13687              break;
13688          }
13689      }
13690  }
13691  filelist=(char **) RelinquishMagickMemory(filelist);
13692  if (images == (Image *) NULL)
13693    {
13694      read_info=DestroyImageInfo(read_info);
13695      XSetCursorState(display,windows,MagickFalse);
13696      ThrowXWindowFatalException(ImageError,"NoImagesWereLoaded",filenames);
13697      return((Image *) NULL);
13698    }
13699  /*
13700    Create the Visual Image Directory.
13701  */
13702  montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13703  montage_info->pointsize=10;
13704  if (resource_info->font != (char *) NULL)
13705    (void) CloneString(&montage_info->font,resource_info->font);
13706  (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13707  montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13708    images),exception);
13709  images=DestroyImageList(images);
13710  montage_info=DestroyMontageInfo(montage_info);
13711  read_info=DestroyImageInfo(read_info);
13712  XSetCursorState(display,windows,MagickFalse);
13713  if (montage_image == (Image *) NULL)
13714    return(montage_image);
13715  XClientMessage(display,windows->image.id,windows->im_protocols,
13716    windows->im_next_image,CurrentTime);
13717  return(montage_image);
13718}
13719
13720/*
13721%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13722%                                                                             %
13723%                                                                             %
13724%                                                                             %
13725%   X D i s p l a y B a c k g r o u n d I m a g e                             %
13726%                                                                             %
13727%                                                                             %
13728%                                                                             %
13729%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13730%
13731%  XDisplayBackgroundImage() displays an image in the background of a window.
13732%
13733%  The format of the XDisplayBackgroundImage method is:
13734%
13735%      MagickBooleanType XDisplayBackgroundImage(Display *display,
13736%        XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13737%
13738%  A description of each parameter follows:
13739%
13740%    o display: Specifies a connection to an X server;  returned from
13741%      XOpenDisplay.
13742%
13743%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13744%
13745%    o image: the image.
13746%
13747%    o exception: return any errors or warnings in this structure.
13748%
13749*/
13750MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13751  XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13752{
13753  char
13754    geometry[MaxTextExtent],
13755    visual_type[MaxTextExtent];
13756
13757  int
13758    height,
13759    status,
13760    width;
13761
13762  RectangleInfo
13763    geometry_info;
13764
13765  static XPixelInfo
13766    pixel;
13767
13768  static XStandardColormap
13769    *map_info;
13770
13771  static XVisualInfo
13772    *visual_info = (XVisualInfo *) NULL;
13773
13774  static XWindowInfo
13775    window_info;
13776
13777  size_t
13778    delay;
13779
13780  Window
13781    root_window;
13782
13783  XGCValues
13784    context_values;
13785
13786  XResourceInfo
13787    resources;
13788
13789  XWindowAttributes
13790    window_attributes;
13791
13792  /*
13793    Determine target window.
13794  */
13795  assert(image != (Image *) NULL);
13796  assert(image->signature == MagickSignature);
13797  if (image->debug != MagickFalse)
13798    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13799  resources=(*resource_info);
13800  window_info.id=(Window) NULL;
13801  root_window=XRootWindow(display,XDefaultScreen(display));
13802  if (LocaleCompare(resources.window_id,"root") == 0)
13803    window_info.id=root_window;
13804  else
13805    {
13806      if (isdigit((unsigned char) *resources.window_id) != 0)
13807        window_info.id=XWindowByID(display,root_window,
13808          (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13809      if (window_info.id == (Window) NULL)
13810        window_info.id=XWindowByName(display,root_window,resources.window_id);
13811    }
13812  if (window_info.id == (Window) NULL)
13813    {
13814      ThrowXWindowFatalException(XServerError,"NoWindowWithSpecifiedIDExists",
13815        resources.window_id);
13816      return(MagickFalse);
13817    }
13818  /*
13819    Determine window visual id.
13820  */
13821  window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13822  window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13823  (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13824  status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13825  if (status != 0)
13826    (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
13827      XVisualIDFromVisual(window_attributes.visual));
13828  if (visual_info == (XVisualInfo *) NULL)
13829    {
13830      /*
13831        Allocate standard colormap.
13832      */
13833      map_info=XAllocStandardColormap();
13834      if (map_info == (XStandardColormap *) NULL)
13835        ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13836          image->filename);
13837      map_info->colormap=(Colormap) NULL;
13838      pixel.pixels=(unsigned long *) NULL;
13839      /*
13840        Initialize visual info.
13841      */
13842      resources.map_type=(char *) NULL;
13843      resources.visual_type=visual_type;
13844      visual_info=XBestVisualInfo(display,map_info,&resources);
13845      if (visual_info == (XVisualInfo *) NULL)
13846        ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13847          resources.visual_type);
13848      /*
13849        Initialize window info.
13850      */
13851      window_info.ximage=(XImage *) NULL;
13852      window_info.matte_image=(XImage *) NULL;
13853      window_info.pixmap=(Pixmap) NULL;
13854      window_info.matte_pixmap=(Pixmap) NULL;
13855    }
13856  /*
13857    Free previous root colors.
13858  */
13859  if (window_info.id == root_window)
13860    (void) XDestroyWindowColors(display,root_window);
13861  /*
13862    Initialize Standard Colormap.
13863  */
13864  resources.colormap=SharedColormap;
13865  XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel);
13866  /*
13867    Graphic context superclass.
13868  */
13869  context_values.background=pixel.background_color.pixel;
13870  context_values.foreground=pixel.foreground_color.pixel;
13871  pixel.annotate_context=XCreateGC(display,window_info.id,
13872    (size_t) (GCBackground | GCForeground),&context_values);
13873  if (pixel.annotate_context == (GC) NULL)
13874    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13875      image->filename);
13876  /*
13877    Initialize Image window attributes.
13878  */
13879  window_info.name=AcquireString("\0");
13880  window_info.icon_name=AcquireString("\0");
13881  XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13882    &resources,&window_info);
13883  /*
13884    Create the X image.
13885  */
13886  window_info.width=(unsigned int) image->columns;
13887  window_info.height=(unsigned int) image->rows;
13888  if ((image->columns != window_info.width) ||
13889      (image->rows != window_info.height))
13890    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13891      image->filename);
13892  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
13893    window_attributes.width,window_attributes.height);
13894  geometry_info.width=window_info.width;
13895  geometry_info.height=window_info.height;
13896  geometry_info.x=(ssize_t) window_info.x;
13897  geometry_info.y=(ssize_t) window_info.y;
13898  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13899    &geometry_info.width,&geometry_info.height);
13900  window_info.width=(unsigned int) geometry_info.width;
13901  window_info.height=(unsigned int) geometry_info.height;
13902  window_info.x=(int) geometry_info.x;
13903  window_info.y=(int) geometry_info.y;
13904  status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13905    window_info.height,exception);
13906  if (status == MagickFalse)
13907    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13908      image->filename);
13909  window_info.x=0;
13910  window_info.y=0;
13911  if (image->debug != MagickFalse)
13912    {
13913      (void) LogMagickEvent(X11Event,GetMagickModule(),
13914        "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13915        (double) image->columns,(double) image->rows);
13916      if (image->colors != 0)
13917        (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13918          image->colors);
13919      (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13920    }
13921  /*
13922    Adjust image dimensions as specified by backdrop or geometry options.
13923  */
13924  width=(int) window_info.width;
13925  height=(int) window_info.height;
13926  if (resources.backdrop != MagickFalse)
13927    {
13928      /*
13929        Center image on window.
13930      */
13931      window_info.x=(window_attributes.width/2)-
13932        (window_info.ximage->width/2);
13933      window_info.y=(window_attributes.height/2)-
13934        (window_info.ximage->height/2);
13935      width=window_attributes.width;
13936      height=window_attributes.height;
13937    }
13938  if ((resources.image_geometry != (char *) NULL) &&
13939      (*resources.image_geometry != '\0'))
13940    {
13941      char
13942        default_geometry[MaxTextExtent];
13943
13944      int
13945        flags,
13946        gravity;
13947
13948      XSizeHints
13949        *size_hints;
13950
13951      /*
13952        User specified geometry.
13953      */
13954      size_hints=XAllocSizeHints();
13955      if (size_hints == (XSizeHints *) NULL)
13956        ThrowXWindowFatalException(ResourceLimitFatalError,
13957          "MemoryAllocationFailed",image->filename);
13958      size_hints->flags=0L;
13959      (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
13960        width,height);
13961      flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
13962        default_geometry,window_info.border_width,size_hints,&window_info.x,
13963        &window_info.y,&width,&height,&gravity);
13964      if (flags & (XValue | YValue))
13965        {
13966          width=window_attributes.width;
13967          height=window_attributes.height;
13968        }
13969      (void) XFree((void *) size_hints);
13970    }
13971  /*
13972    Create the X pixmap.
13973  */
13974  window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
13975    (unsigned int) height,window_info.depth);
13976  if (window_info.pixmap == (Pixmap) NULL)
13977    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
13978      image->filename);
13979  /*
13980    Display pixmap on the window.
13981  */
13982  if (((unsigned int) width > window_info.width) ||
13983      ((unsigned int) height > window_info.height))
13984    (void) XFillRectangle(display,window_info.pixmap,
13985      window_info.annotate_context,0,0,(unsigned int) width,
13986      (unsigned int) height);
13987  (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
13988    window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
13989    window_info.width,(unsigned int) window_info.height);
13990  (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
13991  (void) XClearWindow(display,window_info.id);
13992  delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
13993  XDelay(display,delay == 0UL ? 10UL : delay);
13994  (void) XSync(display,MagickFalse);
13995  return(window_info.id == root_window ? MagickTrue : MagickFalse);
13996}
13997
13998/*
13999%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14000%                                                                             %
14001%                                                                             %
14002%                                                                             %
14003+   X D i s p l a y I m a g e                                                 %
14004%                                                                             %
14005%                                                                             %
14006%                                                                             %
14007%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14008%
14009%  XDisplayImage() displays an image via X11.  A new image is created and
14010%  returned if the user interactively transforms the displayed image.
14011%
14012%  The format of the XDisplayImage method is:
14013%
14014%      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14015%        char **argv,int argc,Image **image,size_t *state,
14016%        ExceptionInfo *exception)
14017%
14018%  A description of each parameter follows:
14019%
14020%    o nexus:  Method XDisplayImage returns an image when the
14021%      user chooses 'Open Image' from the command menu or picks a tile
14022%      from the image directory.  Otherwise a null image is returned.
14023%
14024%    o display: Specifies a connection to an X server;  returned from
14025%      XOpenDisplay.
14026%
14027%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14028%
14029%    o argv: Specifies the application's argument list.
14030%
14031%    o argc: Specifies the number of arguments.
14032%
14033%    o image: Specifies an address to an address of an Image structure;
14034%
14035%    o exception: return any errors or warnings in this structure.
14036%
14037*/
14038MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14039  char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
14040{
14041#define MagnifySize  256  /* must be a power of 2 */
14042#define MagickMenus  10
14043#define MagickTitle  "Commands"
14044
14045  static const char
14046    *CommandMenu[] =
14047    {
14048      "File",
14049      "Edit",
14050      "View",
14051      "Transform",
14052      "Enhance",
14053      "Effects",
14054      "F/X",
14055      "Image Edit",
14056      "Miscellany",
14057      "Help",
14058      (char *) NULL
14059    },
14060    *FileMenu[] =
14061    {
14062      "Open...",
14063      "Next",
14064      "Former",
14065      "Select...",
14066      "Save...",
14067      "Print...",
14068      "Delete...",
14069      "New...",
14070      "Visual Directory...",
14071      "Quit",
14072      (char *) NULL
14073    },
14074    *EditMenu[] =
14075    {
14076      "Undo",
14077      "Redo",
14078      "Cut",
14079      "Copy",
14080      "Paste",
14081      (char *) NULL
14082    },
14083    *ViewMenu[] =
14084    {
14085      "Half Size",
14086      "Original Size",
14087      "Double Size",
14088      "Resize...",
14089      "Apply",
14090      "Refresh",
14091      "Restore",
14092      (char *) NULL
14093    },
14094    *TransformMenu[] =
14095    {
14096      "Crop",
14097      "Chop",
14098      "Flop",
14099      "Flip",
14100      "Rotate Right",
14101      "Rotate Left",
14102      "Rotate...",
14103      "Shear...",
14104      "Roll...",
14105      "Trim Edges",
14106      (char *) NULL
14107    },
14108    *EnhanceMenu[] =
14109    {
14110      "Hue...",
14111      "Saturation...",
14112      "Brightness...",
14113      "Gamma...",
14114      "Spiff",
14115      "Dull",
14116      "Contrast Stretch...",
14117      "Sigmoidal Contrast...",
14118      "Normalize",
14119      "Equalize",
14120      "Negate",
14121      "Grayscale",
14122      "Map...",
14123      "Quantize...",
14124      (char *) NULL
14125    },
14126    *EffectsMenu[] =
14127    {
14128      "Despeckle",
14129      "Emboss",
14130      "Reduce Noise",
14131      "Add Noise...",
14132      "Sharpen...",
14133      "Blur...",
14134      "Threshold...",
14135      "Edge Detect...",
14136      "Spread...",
14137      "Shade...",
14138      "Raise...",
14139      "Segment...",
14140      (char *) NULL
14141    },
14142    *FXMenu[] =
14143    {
14144      "Solarize...",
14145      "Sepia Tone...",
14146      "Swirl...",
14147      "Implode...",
14148      "Vignette...",
14149      "Wave...",
14150      "Oil Paint...",
14151      "Charcoal Draw...",
14152      (char *) NULL
14153    },
14154    *ImageEditMenu[] =
14155    {
14156      "Annotate...",
14157      "Draw...",
14158      "Color...",
14159      "Matte...",
14160      "Composite...",
14161      "Add Border...",
14162      "Add Frame...",
14163      "Comment...",
14164      "Launch...",
14165      "Region of Interest...",
14166      (char *) NULL
14167    },
14168    *MiscellanyMenu[] =
14169    {
14170      "Image Info",
14171      "Zoom Image",
14172      "Show Preview...",
14173      "Show Histogram",
14174      "Show Matte",
14175      "Background...",
14176      "Slide Show...",
14177      "Preferences...",
14178      (char *) NULL
14179    },
14180    *HelpMenu[] =
14181    {
14182      "Overview",
14183      "Browse Documentation",
14184      "About Display",
14185      (char *) NULL
14186    },
14187    *ShortCutsMenu[] =
14188    {
14189      "Next",
14190      "Former",
14191      "Open...",
14192      "Save...",
14193      "Print...",
14194      "Undo",
14195      "Restore",
14196      "Image Info",
14197      "Quit",
14198      (char *) NULL
14199    },
14200    *VirtualMenu[] =
14201    {
14202      "Image Info",
14203      "Print",
14204      "Next",
14205      "Quit",
14206      (char *) NULL
14207    };
14208
14209  static const char
14210    **Menus[MagickMenus] =
14211    {
14212      FileMenu,
14213      EditMenu,
14214      ViewMenu,
14215      TransformMenu,
14216      EnhanceMenu,
14217      EffectsMenu,
14218      FXMenu,
14219      ImageEditMenu,
14220      MiscellanyMenu,
14221      HelpMenu
14222    };
14223
14224  static CommandType
14225    CommandMenus[] =
14226    {
14227      NullCommand,
14228      NullCommand,
14229      NullCommand,
14230      NullCommand,
14231      NullCommand,
14232      NullCommand,
14233      NullCommand,
14234      NullCommand,
14235      NullCommand,
14236      NullCommand,
14237    },
14238    FileCommands[] =
14239    {
14240      OpenCommand,
14241      NextCommand,
14242      FormerCommand,
14243      SelectCommand,
14244      SaveCommand,
14245      PrintCommand,
14246      DeleteCommand,
14247      NewCommand,
14248      VisualDirectoryCommand,
14249      QuitCommand
14250    },
14251    EditCommands[] =
14252    {
14253      UndoCommand,
14254      RedoCommand,
14255      CutCommand,
14256      CopyCommand,
14257      PasteCommand
14258    },
14259    ViewCommands[] =
14260    {
14261      HalfSizeCommand,
14262      OriginalSizeCommand,
14263      DoubleSizeCommand,
14264      ResizeCommand,
14265      ApplyCommand,
14266      RefreshCommand,
14267      RestoreCommand
14268    },
14269    TransformCommands[] =
14270    {
14271      CropCommand,
14272      ChopCommand,
14273      FlopCommand,
14274      FlipCommand,
14275      RotateRightCommand,
14276      RotateLeftCommand,
14277      RotateCommand,
14278      ShearCommand,
14279      RollCommand,
14280      TrimCommand
14281    },
14282    EnhanceCommands[] =
14283    {
14284      HueCommand,
14285      SaturationCommand,
14286      BrightnessCommand,
14287      GammaCommand,
14288      SpiffCommand,
14289      DullCommand,
14290      ContrastStretchCommand,
14291      SigmoidalContrastCommand,
14292      NormalizeCommand,
14293      EqualizeCommand,
14294      NegateCommand,
14295      GrayscaleCommand,
14296      MapCommand,
14297      QuantizeCommand
14298    },
14299    EffectsCommands[] =
14300    {
14301      DespeckleCommand,
14302      EmbossCommand,
14303      ReduceNoiseCommand,
14304      AddNoiseCommand,
14305      SharpenCommand,
14306      BlurCommand,
14307      ThresholdCommand,
14308      EdgeDetectCommand,
14309      SpreadCommand,
14310      ShadeCommand,
14311      RaiseCommand,
14312      SegmentCommand
14313    },
14314    FXCommands[] =
14315    {
14316      SolarizeCommand,
14317      SepiaToneCommand,
14318      SwirlCommand,
14319      ImplodeCommand,
14320      VignetteCommand,
14321      WaveCommand,
14322      OilPaintCommand,
14323      CharcoalDrawCommand
14324    },
14325    ImageEditCommands[] =
14326    {
14327      AnnotateCommand,
14328      DrawCommand,
14329      ColorCommand,
14330      MatteCommand,
14331      CompositeCommand,
14332      AddBorderCommand,
14333      AddFrameCommand,
14334      CommentCommand,
14335      LaunchCommand,
14336      RegionofInterestCommand
14337    },
14338    MiscellanyCommands[] =
14339    {
14340      InfoCommand,
14341      ZoomCommand,
14342      ShowPreviewCommand,
14343      ShowHistogramCommand,
14344      ShowMatteCommand,
14345      BackgroundCommand,
14346      SlideShowCommand,
14347      PreferencesCommand
14348    },
14349    HelpCommands[] =
14350    {
14351      HelpCommand,
14352      BrowseDocumentationCommand,
14353      VersionCommand
14354    },
14355    ShortCutsCommands[] =
14356    {
14357      NextCommand,
14358      FormerCommand,
14359      OpenCommand,
14360      SaveCommand,
14361      PrintCommand,
14362      UndoCommand,
14363      RestoreCommand,
14364      InfoCommand,
14365      QuitCommand
14366    },
14367    VirtualCommands[] =
14368    {
14369      InfoCommand,
14370      PrintCommand,
14371      NextCommand,
14372      QuitCommand
14373    };
14374
14375  static CommandType
14376    *Commands[MagickMenus] =
14377    {
14378      FileCommands,
14379      EditCommands,
14380      ViewCommands,
14381      TransformCommands,
14382      EnhanceCommands,
14383      EffectsCommands,
14384      FXCommands,
14385      ImageEditCommands,
14386      MiscellanyCommands,
14387      HelpCommands
14388    };
14389
14390  char
14391    command[MaxTextExtent],
14392    *directory,
14393    geometry[MaxTextExtent],
14394    resource_name[MaxTextExtent];
14395
14396  CommandType
14397    command_type;
14398
14399  Image
14400    *display_image,
14401    *nexus;
14402
14403  int
14404    entry,
14405    id;
14406
14407  KeySym
14408    key_symbol;
14409
14410  MagickStatusType
14411    context_mask,
14412    status;
14413
14414  RectangleInfo
14415    geometry_info;
14416
14417  register int
14418    i;
14419
14420  static char
14421    working_directory[MaxTextExtent];
14422
14423  static XPoint
14424    vid_info;
14425
14426  static XWindowInfo
14427    *magick_windows[MaxXWindows];
14428
14429  static unsigned int
14430    number_windows;
14431
14432  struct stat
14433    attributes;
14434
14435  time_t
14436    timer,
14437    timestamp,
14438    update_time;
14439
14440  unsigned int
14441    height,
14442    width;
14443
14444  size_t
14445    delay;
14446
14447  WarningHandler
14448    warning_handler;
14449
14450  Window
14451    root_window;
14452
14453  XClassHint
14454    *class_hints;
14455
14456  XEvent
14457    event;
14458
14459  XFontStruct
14460    *font_info;
14461
14462  XGCValues
14463    context_values;
14464
14465  XPixelInfo
14466    *icon_pixel,
14467    *pixel;
14468
14469  XResourceInfo
14470    *icon_resources;
14471
14472  XStandardColormap
14473    *icon_map,
14474    *map_info;
14475
14476  XVisualInfo
14477    *icon_visual,
14478    *visual_info;
14479
14480  XWindowChanges
14481    window_changes;
14482
14483  XWindows
14484    *windows;
14485
14486  XWMHints
14487    *manager_hints;
14488
14489  assert(image != (Image **) NULL);
14490  assert((*image)->signature == MagickSignature);
14491  if ((*image)->debug != MagickFalse)
14492    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14493  display_image=(*image);
14494  warning_handler=(WarningHandler) NULL;
14495  windows=XSetWindows((XWindows *) ~0);
14496  if (windows != (XWindows *) NULL)
14497    {
14498      int
14499        status;
14500
14501      status=chdir(working_directory);
14502      if (status == -1)
14503        (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14504          "UnableToOpenFile","%s",working_directory);
14505      warning_handler=resource_info->display_warnings ?
14506        SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14507      warning_handler=resource_info->display_warnings ?
14508        SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14509    }
14510  else
14511    {
14512      /*
14513        Allocate windows structure.
14514      */
14515      resource_info->colors=display_image->colors;
14516      windows=XSetWindows(XInitializeWindows(display,resource_info));
14517      if (windows == (XWindows *) NULL)
14518        ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14519          (*image)->filename);
14520      /*
14521        Initialize window id's.
14522      */
14523      number_windows=0;
14524      magick_windows[number_windows++]=(&windows->icon);
14525      magick_windows[number_windows++]=(&windows->backdrop);
14526      magick_windows[number_windows++]=(&windows->image);
14527      magick_windows[number_windows++]=(&windows->info);
14528      magick_windows[number_windows++]=(&windows->command);
14529      magick_windows[number_windows++]=(&windows->widget);
14530      magick_windows[number_windows++]=(&windows->popup);
14531      magick_windows[number_windows++]=(&windows->magnify);
14532      magick_windows[number_windows++]=(&windows->pan);
14533      for (i=0; i < (int) number_windows; i++)
14534        magick_windows[i]->id=(Window) NULL;
14535      vid_info.x=0;
14536      vid_info.y=0;
14537    }
14538  /*
14539    Initialize font info.
14540  */
14541  if (windows->font_info != (XFontStruct *) NULL)
14542    (void) XFreeFont(display,windows->font_info);
14543  windows->font_info=XBestFont(display,resource_info,MagickFalse);
14544  if (windows->font_info == (XFontStruct *) NULL)
14545    ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14546      resource_info->font);
14547  /*
14548    Initialize Standard Colormap.
14549  */
14550  map_info=windows->map_info;
14551  icon_map=windows->icon_map;
14552  visual_info=windows->visual_info;
14553  icon_visual=windows->icon_visual;
14554  pixel=windows->pixel_info;
14555  icon_pixel=windows->icon_pixel;
14556  font_info=windows->font_info;
14557  icon_resources=windows->icon_resources;
14558  class_hints=windows->class_hints;
14559  manager_hints=windows->manager_hints;
14560  root_window=XRootWindow(display,visual_info->screen);
14561  nexus=NewImageList();
14562  if (display_image->debug != MagickFalse)
14563    {
14564      (void) LogMagickEvent(X11Event,GetMagickModule(),
14565        "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14566        (double) display_image->scene,(double) display_image->columns,
14567        (double) display_image->rows);
14568      if (display_image->colors != 0)
14569        (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14570          display_image->colors);
14571      (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14572        display_image->magick);
14573    }
14574  XMakeStandardColormap(display,visual_info,resource_info,display_image,
14575    map_info,pixel);
14576  display_image->taint=MagickFalse;
14577  /*
14578    Initialize graphic context.
14579  */
14580  windows->context.id=(Window) NULL;
14581  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14582    resource_info,&windows->context);
14583  (void) CloneString(&class_hints->res_name,resource_info->client_name);
14584  (void) CloneString(&class_hints->res_class,resource_info->client_name);
14585  class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14586  manager_hints->flags=InputHint | StateHint;
14587  manager_hints->input=MagickFalse;
14588  manager_hints->initial_state=WithdrawnState;
14589  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14590    &windows->context);
14591  if (display_image->debug != MagickFalse)
14592    (void) LogMagickEvent(X11Event,GetMagickModule(),
14593      "Window id: 0x%lx (context)",windows->context.id);
14594  context_values.background=pixel->background_color.pixel;
14595  context_values.font=font_info->fid;
14596  context_values.foreground=pixel->foreground_color.pixel;
14597  context_values.graphics_exposures=MagickFalse;
14598  context_mask=(MagickStatusType)
14599    (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14600  if (pixel->annotate_context != (GC) NULL)
14601    (void) XFreeGC(display,pixel->annotate_context);
14602  pixel->annotate_context=XCreateGC(display,windows->context.id,
14603    context_mask,&context_values);
14604  if (pixel->annotate_context == (GC) NULL)
14605    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14606      display_image->filename);
14607  context_values.background=pixel->depth_color.pixel;
14608  if (pixel->widget_context != (GC) NULL)
14609    (void) XFreeGC(display,pixel->widget_context);
14610  pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14611    &context_values);
14612  if (pixel->widget_context == (GC) NULL)
14613    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14614      display_image->filename);
14615  context_values.background=pixel->foreground_color.pixel;
14616  context_values.foreground=pixel->background_color.pixel;
14617  context_values.plane_mask=context_values.background ^
14618    context_values.foreground;
14619  if (pixel->highlight_context != (GC) NULL)
14620    (void) XFreeGC(display,pixel->highlight_context);
14621  pixel->highlight_context=XCreateGC(display,windows->context.id,
14622    (size_t) (context_mask | GCPlaneMask),&context_values);
14623  if (pixel->highlight_context == (GC) NULL)
14624    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14625      display_image->filename);
14626  (void) XDestroyWindow(display,windows->context.id);
14627  /*
14628    Initialize icon window.
14629  */
14630  XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14631    icon_resources,&windows->icon);
14632  windows->icon.geometry=resource_info->icon_geometry;
14633  XBestIconSize(display,&windows->icon,display_image);
14634  windows->icon.attributes.colormap=XDefaultColormap(display,
14635    icon_visual->screen);
14636  windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14637  manager_hints->flags=InputHint | StateHint;
14638  manager_hints->input=MagickFalse;
14639  manager_hints->initial_state=IconicState;
14640  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14641    &windows->icon);
14642  if (display_image->debug != MagickFalse)
14643    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14644      windows->icon.id);
14645  /*
14646    Initialize graphic context for icon window.
14647  */
14648  if (icon_pixel->annotate_context != (GC) NULL)
14649    (void) XFreeGC(display,icon_pixel->annotate_context);
14650  context_values.background=icon_pixel->background_color.pixel;
14651  context_values.foreground=icon_pixel->foreground_color.pixel;
14652  icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14653    (size_t) (GCBackground | GCForeground),&context_values);
14654  if (icon_pixel->annotate_context == (GC) NULL)
14655    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14656      display_image->filename);
14657  windows->icon.annotate_context=icon_pixel->annotate_context;
14658  /*
14659    Initialize Image window.
14660  */
14661  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14662    &windows->image);
14663  windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14664  if (resource_info->use_shared_memory == MagickFalse)
14665    windows->image.shared_memory=MagickFalse;
14666  if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14667    {
14668      char
14669        *title;
14670
14671      title=InterpretImageProperties(resource_info->image_info,display_image,
14672        resource_info->title,exception);
14673      (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
14674      (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
14675      title=DestroyString(title);
14676    }
14677  else
14678    {
14679      char
14680        filename[MaxTextExtent];
14681
14682      /*
14683        Window name is the base of the filename.
14684      */
14685      GetPathComponent(display_image->magick_filename,TailPath,filename);
14686      if (display_image->scene == 0)
14687        (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14688          "%s: %s",MagickPackageName,filename);
14689      else
14690        (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14691          "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14692          (double) display_image->scene,(double) GetImageListLength(
14693          display_image));
14694      (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
14695    }
14696  if (resource_info->immutable)
14697    windows->image.immutable=MagickTrue;
14698  windows->image.use_pixmap=resource_info->use_pixmap;
14699  windows->image.geometry=resource_info->image_geometry;
14700  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14701    XDisplayWidth(display,visual_info->screen),
14702    XDisplayHeight(display,visual_info->screen));
14703  geometry_info.width=display_image->columns;
14704  geometry_info.height=display_image->rows;
14705  geometry_info.x=0;
14706  geometry_info.y=0;
14707  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14708    &geometry_info.width,&geometry_info.height);
14709  windows->image.width=(unsigned int) geometry_info.width;
14710  windows->image.height=(unsigned int) geometry_info.height;
14711  windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14712    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14713    KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14714    PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14715  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14716    resource_info,&windows->backdrop);
14717  if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14718    {
14719      /*
14720        Initialize backdrop window.
14721      */
14722      windows->backdrop.x=0;
14723      windows->backdrop.y=0;
14724      (void) CloneString(&windows->backdrop.name,"Backdrop");
14725      windows->backdrop.flags=(size_t) (USSize | USPosition);
14726      windows->backdrop.width=(unsigned int)
14727        XDisplayWidth(display,visual_info->screen);
14728      windows->backdrop.height=(unsigned int)
14729        XDisplayHeight(display,visual_info->screen);
14730      windows->backdrop.border_width=0;
14731      windows->backdrop.immutable=MagickTrue;
14732      windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14733        ButtonReleaseMask;
14734      windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14735        StructureNotifyMask;
14736      manager_hints->flags=IconWindowHint | InputHint | StateHint;
14737      manager_hints->icon_window=windows->icon.id;
14738      manager_hints->input=MagickTrue;
14739      manager_hints->initial_state=resource_info->iconic ? IconicState :
14740        NormalState;
14741      XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14742        &windows->backdrop);
14743      if (display_image->debug != MagickFalse)
14744        (void) LogMagickEvent(X11Event,GetMagickModule(),
14745          "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14746      (void) XMapWindow(display,windows->backdrop.id);
14747      (void) XClearWindow(display,windows->backdrop.id);
14748      if (windows->image.id != (Window) NULL)
14749        {
14750          (void) XDestroyWindow(display,windows->image.id);
14751          windows->image.id=(Window) NULL;
14752        }
14753      /*
14754        Position image in the center the backdrop.
14755      */
14756      windows->image.flags|=USPosition;
14757      windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14758        (windows->image.width/2);
14759      windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14760        (windows->image.height/2);
14761    }
14762  manager_hints->flags=IconWindowHint | InputHint | StateHint;
14763  manager_hints->icon_window=windows->icon.id;
14764  manager_hints->input=MagickTrue;
14765  manager_hints->initial_state=resource_info->iconic ? IconicState :
14766    NormalState;
14767  if (windows->group_leader.id != (Window) NULL)
14768    {
14769      /*
14770        Follow the leader.
14771      */
14772      manager_hints->flags|=WindowGroupHint;
14773      manager_hints->window_group=windows->group_leader.id;
14774      (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14775      if (display_image->debug != MagickFalse)
14776        (void) LogMagickEvent(X11Event,GetMagickModule(),
14777          "Window id: 0x%lx (group leader)",windows->group_leader.id);
14778    }
14779  XMakeWindow(display,
14780    (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14781    argv,argc,class_hints,manager_hints,&windows->image);
14782  (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14783    XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14784  if (windows->group_leader.id != (Window) NULL)
14785    (void) XSetTransientForHint(display,windows->image.id,
14786      windows->group_leader.id);
14787  if (display_image->debug != MagickFalse)
14788    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14789      windows->image.id);
14790  /*
14791    Initialize Info widget.
14792  */
14793  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14794    &windows->info);
14795  (void) CloneString(&windows->info.name,"Info");
14796  (void) CloneString(&windows->info.icon_name,"Info");
14797  windows->info.border_width=1;
14798  windows->info.x=2;
14799  windows->info.y=2;
14800  windows->info.flags|=PPosition;
14801  windows->info.attributes.win_gravity=UnmapGravity;
14802  windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14803    StructureNotifyMask;
14804  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14805  manager_hints->input=MagickFalse;
14806  manager_hints->initial_state=NormalState;
14807  manager_hints->window_group=windows->image.id;
14808  XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14809    &windows->info);
14810  windows->info.highlight_stipple=XCreateBitmapFromData(display,
14811    windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14812  windows->info.shadow_stipple=XCreateBitmapFromData(display,
14813    windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14814  (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14815  if (windows->image.mapped != MagickFalse)
14816    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14817  if (display_image->debug != MagickFalse)
14818    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14819      windows->info.id);
14820  /*
14821    Initialize Command widget.
14822  */
14823  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14824    resource_info,&windows->command);
14825  windows->command.data=MagickMenus;
14826  (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14827  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
14828    resource_info->client_name);
14829  windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14830    resource_name,"geometry",(char *) NULL);
14831  (void) CloneString(&windows->command.name,MagickTitle);
14832  windows->command.border_width=0;
14833  windows->command.flags|=PPosition;
14834  windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14835    ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14836    OwnerGrabButtonMask | StructureNotifyMask;
14837  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14838  manager_hints->input=MagickTrue;
14839  manager_hints->initial_state=NormalState;
14840  manager_hints->window_group=windows->image.id;
14841  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14842    &windows->command);
14843  windows->command.highlight_stipple=XCreateBitmapFromData(display,
14844    windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14845    HighlightHeight);
14846  windows->command.shadow_stipple=XCreateBitmapFromData(display,
14847    windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14848  (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14849  if (windows->command.mapped != MagickFalse)
14850    (void) XMapRaised(display,windows->command.id);
14851  if (display_image->debug != MagickFalse)
14852    (void) LogMagickEvent(X11Event,GetMagickModule(),
14853      "Window id: 0x%lx (command)",windows->command.id);
14854  /*
14855    Initialize Widget window.
14856  */
14857  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14858    resource_info,&windows->widget);
14859  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
14860    resource_info->client_name);
14861  windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14862    resource_name,"geometry",(char *) NULL);
14863  windows->widget.border_width=0;
14864  windows->widget.flags|=PPosition;
14865  windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14866    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14867    KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14868    StructureNotifyMask;
14869  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14870  manager_hints->input=MagickTrue;
14871  manager_hints->initial_state=NormalState;
14872  manager_hints->window_group=windows->image.id;
14873  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14874    &windows->widget);
14875  windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14876    windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14877  windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14878    windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14879  (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14880  if (display_image->debug != MagickFalse)
14881    (void) LogMagickEvent(X11Event,GetMagickModule(),
14882      "Window id: 0x%lx (widget)",windows->widget.id);
14883  /*
14884    Initialize popup window.
14885  */
14886  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14887    resource_info,&windows->popup);
14888  windows->popup.border_width=0;
14889  windows->popup.flags|=PPosition;
14890  windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14891    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14892    KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14893  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14894  manager_hints->input=MagickTrue;
14895  manager_hints->initial_state=NormalState;
14896  manager_hints->window_group=windows->image.id;
14897  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14898    &windows->popup);
14899  windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14900    windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14901  windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14902    windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14903  (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14904  if (display_image->debug != MagickFalse)
14905    (void) LogMagickEvent(X11Event,GetMagickModule(),
14906      "Window id: 0x%lx (pop up)",windows->popup.id);
14907  /*
14908    Initialize Magnify window and cursor.
14909  */
14910  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14911    resource_info,&windows->magnify);
14912  if (resource_info->use_shared_memory == MagickFalse)
14913    windows->magnify.shared_memory=MagickFalse;
14914  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
14915    resource_info->client_name);
14916  windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14917    resource_name,"geometry",(char *) NULL);
14918  (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14919    resource_info->magnify);
14920  if (windows->magnify.cursor != (Cursor) NULL)
14921    (void) XFreeCursor(display,windows->magnify.cursor);
14922  windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14923    map_info->colormap,resource_info->background_color,
14924    resource_info->foreground_color);
14925  if (windows->magnify.cursor == (Cursor) NULL)
14926    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14927      display_image->filename);
14928  windows->magnify.width=MagnifySize;
14929  windows->magnify.height=MagnifySize;
14930  windows->magnify.flags|=PPosition;
14931  windows->magnify.min_width=MagnifySize;
14932  windows->magnify.min_height=MagnifySize;
14933  windows->magnify.width_inc=MagnifySize;
14934  windows->magnify.height_inc=MagnifySize;
14935  windows->magnify.data=resource_info->magnify;
14936  windows->magnify.attributes.cursor=windows->magnify.cursor;
14937  windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14938    ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14939    StructureNotifyMask;
14940  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14941  manager_hints->input=MagickTrue;
14942  manager_hints->initial_state=NormalState;
14943  manager_hints->window_group=windows->image.id;
14944  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14945    &windows->magnify);
14946  if (display_image->debug != MagickFalse)
14947    (void) LogMagickEvent(X11Event,GetMagickModule(),
14948      "Window id: 0x%lx (magnify)",windows->magnify.id);
14949  (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14950  /*
14951    Initialize panning window.
14952  */
14953  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14954    resource_info,&windows->pan);
14955  (void) CloneString(&windows->pan.name,"Pan Icon");
14956  windows->pan.width=windows->icon.width;
14957  windows->pan.height=windows->icon.height;
14958  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
14959    resource_info->client_name);
14960  windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
14961    resource_name,"geometry",(char *) NULL);
14962  (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
14963    &windows->pan.width,&windows->pan.height);
14964  windows->pan.flags|=PPosition;
14965  windows->pan.immutable=MagickTrue;
14966  windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14967    ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
14968    StructureNotifyMask;
14969  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14970  manager_hints->input=MagickFalse;
14971  manager_hints->initial_state=NormalState;
14972  manager_hints->window_group=windows->image.id;
14973  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14974    &windows->pan);
14975  if (display_image->debug != MagickFalse)
14976    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
14977      windows->pan.id);
14978  (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
14979  if (windows->info.mapped != MagickFalse)
14980    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14981  if ((windows->image.mapped == MagickFalse) ||
14982      (windows->backdrop.id != (Window) NULL))
14983    (void) XMapWindow(display,windows->image.id);
14984  /*
14985    Set our progress monitor and warning handlers.
14986  */
14987  if (warning_handler == (WarningHandler) NULL)
14988    {
14989      warning_handler=resource_info->display_warnings ?
14990        SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14991      warning_handler=resource_info->display_warnings ?
14992        SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14993    }
14994  /*
14995    Initialize Image and Magnify X images.
14996  */
14997  windows->image.x=0;
14998  windows->image.y=0;
14999  windows->magnify.shape=MagickFalse;
15000  width=(unsigned int) display_image->columns;
15001  height=(unsigned int) display_image->rows;
15002  if ((display_image->columns != width) || (display_image->rows != height))
15003    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15004      display_image->filename);
15005  status=XMakeImage(display,resource_info,&windows->image,display_image,
15006    width,height,exception);
15007  if (status == MagickFalse)
15008    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15009      display_image->filename);
15010  status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
15011    windows->magnify.width,windows->magnify.height,exception);
15012  if (status == MagickFalse)
15013    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15014      display_image->filename);
15015  if (windows->magnify.mapped != MagickFalse)
15016    (void) XMapRaised(display,windows->magnify.id);
15017  if (windows->pan.mapped != MagickFalse)
15018    (void) XMapRaised(display,windows->pan.id);
15019  windows->image.window_changes.width=(int) display_image->columns;
15020  windows->image.window_changes.height=(int) display_image->rows;
15021  (void) XConfigureImage(display,resource_info,windows,display_image,exception);
15022  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15023  (void) XSync(display,MagickFalse);
15024  /*
15025    Respond to events.
15026  */
15027  delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
15028  timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15029  update_time=0;
15030  if (resource_info->update != MagickFalse)
15031    {
15032      MagickBooleanType
15033        status;
15034
15035      /*
15036        Determine when file data was last modified.
15037      */
15038      status=GetPathAttributes(display_image->filename,&attributes);
15039      if (status != MagickFalse)
15040        update_time=attributes.st_mtime;
15041    }
15042  *state&=(~FormerImageState);
15043  *state&=(~MontageImageState);
15044  *state&=(~NextImageState);
15045  do
15046  {
15047    /*
15048      Handle a window event.
15049    */
15050    if (windows->image.mapped != MagickFalse)
15051      if ((display_image->delay != 0) || (resource_info->update != 0))
15052        {
15053          if (timer < time((time_t *) NULL))
15054            {
15055              if (resource_info->update == MagickFalse)
15056                *state|=NextImageState | ExitState;
15057              else
15058                {
15059                  MagickBooleanType
15060                    status;
15061
15062                  /*
15063                    Determine if image file was modified.
15064                  */
15065                  status=GetPathAttributes(display_image->filename,&attributes);
15066                  if (status != MagickFalse)
15067                    if (update_time != attributes.st_mtime)
15068                      {
15069                        /*
15070                          Redisplay image.
15071                        */
15072                        (void) FormatLocaleString(
15073                          resource_info->image_info->filename,MaxTextExtent,
15074                          "%s:%s",display_image->magick,
15075                          display_image->filename);
15076                        nexus=ReadImage(resource_info->image_info,
15077                          &display_image->exception);
15078                        if (nexus != (Image *) NULL)
15079                          {
15080                            nexus=DestroyImage(nexus);
15081                            *state|=NextImageState | ExitState;
15082                          }
15083                      }
15084                  delay=display_image->delay/MagickMax(
15085                    display_image->ticks_per_second,1L);
15086                  timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15087                }
15088            }
15089          if (XEventsQueued(display,QueuedAfterFlush) == 0)
15090            {
15091              /*
15092                Do not block if delay > 0.
15093              */
15094              XDelay(display,SuspendTime << 2);
15095              continue;
15096            }
15097        }
15098    timestamp=time((time_t *) NULL);
15099    (void) XNextEvent(display,&event);
15100    if (windows->image.stasis == MagickFalse)
15101      windows->image.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15102        MagickTrue : MagickFalse;
15103    if (windows->magnify.stasis == MagickFalse)
15104      windows->magnify.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15105        MagickTrue : MagickFalse;
15106    if (event.xany.window == windows->command.id)
15107      {
15108        /*
15109          Select a command from the Command widget.
15110        */
15111        id=XCommandWidget(display,windows,CommandMenu,&event);
15112        if (id < 0)
15113          continue;
15114        (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15115        command_type=CommandMenus[id];
15116        if (id < MagickMenus)
15117          {
15118            /*
15119              Select a command from a pop-up menu.
15120            */
15121            entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15122              command);
15123            if (entry < 0)
15124              continue;
15125            (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15126            command_type=Commands[id][entry];
15127          }
15128        if (command_type != NullCommand)
15129          nexus=XMagickCommand(display,resource_info,windows,command_type,
15130            &display_image,exception);
15131        continue;
15132      }
15133    switch (event.type)
15134    {
15135      case ButtonPress:
15136      {
15137        if (display_image->debug != MagickFalse)
15138          (void) LogMagickEvent(X11Event,GetMagickModule(),
15139            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15140            event.xbutton.button,event.xbutton.x,event.xbutton.y);
15141        if ((event.xbutton.button == Button3) &&
15142            (event.xbutton.state & Mod1Mask))
15143          {
15144            /*
15145              Convert Alt-Button3 to Button2.
15146            */
15147            event.xbutton.button=Button2;
15148            event.xbutton.state&=(~Mod1Mask);
15149          }
15150        if (event.xbutton.window == windows->backdrop.id)
15151          {
15152            (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15153              event.xbutton.time);
15154            break;
15155          }
15156        if (event.xbutton.window == windows->image.id)
15157          {
15158            switch (event.xbutton.button)
15159            {
15160              case Button1:
15161              {
15162                if (resource_info->immutable)
15163                  {
15164                    /*
15165                      Select a command from the Virtual menu.
15166                    */
15167                    entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15168                      command);
15169                    if (entry >= 0)
15170                      nexus=XMagickCommand(display,resource_info,windows,
15171                        VirtualCommands[entry],&display_image,exception);
15172                    break;
15173                  }
15174                /*
15175                  Map/unmap Command widget.
15176                */
15177                if (windows->command.mapped != MagickFalse)
15178                  (void) XWithdrawWindow(display,windows->command.id,
15179                    windows->command.screen);
15180                else
15181                  {
15182                    (void) XCommandWidget(display,windows,CommandMenu,
15183                      (XEvent *) NULL);
15184                    (void) XMapRaised(display,windows->command.id);
15185                  }
15186                break;
15187              }
15188              case Button2:
15189              {
15190                /*
15191                  User pressed the image magnify button.
15192                */
15193                (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15194                  &display_image,exception);
15195                XMagnifyImage(display,windows,&event);
15196                break;
15197              }
15198              case Button3:
15199              {
15200                if (resource_info->immutable)
15201                  {
15202                    /*
15203                      Select a command from the Virtual menu.
15204                    */
15205                    entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15206                      command);
15207                    if (entry >= 0)
15208                      nexus=XMagickCommand(display,resource_info,windows,
15209                        VirtualCommands[entry],&display_image,exception);
15210                    break;
15211                  }
15212                if (display_image->montage != (char *) NULL)
15213                  {
15214                    /*
15215                      Open or delete a tile from a visual image directory.
15216                    */
15217                    nexus=XTileImage(display,resource_info,windows,
15218                      display_image,&event,exception);
15219                    if (nexus != (Image *) NULL)
15220                      *state|=MontageImageState | NextImageState | ExitState;
15221                    vid_info.x=(short int) windows->image.x;
15222                    vid_info.y=(short int) windows->image.y;
15223                    break;
15224                  }
15225                /*
15226                  Select a command from the Short Cuts menu.
15227                */
15228                entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15229                  command);
15230                if (entry >= 0)
15231                  nexus=XMagickCommand(display,resource_info,windows,
15232                    ShortCutsCommands[entry],&display_image,exception);
15233                break;
15234              }
15235              case Button4:
15236              {
15237                /*
15238                  Wheel up.
15239                */
15240                XTranslateImage(display,windows,*image,XK_Up);
15241                break;
15242              }
15243              case Button5:
15244              {
15245                /*
15246                  Wheel down.
15247                */
15248                XTranslateImage(display,windows,*image,XK_Down);
15249                break;
15250              }
15251              default:
15252                break;
15253            }
15254            break;
15255          }
15256        if (event.xbutton.window == windows->magnify.id)
15257          {
15258            int
15259              factor;
15260
15261            static const char
15262              *MagnifyMenu[] =
15263              {
15264                "2",
15265                "4",
15266                "5",
15267                "6",
15268                "7",
15269                "8",
15270                "9",
15271                "3",
15272                (char *) NULL,
15273              };
15274
15275            static KeySym
15276              MagnifyCommands[] =
15277              {
15278                XK_2,
15279                XK_4,
15280                XK_5,
15281                XK_6,
15282                XK_7,
15283                XK_8,
15284                XK_9,
15285                XK_3
15286              };
15287
15288            /*
15289              Select a magnify factor from the pop-up menu.
15290            */
15291            factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15292            if (factor >= 0)
15293              XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor]);
15294            break;
15295          }
15296        if (event.xbutton.window == windows->pan.id)
15297          {
15298            switch (event.xbutton.button)
15299            {
15300              case Button4:
15301              {
15302                /*
15303                  Wheel up.
15304                */
15305                XTranslateImage(display,windows,*image,XK_Up);
15306                break;
15307              }
15308              case Button5:
15309              {
15310                /*
15311                  Wheel down.
15312                */
15313                XTranslateImage(display,windows,*image,XK_Down);
15314                break;
15315              }
15316              default:
15317              {
15318                XPanImage(display,windows,&event);
15319                break;
15320              }
15321            }
15322            break;
15323          }
15324        delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15325          1L);
15326        timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15327        break;
15328      }
15329      case ButtonRelease:
15330      {
15331        if (display_image->debug != MagickFalse)
15332          (void) LogMagickEvent(X11Event,GetMagickModule(),
15333            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15334            event.xbutton.button,event.xbutton.x,event.xbutton.y);
15335        break;
15336      }
15337      case ClientMessage:
15338      {
15339        if (display_image->debug != MagickFalse)
15340          (void) LogMagickEvent(X11Event,GetMagickModule(),
15341            "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15342            event.xclient.message_type,event.xclient.format,(unsigned long)
15343            event.xclient.data.l[0]);
15344        if (event.xclient.message_type == windows->im_protocols)
15345          {
15346            if (*event.xclient.data.l == (long) windows->im_update_widget)
15347              {
15348                (void) CloneString(&windows->command.name,MagickTitle);
15349                windows->command.data=MagickMenus;
15350                (void) XCommandWidget(display,windows,CommandMenu,
15351                  (XEvent *) NULL);
15352                break;
15353              }
15354            if (*event.xclient.data.l == (long) windows->im_update_colormap)
15355              {
15356                /*
15357                  Update graphic context and window colormap.
15358                */
15359                for (i=0; i < (int) number_windows; i++)
15360                {
15361                  if (magick_windows[i]->id == windows->icon.id)
15362                    continue;
15363                  context_values.background=pixel->background_color.pixel;
15364                  context_values.foreground=pixel->foreground_color.pixel;
15365                  (void) XChangeGC(display,magick_windows[i]->annotate_context,
15366                    context_mask,&context_values);
15367                  (void) XChangeGC(display,magick_windows[i]->widget_context,
15368                    context_mask,&context_values);
15369                  context_values.background=pixel->foreground_color.pixel;
15370                  context_values.foreground=pixel->background_color.pixel;
15371                  context_values.plane_mask=context_values.background ^
15372                    context_values.foreground;
15373                  (void) XChangeGC(display,magick_windows[i]->highlight_context,
15374                    (size_t) (context_mask | GCPlaneMask),
15375                    &context_values);
15376                  magick_windows[i]->attributes.background_pixel=
15377                    pixel->background_color.pixel;
15378                  magick_windows[i]->attributes.border_pixel=
15379                    pixel->border_color.pixel;
15380                  magick_windows[i]->attributes.colormap=map_info->colormap;
15381                  (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15382                    (unsigned long) magick_windows[i]->mask,
15383                    &magick_windows[i]->attributes);
15384                }
15385                if (windows->pan.mapped != MagickFalse)
15386                  {
15387                    (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15388                      windows->pan.pixmap);
15389                    (void) XClearWindow(display,windows->pan.id);
15390                    XDrawPanRectangle(display,windows);
15391                  }
15392                if (windows->backdrop.id != (Window) NULL)
15393                  (void) XInstallColormap(display,map_info->colormap);
15394                break;
15395              }
15396            if (*event.xclient.data.l == (long) windows->im_former_image)
15397              {
15398                *state|=FormerImageState | ExitState;
15399                break;
15400              }
15401            if (*event.xclient.data.l == (long) windows->im_next_image)
15402              {
15403                *state|=NextImageState | ExitState;
15404                break;
15405              }
15406            if (*event.xclient.data.l == (long) windows->im_retain_colors)
15407              {
15408                *state|=RetainColorsState;
15409                break;
15410              }
15411            if (*event.xclient.data.l == (long) windows->im_exit)
15412              {
15413                *state|=ExitState;
15414                break;
15415              }
15416            break;
15417          }
15418        if (event.xclient.message_type == windows->dnd_protocols)
15419          {
15420            Atom
15421              selection,
15422              type;
15423
15424            int
15425              format,
15426              status;
15427
15428            unsigned char
15429              *data;
15430
15431            unsigned long
15432              after,
15433              length;
15434
15435            /*
15436              Display image named by the Drag-and-Drop selection.
15437            */
15438            if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15439              break;
15440            selection=XInternAtom(display,"DndSelection",MagickFalse);
15441            status=XGetWindowProperty(display,root_window,selection,0L,(long)
15442              MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15443              &length,&after,&data);
15444            if ((status != Success) || (length == 0))
15445              break;
15446            if (*event.xclient.data.l == 2)
15447              {
15448                /*
15449                  Offix DND.
15450                */
15451                (void) CopyMagickString(resource_info->image_info->filename,
15452                  (char *) data,MaxTextExtent);
15453              }
15454            else
15455              {
15456                /*
15457                  XDND.
15458                */
15459                if (strncmp((char *) data, "file:", 5) != 0)
15460                  {
15461                    (void) XFree((void *) data);
15462                    break;
15463                  }
15464                (void) CopyMagickString(resource_info->image_info->filename,
15465                  ((char *) data)+5,MaxTextExtent);
15466              }
15467            nexus=ReadImage(resource_info->image_info,
15468              &display_image->exception);
15469            CatchException(&display_image->exception);
15470            if (nexus != (Image *) NULL)
15471              *state|=NextImageState | ExitState;
15472            (void) XFree((void *) data);
15473            break;
15474          }
15475        /*
15476          If client window delete message, exit.
15477        */
15478        if (event.xclient.message_type != windows->wm_protocols)
15479          break;
15480        if (*event.xclient.data.l != (long) windows->wm_delete_window)
15481          break;
15482        (void) XWithdrawWindow(display,event.xclient.window,
15483          visual_info->screen);
15484        if (event.xclient.window == windows->image.id)
15485          {
15486            *state|=ExitState;
15487            break;
15488          }
15489        if (event.xclient.window == windows->pan.id)
15490          {
15491            /*
15492              Restore original image size when pan window is deleted.
15493            */
15494            windows->image.window_changes.width=windows->image.ximage->width;
15495            windows->image.window_changes.height=windows->image.ximage->height;
15496            (void) XConfigureImage(display,resource_info,windows,
15497              display_image,exception);
15498          }
15499        break;
15500      }
15501      case ConfigureNotify:
15502      {
15503        if (display_image->debug != MagickFalse)
15504          (void) LogMagickEvent(X11Event,GetMagickModule(),
15505            "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15506            event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15507            event.xconfigure.y,event.xconfigure.send_event);
15508        if (event.xconfigure.window == windows->image.id)
15509          {
15510            /*
15511              Image window has a new configuration.
15512            */
15513            if (event.xconfigure.send_event != 0)
15514              {
15515                XWindowChanges
15516                  window_changes;
15517
15518                /*
15519                  Position the transient windows relative of the Image window.
15520                */
15521                if (windows->command.geometry == (char *) NULL)
15522                  if (windows->command.mapped == MagickFalse)
15523                    {
15524                      windows->command.x=event.xconfigure.x-
15525                        windows->command.width-25;
15526                      windows->command.y=event.xconfigure.y;
15527                      XConstrainWindowPosition(display,&windows->command);
15528                      window_changes.x=windows->command.x;
15529                      window_changes.y=windows->command.y;
15530                      (void) XReconfigureWMWindow(display,windows->command.id,
15531                        windows->command.screen,(unsigned int) (CWX | CWY),
15532                        &window_changes);
15533                    }
15534                if (windows->widget.geometry == (char *) NULL)
15535                  if (windows->widget.mapped == MagickFalse)
15536                    {
15537                      windows->widget.x=event.xconfigure.x+
15538                        event.xconfigure.width/10;
15539                      windows->widget.y=event.xconfigure.y+
15540                        event.xconfigure.height/10;
15541                      XConstrainWindowPosition(display,&windows->widget);
15542                      window_changes.x=windows->widget.x;
15543                      window_changes.y=windows->widget.y;
15544                      (void) XReconfigureWMWindow(display,windows->widget.id,
15545                        windows->widget.screen,(unsigned int) (CWX | CWY),
15546                        &window_changes);
15547                    }
15548                if (windows->magnify.geometry == (char *) NULL)
15549                  if (windows->magnify.mapped == MagickFalse)
15550                    {
15551                      windows->magnify.x=event.xconfigure.x+
15552                        event.xconfigure.width+25;
15553                      windows->magnify.y=event.xconfigure.y;
15554                      XConstrainWindowPosition(display,&windows->magnify);
15555                      window_changes.x=windows->magnify.x;
15556                      window_changes.y=windows->magnify.y;
15557                      (void) XReconfigureWMWindow(display,windows->magnify.id,
15558                        windows->magnify.screen,(unsigned int) (CWX | CWY),
15559                        &window_changes);
15560                    }
15561                if (windows->pan.geometry == (char *) NULL)
15562                  if (windows->pan.mapped == MagickFalse)
15563                    {
15564                      windows->pan.x=event.xconfigure.x+
15565                        event.xconfigure.width+25;
15566                      windows->pan.y=event.xconfigure.y+
15567                        windows->magnify.height+50;
15568                      XConstrainWindowPosition(display,&windows->pan);
15569                      window_changes.x=windows->pan.x;
15570                      window_changes.y=windows->pan.y;
15571                      (void) XReconfigureWMWindow(display,windows->pan.id,
15572                        windows->pan.screen,(unsigned int) (CWX | CWY),
15573                        &window_changes);
15574                    }
15575              }
15576            if ((event.xconfigure.width == (int) windows->image.width) &&
15577                (event.xconfigure.height == (int) windows->image.height))
15578              break;
15579            windows->image.width=(unsigned int) event.xconfigure.width;
15580            windows->image.height=(unsigned int) event.xconfigure.height;
15581            windows->image.x=0;
15582            windows->image.y=0;
15583            if (display_image->montage != (char *) NULL)
15584              {
15585                windows->image.x=vid_info.x;
15586                windows->image.y=vid_info.y;
15587              }
15588            if ((windows->image.mapped != MagickFalse) &&
15589                (windows->image.stasis != MagickFalse))
15590              {
15591                /*
15592                  Update image window configuration.
15593                */
15594                windows->image.window_changes.width=event.xconfigure.width;
15595                windows->image.window_changes.height=event.xconfigure.height;
15596                (void) XConfigureImage(display,resource_info,windows,
15597                  display_image,exception);
15598              }
15599            /*
15600              Update pan window configuration.
15601            */
15602            if ((event.xconfigure.width < windows->image.ximage->width) ||
15603                (event.xconfigure.height < windows->image.ximage->height))
15604              {
15605                (void) XMapRaised(display,windows->pan.id);
15606                XDrawPanRectangle(display,windows);
15607              }
15608            else
15609              if (windows->pan.mapped != MagickFalse)
15610                (void) XWithdrawWindow(display,windows->pan.id,
15611                  windows->pan.screen);
15612            break;
15613          }
15614        if (event.xconfigure.window == windows->magnify.id)
15615          {
15616            unsigned int
15617              magnify;
15618
15619            /*
15620              Magnify window has a new configuration.
15621            */
15622            windows->magnify.width=(unsigned int) event.xconfigure.width;
15623            windows->magnify.height=(unsigned int) event.xconfigure.height;
15624            if (windows->magnify.mapped == MagickFalse)
15625              break;
15626            magnify=1;
15627            while ((int) magnify <= event.xconfigure.width)
15628              magnify<<=1;
15629            while ((int) magnify <= event.xconfigure.height)
15630              magnify<<=1;
15631            magnify>>=1;
15632            if (((int) magnify != event.xconfigure.width) ||
15633                ((int) magnify != event.xconfigure.height))
15634              {
15635                window_changes.width=(int) magnify;
15636                window_changes.height=(int) magnify;
15637                (void) XReconfigureWMWindow(display,windows->magnify.id,
15638                  windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15639                  &window_changes);
15640                break;
15641              }
15642            if ((windows->magnify.mapped != MagickFalse) &&
15643                (windows->magnify.stasis != MagickFalse))
15644              {
15645                status=XMakeImage(display,resource_info,&windows->magnify,
15646                  display_image,windows->magnify.width,windows->magnify.height,
15647                  exception);
15648                XMakeMagnifyImage(display,windows);
15649              }
15650            break;
15651          }
15652        if ((windows->magnify.mapped != MagickFalse) &&
15653            (event.xconfigure.window == windows->pan.id))
15654          {
15655            /*
15656              Pan icon window has a new configuration.
15657            */
15658            if (event.xconfigure.send_event != 0)
15659              {
15660                windows->pan.x=event.xconfigure.x;
15661                windows->pan.y=event.xconfigure.y;
15662              }
15663            windows->pan.width=(unsigned int) event.xconfigure.width;
15664            windows->pan.height=(unsigned int) event.xconfigure.height;
15665            break;
15666          }
15667        if (event.xconfigure.window == windows->icon.id)
15668          {
15669            /*
15670              Icon window has a new configuration.
15671            */
15672            windows->icon.width=(unsigned int) event.xconfigure.width;
15673            windows->icon.height=(unsigned int) event.xconfigure.height;
15674            break;
15675          }
15676        break;
15677      }
15678      case DestroyNotify:
15679      {
15680        /*
15681          Group leader has exited.
15682        */
15683        if (display_image->debug != MagickFalse)
15684          (void) LogMagickEvent(X11Event,GetMagickModule(),
15685            "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15686        if (event.xdestroywindow.window == windows->group_leader.id)
15687          {
15688            *state|=ExitState;
15689            break;
15690          }
15691        break;
15692      }
15693      case EnterNotify:
15694      {
15695        /*
15696          Selectively install colormap.
15697        */
15698        if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15699          if (event.xcrossing.mode != NotifyUngrab)
15700            XInstallColormap(display,map_info->colormap);
15701        break;
15702      }
15703      case Expose:
15704      {
15705        if (display_image->debug != MagickFalse)
15706          (void) LogMagickEvent(X11Event,GetMagickModule(),
15707            "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15708            event.xexpose.width,event.xexpose.height,event.xexpose.x,
15709            event.xexpose.y);
15710        /*
15711          Refresh windows that are now exposed.
15712        */
15713        if ((event.xexpose.window == windows->image.id) &&
15714            (windows->image.mapped != MagickFalse))
15715          {
15716            XRefreshWindow(display,&windows->image,&event);
15717            delay=display_image->delay/MagickMax(
15718              display_image->ticks_per_second,1L);
15719            timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15720            break;
15721          }
15722        if ((event.xexpose.window == windows->magnify.id) &&
15723            (windows->magnify.mapped != MagickFalse))
15724          {
15725            XMakeMagnifyImage(display,windows);
15726            break;
15727          }
15728        if (event.xexpose.window == windows->pan.id)
15729          {
15730            XDrawPanRectangle(display,windows);
15731            break;
15732          }
15733        if (event.xexpose.window == windows->icon.id)
15734          {
15735            XRefreshWindow(display,&windows->icon,&event);
15736            break;
15737          }
15738        break;
15739      }
15740      case KeyPress:
15741      {
15742        int
15743          length;
15744
15745        /*
15746          Respond to a user key press.
15747        */
15748        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15749          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15750        *(command+length)='\0';
15751        if (display_image->debug != MagickFalse)
15752          (void) LogMagickEvent(X11Event,GetMagickModule(),
15753            "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15754            key_symbol,command);
15755        if (event.xkey.window == windows->image.id)
15756          {
15757            command_type=XImageWindowCommand(display,resource_info,windows,
15758              event.xkey.state,key_symbol,&display_image,exception);
15759            if (command_type != NullCommand)
15760              nexus=XMagickCommand(display,resource_info,windows,command_type,
15761                &display_image,exception);
15762          }
15763        if (event.xkey.window == windows->magnify.id)
15764          XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol);
15765        if (event.xkey.window == windows->pan.id)
15766          {
15767            if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15768              (void) XWithdrawWindow(display,windows->pan.id,
15769                windows->pan.screen);
15770            else
15771              if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15772                XTextViewWidget(display,resource_info,windows,MagickFalse,
15773                  "Help Viewer - Image Pan",ImagePanHelp);
15774              else
15775                XTranslateImage(display,windows,*image,key_symbol);
15776          }
15777        delay=display_image->delay/MagickMax(
15778          display_image->ticks_per_second,1L);
15779        timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15780        break;
15781      }
15782      case KeyRelease:
15783      {
15784        /*
15785          Respond to a user key release.
15786        */
15787        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15788          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15789        if (display_image->debug != MagickFalse)
15790          (void) LogMagickEvent(X11Event,GetMagickModule(),
15791            "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15792        break;
15793      }
15794      case LeaveNotify:
15795      {
15796        /*
15797          Selectively uninstall colormap.
15798        */
15799        if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15800          if (event.xcrossing.mode != NotifyUngrab)
15801            XUninstallColormap(display,map_info->colormap);
15802        break;
15803      }
15804      case MapNotify:
15805      {
15806        if (display_image->debug != MagickFalse)
15807          (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15808            event.xmap.window);
15809        if (event.xmap.window == windows->backdrop.id)
15810          {
15811            (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15812              CurrentTime);
15813            windows->backdrop.mapped=MagickTrue;
15814            break;
15815          }
15816        if (event.xmap.window == windows->image.id)
15817          {
15818            if (windows->backdrop.id != (Window) NULL)
15819              (void) XInstallColormap(display,map_info->colormap);
15820            if (LocaleCompare(display_image->magick,"LOGO") == 0)
15821              {
15822                if (LocaleCompare(display_image->filename,"LOGO") == 0)
15823                  nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15824              }
15825            if (((int) windows->image.width < windows->image.ximage->width) ||
15826                ((int) windows->image.height < windows->image.ximage->height))
15827              (void) XMapRaised(display,windows->pan.id);
15828            windows->image.mapped=MagickTrue;
15829            break;
15830          }
15831        if (event.xmap.window == windows->magnify.id)
15832          {
15833            XMakeMagnifyImage(display,windows);
15834            windows->magnify.mapped=MagickTrue;
15835            (void) XWithdrawWindow(display,windows->info.id,
15836              windows->info.screen);
15837            break;
15838          }
15839        if (event.xmap.window == windows->pan.id)
15840          {
15841            XMakePanImage(display,resource_info,windows,display_image,
15842              exception);
15843            windows->pan.mapped=MagickTrue;
15844            break;
15845          }
15846        if (event.xmap.window == windows->info.id)
15847          {
15848            windows->info.mapped=MagickTrue;
15849            break;
15850          }
15851        if (event.xmap.window == windows->icon.id)
15852          {
15853            MagickBooleanType
15854              taint;
15855
15856            /*
15857              Create an icon image.
15858            */
15859            taint=display_image->taint;
15860            XMakeStandardColormap(display,icon_visual,icon_resources,
15861              display_image,icon_map,icon_pixel);
15862            (void) XMakeImage(display,icon_resources,&windows->icon,
15863              display_image,windows->icon.width,windows->icon.height,
15864              exception);
15865            display_image->taint=taint;
15866            (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15867              windows->icon.pixmap);
15868            (void) XClearWindow(display,windows->icon.id);
15869            (void) XWithdrawWindow(display,windows->info.id,
15870              windows->info.screen);
15871            windows->icon.mapped=MagickTrue;
15872            break;
15873          }
15874        if (event.xmap.window == windows->command.id)
15875          {
15876            windows->command.mapped=MagickTrue;
15877            break;
15878          }
15879        if (event.xmap.window == windows->popup.id)
15880          {
15881            windows->popup.mapped=MagickTrue;
15882            break;
15883          }
15884        if (event.xmap.window == windows->widget.id)
15885          {
15886            windows->widget.mapped=MagickTrue;
15887            break;
15888          }
15889        break;
15890      }
15891      case MappingNotify:
15892      {
15893        (void) XRefreshKeyboardMapping(&event.xmapping);
15894        break;
15895      }
15896      case NoExpose:
15897        break;
15898      case PropertyNotify:
15899      {
15900        Atom
15901          type;
15902
15903        int
15904          format,
15905          status;
15906
15907        unsigned char
15908          *data;
15909
15910        unsigned long
15911          after,
15912          length;
15913
15914        if (display_image->debug != MagickFalse)
15915          (void) LogMagickEvent(X11Event,GetMagickModule(),
15916            "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15917            event.xproperty.atom,event.xproperty.state);
15918        if (event.xproperty.atom != windows->im_remote_command)
15919          break;
15920        /*
15921          Display image named by the remote command protocol.
15922        */
15923        status=XGetWindowProperty(display,event.xproperty.window,
15924          event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15925          AnyPropertyType,&type,&format,&length,&after,&data);
15926        if ((status != Success) || (length == 0))
15927          break;
15928        if (LocaleCompare((char *) data,"-quit") == 0)
15929          {
15930            XClientMessage(display,windows->image.id,windows->im_protocols,
15931              windows->im_exit,CurrentTime);
15932            (void) XFree((void *) data);
15933            break;
15934          }
15935        (void) CopyMagickString(resource_info->image_info->filename,
15936          (char *) data,MaxTextExtent);
15937        (void) XFree((void *) data);
15938        nexus=ReadImage(resource_info->image_info,&display_image->exception);
15939        CatchException(&display_image->exception);
15940        if (nexus != (Image *) NULL)
15941          *state|=NextImageState | ExitState;
15942        break;
15943      }
15944      case ReparentNotify:
15945      {
15946        if (display_image->debug != MagickFalse)
15947          (void) LogMagickEvent(X11Event,GetMagickModule(),
15948            "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15949            event.xreparent.window);
15950        break;
15951      }
15952      case UnmapNotify:
15953      {
15954        if (display_image->debug != MagickFalse)
15955          (void) LogMagickEvent(X11Event,GetMagickModule(),
15956            "Unmap Notify: 0x%lx",event.xunmap.window);
15957        if (event.xunmap.window == windows->backdrop.id)
15958          {
15959            windows->backdrop.mapped=MagickFalse;
15960            break;
15961          }
15962        if (event.xunmap.window == windows->image.id)
15963          {
15964            windows->image.mapped=MagickFalse;
15965            break;
15966          }
15967        if (event.xunmap.window == windows->magnify.id)
15968          {
15969            windows->magnify.mapped=MagickFalse;
15970            break;
15971          }
15972        if (event.xunmap.window == windows->pan.id)
15973          {
15974            windows->pan.mapped=MagickFalse;
15975            break;
15976          }
15977        if (event.xunmap.window == windows->info.id)
15978          {
15979            windows->info.mapped=MagickFalse;
15980            break;
15981          }
15982        if (event.xunmap.window == windows->icon.id)
15983          {
15984            if (map_info->colormap == icon_map->colormap)
15985              XConfigureImageColormap(display,resource_info,windows,
15986                display_image);
15987            (void) XFreeStandardColormap(display,icon_visual,icon_map,
15988              icon_pixel);
15989            windows->icon.mapped=MagickFalse;
15990            break;
15991          }
15992        if (event.xunmap.window == windows->command.id)
15993          {
15994            windows->command.mapped=MagickFalse;
15995            break;
15996          }
15997        if (event.xunmap.window == windows->popup.id)
15998          {
15999            if (windows->backdrop.id != (Window) NULL)
16000              (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16001                CurrentTime);
16002            windows->popup.mapped=MagickFalse;
16003            break;
16004          }
16005        if (event.xunmap.window == windows->widget.id)
16006          {
16007            if (windows->backdrop.id != (Window) NULL)
16008              (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16009                CurrentTime);
16010            windows->widget.mapped=MagickFalse;
16011            break;
16012          }
16013        break;
16014      }
16015      default:
16016      {
16017        if (display_image->debug != MagickFalse)
16018          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
16019            event.type);
16020        break;
16021      }
16022    }
16023  } while (!(*state & ExitState));
16024  if ((*state & ExitState) == 0)
16025    (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
16026      &display_image,exception);
16027  else
16028    if (resource_info->confirm_edit != MagickFalse)
16029      {
16030        /*
16031          Query user if image has changed.
16032        */
16033        if ((resource_info->immutable == MagickFalse) &&
16034            (display_image->taint != MagickFalse))
16035          {
16036            int
16037              status;
16038
16039            status=XConfirmWidget(display,windows,"Your image changed.",
16040              "Do you want to save it");
16041            if (status == 0)
16042              *state&=(~ExitState);
16043            else
16044              if (status > 0)
16045                (void) XMagickCommand(display,resource_info,windows,SaveCommand,
16046                  &display_image,exception);
16047          }
16048      }
16049  if ((windows->visual_info->klass == GrayScale) ||
16050      (windows->visual_info->klass == PseudoColor) ||
16051      (windows->visual_info->klass == DirectColor))
16052    {
16053      /*
16054        Withdraw pan and Magnify window.
16055      */
16056      if (windows->info.mapped != MagickFalse)
16057        (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16058      if (windows->magnify.mapped != MagickFalse)
16059        (void) XWithdrawWindow(display,windows->magnify.id,
16060          windows->magnify.screen);
16061      if (windows->command.mapped != MagickFalse)
16062        (void) XWithdrawWindow(display,windows->command.id,
16063          windows->command.screen);
16064    }
16065  if (windows->pan.mapped != MagickFalse)
16066    (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16067  if (resource_info->backdrop == MagickFalse)
16068    if (windows->backdrop.mapped)
16069      {
16070        (void) XWithdrawWindow(display,windows->backdrop.id,
16071          windows->backdrop.screen);
16072        (void) XDestroyWindow(display,windows->backdrop.id);
16073        windows->backdrop.id=(Window) NULL;
16074        (void) XWithdrawWindow(display,windows->image.id,
16075          windows->image.screen);
16076        (void) XDestroyWindow(display,windows->image.id);
16077        windows->image.id=(Window) NULL;
16078      }
16079  XSetCursorState(display,windows,MagickTrue);
16080  XCheckRefreshWindows(display,windows);
16081  if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16082    *state&=(~ExitState);
16083  if (*state & ExitState)
16084    {
16085      /*
16086        Free Standard Colormap.
16087      */
16088      (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16089      if (resource_info->map_type == (char *) NULL)
16090        (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16091      /*
16092        Free X resources.
16093      */
16094      if (resource_info->copy_image != (Image *) NULL)
16095        {
16096          resource_info->copy_image=DestroyImage(resource_info->copy_image);
16097          resource_info->copy_image=NewImageList();
16098        }
16099      DestroyXResources();
16100    }
16101  (void) XSync(display,MagickFalse);
16102  /*
16103    Restore our progress monitor and warning handlers.
16104  */
16105  (void) SetErrorHandler(warning_handler);
16106  (void) SetWarningHandler(warning_handler);
16107  /*
16108    Change to home directory.
16109  */
16110  directory=getcwd(working_directory,MaxTextExtent);
16111  (void) directory;
16112  {
16113    int
16114      status;
16115
16116    status=chdir(resource_info->home_directory);
16117    if (status == -1)
16118      (void) ThrowMagickException(&display_image->exception,GetMagickModule(),
16119        FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
16120  }
16121  *image=display_image;
16122  return(nexus);
16123}
16124#else
16125
16126/*
16127%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16128%                                                                             %
16129%                                                                             %
16130%                                                                             %
16131+   D i s p l a y I m a g e s                                                 %
16132%                                                                             %
16133%                                                                             %
16134%                                                                             %
16135%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16136%
16137%  DisplayImages() displays an image sequence to any X window screen.  It
16138%  returns a value other than 0 if successful.  Check the exception member
16139%  of image to determine the reason for any failure.
16140%
16141%  The format of the DisplayImages method is:
16142%
16143%      MagickBooleanType DisplayImages(const ImageInfo *image_info,
16144%        Image *images,ExceptionInfo *exception)
16145%
16146%  A description of each parameter follows:
16147%
16148%    o image_info: the image info.
16149%
16150%    o image: the image.
16151%
16152%    o exception: return any errors or warnings in this structure.
16153%
16154*/
16155MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16156  Image *image,ExceptionInfo *exception)
16157{
16158  assert(image_info != (const ImageInfo *) NULL);
16159  assert(image_info->signature == MagickSignature);
16160  assert(image != (Image *) NULL);
16161  assert(image->signature == MagickSignature);
16162  if (image->debug != MagickFalse)
16163    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16164  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16165    "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image->filename);
16166  return(MagickFalse);
16167}
16168
16169/*
16170%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16171%                                                                             %
16172%                                                                             %
16173%                                                                             %
16174+   R e m o t e D i s p l a y C o m m a n d                                   %
16175%                                                                             %
16176%                                                                             %
16177%                                                                             %
16178%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16179%
16180%  RemoteDisplayCommand() encourages a remote display program to display the
16181%  specified image filename.
16182%
16183%  The format of the RemoteDisplayCommand method is:
16184%
16185%      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16186%        const char *window,const char *filename,ExceptionInfo *exception)
16187%
16188%  A description of each parameter follows:
16189%
16190%    o image_info: the image info.
16191%
16192%    o window: Specifies the name or id of an X window.
16193%
16194%    o filename: the name of the image filename to display.
16195%
16196%    o exception: return any errors or warnings in this structure.
16197%
16198*/
16199MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16200  const char *window,const char *filename,ExceptionInfo *exception)
16201{
16202  assert(image_info != (const ImageInfo *) NULL);
16203  assert(image_info->signature == MagickSignature);
16204  assert(filename != (char *) NULL);
16205  (void) window;
16206  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16207  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16208    "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image_info->filename);
16209  return(MagickFalse);
16210}
16211#endif
16212