display.c revision 947cb4c68bebf79b15b6f3e824bc973491a77709
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      exception);
2735    if (status == 0)
2736      return(MagickFalse);
2737    /*
2738      Free up memory.
2739    */
2740    previous_info=annotate_info->previous;
2741    annotate_info->text=DestroyString(annotate_info->text);
2742    annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2743    annotate_info=previous_info;
2744  }
2745  (void) XSetForeground(display,annotate_context,
2746    windows->pixel_info->foreground_color.pixel);
2747  (void) XSetBackground(display,annotate_context,
2748    windows->pixel_info->background_color.pixel);
2749  (void) XSetFont(display,annotate_context,windows->font_info->fid);
2750  XSetCursorState(display,windows,MagickFalse);
2751  (void) XFreeFont(display,font_info);
2752  /*
2753    Update image configuration.
2754  */
2755  XConfigureImageColormap(display,resource_info,windows,image);
2756  (void) XConfigureImage(display,resource_info,windows,image,exception);
2757  return(MagickTrue);
2758}
2759
2760/*
2761%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2762%                                                                             %
2763%                                                                             %
2764%                                                                             %
2765+   X B a c k g r o u n d I m a g e                                           %
2766%                                                                             %
2767%                                                                             %
2768%                                                                             %
2769%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2770%
2771%  XBackgroundImage() displays the image in the background of a window.
2772%
2773%  The format of the XBackgroundImage method is:
2774%
2775%      MagickBooleanType XBackgroundImage(Display *display,
2776%        XResourceInfo *resource_info,XWindows *windows,Image **image,
2777%        ExceptionInfo *exception)
2778%
2779%  A description of each parameter follows:
2780%
2781%    o display: Specifies a connection to an X server; returned from
2782%      XOpenDisplay.
2783%
2784%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2785%
2786%    o windows: Specifies a pointer to a XWindows structure.
2787%
2788%    o image: the image.
2789%
2790%    o exception: return any errors or warnings in this structure.
2791%
2792*/
2793static MagickBooleanType XBackgroundImage(Display *display,
2794  XResourceInfo *resource_info,XWindows *windows,Image **image,
2795  ExceptionInfo *exception)
2796{
2797#define BackgroundImageTag  "Background/Image"
2798
2799  int
2800    status;
2801
2802  static char
2803    window_id[MaxTextExtent] = "root";
2804
2805  XResourceInfo
2806    background_resources;
2807
2808  /*
2809    Put image in background.
2810  */
2811  status=XDialogWidget(display,windows,"Background",
2812    "Enter window id (id 0x00 selects window with pointer):",window_id);
2813  if (*window_id == '\0')
2814    return(MagickFalse);
2815  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
2816    exception);
2817  XInfoWidget(display,windows,BackgroundImageTag);
2818  XSetCursorState(display,windows,MagickTrue);
2819  XCheckRefreshWindows(display,windows);
2820  background_resources=(*resource_info);
2821  background_resources.window_id=window_id;
2822  background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2823  status=XDisplayBackgroundImage(display,&background_resources,*image,
2824    exception);
2825  if (status != MagickFalse)
2826    XClientMessage(display,windows->image.id,windows->im_protocols,
2827      windows->im_retain_colors,CurrentTime);
2828  XSetCursorState(display,windows,MagickFalse);
2829  (void) XMagickCommand(display,resource_info,windows,UndoCommand,image,
2830    exception);
2831  return(MagickTrue);
2832}
2833
2834/*
2835%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2836%                                                                             %
2837%                                                                             %
2838%                                                                             %
2839+   X C h o p I m a g e                                                       %
2840%                                                                             %
2841%                                                                             %
2842%                                                                             %
2843%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2844%
2845%  XChopImage() chops the X image.
2846%
2847%  The format of the XChopImage method is:
2848%
2849%    MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2850%      XWindows *windows,Image **image,ExceptionInfo *exception)
2851%
2852%  A description of each parameter follows:
2853%
2854%    o display: Specifies a connection to an X server; returned from
2855%      XOpenDisplay.
2856%
2857%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2858%
2859%    o windows: Specifies a pointer to a XWindows structure.
2860%
2861%    o image: the image.
2862%
2863%    o exception: return any errors or warnings in this structure.
2864%
2865*/
2866static MagickBooleanType XChopImage(Display *display,
2867  XResourceInfo *resource_info,XWindows *windows,Image **image,
2868  ExceptionInfo *exception)
2869{
2870  static const char
2871    *ChopMenu[] =
2872    {
2873      "Direction",
2874      "Help",
2875      "Dismiss",
2876      (char *) NULL
2877    };
2878
2879  static ModeType
2880    direction = HorizontalChopCommand;
2881
2882  static const ModeType
2883    ChopCommands[] =
2884    {
2885      ChopDirectionCommand,
2886      ChopHelpCommand,
2887      ChopDismissCommand
2888    },
2889    DirectionCommands[] =
2890    {
2891      HorizontalChopCommand,
2892      VerticalChopCommand
2893    };
2894
2895  char
2896    text[MaxTextExtent];
2897
2898  Image
2899    *chop_image;
2900
2901  int
2902    id,
2903    x,
2904    y;
2905
2906  MagickRealType
2907    scale_factor;
2908
2909  RectangleInfo
2910    chop_info;
2911
2912  unsigned int
2913    distance,
2914    height,
2915    width;
2916
2917  size_t
2918    state;
2919
2920  XEvent
2921    event;
2922
2923  XSegment
2924    segment_info;
2925
2926  /*
2927    Map Command widget.
2928  */
2929  (void) CloneString(&windows->command.name,"Chop");
2930  windows->command.data=1;
2931  (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2932  (void) XMapRaised(display,windows->command.id);
2933  XClientMessage(display,windows->image.id,windows->im_protocols,
2934    windows->im_update_widget,CurrentTime);
2935  /*
2936    Track pointer until button 1 is pressed.
2937  */
2938  XQueryPosition(display,windows->image.id,&x,&y);
2939  (void) XSelectInput(display,windows->image.id,
2940    windows->image.attributes.event_mask | PointerMotionMask);
2941  state=DefaultState;
2942  do
2943  {
2944    if (windows->info.mapped != MagickFalse)
2945      {
2946        /*
2947          Display pointer position.
2948        */
2949        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
2950          x+windows->image.x,y+windows->image.y);
2951        XInfoWidget(display,windows,text);
2952      }
2953    /*
2954      Wait for next event.
2955    */
2956    XScreenEvent(display,windows,&event);
2957    if (event.xany.window == windows->command.id)
2958      {
2959        /*
2960          Select a command from the Command widget.
2961        */
2962        id=XCommandWidget(display,windows,ChopMenu,&event);
2963        if (id < 0)
2964          continue;
2965        switch (ChopCommands[id])
2966        {
2967          case ChopDirectionCommand:
2968          {
2969            char
2970              command[MaxTextExtent];
2971
2972            static const char
2973              *Directions[] =
2974              {
2975                "horizontal",
2976                "vertical",
2977                (char *) NULL,
2978              };
2979
2980            /*
2981              Select a command from the pop-up menu.
2982            */
2983            id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2984            if (id >= 0)
2985              direction=DirectionCommands[id];
2986            break;
2987          }
2988          case ChopHelpCommand:
2989          {
2990            XTextViewWidget(display,resource_info,windows,MagickFalse,
2991              "Help Viewer - Image Chop",ImageChopHelp);
2992            break;
2993          }
2994          case ChopDismissCommand:
2995          {
2996            /*
2997              Prematurely exit.
2998            */
2999            state|=EscapeState;
3000            state|=ExitState;
3001            break;
3002          }
3003          default:
3004            break;
3005        }
3006        continue;
3007      }
3008    switch (event.type)
3009    {
3010      case ButtonPress:
3011      {
3012        if (event.xbutton.button != Button1)
3013          break;
3014        if (event.xbutton.window != windows->image.id)
3015          break;
3016        /*
3017          User has committed to start point of chopping line.
3018        */
3019        segment_info.x1=(short int) event.xbutton.x;
3020        segment_info.x2=(short int) event.xbutton.x;
3021        segment_info.y1=(short int) event.xbutton.y;
3022        segment_info.y2=(short int) event.xbutton.y;
3023        state|=ExitState;
3024        break;
3025      }
3026      case ButtonRelease:
3027        break;
3028      case Expose:
3029        break;
3030      case KeyPress:
3031      {
3032        char
3033          command[MaxTextExtent];
3034
3035        KeySym
3036          key_symbol;
3037
3038        if (event.xkey.window != windows->image.id)
3039          break;
3040        /*
3041          Respond to a user key press.
3042        */
3043        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3044          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3045        switch ((int) key_symbol)
3046        {
3047          case XK_Escape:
3048          case XK_F20:
3049          {
3050            /*
3051              Prematurely exit.
3052            */
3053            state|=EscapeState;
3054            state|=ExitState;
3055            break;
3056          }
3057          case XK_F1:
3058          case XK_Help:
3059          {
3060            (void) XSetFunction(display,windows->image.highlight_context,
3061              GXcopy);
3062            XTextViewWidget(display,resource_info,windows,MagickFalse,
3063              "Help Viewer - Image Chop",ImageChopHelp);
3064            (void) XSetFunction(display,windows->image.highlight_context,
3065              GXinvert);
3066            break;
3067          }
3068          default:
3069          {
3070            (void) XBell(display,0);
3071            break;
3072          }
3073        }
3074        break;
3075      }
3076      case MotionNotify:
3077      {
3078        /*
3079          Map and unmap Info widget as text cursor crosses its boundaries.
3080        */
3081        x=event.xmotion.x;
3082        y=event.xmotion.y;
3083        if (windows->info.mapped != MagickFalse)
3084          {
3085            if ((x < (int) (windows->info.x+windows->info.width)) &&
3086                (y < (int) (windows->info.y+windows->info.height)))
3087              (void) XWithdrawWindow(display,windows->info.id,
3088                windows->info.screen);
3089          }
3090        else
3091          if ((x > (int) (windows->info.x+windows->info.width)) ||
3092              (y > (int) (windows->info.y+windows->info.height)))
3093            (void) XMapWindow(display,windows->info.id);
3094      }
3095    }
3096  } while ((state & ExitState) == 0);
3097  (void) XSelectInput(display,windows->image.id,
3098    windows->image.attributes.event_mask);
3099  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3100  if ((state & EscapeState) != 0)
3101    return(MagickTrue);
3102  /*
3103    Draw line as pointer moves until the mouse button is released.
3104  */
3105  chop_info.width=0;
3106  chop_info.height=0;
3107  chop_info.x=0;
3108  chop_info.y=0;
3109  distance=0;
3110  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3111  state=DefaultState;
3112  do
3113  {
3114    if (distance > 9)
3115      {
3116        /*
3117          Display info and draw chopping line.
3118        */
3119        if (windows->info.mapped == MagickFalse)
3120          (void) XMapWindow(display,windows->info.id);
3121        (void) FormatLocaleString(text,MaxTextExtent,
3122          " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3123          chop_info.height,(double) chop_info.x,(double) chop_info.y);
3124        XInfoWidget(display,windows,text);
3125        XHighlightLine(display,windows->image.id,
3126          windows->image.highlight_context,&segment_info);
3127      }
3128    else
3129      if (windows->info.mapped != MagickFalse)
3130        (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3131    /*
3132      Wait for next event.
3133    */
3134    XScreenEvent(display,windows,&event);
3135    if (distance > 9)
3136      XHighlightLine(display,windows->image.id,
3137        windows->image.highlight_context,&segment_info);
3138    switch (event.type)
3139    {
3140      case ButtonPress:
3141      {
3142        segment_info.x2=(short int) event.xmotion.x;
3143        segment_info.y2=(short int) event.xmotion.y;
3144        break;
3145      }
3146      case ButtonRelease:
3147      {
3148        /*
3149          User has committed to chopping line.
3150        */
3151        segment_info.x2=(short int) event.xbutton.x;
3152        segment_info.y2=(short int) event.xbutton.y;
3153        state|=ExitState;
3154        break;
3155      }
3156      case Expose:
3157        break;
3158      case MotionNotify:
3159      {
3160        segment_info.x2=(short int) event.xmotion.x;
3161        segment_info.y2=(short int) event.xmotion.y;
3162      }
3163      default:
3164        break;
3165    }
3166    /*
3167      Check boundary conditions.
3168    */
3169    if (segment_info.x2 < 0)
3170      segment_info.x2=0;
3171    else
3172      if (segment_info.x2 > windows->image.ximage->width)
3173        segment_info.x2=windows->image.ximage->width;
3174    if (segment_info.y2 < 0)
3175      segment_info.y2=0;
3176    else
3177      if (segment_info.y2 > windows->image.ximage->height)
3178        segment_info.y2=windows->image.ximage->height;
3179    distance=(unsigned int)
3180      (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3181       ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3182    /*
3183      Compute chopping geometry.
3184    */
3185    if (direction == HorizontalChopCommand)
3186      {
3187        chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3188        chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3189        chop_info.height=0;
3190        chop_info.y=0;
3191        if (segment_info.x1 > (int) segment_info.x2)
3192          {
3193            chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3194            chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3195          }
3196      }
3197    else
3198      {
3199        chop_info.width=0;
3200        chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3201        chop_info.x=0;
3202        chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3203        if (segment_info.y1 > segment_info.y2)
3204          {
3205            chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3206            chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3207          }
3208      }
3209  } while ((state & ExitState) == 0);
3210  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3211  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3212  if (distance <= 9)
3213    return(MagickTrue);
3214  /*
3215    Image chopping is relative to image configuration.
3216  */
3217  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
3218    exception);
3219  XSetCursorState(display,windows,MagickTrue);
3220  XCheckRefreshWindows(display,windows);
3221  windows->image.window_changes.width=windows->image.ximage->width-
3222    (unsigned int) chop_info.width;
3223  windows->image.window_changes.height=windows->image.ximage->height-
3224    (unsigned int) chop_info.height;
3225  width=(unsigned int) (*image)->columns;
3226  height=(unsigned int) (*image)->rows;
3227  x=0;
3228  y=0;
3229  if (windows->image.crop_geometry != (char *) NULL)
3230    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3231  scale_factor=(MagickRealType) width/windows->image.ximage->width;
3232  chop_info.x+=x;
3233  chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3234  chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3235  scale_factor=(MagickRealType) height/windows->image.ximage->height;
3236  chop_info.y+=y;
3237  chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3238  chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3239  /*
3240    Chop image.
3241  */
3242  chop_image=ChopImage(*image,&chop_info,exception);
3243  XSetCursorState(display,windows,MagickFalse);
3244  if (chop_image == (Image *) NULL)
3245    return(MagickFalse);
3246  *image=DestroyImage(*image);
3247  *image=chop_image;
3248  /*
3249    Update image configuration.
3250  */
3251  XConfigureImageColormap(display,resource_info,windows,*image);
3252  (void) XConfigureImage(display,resource_info,windows,*image,exception);
3253  return(MagickTrue);
3254}
3255
3256/*
3257%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3258%                                                                             %
3259%                                                                             %
3260%                                                                             %
3261+   X C o l o r E d i t I m a g e                                             %
3262%                                                                             %
3263%                                                                             %
3264%                                                                             %
3265%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3266%
3267%  XColorEditImage() allows the user to interactively change the color of one
3268%  pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3269%
3270%  The format of the XColorEditImage method is:
3271%
3272%      MagickBooleanType XColorEditImage(Display *display,
3273%        XResourceInfo *resource_info,XWindows *windows,Image **image,
3274%          ExceptionInfo *exception)
3275%
3276%  A description of each parameter follows:
3277%
3278%    o display: Specifies a connection to an X server;  returned from
3279%      XOpenDisplay.
3280%
3281%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3282%
3283%    o windows: Specifies a pointer to a XWindows structure.
3284%
3285%    o image: the image; returned from ReadImage.
3286%
3287%    o exception: return any errors or warnings in this structure.
3288%
3289*/
3290static MagickBooleanType XColorEditImage(Display *display,
3291  XResourceInfo *resource_info,XWindows *windows,Image **image,
3292  ExceptionInfo *exception)
3293{
3294  static const char
3295    *ColorEditMenu[] =
3296    {
3297      "Method",
3298      "Pixel Color",
3299      "Border Color",
3300      "Fuzz",
3301      "Undo",
3302      "Help",
3303      "Dismiss",
3304      (char *) NULL
3305    };
3306
3307  static const ModeType
3308    ColorEditCommands[] =
3309    {
3310      ColorEditMethodCommand,
3311      ColorEditColorCommand,
3312      ColorEditBorderCommand,
3313      ColorEditFuzzCommand,
3314      ColorEditUndoCommand,
3315      ColorEditHelpCommand,
3316      ColorEditDismissCommand
3317    };
3318
3319  static PaintMethod
3320    method = PointMethod;
3321
3322  static unsigned int
3323    pen_id = 0;
3324
3325  static XColor
3326    border_color = { 0, 0, 0, 0, 0, 0 };
3327
3328  char
3329    command[MaxTextExtent],
3330    text[MaxTextExtent];
3331
3332  Cursor
3333    cursor;
3334
3335  int
3336    entry,
3337    id,
3338    x,
3339    x_offset,
3340    y,
3341    y_offset;
3342
3343  register Quantum
3344    *q;
3345
3346  register ssize_t
3347    i;
3348
3349  unsigned int
3350    height,
3351    width;
3352
3353  size_t
3354    state;
3355
3356  XColor
3357    color;
3358
3359  XEvent
3360    event;
3361
3362  /*
3363    Map Command widget.
3364  */
3365  (void) CloneString(&windows->command.name,"Color Edit");
3366  windows->command.data=4;
3367  (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3368  (void) XMapRaised(display,windows->command.id);
3369  XClientMessage(display,windows->image.id,windows->im_protocols,
3370    windows->im_update_widget,CurrentTime);
3371  /*
3372    Make cursor.
3373  */
3374  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3375    resource_info->background_color,resource_info->foreground_color);
3376  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3377  /*
3378    Track pointer until button 1 is pressed.
3379  */
3380  XQueryPosition(display,windows->image.id,&x,&y);
3381  (void) XSelectInput(display,windows->image.id,
3382    windows->image.attributes.event_mask | PointerMotionMask);
3383  state=DefaultState;
3384  do
3385  {
3386    if (windows->info.mapped != MagickFalse)
3387      {
3388        /*
3389          Display pointer position.
3390        */
3391        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
3392          x+windows->image.x,y+windows->image.y);
3393        XInfoWidget(display,windows,text);
3394      }
3395    /*
3396      Wait for next event.
3397    */
3398    XScreenEvent(display,windows,&event);
3399    if (event.xany.window == windows->command.id)
3400      {
3401        /*
3402          Select a command from the Command widget.
3403        */
3404        id=XCommandWidget(display,windows,ColorEditMenu,&event);
3405        if (id < 0)
3406          {
3407            (void) XCheckDefineCursor(display,windows->image.id,cursor);
3408            continue;
3409          }
3410        switch (ColorEditCommands[id])
3411        {
3412          case ColorEditMethodCommand:
3413          {
3414            char
3415              **methods;
3416
3417            /*
3418              Select a method from the pop-up menu.
3419            */
3420            methods=(char **) GetCommandOptions(MagickMethodOptions);
3421            if (methods == (char **) NULL)
3422              break;
3423            entry=XMenuWidget(display,windows,ColorEditMenu[id],
3424              (const char **) methods,command);
3425            if (entry >= 0)
3426              method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3427                MagickFalse,methods[entry]);
3428            methods=DestroyStringList(methods);
3429            break;
3430          }
3431          case ColorEditColorCommand:
3432          {
3433            const char
3434              *ColorMenu[MaxNumberPens];
3435
3436            int
3437              pen_number;
3438
3439            /*
3440              Initialize menu selections.
3441            */
3442            for (i=0; i < (int) (MaxNumberPens-2); i++)
3443              ColorMenu[i]=resource_info->pen_colors[i];
3444            ColorMenu[MaxNumberPens-2]="Browser...";
3445            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3446            /*
3447              Select a pen color from the pop-up menu.
3448            */
3449            pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3450              (const char **) ColorMenu,command);
3451            if (pen_number < 0)
3452              break;
3453            if (pen_number == (MaxNumberPens-2))
3454              {
3455                static char
3456                  color_name[MaxTextExtent] = "gray";
3457
3458                /*
3459                  Select a pen color from a dialog.
3460                */
3461                resource_info->pen_colors[pen_number]=color_name;
3462                XColorBrowserWidget(display,windows,"Select",color_name);
3463                if (*color_name == '\0')
3464                  break;
3465              }
3466            /*
3467              Set pen color.
3468            */
3469            (void) XParseColor(display,windows->map_info->colormap,
3470              resource_info->pen_colors[pen_number],&color);
3471            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3472              (unsigned int) MaxColors,&color);
3473            windows->pixel_info->pen_colors[pen_number]=color;
3474            pen_id=(unsigned int) pen_number;
3475            break;
3476          }
3477          case ColorEditBorderCommand:
3478          {
3479            const char
3480              *ColorMenu[MaxNumberPens];
3481
3482            int
3483              pen_number;
3484
3485            /*
3486              Initialize menu selections.
3487            */
3488            for (i=0; i < (int) (MaxNumberPens-2); i++)
3489              ColorMenu[i]=resource_info->pen_colors[i];
3490            ColorMenu[MaxNumberPens-2]="Browser...";
3491            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3492            /*
3493              Select a pen color from the pop-up menu.
3494            */
3495            pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3496              (const char **) ColorMenu,command);
3497            if (pen_number < 0)
3498              break;
3499            if (pen_number == (MaxNumberPens-2))
3500              {
3501                static char
3502                  color_name[MaxTextExtent] = "gray";
3503
3504                /*
3505                  Select a pen color from a dialog.
3506                */
3507                resource_info->pen_colors[pen_number]=color_name;
3508                XColorBrowserWidget(display,windows,"Select",color_name);
3509                if (*color_name == '\0')
3510                  break;
3511              }
3512            /*
3513              Set border color.
3514            */
3515            (void) XParseColor(display,windows->map_info->colormap,
3516              resource_info->pen_colors[pen_number],&border_color);
3517            break;
3518          }
3519          case ColorEditFuzzCommand:
3520          {
3521            static char
3522              fuzz[MaxTextExtent];
3523
3524            static const char
3525              *FuzzMenu[] =
3526              {
3527                "0%",
3528                "2%",
3529                "5%",
3530                "10%",
3531                "15%",
3532                "Dialog...",
3533                (char *) NULL,
3534              };
3535
3536            /*
3537              Select a command from the pop-up menu.
3538            */
3539            entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3540              command);
3541            if (entry < 0)
3542              break;
3543            if (entry != 5)
3544              {
3545                (*image)->fuzz=SiPrefixToDouble(FuzzMenu[entry],(double)
3546                  QuantumRange+1.0);
3547                break;
3548              }
3549            (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
3550            (void) XDialogWidget(display,windows,"Ok",
3551              "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3552            if (*fuzz == '\0')
3553              break;
3554            (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
3555            (*image)->fuzz=SiPrefixToDouble(fuzz,(double) QuantumRange+1.0);
3556            break;
3557          }
3558          case ColorEditUndoCommand:
3559          {
3560            (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3561              image,exception);
3562            break;
3563          }
3564          case ColorEditHelpCommand:
3565          default:
3566          {
3567            XTextViewWidget(display,resource_info,windows,MagickFalse,
3568              "Help Viewer - Image Annotation",ImageColorEditHelp);
3569            break;
3570          }
3571          case ColorEditDismissCommand:
3572          {
3573            /*
3574              Prematurely exit.
3575            */
3576            state|=EscapeState;
3577            state|=ExitState;
3578            break;
3579          }
3580        }
3581        (void) XCheckDefineCursor(display,windows->image.id,cursor);
3582        continue;
3583      }
3584    switch (event.type)
3585    {
3586      case ButtonPress:
3587      {
3588        if (event.xbutton.button != Button1)
3589          break;
3590        if ((event.xbutton.window != windows->image.id) &&
3591            (event.xbutton.window != windows->magnify.id))
3592          break;
3593        /*
3594          exit loop.
3595        */
3596        x=event.xbutton.x;
3597        y=event.xbutton.y;
3598        (void) XMagickCommand(display,resource_info,windows,
3599          SaveToUndoBufferCommand,image,exception);
3600        state|=UpdateConfigurationState;
3601        break;
3602      }
3603      case ButtonRelease:
3604      {
3605        if (event.xbutton.button != Button1)
3606          break;
3607        if ((event.xbutton.window != windows->image.id) &&
3608            (event.xbutton.window != windows->magnify.id))
3609          break;
3610        /*
3611          Update colormap information.
3612        */
3613        x=event.xbutton.x;
3614        y=event.xbutton.y;
3615        XConfigureImageColormap(display,resource_info,windows,*image);
3616        (void) XConfigureImage(display,resource_info,windows,*image,exception);
3617        XInfoWidget(display,windows,text);
3618        (void) XCheckDefineCursor(display,windows->image.id,cursor);
3619        state&=(~UpdateConfigurationState);
3620        break;
3621      }
3622      case Expose:
3623        break;
3624      case KeyPress:
3625      {
3626        KeySym
3627          key_symbol;
3628
3629        if (event.xkey.window == windows->magnify.id)
3630          {
3631            Window
3632              window;
3633
3634            window=windows->magnify.id;
3635            while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3636          }
3637        if (event.xkey.window != windows->image.id)
3638          break;
3639        /*
3640          Respond to a user key press.
3641        */
3642        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3643          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3644        switch ((int) key_symbol)
3645        {
3646          case XK_Escape:
3647          case XK_F20:
3648          {
3649            /*
3650              Prematurely exit.
3651            */
3652            state|=ExitState;
3653            break;
3654          }
3655          case XK_F1:
3656          case XK_Help:
3657          {
3658            XTextViewWidget(display,resource_info,windows,MagickFalse,
3659              "Help Viewer - Image Annotation",ImageColorEditHelp);
3660            break;
3661          }
3662          default:
3663          {
3664            (void) XBell(display,0);
3665            break;
3666          }
3667        }
3668        break;
3669      }
3670      case MotionNotify:
3671      {
3672        /*
3673          Map and unmap Info widget as cursor crosses its boundaries.
3674        */
3675        x=event.xmotion.x;
3676        y=event.xmotion.y;
3677        if (windows->info.mapped != MagickFalse)
3678          {
3679            if ((x < (int) (windows->info.x+windows->info.width)) &&
3680                (y < (int) (windows->info.y+windows->info.height)))
3681              (void) XWithdrawWindow(display,windows->info.id,
3682                windows->info.screen);
3683          }
3684        else
3685          if ((x > (int) (windows->info.x+windows->info.width)) ||
3686              (y > (int) (windows->info.y+windows->info.height)))
3687            (void) XMapWindow(display,windows->info.id);
3688        break;
3689      }
3690      default:
3691        break;
3692    }
3693    if (event.xany.window == windows->magnify.id)
3694      {
3695        x=windows->magnify.x-windows->image.x;
3696        y=windows->magnify.y-windows->image.y;
3697      }
3698    x_offset=x;
3699    y_offset=y;
3700    if ((state & UpdateConfigurationState) != 0)
3701      {
3702        CacheView
3703          *image_view;
3704
3705        int
3706          x,
3707          y;
3708
3709        /*
3710          Pixel edit is relative to image configuration.
3711        */
3712        (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3713          MagickTrue);
3714        color=windows->pixel_info->pen_colors[pen_id];
3715        XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3716        width=(unsigned int) (*image)->columns;
3717        height=(unsigned int) (*image)->rows;
3718        x=0;
3719        y=0;
3720        if (windows->image.crop_geometry != (char *) NULL)
3721          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3722            &width,&height);
3723        x_offset=(int)
3724          (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3725        y_offset=(int)
3726          (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3727        if ((x_offset < 0) || (y_offset < 0))
3728          continue;
3729        if ((x_offset >= (int) (*image)->columns) ||
3730            (y_offset >= (int) (*image)->rows))
3731          continue;
3732        image_view=AcquireCacheView(*image);
3733        switch (method)
3734        {
3735          case PointMethod:
3736          default:
3737          {
3738            /*
3739              Update color information using point algorithm.
3740            */
3741            if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3742              return(MagickFalse);
3743            q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3744              (ssize_t) y_offset,1,1,exception);
3745            if (q == (Quantum *) NULL)
3746              break;
3747            SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3748            SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3749            SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3750            (void) SyncCacheViewAuthenticPixels(image_view,exception);
3751            break;
3752          }
3753          case ReplaceMethod:
3754          {
3755            PixelInfo
3756              pixel,
3757              target;
3758
3759            Quantum
3760              virtual_pixel[MaxPixelChannels];
3761
3762            /*
3763              Update color information using replace algorithm.
3764            */
3765            (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
3766              (ssize_t) y_offset,virtual_pixel,exception);
3767            target.red=virtual_pixel[RedPixelChannel];
3768            target.green=virtual_pixel[GreenPixelChannel];
3769            target.blue=virtual_pixel[BluePixelChannel];
3770            target.alpha=virtual_pixel[AlphaPixelChannel];
3771            if ((*image)->storage_class == DirectClass)
3772              {
3773                for (y=0; y < (int) (*image)->rows; y++)
3774                {
3775                  q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3776                    (*image)->columns,1,exception);
3777                  if (q == (Quantum *) NULL)
3778                    break;
3779                  for (x=0; x < (int) (*image)->columns; x++)
3780                  {
3781                    GetPixelInfoPixel(*image,q,&pixel);
3782                    if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
3783                      {
3784                        SetPixelRed(*image,ScaleShortToQuantum(
3785                          color.red),q);
3786                        SetPixelGreen(*image,ScaleShortToQuantum(
3787                          color.green),q);
3788                        SetPixelBlue(*image,ScaleShortToQuantum(
3789                          color.blue),q);
3790                      }
3791                    q+=GetPixelChannels(*image);
3792                  }
3793                  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3794                    break;
3795                }
3796              }
3797            else
3798              {
3799                for (i=0; i < (ssize_t) (*image)->colors; i++)
3800                  if (IsFuzzyEquivalencePixelInfo((*image)->colormap+i,&target))
3801                    {
3802                      (*image)->colormap[i].red=ScaleShortToQuantum(
3803                        color.red);
3804                      (*image)->colormap[i].green=ScaleShortToQuantum(
3805                        color.green);
3806                      (*image)->colormap[i].blue=ScaleShortToQuantum(
3807                        color.blue);
3808                    }
3809                (void) SyncImage(*image,exception);
3810              }
3811            break;
3812          }
3813          case FloodfillMethod:
3814          case FillToBorderMethod:
3815          {
3816            DrawInfo
3817              *draw_info;
3818
3819            PixelInfo
3820              target;
3821
3822            /*
3823              Update color information using floodfill algorithm.
3824            */
3825            (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
3826              (ssize_t) y_offset,&target,exception);
3827            if (method == FillToBorderMethod)
3828              {
3829                target.red=(MagickRealType)
3830                  ScaleShortToQuantum(border_color.red);
3831                target.green=(MagickRealType)
3832                  ScaleShortToQuantum(border_color.green);
3833                target.blue=(MagickRealType)
3834                  ScaleShortToQuantum(border_color.blue);
3835              }
3836            draw_info=CloneDrawInfo(resource_info->image_info,
3837              (DrawInfo *) NULL);
3838            (void) QueryColorCompliance(resource_info->pen_colors[pen_id],
3839              AllCompliance,&draw_info->fill,exception);
3840            (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
3841              x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
3842              MagickFalse : MagickTrue,exception);
3843            draw_info=DestroyDrawInfo(draw_info);
3844            break;
3845          }
3846          case ResetMethod:
3847          {
3848            /*
3849              Update color information using reset algorithm.
3850            */
3851            if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3852              return(MagickFalse);
3853            for (y=0; y < (int) (*image)->rows; y++)
3854            {
3855              q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3856                (*image)->columns,1,exception);
3857              if (q == (Quantum *) NULL)
3858                break;
3859              for (x=0; x < (int) (*image)->columns; x++)
3860              {
3861                SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3862                SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3863                SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3864                q+=GetPixelChannels(*image);
3865              }
3866              if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3867                break;
3868            }
3869            break;
3870          }
3871        }
3872        image_view=DestroyCacheView(image_view);
3873        state&=(~UpdateConfigurationState);
3874      }
3875  } while ((state & ExitState) == 0);
3876  (void) XSelectInput(display,windows->image.id,
3877    windows->image.attributes.event_mask);
3878  XSetCursorState(display,windows,MagickFalse);
3879  (void) XFreeCursor(display,cursor);
3880  return(MagickTrue);
3881}
3882
3883/*
3884%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3885%                                                                             %
3886%                                                                             %
3887%                                                                             %
3888+   X C o m p o s i t e I m a g e                                             %
3889%                                                                             %
3890%                                                                             %
3891%                                                                             %
3892%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3893%
3894%  XCompositeImage() requests an image name from the user, reads the image and
3895%  composites it with the X window image at a location the user chooses with
3896%  the pointer.
3897%
3898%  The format of the XCompositeImage method is:
3899%
3900%      MagickBooleanType XCompositeImage(Display *display,
3901%        XResourceInfo *resource_info,XWindows *windows,Image *image,
3902%        ExceptionInfo *exception)
3903%
3904%  A description of each parameter follows:
3905%
3906%    o display: Specifies a connection to an X server;  returned from
3907%      XOpenDisplay.
3908%
3909%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3910%
3911%    o windows: Specifies a pointer to a XWindows structure.
3912%
3913%    o image: the image; returned from ReadImage.
3914%
3915%    o exception: return any errors or warnings in this structure.
3916%
3917*/
3918static MagickBooleanType XCompositeImage(Display *display,
3919  XResourceInfo *resource_info,XWindows *windows,Image *image,
3920  ExceptionInfo *exception)
3921{
3922  static char
3923    displacement_geometry[MaxTextExtent] = "30x30",
3924    filename[MaxTextExtent] = "\0";
3925
3926  static const char
3927    *CompositeMenu[] =
3928    {
3929      "Operators",
3930      "Dissolve",
3931      "Displace",
3932      "Help",
3933      "Dismiss",
3934      (char *) NULL
3935    };
3936
3937  static CompositeOperator
3938    compose = CopyCompositeOp;
3939
3940  static const ModeType
3941    CompositeCommands[] =
3942    {
3943      CompositeOperatorsCommand,
3944      CompositeDissolveCommand,
3945      CompositeDisplaceCommand,
3946      CompositeHelpCommand,
3947      CompositeDismissCommand
3948    };
3949
3950  char
3951    text[MaxTextExtent];
3952
3953  Cursor
3954    cursor;
3955
3956  Image
3957    *composite_image;
3958
3959  int
3960    entry,
3961    id,
3962    x,
3963    y;
3964
3965  MagickRealType
3966    blend,
3967    scale_factor;
3968
3969  RectangleInfo
3970    highlight_info,
3971    composite_info;
3972
3973  unsigned int
3974    height,
3975    width;
3976
3977  size_t
3978    state;
3979
3980  XEvent
3981    event;
3982
3983  /*
3984    Request image file name from user.
3985  */
3986  XFileBrowserWidget(display,windows,"Composite",filename);
3987  if (*filename == '\0')
3988    return(MagickTrue);
3989  /*
3990    Read image.
3991  */
3992  XSetCursorState(display,windows,MagickTrue);
3993  XCheckRefreshWindows(display,windows);
3994  (void) CopyMagickString(resource_info->image_info->filename,filename,
3995    MaxTextExtent);
3996  composite_image=ReadImage(resource_info->image_info,exception);
3997  CatchException(exception);
3998  XSetCursorState(display,windows,MagickFalse);
3999  if (composite_image == (Image *) NULL)
4000    return(MagickFalse);
4001  /*
4002    Map Command widget.
4003  */
4004  (void) CloneString(&windows->command.name,"Composite");
4005  windows->command.data=1;
4006  (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
4007  (void) XMapRaised(display,windows->command.id);
4008  XClientMessage(display,windows->image.id,windows->im_protocols,
4009    windows->im_update_widget,CurrentTime);
4010  /*
4011    Track pointer until button 1 is pressed.
4012  */
4013  XQueryPosition(display,windows->image.id,&x,&y);
4014  (void) XSelectInput(display,windows->image.id,
4015    windows->image.attributes.event_mask | PointerMotionMask);
4016  composite_info.x=(ssize_t) windows->image.x+x;
4017  composite_info.y=(ssize_t) windows->image.y+y;
4018  composite_info.width=0;
4019  composite_info.height=0;
4020  cursor=XCreateFontCursor(display,XC_ul_angle);
4021  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4022  blend=0.0;
4023  state=DefaultState;
4024  do
4025  {
4026    if (windows->info.mapped != MagickFalse)
4027      {
4028        /*
4029          Display pointer position.
4030        */
4031        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4032          (long) composite_info.x,(long) composite_info.y);
4033        XInfoWidget(display,windows,text);
4034      }
4035    highlight_info=composite_info;
4036    highlight_info.x=composite_info.x-windows->image.x;
4037    highlight_info.y=composite_info.y-windows->image.y;
4038    XHighlightRectangle(display,windows->image.id,
4039      windows->image.highlight_context,&highlight_info);
4040    /*
4041      Wait for next event.
4042    */
4043    XScreenEvent(display,windows,&event);
4044    XHighlightRectangle(display,windows->image.id,
4045      windows->image.highlight_context,&highlight_info);
4046    if (event.xany.window == windows->command.id)
4047      {
4048        /*
4049          Select a command from the Command widget.
4050        */
4051        id=XCommandWidget(display,windows,CompositeMenu,&event);
4052        if (id < 0)
4053          continue;
4054        switch (CompositeCommands[id])
4055        {
4056          case CompositeOperatorsCommand:
4057          {
4058            char
4059              command[MaxTextExtent],
4060              **operators;
4061
4062            /*
4063              Select a command from the pop-up menu.
4064            */
4065            operators=GetCommandOptions(MagickComposeOptions);
4066            if (operators == (char **) NULL)
4067              break;
4068            entry=XMenuWidget(display,windows,CompositeMenu[id],
4069              (const char **) operators,command);
4070            if (entry >= 0)
4071              compose=(CompositeOperator) ParseCommandOption(
4072                MagickComposeOptions,MagickFalse,operators[entry]);
4073            operators=DestroyStringList(operators);
4074            break;
4075          }
4076          case CompositeDissolveCommand:
4077          {
4078            static char
4079              factor[MaxTextExtent] = "20.0";
4080
4081            /*
4082              Dissolve the two images a given percent.
4083            */
4084            (void) XSetFunction(display,windows->image.highlight_context,
4085              GXcopy);
4086            (void) XDialogWidget(display,windows,"Dissolve",
4087              "Enter the blend factor (0.0 - 99.9%):",factor);
4088            (void) XSetFunction(display,windows->image.highlight_context,
4089              GXinvert);
4090            if (*factor == '\0')
4091              break;
4092            blend=InterpretLocaleValue(factor,(char **) NULL);
4093            compose=DissolveCompositeOp;
4094            break;
4095          }
4096          case CompositeDisplaceCommand:
4097          {
4098            /*
4099              Get horizontal and vertical scale displacement geometry.
4100            */
4101            (void) XSetFunction(display,windows->image.highlight_context,
4102              GXcopy);
4103            (void) XDialogWidget(display,windows,"Displace",
4104              "Enter the horizontal and vertical scale:",displacement_geometry);
4105            (void) XSetFunction(display,windows->image.highlight_context,
4106              GXinvert);
4107            if (*displacement_geometry == '\0')
4108              break;
4109            compose=DisplaceCompositeOp;
4110            break;
4111          }
4112          case CompositeHelpCommand:
4113          {
4114            (void) XSetFunction(display,windows->image.highlight_context,
4115              GXcopy);
4116            XTextViewWidget(display,resource_info,windows,MagickFalse,
4117              "Help Viewer - Image Composite",ImageCompositeHelp);
4118            (void) XSetFunction(display,windows->image.highlight_context,
4119              GXinvert);
4120            break;
4121          }
4122          case CompositeDismissCommand:
4123          {
4124            /*
4125              Prematurely exit.
4126            */
4127            state|=EscapeState;
4128            state|=ExitState;
4129            break;
4130          }
4131          default:
4132            break;
4133        }
4134        continue;
4135      }
4136    switch (event.type)
4137    {
4138      case ButtonPress:
4139      {
4140        if (image->debug != MagickFalse)
4141          (void) LogMagickEvent(X11Event,GetMagickModule(),
4142            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4143            event.xbutton.button,event.xbutton.x,event.xbutton.y);
4144        if (event.xbutton.button != Button1)
4145          break;
4146        if (event.xbutton.window != windows->image.id)
4147          break;
4148        /*
4149          Change cursor.
4150        */
4151        composite_info.width=composite_image->columns;
4152        composite_info.height=composite_image->rows;
4153        (void) XCheckDefineCursor(display,windows->image.id,cursor);
4154        composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4155        composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4156        break;
4157      }
4158      case ButtonRelease:
4159      {
4160        if (image->debug != MagickFalse)
4161          (void) LogMagickEvent(X11Event,GetMagickModule(),
4162            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4163            event.xbutton.button,event.xbutton.x,event.xbutton.y);
4164        if (event.xbutton.button != Button1)
4165          break;
4166        if (event.xbutton.window != windows->image.id)
4167          break;
4168        if ((composite_info.width != 0) && (composite_info.height != 0))
4169          {
4170            /*
4171              User has selected the location of the composite image.
4172            */
4173            composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4174            composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4175            state|=ExitState;
4176          }
4177        break;
4178      }
4179      case Expose:
4180        break;
4181      case KeyPress:
4182      {
4183        char
4184          command[MaxTextExtent];
4185
4186        KeySym
4187          key_symbol;
4188
4189        int
4190          length;
4191
4192        if (event.xkey.window != windows->image.id)
4193          break;
4194        /*
4195          Respond to a user key press.
4196        */
4197        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4198          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4199        *(command+length)='\0';
4200        if (image->debug != MagickFalse)
4201          (void) LogMagickEvent(X11Event,GetMagickModule(),
4202            "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4203        switch ((int) key_symbol)
4204        {
4205          case XK_Escape:
4206          case XK_F20:
4207          {
4208            /*
4209              Prematurely exit.
4210            */
4211            composite_image=DestroyImage(composite_image);
4212            state|=EscapeState;
4213            state|=ExitState;
4214            break;
4215          }
4216          case XK_F1:
4217          case XK_Help:
4218          {
4219            (void) XSetFunction(display,windows->image.highlight_context,
4220              GXcopy);
4221            XTextViewWidget(display,resource_info,windows,MagickFalse,
4222              "Help Viewer - Image Composite",ImageCompositeHelp);
4223            (void) XSetFunction(display,windows->image.highlight_context,
4224              GXinvert);
4225            break;
4226          }
4227          default:
4228          {
4229            (void) XBell(display,0);
4230            break;
4231          }
4232        }
4233        break;
4234      }
4235      case MotionNotify:
4236      {
4237        /*
4238          Map and unmap Info widget as text cursor crosses its boundaries.
4239        */
4240        x=event.xmotion.x;
4241        y=event.xmotion.y;
4242        if (windows->info.mapped != MagickFalse)
4243          {
4244            if ((x < (int) (windows->info.x+windows->info.width)) &&
4245                (y < (int) (windows->info.y+windows->info.height)))
4246              (void) XWithdrawWindow(display,windows->info.id,
4247                windows->info.screen);
4248          }
4249        else
4250          if ((x > (int) (windows->info.x+windows->info.width)) ||
4251              (y > (int) (windows->info.y+windows->info.height)))
4252            (void) XMapWindow(display,windows->info.id);
4253        composite_info.x=(ssize_t) windows->image.x+x;
4254        composite_info.y=(ssize_t) windows->image.y+y;
4255        break;
4256      }
4257      default:
4258      {
4259        if (image->debug != MagickFalse)
4260          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4261            event.type);
4262        break;
4263      }
4264    }
4265  } while ((state & ExitState) == 0);
4266  (void) XSelectInput(display,windows->image.id,
4267    windows->image.attributes.event_mask);
4268  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4269  XSetCursorState(display,windows,MagickFalse);
4270  (void) XFreeCursor(display,cursor);
4271  if ((state & EscapeState) != 0)
4272    return(MagickTrue);
4273  /*
4274    Image compositing is relative to image configuration.
4275  */
4276  XSetCursorState(display,windows,MagickTrue);
4277  XCheckRefreshWindows(display,windows);
4278  width=(unsigned int) image->columns;
4279  height=(unsigned int) image->rows;
4280  x=0;
4281  y=0;
4282  if (windows->image.crop_geometry != (char *) NULL)
4283    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4284  scale_factor=(MagickRealType) width/windows->image.ximage->width;
4285  composite_info.x+=x;
4286  composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4287  composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4288  scale_factor=(MagickRealType) height/windows->image.ximage->height;
4289  composite_info.y+=y;
4290  composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4291  composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4292  if ((composite_info.width != composite_image->columns) ||
4293      (composite_info.height != composite_image->rows))
4294    {
4295      Image
4296        *resize_image;
4297
4298      /*
4299        Scale composite image.
4300      */
4301      resize_image=ResizeImage(composite_image,composite_info.width,
4302        composite_info.height,composite_image->filter,composite_image->blur,
4303        exception);
4304      composite_image=DestroyImage(composite_image);
4305      if (resize_image == (Image *) NULL)
4306        {
4307          XSetCursorState(display,windows,MagickFalse);
4308          return(MagickFalse);
4309        }
4310      composite_image=resize_image;
4311    }
4312  if (compose == DisplaceCompositeOp)
4313    (void) SetImageArtifact(composite_image,"compose:args",
4314      displacement_geometry);
4315  if (blend != 0.0)
4316    {
4317      CacheView
4318        *image_view;
4319
4320      int
4321        y;
4322
4323      Quantum
4324        opacity;
4325
4326      register int
4327        x;
4328
4329      register Quantum
4330        *q;
4331
4332      /*
4333        Create mattes for blending.
4334      */
4335      (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,exception);
4336      opacity=(Quantum) (ScaleQuantumToChar((Quantum) QuantumRange)-
4337        ((ssize_t) ScaleQuantumToChar((Quantum) QuantumRange)*blend)/100);
4338      if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
4339        return(MagickFalse);
4340      image->matte=MagickTrue;
4341      image_view=AcquireCacheView(image);
4342      for (y=0; y < (int) image->rows; y++)
4343      {
4344        q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4345          exception);
4346        if (q == (Quantum *) NULL)
4347          break;
4348        for (x=0; x < (int) image->columns; x++)
4349        {
4350          SetPixelAlpha(image,opacity,q);
4351          q+=GetPixelChannels(image);
4352        }
4353        if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4354          break;
4355      }
4356      image_view=DestroyCacheView(image_view);
4357    }
4358  /*
4359    Composite image with X Image window.
4360  */
4361  (void) CompositeImage(image,compose,composite_image,composite_info.x,
4362    composite_info.y,exception);
4363  composite_image=DestroyImage(composite_image);
4364  XSetCursorState(display,windows,MagickFalse);
4365  /*
4366    Update image configuration.
4367  */
4368  XConfigureImageColormap(display,resource_info,windows,image);
4369  (void) XConfigureImage(display,resource_info,windows,image,exception);
4370  return(MagickTrue);
4371}
4372
4373/*
4374%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4375%                                                                             %
4376%                                                                             %
4377%                                                                             %
4378+   X C o n f i g u r e I m a g e                                             %
4379%                                                                             %
4380%                                                                             %
4381%                                                                             %
4382%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4383%
4384%  XConfigureImage() creates a new X image.  It also notifies the window
4385%  manager of the new image size and configures the transient widows.
4386%
4387%  The format of the XConfigureImage method is:
4388%
4389%      MagickBooleanType XConfigureImage(Display *display,
4390%        XResourceInfo *resource_info,XWindows *windows,Image *image,
4391%        ExceptionInfo *exception)
4392%
4393%  A description of each parameter follows:
4394%
4395%    o display: Specifies a connection to an X server; returned from
4396%      XOpenDisplay.
4397%
4398%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4399%
4400%    o windows: Specifies a pointer to a XWindows structure.
4401%
4402%    o image: the image.
4403%
4404%    o exception: return any errors or warnings in this structure.
4405%
4406%    o exception: return any errors or warnings in this structure.
4407%
4408*/
4409static MagickBooleanType XConfigureImage(Display *display,
4410  XResourceInfo *resource_info,XWindows *windows,Image *image,
4411  ExceptionInfo *exception)
4412{
4413  char
4414    geometry[MaxTextExtent];
4415
4416  MagickStatusType
4417    status;
4418
4419  size_t
4420    mask,
4421    height,
4422    width;
4423
4424  ssize_t
4425    x,
4426    y;
4427
4428  XSizeHints
4429    *size_hints;
4430
4431  XWindowChanges
4432    window_changes;
4433
4434  /*
4435    Dismiss if window dimensions are zero.
4436  */
4437  width=(unsigned int) windows->image.window_changes.width;
4438  height=(unsigned int) windows->image.window_changes.height;
4439  if (image->debug != MagickFalse)
4440    (void) LogMagickEvent(X11Event,GetMagickModule(),
4441      "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4442      windows->image.ximage->height,(double) width,(double) height);
4443  if ((width*height) == 0)
4444    return(MagickTrue);
4445  x=0;
4446  y=0;
4447  /*
4448    Resize image to fit Image window dimensions.
4449  */
4450  XSetCursorState(display,windows,MagickTrue);
4451  (void) XFlush(display);
4452  if (((int) width != windows->image.ximage->width) ||
4453      ((int) height != windows->image.ximage->height))
4454    image->taint=MagickTrue;
4455  windows->magnify.x=(int)
4456    width*windows->magnify.x/windows->image.ximage->width;
4457  windows->magnify.y=(int)
4458    height*windows->magnify.y/windows->image.ximage->height;
4459  windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4460  windows->image.y=(int)
4461    (height*windows->image.y/windows->image.ximage->height);
4462  status=XMakeImage(display,resource_info,&windows->image,image,
4463    (unsigned int) width,(unsigned int) height,exception);
4464  if (status == MagickFalse)
4465    XNoticeWidget(display,windows,"Unable to configure X image:",
4466      windows->image.name);
4467  /*
4468    Notify window manager of the new configuration.
4469  */
4470  if (resource_info->image_geometry != (char *) NULL)
4471    (void) FormatLocaleString(geometry,MaxTextExtent,"%s>!",
4472      resource_info->image_geometry);
4473  else
4474    (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
4475      XDisplayWidth(display,windows->image.screen),
4476      XDisplayHeight(display,windows->image.screen));
4477  (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4478  window_changes.width=(int) width;
4479  if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4480    window_changes.width=XDisplayWidth(display,windows->image.screen);
4481  window_changes.height=(int) height;
4482  if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4483    window_changes.height=XDisplayHeight(display,windows->image.screen);
4484  mask=(size_t) (CWWidth | CWHeight);
4485  if (resource_info->backdrop)
4486    {
4487      mask|=CWX | CWY;
4488      window_changes.x=(int)
4489        ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4490      window_changes.y=(int)
4491        ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4492    }
4493  (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4494    (unsigned int) mask,&window_changes);
4495  (void) XClearWindow(display,windows->image.id);
4496  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4497  /*
4498    Update Magnify window configuration.
4499  */
4500  if (windows->magnify.mapped != MagickFalse)
4501    XMakeMagnifyImage(display,windows);
4502  windows->pan.crop_geometry=windows->image.crop_geometry;
4503  XBestIconSize(display,&windows->pan,image);
4504  while (((windows->pan.width << 1) < MaxIconSize) &&
4505         ((windows->pan.height << 1) < MaxIconSize))
4506  {
4507    windows->pan.width<<=1;
4508    windows->pan.height<<=1;
4509  }
4510  if (windows->pan.geometry != (char *) NULL)
4511    (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4512      &windows->pan.width,&windows->pan.height);
4513  window_changes.width=(int) windows->pan.width;
4514  window_changes.height=(int) windows->pan.height;
4515  size_hints=XAllocSizeHints();
4516  if (size_hints != (XSizeHints *) NULL)
4517    {
4518      /*
4519        Set new size hints.
4520      */
4521      size_hints->flags=PSize | PMinSize | PMaxSize;
4522      size_hints->width=window_changes.width;
4523      size_hints->height=window_changes.height;
4524      size_hints->min_width=size_hints->width;
4525      size_hints->min_height=size_hints->height;
4526      size_hints->max_width=size_hints->width;
4527      size_hints->max_height=size_hints->height;
4528      (void) XSetNormalHints(display,windows->pan.id,size_hints);
4529      (void) XFree((void *) size_hints);
4530    }
4531  (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4532    (unsigned int) (CWWidth | CWHeight),&window_changes);
4533  /*
4534    Update icon window configuration.
4535  */
4536  windows->icon.crop_geometry=windows->image.crop_geometry;
4537  XBestIconSize(display,&windows->icon,image);
4538  window_changes.width=(int) windows->icon.width;
4539  window_changes.height=(int) windows->icon.height;
4540  (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4541    (unsigned int) (CWWidth | CWHeight),&window_changes);
4542  XSetCursorState(display,windows,MagickFalse);
4543  return(status != 0 ? MagickTrue : MagickFalse);
4544}
4545
4546/*
4547%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4548%                                                                             %
4549%                                                                             %
4550%                                                                             %
4551+   X C r o p I m a g e                                                       %
4552%                                                                             %
4553%                                                                             %
4554%                                                                             %
4555%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4556%
4557%  XCropImage() allows the user to select a region of the image and crop, copy,
4558%  or cut it.  For copy or cut, the image can subsequently be composited onto
4559%  the image with XPasteImage.
4560%
4561%  The format of the XCropImage method is:
4562%
4563%      MagickBooleanType XCropImage(Display *display,
4564%        XResourceInfo *resource_info,XWindows *windows,Image *image,
4565%        const ClipboardMode mode,ExceptionInfo *exception)
4566%
4567%  A description of each parameter follows:
4568%
4569%    o display: Specifies a connection to an X server; returned from
4570%      XOpenDisplay.
4571%
4572%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4573%
4574%    o windows: Specifies a pointer to a XWindows structure.
4575%
4576%    o image: the image; returned from ReadImage.
4577%
4578%    o mode: This unsigned value specified whether the image should be
4579%      cropped, copied, or cut.
4580%
4581%    o exception: return any errors or warnings in this structure.
4582%
4583*/
4584static MagickBooleanType XCropImage(Display *display,
4585  XResourceInfo *resource_info,XWindows *windows,Image *image,
4586  const ClipboardMode mode,ExceptionInfo *exception)
4587{
4588  static const char
4589    *CropModeMenu[] =
4590    {
4591      "Help",
4592      "Dismiss",
4593      (char *) NULL
4594    },
4595    *RectifyModeMenu[] =
4596    {
4597      "Crop",
4598      "Help",
4599      "Dismiss",
4600      (char *) NULL
4601    };
4602
4603  static const ModeType
4604    CropCommands[] =
4605    {
4606      CropHelpCommand,
4607      CropDismissCommand
4608    },
4609    RectifyCommands[] =
4610    {
4611      RectifyCopyCommand,
4612      RectifyHelpCommand,
4613      RectifyDismissCommand
4614    };
4615
4616  CacheView
4617    *image_view;
4618
4619  char
4620    command[MaxTextExtent],
4621    text[MaxTextExtent];
4622
4623  Cursor
4624    cursor;
4625
4626  int
4627    id,
4628    x,
4629    y;
4630
4631  KeySym
4632    key_symbol;
4633
4634  Image
4635    *crop_image;
4636
4637  MagickRealType
4638    scale_factor;
4639
4640  RectangleInfo
4641    crop_info,
4642    highlight_info;
4643
4644  register Quantum
4645    *q;
4646
4647  unsigned int
4648    height,
4649    width;
4650
4651  size_t
4652    state;
4653
4654  XEvent
4655    event;
4656
4657  /*
4658    Map Command widget.
4659  */
4660  switch (mode)
4661  {
4662    case CopyMode:
4663    {
4664      (void) CloneString(&windows->command.name,"Copy");
4665      break;
4666    }
4667    case CropMode:
4668    {
4669      (void) CloneString(&windows->command.name,"Crop");
4670      break;
4671    }
4672    case CutMode:
4673    {
4674      (void) CloneString(&windows->command.name,"Cut");
4675      break;
4676    }
4677  }
4678  RectifyModeMenu[0]=windows->command.name;
4679  windows->command.data=0;
4680  (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4681  (void) XMapRaised(display,windows->command.id);
4682  XClientMessage(display,windows->image.id,windows->im_protocols,
4683    windows->im_update_widget,CurrentTime);
4684  /*
4685    Track pointer until button 1 is pressed.
4686  */
4687  XQueryPosition(display,windows->image.id,&x,&y);
4688  (void) XSelectInput(display,windows->image.id,
4689    windows->image.attributes.event_mask | PointerMotionMask);
4690  crop_info.x=(ssize_t) windows->image.x+x;
4691  crop_info.y=(ssize_t) windows->image.y+y;
4692  crop_info.width=0;
4693  crop_info.height=0;
4694  cursor=XCreateFontCursor(display,XC_fleur);
4695  state=DefaultState;
4696  do
4697  {
4698    if (windows->info.mapped != MagickFalse)
4699      {
4700        /*
4701          Display pointer position.
4702        */
4703        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4704          (long) crop_info.x,(long) crop_info.y);
4705        XInfoWidget(display,windows,text);
4706      }
4707    /*
4708      Wait for next event.
4709    */
4710    XScreenEvent(display,windows,&event);
4711    if (event.xany.window == windows->command.id)
4712      {
4713        /*
4714          Select a command from the Command widget.
4715        */
4716        id=XCommandWidget(display,windows,CropModeMenu,&event);
4717        if (id < 0)
4718          continue;
4719        switch (CropCommands[id])
4720        {
4721          case CropHelpCommand:
4722          {
4723            switch (mode)
4724            {
4725              case CopyMode:
4726              {
4727                XTextViewWidget(display,resource_info,windows,MagickFalse,
4728                  "Help Viewer - Image Copy",ImageCopyHelp);
4729                break;
4730              }
4731              case CropMode:
4732              {
4733                XTextViewWidget(display,resource_info,windows,MagickFalse,
4734                  "Help Viewer - Image Crop",ImageCropHelp);
4735                break;
4736              }
4737              case CutMode:
4738              {
4739                XTextViewWidget(display,resource_info,windows,MagickFalse,
4740                  "Help Viewer - Image Cut",ImageCutHelp);
4741                break;
4742              }
4743            }
4744            break;
4745          }
4746          case CropDismissCommand:
4747          {
4748            /*
4749              Prematurely exit.
4750            */
4751            state|=EscapeState;
4752            state|=ExitState;
4753            break;
4754          }
4755          default:
4756            break;
4757        }
4758        continue;
4759      }
4760    switch (event.type)
4761    {
4762      case ButtonPress:
4763      {
4764        if (event.xbutton.button != Button1)
4765          break;
4766        if (event.xbutton.window != windows->image.id)
4767          break;
4768        /*
4769          Note first corner of cropping rectangle-- exit loop.
4770        */
4771        (void) XCheckDefineCursor(display,windows->image.id,cursor);
4772        crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4773        crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4774        state|=ExitState;
4775        break;
4776      }
4777      case ButtonRelease:
4778        break;
4779      case Expose:
4780        break;
4781      case KeyPress:
4782      {
4783        if (event.xkey.window != windows->image.id)
4784          break;
4785        /*
4786          Respond to a user key press.
4787        */
4788        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4789          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4790        switch ((int) key_symbol)
4791        {
4792          case XK_Escape:
4793          case XK_F20:
4794          {
4795            /*
4796              Prematurely exit.
4797            */
4798            state|=EscapeState;
4799            state|=ExitState;
4800            break;
4801          }
4802          case XK_F1:
4803          case XK_Help:
4804          {
4805            switch (mode)
4806            {
4807              case CopyMode:
4808              {
4809                XTextViewWidget(display,resource_info,windows,MagickFalse,
4810                  "Help Viewer - Image Copy",ImageCopyHelp);
4811                break;
4812              }
4813              case CropMode:
4814              {
4815                XTextViewWidget(display,resource_info,windows,MagickFalse,
4816                  "Help Viewer - Image Crop",ImageCropHelp);
4817                break;
4818              }
4819              case CutMode:
4820              {
4821                XTextViewWidget(display,resource_info,windows,MagickFalse,
4822                  "Help Viewer - Image Cut",ImageCutHelp);
4823                break;
4824              }
4825            }
4826            break;
4827          }
4828          default:
4829          {
4830            (void) XBell(display,0);
4831            break;
4832          }
4833        }
4834        break;
4835      }
4836      case MotionNotify:
4837      {
4838        if (event.xmotion.window != windows->image.id)
4839          break;
4840        /*
4841          Map and unmap Info widget as text cursor crosses its boundaries.
4842        */
4843        x=event.xmotion.x;
4844        y=event.xmotion.y;
4845        if (windows->info.mapped != MagickFalse)
4846          {
4847            if ((x < (int) (windows->info.x+windows->info.width)) &&
4848                (y < (int) (windows->info.y+windows->info.height)))
4849              (void) XWithdrawWindow(display,windows->info.id,
4850                windows->info.screen);
4851          }
4852        else
4853          if ((x > (int) (windows->info.x+windows->info.width)) ||
4854              (y > (int) (windows->info.y+windows->info.height)))
4855            (void) XMapWindow(display,windows->info.id);
4856        crop_info.x=(ssize_t) windows->image.x+x;
4857        crop_info.y=(ssize_t) windows->image.y+y;
4858        break;
4859      }
4860      default:
4861        break;
4862    }
4863  } while ((state & ExitState) == 0);
4864  (void) XSelectInput(display,windows->image.id,
4865    windows->image.attributes.event_mask);
4866  if ((state & EscapeState) != 0)
4867    {
4868      /*
4869        User want to exit without cropping.
4870      */
4871      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4872      (void) XFreeCursor(display,cursor);
4873      return(MagickTrue);
4874    }
4875  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4876  do
4877  {
4878    /*
4879      Size rectangle as pointer moves until the mouse button is released.
4880    */
4881    x=(int) crop_info.x;
4882    y=(int) crop_info.y;
4883    crop_info.width=0;
4884    crop_info.height=0;
4885    state=DefaultState;
4886    do
4887    {
4888      highlight_info=crop_info;
4889      highlight_info.x=crop_info.x-windows->image.x;
4890      highlight_info.y=crop_info.y-windows->image.y;
4891      if ((highlight_info.width > 3) && (highlight_info.height > 3))
4892        {
4893          /*
4894            Display info and draw cropping rectangle.
4895          */
4896          if (windows->info.mapped == MagickFalse)
4897            (void) XMapWindow(display,windows->info.id);
4898          (void) FormatLocaleString(text,MaxTextExtent,
4899            " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4900            crop_info.height,(double) crop_info.x,(double) crop_info.y);
4901          XInfoWidget(display,windows,text);
4902          XHighlightRectangle(display,windows->image.id,
4903            windows->image.highlight_context,&highlight_info);
4904        }
4905      else
4906        if (windows->info.mapped != MagickFalse)
4907          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4908      /*
4909        Wait for next event.
4910      */
4911      XScreenEvent(display,windows,&event);
4912      if ((highlight_info.width > 3) && (highlight_info.height > 3))
4913        XHighlightRectangle(display,windows->image.id,
4914          windows->image.highlight_context,&highlight_info);
4915      switch (event.type)
4916      {
4917        case ButtonPress:
4918        {
4919          crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4920          crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4921          break;
4922        }
4923        case ButtonRelease:
4924        {
4925          /*
4926            User has committed to cropping rectangle.
4927          */
4928          crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4929          crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4930          XSetCursorState(display,windows,MagickFalse);
4931          state|=ExitState;
4932          windows->command.data=0;
4933          (void) XCommandWidget(display,windows,RectifyModeMenu,
4934            (XEvent *) NULL);
4935          break;
4936        }
4937        case Expose:
4938          break;
4939        case MotionNotify:
4940        {
4941          crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4942          crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4943        }
4944        default:
4945          break;
4946      }
4947      if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4948          ((state & ExitState) != 0))
4949        {
4950          /*
4951            Check boundary conditions.
4952          */
4953          if (crop_info.x < 0)
4954            crop_info.x=0;
4955          else
4956            if (crop_info.x > (ssize_t) windows->image.ximage->width)
4957              crop_info.x=(ssize_t) windows->image.ximage->width;
4958          if ((int) crop_info.x < x)
4959            crop_info.width=(unsigned int) (x-crop_info.x);
4960          else
4961            {
4962              crop_info.width=(unsigned int) (crop_info.x-x);
4963              crop_info.x=(ssize_t) x;
4964            }
4965          if (crop_info.y < 0)
4966            crop_info.y=0;
4967          else
4968            if (crop_info.y > (ssize_t) windows->image.ximage->height)
4969              crop_info.y=(ssize_t) windows->image.ximage->height;
4970          if ((int) crop_info.y < y)
4971            crop_info.height=(unsigned int) (y-crop_info.y);
4972          else
4973            {
4974              crop_info.height=(unsigned int) (crop_info.y-y);
4975              crop_info.y=(ssize_t) y;
4976            }
4977        }
4978    } while ((state & ExitState) == 0);
4979    /*
4980      Wait for user to grab a corner of the rectangle or press return.
4981    */
4982    state=DefaultState;
4983    (void) XMapWindow(display,windows->info.id);
4984    do
4985    {
4986      if (windows->info.mapped != MagickFalse)
4987        {
4988          /*
4989            Display pointer position.
4990          */
4991          (void) FormatLocaleString(text,MaxTextExtent,
4992            " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4993            crop_info.height,(double) crop_info.x,(double) crop_info.y);
4994          XInfoWidget(display,windows,text);
4995        }
4996      highlight_info=crop_info;
4997      highlight_info.x=crop_info.x-windows->image.x;
4998      highlight_info.y=crop_info.y-windows->image.y;
4999      if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
5000        {
5001          state|=EscapeState;
5002          state|=ExitState;
5003          break;
5004        }
5005      XHighlightRectangle(display,windows->image.id,
5006        windows->image.highlight_context,&highlight_info);
5007      XScreenEvent(display,windows,&event);
5008      if (event.xany.window == windows->command.id)
5009        {
5010          /*
5011            Select a command from the Command widget.
5012          */
5013          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5014          id=XCommandWidget(display,windows,RectifyModeMenu,&event);
5015          (void) XSetFunction(display,windows->image.highlight_context,
5016            GXinvert);
5017          XHighlightRectangle(display,windows->image.id,
5018            windows->image.highlight_context,&highlight_info);
5019          if (id >= 0)
5020            switch (RectifyCommands[id])
5021            {
5022              case RectifyCopyCommand:
5023              {
5024                state|=ExitState;
5025                break;
5026              }
5027              case RectifyHelpCommand:
5028              {
5029                (void) XSetFunction(display,windows->image.highlight_context,
5030                  GXcopy);
5031                switch (mode)
5032                {
5033                  case CopyMode:
5034                  {
5035                    XTextViewWidget(display,resource_info,windows,MagickFalse,
5036                      "Help Viewer - Image Copy",ImageCopyHelp);
5037                    break;
5038                  }
5039                  case CropMode:
5040                  {
5041                    XTextViewWidget(display,resource_info,windows,MagickFalse,
5042                      "Help Viewer - Image Crop",ImageCropHelp);
5043                    break;
5044                  }
5045                  case CutMode:
5046                  {
5047                    XTextViewWidget(display,resource_info,windows,MagickFalse,
5048                      "Help Viewer - Image Cut",ImageCutHelp);
5049                    break;
5050                  }
5051                }
5052                (void) XSetFunction(display,windows->image.highlight_context,
5053                  GXinvert);
5054                break;
5055              }
5056              case RectifyDismissCommand:
5057              {
5058                /*
5059                  Prematurely exit.
5060                */
5061                state|=EscapeState;
5062                state|=ExitState;
5063                break;
5064              }
5065              default:
5066                break;
5067            }
5068          continue;
5069        }
5070      XHighlightRectangle(display,windows->image.id,
5071        windows->image.highlight_context,&highlight_info);
5072      switch (event.type)
5073      {
5074        case ButtonPress:
5075        {
5076          if (event.xbutton.button != Button1)
5077            break;
5078          if (event.xbutton.window != windows->image.id)
5079            break;
5080          x=windows->image.x+event.xbutton.x;
5081          y=windows->image.y+event.xbutton.y;
5082          if ((x < (int) (crop_info.x+RoiDelta)) &&
5083              (x > (int) (crop_info.x-RoiDelta)) &&
5084              (y < (int) (crop_info.y+RoiDelta)) &&
5085              (y > (int) (crop_info.y-RoiDelta)))
5086            {
5087              crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5088              crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5089              state|=UpdateConfigurationState;
5090              break;
5091            }
5092          if ((x < (int) (crop_info.x+RoiDelta)) &&
5093              (x > (int) (crop_info.x-RoiDelta)) &&
5094              (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5095              (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5096            {
5097              crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5098              state|=UpdateConfigurationState;
5099              break;
5100            }
5101          if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5102              (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5103              (y < (int) (crop_info.y+RoiDelta)) &&
5104              (y > (int) (crop_info.y-RoiDelta)))
5105            {
5106              crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5107              state|=UpdateConfigurationState;
5108              break;
5109            }
5110          if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5111              (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5112              (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5113              (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5114            {
5115              state|=UpdateConfigurationState;
5116              break;
5117            }
5118        }
5119        case ButtonRelease:
5120        {
5121          if (event.xbutton.window == windows->pan.id)
5122            if ((highlight_info.x != crop_info.x-windows->image.x) ||
5123                (highlight_info.y != crop_info.y-windows->image.y))
5124              XHighlightRectangle(display,windows->image.id,
5125                windows->image.highlight_context,&highlight_info);
5126          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5127            event.xbutton.time);
5128          break;
5129        }
5130        case Expose:
5131        {
5132          if (event.xexpose.window == windows->image.id)
5133            if (event.xexpose.count == 0)
5134              {
5135                event.xexpose.x=(int) highlight_info.x;
5136                event.xexpose.y=(int) highlight_info.y;
5137                event.xexpose.width=(int) highlight_info.width;
5138                event.xexpose.height=(int) highlight_info.height;
5139                XRefreshWindow(display,&windows->image,&event);
5140              }
5141          if (event.xexpose.window == windows->info.id)
5142            if (event.xexpose.count == 0)
5143              XInfoWidget(display,windows,text);
5144          break;
5145        }
5146        case KeyPress:
5147        {
5148          if (event.xkey.window != windows->image.id)
5149            break;
5150          /*
5151            Respond to a user key press.
5152          */
5153          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5154            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5155          switch ((int) key_symbol)
5156          {
5157            case XK_Escape:
5158            case XK_F20:
5159              state|=EscapeState;
5160            case XK_Return:
5161            {
5162              state|=ExitState;
5163              break;
5164            }
5165            case XK_Home:
5166            case XK_KP_Home:
5167            {
5168              crop_info.x=(ssize_t) (windows->image.width/2L-
5169                crop_info.width/2L);
5170              crop_info.y=(ssize_t) (windows->image.height/2L-
5171                crop_info.height/2L);
5172              break;
5173            }
5174            case XK_Left:
5175            case XK_KP_Left:
5176            {
5177              crop_info.x--;
5178              break;
5179            }
5180            case XK_Up:
5181            case XK_KP_Up:
5182            case XK_Next:
5183            {
5184              crop_info.y--;
5185              break;
5186            }
5187            case XK_Right:
5188            case XK_KP_Right:
5189            {
5190              crop_info.x++;
5191              break;
5192            }
5193            case XK_Prior:
5194            case XK_Down:
5195            case XK_KP_Down:
5196            {
5197              crop_info.y++;
5198              break;
5199            }
5200            case XK_F1:
5201            case XK_Help:
5202            {
5203              (void) XSetFunction(display,windows->image.highlight_context,
5204                GXcopy);
5205              switch (mode)
5206              {
5207                case CopyMode:
5208                {
5209                  XTextViewWidget(display,resource_info,windows,MagickFalse,
5210                    "Help Viewer - Image Copy",ImageCopyHelp);
5211                  break;
5212                }
5213                case CropMode:
5214                {
5215                  XTextViewWidget(display,resource_info,windows,MagickFalse,
5216                    "Help Viewer - Image Cropg",ImageCropHelp);
5217                  break;
5218                }
5219                case CutMode:
5220                {
5221                  XTextViewWidget(display,resource_info,windows,MagickFalse,
5222                    "Help Viewer - Image Cutg",ImageCutHelp);
5223                  break;
5224                }
5225              }
5226              (void) XSetFunction(display,windows->image.highlight_context,
5227                GXinvert);
5228              break;
5229            }
5230            default:
5231            {
5232              (void) XBell(display,0);
5233              break;
5234            }
5235          }
5236          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5237            event.xkey.time);
5238          break;
5239        }
5240        case KeyRelease:
5241          break;
5242        case MotionNotify:
5243        {
5244          if (event.xmotion.window != windows->image.id)
5245            break;
5246          /*
5247            Map and unmap Info widget as text cursor crosses its boundaries.
5248          */
5249          x=event.xmotion.x;
5250          y=event.xmotion.y;
5251          if (windows->info.mapped != MagickFalse)
5252            {
5253              if ((x < (int) (windows->info.x+windows->info.width)) &&
5254                  (y < (int) (windows->info.y+windows->info.height)))
5255                (void) XWithdrawWindow(display,windows->info.id,
5256                  windows->info.screen);
5257            }
5258          else
5259            if ((x > (int) (windows->info.x+windows->info.width)) ||
5260                (y > (int) (windows->info.y+windows->info.height)))
5261              (void) XMapWindow(display,windows->info.id);
5262          crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5263          crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5264          break;
5265        }
5266        case SelectionRequest:
5267        {
5268          XSelectionEvent
5269            notify;
5270
5271          XSelectionRequestEvent
5272            *request;
5273
5274          /*
5275            Set primary selection.
5276          */
5277          (void) FormatLocaleString(text,MaxTextExtent,
5278            "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5279            crop_info.height,(double) crop_info.x,(double) crop_info.y);
5280          request=(&(event.xselectionrequest));
5281          (void) XChangeProperty(request->display,request->requestor,
5282            request->property,request->target,8,PropModeReplace,
5283            (unsigned char *) text,(int) strlen(text));
5284          notify.type=SelectionNotify;
5285          notify.display=request->display;
5286          notify.requestor=request->requestor;
5287          notify.selection=request->selection;
5288          notify.target=request->target;
5289          notify.time=request->time;
5290          if (request->property == None)
5291            notify.property=request->target;
5292          else
5293            notify.property=request->property;
5294          (void) XSendEvent(request->display,request->requestor,False,0,
5295            (XEvent *) &notify);
5296        }
5297        default:
5298          break;
5299      }
5300      if ((state & UpdateConfigurationState) != 0)
5301        {
5302          (void) XPutBackEvent(display,&event);
5303          (void) XCheckDefineCursor(display,windows->image.id,cursor);
5304          break;
5305        }
5306    } while ((state & ExitState) == 0);
5307  } while ((state & ExitState) == 0);
5308  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5309  XSetCursorState(display,windows,MagickFalse);
5310  if ((state & EscapeState) != 0)
5311    return(MagickTrue);
5312  if (mode == CropMode)
5313    if (((int) crop_info.width != windows->image.ximage->width) ||
5314        ((int) crop_info.height != windows->image.ximage->height))
5315      {
5316        /*
5317          Reconfigure Image window as defined by cropping rectangle.
5318        */
5319        XSetCropGeometry(display,windows,&crop_info,image);
5320        windows->image.window_changes.width=(int) crop_info.width;
5321        windows->image.window_changes.height=(int) crop_info.height;
5322        (void) XConfigureImage(display,resource_info,windows,image,exception);
5323        return(MagickTrue);
5324      }
5325  /*
5326    Copy image before applying image transforms.
5327  */
5328  XSetCursorState(display,windows,MagickTrue);
5329  XCheckRefreshWindows(display,windows);
5330  width=(unsigned int) image->columns;
5331  height=(unsigned int) image->rows;
5332  x=0;
5333  y=0;
5334  if (windows->image.crop_geometry != (char *) NULL)
5335    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5336  scale_factor=(MagickRealType) width/windows->image.ximage->width;
5337  crop_info.x+=x;
5338  crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5339  crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5340  scale_factor=(MagickRealType) height/windows->image.ximage->height;
5341  crop_info.y+=y;
5342  crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5343  crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5344  crop_image=CropImage(image,&crop_info,exception);
5345  XSetCursorState(display,windows,MagickFalse);
5346  if (crop_image == (Image *) NULL)
5347    return(MagickFalse);
5348  if (resource_info->copy_image != (Image *) NULL)
5349    resource_info->copy_image=DestroyImage(resource_info->copy_image);
5350  resource_info->copy_image=crop_image;
5351  if (mode == CopyMode)
5352    {
5353      (void) XConfigureImage(display,resource_info,windows,image,exception);
5354      return(MagickTrue);
5355    }
5356  /*
5357    Cut image.
5358  */
5359  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
5360    return(MagickFalse);
5361  image->matte=MagickTrue;
5362  image_view=AcquireCacheView(image);
5363  for (y=0; y < (int) crop_info.height; y++)
5364  {
5365    q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5366      crop_info.width,1,exception);
5367    if (q == (Quantum *) NULL)
5368      break;
5369    for (x=0; x < (int) crop_info.width; x++)
5370    {
5371      SetPixelAlpha(image,TransparentAlpha,q);
5372      q+=GetPixelChannels(image);
5373    }
5374    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5375      break;
5376  }
5377  image_view=DestroyCacheView(image_view);
5378  /*
5379    Update image configuration.
5380  */
5381  XConfigureImageColormap(display,resource_info,windows,image);
5382  (void) XConfigureImage(display,resource_info,windows,image,exception);
5383  return(MagickTrue);
5384}
5385
5386/*
5387%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5388%                                                                             %
5389%                                                                             %
5390%                                                                             %
5391+   X D r a w I m a g e                                                       %
5392%                                                                             %
5393%                                                                             %
5394%                                                                             %
5395%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5396%
5397%  XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5398%  the image.
5399%
5400%  The format of the XDrawEditImage method is:
5401%
5402%      MagickBooleanType XDrawEditImage(Display *display,
5403%        XResourceInfo *resource_info,XWindows *windows,Image **image,
5404%        ExceptionInfo *exception)
5405%
5406%  A description of each parameter follows:
5407%
5408%    o display: Specifies a connection to an X server; returned from
5409%      XOpenDisplay.
5410%
5411%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5412%
5413%    o windows: Specifies a pointer to a XWindows structure.
5414%
5415%    o image: the image.
5416%
5417%    o exception: return any errors or warnings in this structure.
5418%
5419*/
5420static MagickBooleanType XDrawEditImage(Display *display,
5421  XResourceInfo *resource_info,XWindows *windows,Image **image,
5422  ExceptionInfo *exception)
5423{
5424  static const char
5425    *DrawMenu[] =
5426    {
5427      "Element",
5428      "Color",
5429      "Stipple",
5430      "Width",
5431      "Undo",
5432      "Help",
5433      "Dismiss",
5434      (char *) NULL
5435    };
5436
5437  static ElementType
5438    element = PointElement;
5439
5440  static const ModeType
5441    DrawCommands[] =
5442    {
5443      DrawElementCommand,
5444      DrawColorCommand,
5445      DrawStippleCommand,
5446      DrawWidthCommand,
5447      DrawUndoCommand,
5448      DrawHelpCommand,
5449      DrawDismissCommand
5450    };
5451
5452  static Pixmap
5453    stipple = (Pixmap) NULL;
5454
5455  static unsigned int
5456    pen_id = 0,
5457    line_width = 1;
5458
5459  char
5460    command[MaxTextExtent],
5461    text[MaxTextExtent];
5462
5463  Cursor
5464    cursor;
5465
5466  int
5467    entry,
5468    id,
5469    number_coordinates,
5470    x,
5471    y;
5472
5473  MagickRealType
5474    degrees;
5475
5476  MagickStatusType
5477    status;
5478
5479  RectangleInfo
5480    rectangle_info;
5481
5482  register int
5483    i;
5484
5485  unsigned int
5486    distance,
5487    height,
5488    max_coordinates,
5489    width;
5490
5491  size_t
5492    state;
5493
5494  Window
5495    root_window;
5496
5497  XDrawInfo
5498    draw_info;
5499
5500  XEvent
5501    event;
5502
5503  XPoint
5504    *coordinate_info;
5505
5506  XSegment
5507    line_info;
5508
5509  /*
5510    Allocate polygon info.
5511  */
5512  max_coordinates=2048;
5513  coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5514    sizeof(*coordinate_info));
5515  if (coordinate_info == (XPoint *) NULL)
5516    {
5517      (void) ThrowMagickException(exception,GetMagickModule(),
5518        ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5519      return(MagickFalse);
5520    }
5521  /*
5522    Map Command widget.
5523  */
5524  (void) CloneString(&windows->command.name,"Draw");
5525  windows->command.data=4;
5526  (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5527  (void) XMapRaised(display,windows->command.id);
5528  XClientMessage(display,windows->image.id,windows->im_protocols,
5529    windows->im_update_widget,CurrentTime);
5530  /*
5531    Wait for first button press.
5532  */
5533  root_window=XRootWindow(display,XDefaultScreen(display));
5534  draw_info.stencil=OpaqueStencil;
5535  status=MagickTrue;
5536  cursor=XCreateFontCursor(display,XC_tcross);
5537  for ( ; ; )
5538  {
5539    XQueryPosition(display,windows->image.id,&x,&y);
5540    (void) XSelectInput(display,windows->image.id,
5541      windows->image.attributes.event_mask | PointerMotionMask);
5542    (void) XCheckDefineCursor(display,windows->image.id,cursor);
5543    state=DefaultState;
5544    do
5545    {
5546      if (windows->info.mapped != MagickFalse)
5547        {
5548          /*
5549            Display pointer position.
5550          */
5551          (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
5552            x+windows->image.x,y+windows->image.y);
5553          XInfoWidget(display,windows,text);
5554        }
5555      /*
5556        Wait for next event.
5557      */
5558      XScreenEvent(display,windows,&event);
5559      if (event.xany.window == windows->command.id)
5560        {
5561          /*
5562            Select a command from the Command widget.
5563          */
5564          id=XCommandWidget(display,windows,DrawMenu,&event);
5565          if (id < 0)
5566            continue;
5567          switch (DrawCommands[id])
5568          {
5569            case DrawElementCommand:
5570            {
5571              static const char
5572                *Elements[] =
5573                {
5574                  "point",
5575                  "line",
5576                  "rectangle",
5577                  "fill rectangle",
5578                  "circle",
5579                  "fill circle",
5580                  "ellipse",
5581                  "fill ellipse",
5582                  "polygon",
5583                  "fill polygon",
5584                  (char *) NULL,
5585                };
5586
5587              /*
5588                Select a command from the pop-up menu.
5589              */
5590              element=(ElementType) (XMenuWidget(display,windows,
5591                DrawMenu[id],Elements,command)+1);
5592              break;
5593            }
5594            case DrawColorCommand:
5595            {
5596              const char
5597                *ColorMenu[MaxNumberPens+1];
5598
5599              int
5600                pen_number;
5601
5602              MagickBooleanType
5603                transparent;
5604
5605              XColor
5606                color;
5607
5608              /*
5609                Initialize menu selections.
5610              */
5611              for (i=0; i < (int) (MaxNumberPens-2); i++)
5612                ColorMenu[i]=resource_info->pen_colors[i];
5613              ColorMenu[MaxNumberPens-2]="transparent";
5614              ColorMenu[MaxNumberPens-1]="Browser...";
5615              ColorMenu[MaxNumberPens]=(char *) NULL;
5616              /*
5617                Select a pen color from the pop-up menu.
5618              */
5619              pen_number=XMenuWidget(display,windows,DrawMenu[id],
5620                (const char **) ColorMenu,command);
5621              if (pen_number < 0)
5622                break;
5623              transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5624                MagickFalse;
5625              if (transparent != MagickFalse)
5626                {
5627                  draw_info.stencil=TransparentStencil;
5628                  break;
5629                }
5630              if (pen_number == (MaxNumberPens-1))
5631                {
5632                  static char
5633                    color_name[MaxTextExtent] = "gray";
5634
5635                  /*
5636                    Select a pen color from a dialog.
5637                  */
5638                  resource_info->pen_colors[pen_number]=color_name;
5639                  XColorBrowserWidget(display,windows,"Select",color_name);
5640                  if (*color_name == '\0')
5641                    break;
5642                }
5643              /*
5644                Set pen color.
5645              */
5646              (void) XParseColor(display,windows->map_info->colormap,
5647                resource_info->pen_colors[pen_number],&color);
5648              XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5649                (unsigned int) MaxColors,&color);
5650              windows->pixel_info->pen_colors[pen_number]=color;
5651              pen_id=(unsigned int) pen_number;
5652              draw_info.stencil=OpaqueStencil;
5653              break;
5654            }
5655            case DrawStippleCommand:
5656            {
5657              Image
5658                *stipple_image;
5659
5660              ImageInfo
5661                *image_info;
5662
5663              int
5664                status;
5665
5666              static char
5667                filename[MaxTextExtent] = "\0";
5668
5669              static const char
5670                *StipplesMenu[] =
5671                {
5672                  "Brick",
5673                  "Diagonal",
5674                  "Scales",
5675                  "Vertical",
5676                  "Wavy",
5677                  "Translucent",
5678                  "Opaque",
5679                  (char *) NULL,
5680                  (char *) NULL,
5681                };
5682
5683              /*
5684                Select a command from the pop-up menu.
5685              */
5686              StipplesMenu[7]="Open...";
5687              entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5688                command);
5689              if (entry < 0)
5690                break;
5691              if (stipple != (Pixmap) NULL)
5692                (void) XFreePixmap(display,stipple);
5693              stipple=(Pixmap) NULL;
5694              if (entry != 7)
5695                {
5696                  switch (entry)
5697                  {
5698                    case 0:
5699                    {
5700                      stipple=XCreateBitmapFromData(display,root_window,
5701                        (char *) BricksBitmap,BricksWidth,BricksHeight);
5702                      break;
5703                    }
5704                    case 1:
5705                    {
5706                      stipple=XCreateBitmapFromData(display,root_window,
5707                        (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5708                      break;
5709                    }
5710                    case 2:
5711                    {
5712                      stipple=XCreateBitmapFromData(display,root_window,
5713                        (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5714                      break;
5715                    }
5716                    case 3:
5717                    {
5718                      stipple=XCreateBitmapFromData(display,root_window,
5719                        (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5720                      break;
5721                    }
5722                    case 4:
5723                    {
5724                      stipple=XCreateBitmapFromData(display,root_window,
5725                        (char *) WavyBitmap,WavyWidth,WavyHeight);
5726                      break;
5727                    }
5728                    case 5:
5729                    {
5730                      stipple=XCreateBitmapFromData(display,root_window,
5731                        (char *) HighlightBitmap,HighlightWidth,
5732                        HighlightHeight);
5733                      break;
5734                    }
5735                    case 6:
5736                    default:
5737                    {
5738                      stipple=XCreateBitmapFromData(display,root_window,
5739                        (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5740                      break;
5741                    }
5742                  }
5743                  break;
5744                }
5745              XFileBrowserWidget(display,windows,"Stipple",filename);
5746              if (*filename == '\0')
5747                break;
5748              /*
5749                Read image.
5750              */
5751              XSetCursorState(display,windows,MagickTrue);
5752              XCheckRefreshWindows(display,windows);
5753              image_info=AcquireImageInfo();
5754              (void) CopyMagickString(image_info->filename,filename,
5755                MaxTextExtent);
5756              stipple_image=ReadImage(image_info,exception);
5757              CatchException(exception);
5758              XSetCursorState(display,windows,MagickFalse);
5759              if (stipple_image == (Image *) NULL)
5760                break;
5761              (void) AcquireUniqueFileResource(filename);
5762              (void) FormatLocaleString(stipple_image->filename,MaxTextExtent,
5763                "xbm:%s",filename);
5764              (void) WriteImage(image_info,stipple_image,exception);
5765              stipple_image=DestroyImage(stipple_image);
5766              image_info=DestroyImageInfo(image_info);
5767              status=XReadBitmapFile(display,root_window,filename,&width,
5768                &height,&stipple,&x,&y);
5769              (void) RelinquishUniqueFileResource(filename);
5770              if ((status != BitmapSuccess) != 0)
5771                XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5772                  filename);
5773              break;
5774            }
5775            case DrawWidthCommand:
5776            {
5777              static char
5778                width[MaxTextExtent] = "0";
5779
5780              static const char
5781                *WidthsMenu[] =
5782                {
5783                  "1",
5784                  "2",
5785                  "4",
5786                  "8",
5787                  "16",
5788                  "Dialog...",
5789                  (char *) NULL,
5790                };
5791
5792              /*
5793                Select a command from the pop-up menu.
5794              */
5795              entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5796                command);
5797              if (entry < 0)
5798                break;
5799              if (entry != 5)
5800                {
5801                  line_width=(unsigned int) StringToUnsignedLong(
5802                    WidthsMenu[entry]);
5803                  break;
5804                }
5805              (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5806                width);
5807              if (*width == '\0')
5808                break;
5809              line_width=(unsigned int) StringToUnsignedLong(width);
5810              break;
5811            }
5812            case DrawUndoCommand:
5813            {
5814              (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5815                image,exception);
5816              break;
5817            }
5818            case DrawHelpCommand:
5819            {
5820              XTextViewWidget(display,resource_info,windows,MagickFalse,
5821                "Help Viewer - Image Rotation",ImageDrawHelp);
5822              (void) XCheckDefineCursor(display,windows->image.id,cursor);
5823              break;
5824            }
5825            case DrawDismissCommand:
5826            {
5827              /*
5828                Prematurely exit.
5829              */
5830              state|=EscapeState;
5831              state|=ExitState;
5832              break;
5833            }
5834            default:
5835              break;
5836          }
5837          (void) XCheckDefineCursor(display,windows->image.id,cursor);
5838          continue;
5839        }
5840      switch (event.type)
5841      {
5842        case ButtonPress:
5843        {
5844          if (event.xbutton.button != Button1)
5845            break;
5846          if (event.xbutton.window != windows->image.id)
5847            break;
5848          /*
5849            exit loop.
5850          */
5851          x=event.xbutton.x;
5852          y=event.xbutton.y;
5853          state|=ExitState;
5854          break;
5855        }
5856        case ButtonRelease:
5857          break;
5858        case Expose:
5859          break;
5860        case KeyPress:
5861        {
5862          KeySym
5863            key_symbol;
5864
5865          if (event.xkey.window != windows->image.id)
5866            break;
5867          /*
5868            Respond to a user key press.
5869          */
5870          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5871            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5872          switch ((int) key_symbol)
5873          {
5874            case XK_Escape:
5875            case XK_F20:
5876            {
5877              /*
5878                Prematurely exit.
5879              */
5880              state|=EscapeState;
5881              state|=ExitState;
5882              break;
5883            }
5884            case XK_F1:
5885            case XK_Help:
5886            {
5887              XTextViewWidget(display,resource_info,windows,MagickFalse,
5888                "Help Viewer - Image Rotation",ImageDrawHelp);
5889              break;
5890            }
5891            default:
5892            {
5893              (void) XBell(display,0);
5894              break;
5895            }
5896          }
5897          break;
5898        }
5899        case MotionNotify:
5900        {
5901          /*
5902            Map and unmap Info widget as text cursor crosses its boundaries.
5903          */
5904          x=event.xmotion.x;
5905          y=event.xmotion.y;
5906          if (windows->info.mapped != MagickFalse)
5907            {
5908              if ((x < (int) (windows->info.x+windows->info.width)) &&
5909                  (y < (int) (windows->info.y+windows->info.height)))
5910                (void) XWithdrawWindow(display,windows->info.id,
5911                  windows->info.screen);
5912            }
5913          else
5914            if ((x > (int) (windows->info.x+windows->info.width)) ||
5915                (y > (int) (windows->info.y+windows->info.height)))
5916              (void) XMapWindow(display,windows->info.id);
5917          break;
5918        }
5919      }
5920    } while ((state & ExitState) == 0);
5921    (void) XSelectInput(display,windows->image.id,
5922      windows->image.attributes.event_mask);
5923    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5924    if ((state & EscapeState) != 0)
5925      break;
5926    /*
5927      Draw element as pointer moves until the button is released.
5928    */
5929    distance=0;
5930    degrees=0.0;
5931    line_info.x1=x;
5932    line_info.y1=y;
5933    line_info.x2=x;
5934    line_info.y2=y;
5935    rectangle_info.x=(ssize_t) x;
5936    rectangle_info.y=(ssize_t) y;
5937    rectangle_info.width=0;
5938    rectangle_info.height=0;
5939    number_coordinates=1;
5940    coordinate_info->x=x;
5941    coordinate_info->y=y;
5942    (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5943    state=DefaultState;
5944    do
5945    {
5946      switch (element)
5947      {
5948        case PointElement:
5949        default:
5950        {
5951          if (number_coordinates > 1)
5952            {
5953              (void) XDrawLines(display,windows->image.id,
5954                windows->image.highlight_context,coordinate_info,
5955                number_coordinates,CoordModeOrigin);
5956              (void) FormatLocaleString(text,MaxTextExtent," %+d%+d",
5957                coordinate_info[number_coordinates-1].x,
5958                coordinate_info[number_coordinates-1].y);
5959              XInfoWidget(display,windows,text);
5960            }
5961          break;
5962        }
5963        case LineElement:
5964        {
5965          if (distance > 9)
5966            {
5967              /*
5968                Display angle of the line.
5969              */
5970              degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5971                line_info.y1),(double) (line_info.x2-line_info.x1)));
5972              (void) FormatLocaleString(text,MaxTextExtent," %g",
5973                (double) degrees);
5974              XInfoWidget(display,windows,text);
5975              XHighlightLine(display,windows->image.id,
5976                windows->image.highlight_context,&line_info);
5977            }
5978          else
5979            if (windows->info.mapped != MagickFalse)
5980              (void) XWithdrawWindow(display,windows->info.id,
5981                windows->info.screen);
5982          break;
5983        }
5984        case RectangleElement:
5985        case FillRectangleElement:
5986        {
5987          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5988            {
5989              /*
5990                Display info and draw drawing rectangle.
5991              */
5992              (void) FormatLocaleString(text,MaxTextExtent,
5993                " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5994                (double) rectangle_info.height,(double) rectangle_info.x,
5995                (double) rectangle_info.y);
5996              XInfoWidget(display,windows,text);
5997              XHighlightRectangle(display,windows->image.id,
5998                windows->image.highlight_context,&rectangle_info);
5999            }
6000          else
6001            if (windows->info.mapped != MagickFalse)
6002              (void) XWithdrawWindow(display,windows->info.id,
6003                windows->info.screen);
6004          break;
6005        }
6006        case CircleElement:
6007        case FillCircleElement:
6008        case EllipseElement:
6009        case FillEllipseElement:
6010        {
6011          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6012            {
6013              /*
6014                Display info and draw drawing rectangle.
6015              */
6016              (void) FormatLocaleString(text,MaxTextExtent,
6017                " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
6018                (double) rectangle_info.height,(double) rectangle_info.x,
6019                (double) rectangle_info.y);
6020              XInfoWidget(display,windows,text);
6021              XHighlightEllipse(display,windows->image.id,
6022                windows->image.highlight_context,&rectangle_info);
6023            }
6024          else
6025            if (windows->info.mapped != MagickFalse)
6026              (void) XWithdrawWindow(display,windows->info.id,
6027                windows->info.screen);
6028          break;
6029        }
6030        case PolygonElement:
6031        case FillPolygonElement:
6032        {
6033          if (number_coordinates > 1)
6034            (void) XDrawLines(display,windows->image.id,
6035              windows->image.highlight_context,coordinate_info,
6036              number_coordinates,CoordModeOrigin);
6037          if (distance > 9)
6038            {
6039              /*
6040                Display angle of the line.
6041              */
6042              degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
6043                line_info.y1),(double) (line_info.x2-line_info.x1)));
6044              (void) FormatLocaleString(text,MaxTextExtent," %g",
6045                (double) degrees);
6046              XInfoWidget(display,windows,text);
6047              XHighlightLine(display,windows->image.id,
6048                windows->image.highlight_context,&line_info);
6049            }
6050          else
6051            if (windows->info.mapped != MagickFalse)
6052              (void) XWithdrawWindow(display,windows->info.id,
6053                windows->info.screen);
6054          break;
6055        }
6056      }
6057      /*
6058        Wait for next event.
6059      */
6060      XScreenEvent(display,windows,&event);
6061      switch (element)
6062      {
6063        case PointElement:
6064        default:
6065        {
6066          if (number_coordinates > 1)
6067            (void) XDrawLines(display,windows->image.id,
6068              windows->image.highlight_context,coordinate_info,
6069              number_coordinates,CoordModeOrigin);
6070          break;
6071        }
6072        case LineElement:
6073        {
6074          if (distance > 9)
6075            XHighlightLine(display,windows->image.id,
6076              windows->image.highlight_context,&line_info);
6077          break;
6078        }
6079        case RectangleElement:
6080        case FillRectangleElement:
6081        {
6082          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6083            XHighlightRectangle(display,windows->image.id,
6084              windows->image.highlight_context,&rectangle_info);
6085          break;
6086        }
6087        case CircleElement:
6088        case FillCircleElement:
6089        case EllipseElement:
6090        case FillEllipseElement:
6091        {
6092          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6093            XHighlightEllipse(display,windows->image.id,
6094              windows->image.highlight_context,&rectangle_info);
6095          break;
6096        }
6097        case PolygonElement:
6098        case FillPolygonElement:
6099        {
6100          if (number_coordinates > 1)
6101            (void) XDrawLines(display,windows->image.id,
6102              windows->image.highlight_context,coordinate_info,
6103              number_coordinates,CoordModeOrigin);
6104          if (distance > 9)
6105            XHighlightLine(display,windows->image.id,
6106              windows->image.highlight_context,&line_info);
6107          break;
6108        }
6109      }
6110      switch (event.type)
6111      {
6112        case ButtonPress:
6113          break;
6114        case ButtonRelease:
6115        {
6116          /*
6117            User has committed to element.
6118          */
6119          line_info.x2=event.xbutton.x;
6120          line_info.y2=event.xbutton.y;
6121          rectangle_info.x=(ssize_t) event.xbutton.x;
6122          rectangle_info.y=(ssize_t) event.xbutton.y;
6123          coordinate_info[number_coordinates].x=event.xbutton.x;
6124          coordinate_info[number_coordinates].y=event.xbutton.y;
6125          if (((element != PolygonElement) &&
6126               (element != FillPolygonElement)) || (distance <= 9))
6127            {
6128              state|=ExitState;
6129              break;
6130            }
6131          number_coordinates++;
6132          if (number_coordinates < (int) max_coordinates)
6133            {
6134              line_info.x1=event.xbutton.x;
6135              line_info.y1=event.xbutton.y;
6136              break;
6137            }
6138          max_coordinates<<=1;
6139          coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6140            max_coordinates,sizeof(*coordinate_info));
6141          if (coordinate_info == (XPoint *) NULL)
6142            (void) ThrowMagickException(exception,GetMagickModule(),
6143              ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6144          break;
6145        }
6146        case Expose:
6147          break;
6148        case MotionNotify:
6149        {
6150          if (event.xmotion.window != windows->image.id)
6151            break;
6152          if (element != PointElement)
6153            {
6154              line_info.x2=event.xmotion.x;
6155              line_info.y2=event.xmotion.y;
6156              rectangle_info.x=(ssize_t) event.xmotion.x;
6157              rectangle_info.y=(ssize_t) event.xmotion.y;
6158              break;
6159            }
6160          coordinate_info[number_coordinates].x=event.xbutton.x;
6161          coordinate_info[number_coordinates].y=event.xbutton.y;
6162          number_coordinates++;
6163          if (number_coordinates < (int) max_coordinates)
6164            break;
6165          max_coordinates<<=1;
6166          coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6167            max_coordinates,sizeof(*coordinate_info));
6168          if (coordinate_info == (XPoint *) NULL)
6169            (void) ThrowMagickException(exception,GetMagickModule(),
6170              ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6171          break;
6172        }
6173        default:
6174          break;
6175      }
6176      /*
6177        Check boundary conditions.
6178      */
6179      if (line_info.x2 < 0)
6180        line_info.x2=0;
6181      else
6182        if (line_info.x2 > (int) windows->image.width)
6183          line_info.x2=(short) windows->image.width;
6184      if (line_info.y2 < 0)
6185        line_info.y2=0;
6186      else
6187        if (line_info.y2 > (int) windows->image.height)
6188          line_info.y2=(short) windows->image.height;
6189      distance=(unsigned int)
6190        (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6191         ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6192      if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6193          ((state & ExitState) != 0))
6194        {
6195          if (rectangle_info.x < 0)
6196            rectangle_info.x=0;
6197          else
6198            if (rectangle_info.x > (ssize_t) windows->image.width)
6199              rectangle_info.x=(ssize_t) windows->image.width;
6200          if ((int) rectangle_info.x < x)
6201            rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6202          else
6203            {
6204              rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6205              rectangle_info.x=(ssize_t) x;
6206            }
6207          if (rectangle_info.y < 0)
6208            rectangle_info.y=0;
6209          else
6210            if (rectangle_info.y > (ssize_t) windows->image.height)
6211              rectangle_info.y=(ssize_t) windows->image.height;
6212          if ((int) rectangle_info.y < y)
6213            rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6214          else
6215            {
6216              rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6217              rectangle_info.y=(ssize_t) y;
6218            }
6219        }
6220    } while ((state & ExitState) == 0);
6221    (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6222    if ((element == PointElement) || (element == PolygonElement) ||
6223        (element == FillPolygonElement))
6224      {
6225        /*
6226          Determine polygon bounding box.
6227        */
6228        rectangle_info.x=(ssize_t) coordinate_info->x;
6229        rectangle_info.y=(ssize_t) coordinate_info->y;
6230        x=coordinate_info->x;
6231        y=coordinate_info->y;
6232        for (i=1; i < number_coordinates; i++)
6233        {
6234          if (coordinate_info[i].x > x)
6235            x=coordinate_info[i].x;
6236          if (coordinate_info[i].y > y)
6237            y=coordinate_info[i].y;
6238          if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6239            rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6240          if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6241            rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6242        }
6243        rectangle_info.width=(size_t) (x-rectangle_info.x);
6244        rectangle_info.height=(size_t) (y-rectangle_info.y);
6245        for (i=0; i < number_coordinates; i++)
6246        {
6247          coordinate_info[i].x-=rectangle_info.x;
6248          coordinate_info[i].y-=rectangle_info.y;
6249        }
6250      }
6251    else
6252      if (distance <= 9)
6253        continue;
6254      else
6255        if ((element == RectangleElement) ||
6256            (element == CircleElement) || (element == EllipseElement))
6257          {
6258            rectangle_info.width--;
6259            rectangle_info.height--;
6260          }
6261    /*
6262      Drawing is relative to image configuration.
6263    */
6264    draw_info.x=(int) rectangle_info.x;
6265    draw_info.y=(int) rectangle_info.y;
6266    (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6267      image,exception);
6268    width=(unsigned int) (*image)->columns;
6269    height=(unsigned int) (*image)->rows;
6270    x=0;
6271    y=0;
6272    if (windows->image.crop_geometry != (char *) NULL)
6273      (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6274    draw_info.x+=windows->image.x-(line_width/2);
6275    if (draw_info.x < 0)
6276      draw_info.x=0;
6277    draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6278    draw_info.y+=windows->image.y-(line_width/2);
6279    if (draw_info.y < 0)
6280      draw_info.y=0;
6281    draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6282    draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6283    if (draw_info.width > (unsigned int) (*image)->columns)
6284      draw_info.width=(unsigned int) (*image)->columns;
6285    draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6286    if (draw_info.height > (unsigned int) (*image)->rows)
6287      draw_info.height=(unsigned int) (*image)->rows;
6288    (void) FormatLocaleString(draw_info.geometry,MaxTextExtent,"%ux%u%+d%+d",
6289      width*draw_info.width/windows->image.ximage->width,
6290      height*draw_info.height/windows->image.ximage->height,
6291      draw_info.x+x,draw_info.y+y);
6292    /*
6293      Initialize drawing attributes.
6294    */
6295    draw_info.degrees=0.0;
6296    draw_info.element=element;
6297    draw_info.stipple=stipple;
6298    draw_info.line_width=line_width;
6299    draw_info.line_info=line_info;
6300    if (line_info.x1 > (int) (line_width/2))
6301      draw_info.line_info.x1=(short) line_width/2;
6302    if (line_info.y1 > (int) (line_width/2))
6303      draw_info.line_info.y1=(short) line_width/2;
6304    draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6305    draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6306    if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6307      {
6308        draw_info.line_info.x2=(-draw_info.line_info.x2);
6309        draw_info.line_info.y2=(-draw_info.line_info.y2);
6310      }
6311    if (draw_info.line_info.x2 < 0)
6312      {
6313        draw_info.line_info.x2=(-draw_info.line_info.x2);
6314        Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6315      }
6316    if (draw_info.line_info.y2 < 0)
6317      {
6318        draw_info.line_info.y2=(-draw_info.line_info.y2);
6319        Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6320      }
6321    draw_info.rectangle_info=rectangle_info;
6322    if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6323      draw_info.rectangle_info.x=(ssize_t) line_width/2;
6324    if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6325      draw_info.rectangle_info.y=(ssize_t) line_width/2;
6326    draw_info.number_coordinates=(unsigned int) number_coordinates;
6327    draw_info.coordinate_info=coordinate_info;
6328    windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6329    /*
6330      Draw element on image.
6331    */
6332    XSetCursorState(display,windows,MagickTrue);
6333    XCheckRefreshWindows(display,windows);
6334    status=XDrawImage(display,windows->pixel_info,&draw_info,*image);
6335    XSetCursorState(display,windows,MagickFalse);
6336    /*
6337      Update image colormap and return to image drawing.
6338    */
6339    XConfigureImageColormap(display,resource_info,windows,*image);
6340    (void) XConfigureImage(display,resource_info,windows,*image,exception);
6341  }
6342  XSetCursorState(display,windows,MagickFalse);
6343  coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6344  return(status != 0 ? MagickTrue : MagickFalse);
6345}
6346
6347/*
6348%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6349%                                                                             %
6350%                                                                             %
6351%                                                                             %
6352+   X D r a w P a n R e c t a n g l e                                         %
6353%                                                                             %
6354%                                                                             %
6355%                                                                             %
6356%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6357%
6358%  XDrawPanRectangle() draws a rectangle in the pan window.  The pan window
6359%  displays a zoom image and the rectangle shows which portion of the image is
6360%  displayed in the Image window.
6361%
6362%  The format of the XDrawPanRectangle method is:
6363%
6364%      XDrawPanRectangle(Display *display,XWindows *windows)
6365%
6366%  A description of each parameter follows:
6367%
6368%    o display: Specifies a connection to an X server;  returned from
6369%      XOpenDisplay.
6370%
6371%    o windows: Specifies a pointer to a XWindows structure.
6372%
6373*/
6374static void XDrawPanRectangle(Display *display,XWindows *windows)
6375{
6376  MagickRealType
6377    scale_factor;
6378
6379  RectangleInfo
6380    highlight_info;
6381
6382  /*
6383    Determine dimensions of the panning rectangle.
6384  */
6385  scale_factor=(MagickRealType) windows->pan.width/windows->image.ximage->width;
6386  highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6387  highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6388  scale_factor=(MagickRealType)
6389    windows->pan.height/windows->image.ximage->height;
6390  highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6391  highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6392  /*
6393    Display the panning rectangle.
6394  */
6395  (void) XClearWindow(display,windows->pan.id);
6396  XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6397    &highlight_info);
6398}
6399
6400/*
6401%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6402%                                                                             %
6403%                                                                             %
6404%                                                                             %
6405+   X I m a g e C a c h e                                                     %
6406%                                                                             %
6407%                                                                             %
6408%                                                                             %
6409%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6410%
6411%  XImageCache() handles the creation, manipulation, and destruction of the
6412%  image cache (undo and redo buffers).
6413%
6414%  The format of the XImageCache method is:
6415%
6416%      void XImageCache(Display *display,XResourceInfo *resource_info,
6417%        XWindows *windows,const CommandType command,Image **image,
6418%        ExceptionInfo *exception)
6419%
6420%  A description of each parameter follows:
6421%
6422%    o display: Specifies a connection to an X server; returned from
6423%      XOpenDisplay.
6424%
6425%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6426%
6427%    o windows: Specifies a pointer to a XWindows structure.
6428%
6429%    o command: Specifies a command to perform.
6430%
6431%    o image: the image;  XImageCache may transform the image and return a new
6432%      image pointer.
6433%
6434%    o exception: return any errors or warnings in this structure.
6435%
6436*/
6437static void XImageCache(Display *display,XResourceInfo *resource_info,
6438  XWindows *windows,const CommandType command,Image **image,
6439  ExceptionInfo *exception)
6440{
6441  Image
6442    *cache_image;
6443
6444  static Image
6445    *redo_image = (Image *) NULL,
6446    *undo_image = (Image *) NULL;
6447
6448  switch (command)
6449  {
6450    case FreeBuffersCommand:
6451    {
6452      /*
6453        Free memory from the undo and redo cache.
6454      */
6455      while (undo_image != (Image *) NULL)
6456      {
6457        cache_image=undo_image;
6458        undo_image=GetPreviousImageInList(undo_image);
6459        cache_image->list=DestroyImage(cache_image->list);
6460        cache_image=DestroyImage(cache_image);
6461      }
6462      undo_image=NewImageList();
6463      if (redo_image != (Image *) NULL)
6464        redo_image=DestroyImage(redo_image);
6465      redo_image=NewImageList();
6466      return;
6467    }
6468    case UndoCommand:
6469    {
6470      char
6471        image_geometry[MaxTextExtent];
6472
6473      /*
6474        Undo the last image transformation.
6475      */
6476      if (undo_image == (Image *) NULL)
6477        {
6478          (void) XBell(display,0);
6479          return;
6480        }
6481      cache_image=undo_image;
6482      undo_image=GetPreviousImageInList(undo_image);
6483      windows->image.window_changes.width=(int) cache_image->columns;
6484      windows->image.window_changes.height=(int) cache_image->rows;
6485      (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
6486        windows->image.ximage->width,windows->image.ximage->height);
6487      (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
6488        exception);
6489      if (windows->image.crop_geometry != (char *) NULL)
6490        windows->image.crop_geometry=(char *) RelinquishMagickMemory(
6491          windows->image.crop_geometry);
6492      windows->image.crop_geometry=cache_image->geometry;
6493      if (redo_image != (Image *) NULL)
6494        redo_image=DestroyImage(redo_image);
6495      redo_image=(*image);
6496      *image=cache_image->list;
6497      cache_image=DestroyImage(cache_image);
6498      if (windows->image.orphan != MagickFalse)
6499        return;
6500      XConfigureImageColormap(display,resource_info,windows,*image);
6501      (void) XConfigureImage(display,resource_info,windows,*image,exception);
6502      return;
6503    }
6504    case CutCommand:
6505    case PasteCommand:
6506    case ApplyCommand:
6507    case HalfSizeCommand:
6508    case OriginalSizeCommand:
6509    case DoubleSizeCommand:
6510    case ResizeCommand:
6511    case TrimCommand:
6512    case CropCommand:
6513    case ChopCommand:
6514    case FlipCommand:
6515    case FlopCommand:
6516    case RotateRightCommand:
6517    case RotateLeftCommand:
6518    case RotateCommand:
6519    case ShearCommand:
6520    case RollCommand:
6521    case NegateCommand:
6522    case ContrastStretchCommand:
6523    case SigmoidalContrastCommand:
6524    case NormalizeCommand:
6525    case EqualizeCommand:
6526    case HueCommand:
6527    case SaturationCommand:
6528    case BrightnessCommand:
6529    case GammaCommand:
6530    case SpiffCommand:
6531    case DullCommand:
6532    case GrayscaleCommand:
6533    case MapCommand:
6534    case QuantizeCommand:
6535    case DespeckleCommand:
6536    case EmbossCommand:
6537    case ReduceNoiseCommand:
6538    case AddNoiseCommand:
6539    case SharpenCommand:
6540    case BlurCommand:
6541    case ThresholdCommand:
6542    case EdgeDetectCommand:
6543    case SpreadCommand:
6544    case ShadeCommand:
6545    case RaiseCommand:
6546    case SegmentCommand:
6547    case SolarizeCommand:
6548    case SepiaToneCommand:
6549    case SwirlCommand:
6550    case ImplodeCommand:
6551    case VignetteCommand:
6552    case WaveCommand:
6553    case OilPaintCommand:
6554    case CharcoalDrawCommand:
6555    case AnnotateCommand:
6556    case AddBorderCommand:
6557    case AddFrameCommand:
6558    case CompositeCommand:
6559    case CommentCommand:
6560    case LaunchCommand:
6561    case RegionofInterestCommand:
6562    case SaveToUndoBufferCommand:
6563    case RedoCommand:
6564    {
6565      Image
6566        *previous_image;
6567
6568      ssize_t
6569        bytes;
6570
6571      bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelInfo));
6572      if (undo_image != (Image *) NULL)
6573        {
6574          /*
6575            Ensure the undo cache has enough memory available.
6576          */
6577          previous_image=undo_image;
6578          while (previous_image != (Image *) NULL)
6579          {
6580            bytes+=previous_image->list->columns*previous_image->list->rows*
6581              sizeof(PixelInfo);
6582            if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6583              {
6584                previous_image=GetPreviousImageInList(previous_image);
6585                continue;
6586              }
6587            bytes-=previous_image->list->columns*previous_image->list->rows*
6588              sizeof(PixelInfo);
6589            if (previous_image == undo_image)
6590              undo_image=NewImageList();
6591            else
6592              previous_image->next->previous=NewImageList();
6593            break;
6594          }
6595          while (previous_image != (Image *) NULL)
6596          {
6597            /*
6598              Delete any excess memory from undo cache.
6599            */
6600            cache_image=previous_image;
6601            previous_image=GetPreviousImageInList(previous_image);
6602            cache_image->list=DestroyImage(cache_image->list);
6603            cache_image=DestroyImage(cache_image);
6604          }
6605        }
6606      if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6607        break;
6608      /*
6609        Save image before transformations are applied.
6610      */
6611      cache_image=AcquireImage((ImageInfo *) NULL,exception);
6612      if (cache_image == (Image *) NULL)
6613        break;
6614      XSetCursorState(display,windows,MagickTrue);
6615      XCheckRefreshWindows(display,windows);
6616      cache_image->list=CloneImage(*image,0,0,MagickTrue,exception);
6617      XSetCursorState(display,windows,MagickFalse);
6618      if (cache_image->list == (Image *) NULL)
6619        {
6620          cache_image=DestroyImage(cache_image);
6621          break;
6622        }
6623      cache_image->columns=(size_t) windows->image.ximage->width;
6624      cache_image->rows=(size_t) windows->image.ximage->height;
6625      cache_image->geometry=windows->image.crop_geometry;
6626      if (windows->image.crop_geometry != (char *) NULL)
6627        {
6628          cache_image->geometry=AcquireString((char *) NULL);
6629          (void) CopyMagickString(cache_image->geometry,
6630            windows->image.crop_geometry,MaxTextExtent);
6631        }
6632      if (undo_image == (Image *) NULL)
6633        {
6634          undo_image=cache_image;
6635          break;
6636        }
6637      undo_image->next=cache_image;
6638      undo_image->next->previous=undo_image;
6639      undo_image=undo_image->next;
6640      break;
6641    }
6642    default:
6643      break;
6644  }
6645  if (command == RedoCommand)
6646    {
6647      /*
6648        Redo the last image transformation.
6649      */
6650      if (redo_image == (Image *) NULL)
6651        {
6652          (void) XBell(display,0);
6653          return;
6654        }
6655      windows->image.window_changes.width=(int) redo_image->columns;
6656      windows->image.window_changes.height=(int) redo_image->rows;
6657      if (windows->image.crop_geometry != (char *) NULL)
6658        windows->image.crop_geometry=(char *)
6659          RelinquishMagickMemory(windows->image.crop_geometry);
6660      windows->image.crop_geometry=redo_image->geometry;
6661      *image=DestroyImage(*image);
6662      *image=redo_image;
6663      redo_image=NewImageList();
6664      if (windows->image.orphan != MagickFalse)
6665        return;
6666      XConfigureImageColormap(display,resource_info,windows,*image);
6667      (void) XConfigureImage(display,resource_info,windows,*image,exception);
6668      return;
6669    }
6670  if (command != InfoCommand)
6671    return;
6672  /*
6673    Display image info.
6674  */
6675  XSetCursorState(display,windows,MagickTrue);
6676  XCheckRefreshWindows(display,windows);
6677  XDisplayImageInfo(display,resource_info,windows,undo_image,*image);
6678  XSetCursorState(display,windows,MagickFalse);
6679}
6680
6681/*
6682%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6683%                                                                             %
6684%                                                                             %
6685%                                                                             %
6686+   X I m a g e W i n d o w C o m m a n d                                     %
6687%                                                                             %
6688%                                                                             %
6689%                                                                             %
6690%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6691%
6692%  XImageWindowCommand() makes a transform to the image or Image window as
6693%  specified by a user menu button or keyboard command.
6694%
6695%  The format of the XImageWindowCommand method is:
6696%
6697%      CommandType XImageWindowCommand(Display *display,
6698%        XResourceInfo *resource_info,XWindows *windows,
6699%        const MagickStatusType state,KeySym key_symbol,Image **image,
6700%        ExceptionInfo *exception)
6701%
6702%  A description of each parameter follows:
6703%
6704%    o nexus:  Method XImageWindowCommand returns an image when the
6705%      user chooses 'Open Image' from the command menu.  Otherwise a null
6706%      image is returned.
6707%
6708%    o display: Specifies a connection to an X server; returned from
6709%      XOpenDisplay.
6710%
6711%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6712%
6713%    o windows: Specifies a pointer to a XWindows structure.
6714%
6715%    o state: key mask.
6716%
6717%    o key_symbol: Specifies a command to perform.
6718%
6719%    o image: the image;  XImageWIndowCommand may transform the image and
6720%      return a new image pointer.
6721%
6722%    o exception: return any errors or warnings in this structure.
6723%
6724*/
6725static CommandType XImageWindowCommand(Display *display,
6726  XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6727  KeySym key_symbol,Image **image,ExceptionInfo *exception)
6728{
6729  static char
6730    delta[MaxTextExtent] = "";
6731
6732  static const char
6733    Digits[] = "01234567890";
6734
6735  static KeySym
6736    last_symbol = XK_0;
6737
6738  if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6739    {
6740      if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6741        {
6742          *delta='\0';
6743          resource_info->quantum=1;
6744        }
6745      last_symbol=key_symbol;
6746      delta[strlen(delta)+1]='\0';
6747      delta[strlen(delta)]=Digits[key_symbol-XK_0];
6748      resource_info->quantum=StringToLong(delta);
6749      return(NullCommand);
6750    }
6751  last_symbol=key_symbol;
6752  if (resource_info->immutable)
6753    {
6754      /*
6755        Virtual image window has a restricted command set.
6756      */
6757      switch (key_symbol)
6758      {
6759        case XK_question:
6760          return(InfoCommand);
6761        case XK_p:
6762        case XK_Print:
6763          return(PrintCommand);
6764        case XK_space:
6765          return(NextCommand);
6766        case XK_q:
6767        case XK_Escape:
6768          return(QuitCommand);
6769        default:
6770          break;
6771      }
6772      return(NullCommand);
6773    }
6774  switch ((int) key_symbol)
6775  {
6776    case XK_o:
6777    {
6778      if ((state & ControlMask) == 0)
6779        break;
6780      return(OpenCommand);
6781    }
6782    case XK_space:
6783      return(NextCommand);
6784    case XK_BackSpace:
6785      return(FormerCommand);
6786    case XK_s:
6787    {
6788      if ((state & Mod1Mask) != 0)
6789        return(SwirlCommand);
6790      if ((state & ControlMask) == 0)
6791        return(ShearCommand);
6792      return(SaveCommand);
6793    }
6794    case XK_p:
6795    case XK_Print:
6796    {
6797      if ((state & Mod1Mask) != 0)
6798        return(OilPaintCommand);
6799      if ((state & Mod4Mask) != 0)
6800        return(ColorCommand);
6801      if ((state & ControlMask) == 0)
6802        return(NullCommand);
6803      return(PrintCommand);
6804    }
6805    case XK_d:
6806    {
6807      if ((state & Mod4Mask) != 0)
6808        return(DrawCommand);
6809      if ((state & ControlMask) == 0)
6810        return(NullCommand);
6811      return(DeleteCommand);
6812    }
6813    case XK_Select:
6814    {
6815      if ((state & ControlMask) == 0)
6816        return(NullCommand);
6817      return(SelectCommand);
6818    }
6819    case XK_n:
6820    {
6821      if ((state & ControlMask) == 0)
6822        return(NullCommand);
6823      return(NewCommand);
6824    }
6825    case XK_q:
6826    case XK_Escape:
6827      return(QuitCommand);
6828    case XK_z:
6829    case XK_Undo:
6830    {
6831      if ((state & ControlMask) == 0)
6832        return(NullCommand);
6833      return(UndoCommand);
6834    }
6835    case XK_r:
6836    case XK_Redo:
6837    {
6838      if ((state & ControlMask) == 0)
6839        return(RollCommand);
6840      return(RedoCommand);
6841    }
6842    case XK_x:
6843    {
6844      if ((state & ControlMask) == 0)
6845        return(NullCommand);
6846      return(CutCommand);
6847    }
6848    case XK_c:
6849    {
6850      if ((state & Mod1Mask) != 0)
6851        return(CharcoalDrawCommand);
6852      if ((state & ControlMask) == 0)
6853        return(CropCommand);
6854      return(CopyCommand);
6855    }
6856    case XK_v:
6857    case XK_Insert:
6858    {
6859      if ((state & Mod4Mask) != 0)
6860        return(CompositeCommand);
6861      if ((state & ControlMask) == 0)
6862        return(FlipCommand);
6863      return(PasteCommand);
6864    }
6865    case XK_less:
6866      return(HalfSizeCommand);
6867    case XK_minus:
6868      return(OriginalSizeCommand);
6869    case XK_greater:
6870      return(DoubleSizeCommand);
6871    case XK_percent:
6872      return(ResizeCommand);
6873    case XK_at:
6874      return(RefreshCommand);
6875    case XK_bracketleft:
6876      return(ChopCommand);
6877    case XK_h:
6878      return(FlopCommand);
6879    case XK_slash:
6880      return(RotateRightCommand);
6881    case XK_backslash:
6882      return(RotateLeftCommand);
6883    case XK_asterisk:
6884      return(RotateCommand);
6885    case XK_t:
6886      return(TrimCommand);
6887    case XK_H:
6888      return(HueCommand);
6889    case XK_S:
6890      return(SaturationCommand);
6891    case XK_L:
6892      return(BrightnessCommand);
6893    case XK_G:
6894      return(GammaCommand);
6895    case XK_C:
6896      return(SpiffCommand);
6897    case XK_Z:
6898      return(DullCommand);
6899    case XK_N:
6900      return(NormalizeCommand);
6901    case XK_equal:
6902      return(EqualizeCommand);
6903    case XK_asciitilde:
6904      return(NegateCommand);
6905    case XK_period:
6906      return(GrayscaleCommand);
6907    case XK_numbersign:
6908      return(QuantizeCommand);
6909    case XK_F2:
6910      return(DespeckleCommand);
6911    case XK_F3:
6912      return(EmbossCommand);
6913    case XK_F4:
6914      return(ReduceNoiseCommand);
6915    case XK_F5:
6916      return(AddNoiseCommand);
6917    case XK_F6:
6918      return(SharpenCommand);
6919    case XK_F7:
6920      return(BlurCommand);
6921    case XK_F8:
6922      return(ThresholdCommand);
6923    case XK_F9:
6924      return(EdgeDetectCommand);
6925    case XK_F10:
6926      return(SpreadCommand);
6927    case XK_F11:
6928      return(ShadeCommand);
6929    case XK_F12:
6930      return(RaiseCommand);
6931    case XK_F13:
6932      return(SegmentCommand);
6933    case XK_i:
6934    {
6935      if ((state & Mod1Mask) == 0)
6936        return(NullCommand);
6937      return(ImplodeCommand);
6938    }
6939    case XK_w:
6940    {
6941      if ((state & Mod1Mask) == 0)
6942        return(NullCommand);
6943      return(WaveCommand);
6944    }
6945    case XK_m:
6946    {
6947      if ((state & Mod4Mask) == 0)
6948        return(NullCommand);
6949      return(MatteCommand);
6950    }
6951    case XK_b:
6952    {
6953      if ((state & Mod4Mask) == 0)
6954        return(NullCommand);
6955      return(AddBorderCommand);
6956    }
6957    case XK_f:
6958    {
6959      if ((state & Mod4Mask) == 0)
6960        return(NullCommand);
6961      return(AddFrameCommand);
6962    }
6963    case XK_exclam:
6964    {
6965      if ((state & Mod4Mask) == 0)
6966        return(NullCommand);
6967      return(CommentCommand);
6968    }
6969    case XK_a:
6970    {
6971      if ((state & Mod1Mask) != 0)
6972        return(ApplyCommand);
6973      if ((state & Mod4Mask) != 0)
6974        return(AnnotateCommand);
6975      if ((state & ControlMask) == 0)
6976        return(NullCommand);
6977      return(RegionofInterestCommand);
6978    }
6979    case XK_question:
6980      return(InfoCommand);
6981    case XK_plus:
6982      return(ZoomCommand);
6983    case XK_P:
6984    {
6985      if ((state & ShiftMask) == 0)
6986        return(NullCommand);
6987      return(ShowPreviewCommand);
6988    }
6989    case XK_Execute:
6990      return(LaunchCommand);
6991    case XK_F1:
6992      return(HelpCommand);
6993    case XK_Find:
6994      return(BrowseDocumentationCommand);
6995    case XK_Menu:
6996    {
6997      (void) XMapRaised(display,windows->command.id);
6998      return(NullCommand);
6999    }
7000    case XK_Next:
7001    case XK_Prior:
7002    case XK_Home:
7003    case XK_KP_Home:
7004    {
7005      XTranslateImage(display,windows,*image,key_symbol);
7006      return(NullCommand);
7007    }
7008    case XK_Up:
7009    case XK_KP_Up:
7010    case XK_Down:
7011    case XK_KP_Down:
7012    case XK_Left:
7013    case XK_KP_Left:
7014    case XK_Right:
7015    case XK_KP_Right:
7016    {
7017      if ((state & Mod1Mask) != 0)
7018        {
7019          RectangleInfo
7020            crop_info;
7021
7022          /*
7023            Trim one pixel from edge of image.
7024          */
7025          crop_info.x=0;
7026          crop_info.y=0;
7027          crop_info.width=(size_t) windows->image.ximage->width;
7028          crop_info.height=(size_t) windows->image.ximage->height;
7029          if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
7030            {
7031              if (resource_info->quantum >= (int) crop_info.height)
7032                resource_info->quantum=(int) crop_info.height-1;
7033              crop_info.height-=resource_info->quantum;
7034            }
7035          if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
7036            {
7037              if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
7038                resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
7039              crop_info.y+=resource_info->quantum;
7040              crop_info.height-=resource_info->quantum;
7041            }
7042          if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
7043            {
7044              if (resource_info->quantum >= (int) crop_info.width)
7045                resource_info->quantum=(int) crop_info.width-1;
7046              crop_info.width-=resource_info->quantum;
7047            }
7048          if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
7049            {
7050              if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
7051                resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
7052              crop_info.x+=resource_info->quantum;
7053              crop_info.width-=resource_info->quantum;
7054            }
7055          if ((int) (windows->image.x+windows->image.width) >
7056              (int) crop_info.width)
7057            windows->image.x=(int) (crop_info.width-windows->image.width);
7058          if ((int) (windows->image.y+windows->image.height) >
7059              (int) crop_info.height)
7060            windows->image.y=(int) (crop_info.height-windows->image.height);
7061          XSetCropGeometry(display,windows,&crop_info,*image);
7062          windows->image.window_changes.width=(int) crop_info.width;
7063          windows->image.window_changes.height=(int) crop_info.height;
7064          (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
7065          (void) XConfigureImage(display,resource_info,windows,*image,
7066            exception);
7067          return(NullCommand);
7068        }
7069      XTranslateImage(display,windows,*image,key_symbol);
7070      return(NullCommand);
7071    }
7072    default:
7073      return(NullCommand);
7074  }
7075  return(NullCommand);
7076}
7077
7078/*
7079%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7080%                                                                             %
7081%                                                                             %
7082%                                                                             %
7083+   X M a g i c k C o m m a n d                                               %
7084%                                                                             %
7085%                                                                             %
7086%                                                                             %
7087%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7088%
7089%  XMagickCommand() makes a transform to the image or Image window as
7090%  specified by a user menu button or keyboard command.
7091%
7092%  The format of the XMagickCommand method is:
7093%
7094%      Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7095%        XWindows *windows,const CommandType command,Image **image,
7096%        ExceptionInfo *exception)
7097%
7098%  A description of each parameter follows:
7099%
7100%    o display: Specifies a connection to an X server; returned from
7101%      XOpenDisplay.
7102%
7103%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7104%
7105%    o windows: Specifies a pointer to a XWindows structure.
7106%
7107%    o command: Specifies a command to perform.
7108%
7109%    o image: the image;  XMagickCommand may transform the image and return a
7110%      new image pointer.
7111%
7112%    o exception: return any errors or warnings in this structure.
7113%
7114*/
7115static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7116  XWindows *windows,const CommandType command,Image **image,
7117  ExceptionInfo *exception)
7118{
7119  char
7120    filename[MaxTextExtent],
7121    geometry[MaxTextExtent],
7122    modulate_factors[MaxTextExtent];
7123
7124  GeometryInfo
7125    geometry_info;
7126
7127  Image
7128    *nexus;
7129
7130  ImageInfo
7131    *image_info;
7132
7133  int
7134    x,
7135    y;
7136
7137  MagickStatusType
7138    flags,
7139    status;
7140
7141  QuantizeInfo
7142    quantize_info;
7143
7144  RectangleInfo
7145    page_geometry;
7146
7147  register int
7148    i;
7149
7150  static char
7151    color[MaxTextExtent] = "gray";
7152
7153  unsigned int
7154    height,
7155    width;
7156
7157  /*
7158    Process user command.
7159  */
7160  XCheckRefreshWindows(display,windows);
7161  XImageCache(display,resource_info,windows,command,image,exception);
7162  nexus=NewImageList();
7163  windows->image.window_changes.width=windows->image.ximage->width;
7164  windows->image.window_changes.height=windows->image.ximage->height;
7165  image_info=CloneImageInfo(resource_info->image_info);
7166  SetGeometryInfo(&geometry_info);
7167  GetQuantizeInfo(&quantize_info);
7168  switch (command)
7169  {
7170    case OpenCommand:
7171    {
7172      /*
7173        Load image.
7174      */
7175      nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7176      break;
7177    }
7178    case NextCommand:
7179    {
7180      /*
7181        Display next image.
7182      */
7183      for (i=0; i < resource_info->quantum; i++)
7184        XClientMessage(display,windows->image.id,windows->im_protocols,
7185          windows->im_next_image,CurrentTime);
7186      break;
7187    }
7188    case FormerCommand:
7189    {
7190      /*
7191        Display former image.
7192      */
7193      for (i=0; i < resource_info->quantum; i++)
7194        XClientMessage(display,windows->image.id,windows->im_protocols,
7195          windows->im_former_image,CurrentTime);
7196      break;
7197    }
7198    case SelectCommand:
7199    {
7200      int
7201        status;
7202
7203      /*
7204        Select image.
7205      */
7206      status=chdir(resource_info->home_directory);
7207      if (status == -1)
7208        (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
7209          "UnableToOpenFile","%s",resource_info->home_directory);
7210      nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7211      break;
7212    }
7213    case SaveCommand:
7214    {
7215      /*
7216        Save image.
7217      */
7218      status=XSaveImage(display,resource_info,windows,*image,exception);
7219      if (status == MagickFalse)
7220        {
7221          char
7222            message[MaxTextExtent];
7223
7224          (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7225            exception->reason != (char *) NULL ? exception->reason : "",
7226            exception->description != (char *) NULL ? exception->description :
7227            "");
7228          XNoticeWidget(display,windows,"Unable to save file:",message);
7229          break;
7230        }
7231      break;
7232    }
7233    case PrintCommand:
7234    {
7235      /*
7236        Print image.
7237      */
7238      status=XPrintImage(display,resource_info,windows,*image,exception);
7239      if (status == MagickFalse)
7240        {
7241          char
7242            message[MaxTextExtent];
7243
7244          (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7245            exception->reason != (char *) NULL ? exception->reason : "",
7246            exception->description != (char *) NULL ? exception->description :
7247            "");
7248          XNoticeWidget(display,windows,"Unable to print file:",message);
7249          break;
7250        }
7251      break;
7252    }
7253    case DeleteCommand:
7254    {
7255      static char
7256        filename[MaxTextExtent] = "\0";
7257
7258      /*
7259        Delete image file.
7260      */
7261      XFileBrowserWidget(display,windows,"Delete",filename);
7262      if (*filename == '\0')
7263        break;
7264      status=remove_utf8(filename) != 0 ? MagickTrue : MagickFalse;
7265      if (status != MagickFalse)
7266        XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7267      break;
7268    }
7269    case NewCommand:
7270    {
7271      int
7272        status;
7273
7274      static char
7275        color[MaxTextExtent] = "gray",
7276        geometry[MaxTextExtent] = "640x480";
7277
7278      static const char
7279        *format = "gradient";
7280
7281      /*
7282        Query user for canvas geometry.
7283      */
7284      status=XDialogWidget(display,windows,"New","Enter image geometry:",
7285        geometry);
7286      if (*geometry == '\0')
7287        break;
7288      if (status == 0)
7289        format="xc";
7290      XColorBrowserWidget(display,windows,"Select",color);
7291      if (*color == '\0')
7292        break;
7293      /*
7294        Create canvas.
7295      */
7296      (void) FormatLocaleString(image_info->filename,MaxTextExtent,
7297        "%s:%s",format,color);
7298      (void) CloneString(&image_info->size,geometry);
7299      nexus=ReadImage(image_info,exception);
7300      CatchException(exception);
7301      XClientMessage(display,windows->image.id,windows->im_protocols,
7302        windows->im_next_image,CurrentTime);
7303      break;
7304    }
7305    case VisualDirectoryCommand:
7306    {
7307      /*
7308        Visual Image directory.
7309      */
7310      nexus=XVisualDirectoryImage(display,resource_info,windows,exception);
7311      break;
7312    }
7313    case QuitCommand:
7314    {
7315      /*
7316        exit program.
7317      */
7318      if (resource_info->confirm_exit == MagickFalse)
7319        XClientMessage(display,windows->image.id,windows->im_protocols,
7320          windows->im_exit,CurrentTime);
7321      else
7322        {
7323          int
7324            status;
7325
7326          /*
7327            Confirm program exit.
7328          */
7329          status=XConfirmWidget(display,windows,"Do you really want to exit",
7330            resource_info->client_name);
7331          if (status > 0)
7332            XClientMessage(display,windows->image.id,windows->im_protocols,
7333              windows->im_exit,CurrentTime);
7334        }
7335      break;
7336    }
7337    case CutCommand:
7338    {
7339      /*
7340        Cut image.
7341      */
7342      (void) XCropImage(display,resource_info,windows,*image,CutMode,exception);
7343      break;
7344    }
7345    case CopyCommand:
7346    {
7347      /*
7348        Copy image.
7349      */
7350      (void) XCropImage(display,resource_info,windows,*image,CopyMode,
7351        exception);
7352      break;
7353    }
7354    case PasteCommand:
7355    {
7356      /*
7357        Paste image.
7358      */
7359      status=XPasteImage(display,resource_info,windows,*image,exception);
7360      if (status == MagickFalse)
7361        {
7362          XNoticeWidget(display,windows,"Unable to paste X image",
7363            (*image)->filename);
7364          break;
7365        }
7366      break;
7367    }
7368    case HalfSizeCommand:
7369    {
7370      /*
7371        Half image size.
7372      */
7373      windows->image.window_changes.width=windows->image.ximage->width/2;
7374      windows->image.window_changes.height=windows->image.ximage->height/2;
7375      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7376      break;
7377    }
7378    case OriginalSizeCommand:
7379    {
7380      /*
7381        Original image size.
7382      */
7383      windows->image.window_changes.width=(int) (*image)->columns;
7384      windows->image.window_changes.height=(int) (*image)->rows;
7385      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7386      break;
7387    }
7388    case DoubleSizeCommand:
7389    {
7390      /*
7391        Double the image size.
7392      */
7393      windows->image.window_changes.width=windows->image.ximage->width << 1;
7394      windows->image.window_changes.height=windows->image.ximage->height << 1;
7395      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7396      break;
7397    }
7398    case ResizeCommand:
7399    {
7400      int
7401        status;
7402
7403      size_t
7404        height,
7405        width;
7406
7407      ssize_t
7408        x,
7409        y;
7410
7411      /*
7412        Resize image.
7413      */
7414      width=(size_t) windows->image.ximage->width;
7415      height=(size_t) windows->image.ximage->height;
7416      x=0;
7417      y=0;
7418      (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g+0+0",
7419        (double) width,(double) height);
7420      status=XDialogWidget(display,windows,"Resize",
7421        "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7422      if (*geometry == '\0')
7423        break;
7424      if (status == 0)
7425        (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7426      (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7427      windows->image.window_changes.width=(int) width;
7428      windows->image.window_changes.height=(int) height;
7429      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7430      break;
7431    }
7432    case ApplyCommand:
7433    {
7434      char
7435        image_geometry[MaxTextExtent];
7436
7437      if ((windows->image.crop_geometry == (char *) NULL) &&
7438          ((int) (*image)->columns == windows->image.ximage->width) &&
7439          ((int) (*image)->rows == windows->image.ximage->height))
7440        break;
7441      /*
7442        Apply size transforms to image.
7443      */
7444      XSetCursorState(display,windows,MagickTrue);
7445      XCheckRefreshWindows(display,windows);
7446      /*
7447        Crop and/or scale displayed image.
7448      */
7449      (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
7450        windows->image.ximage->width,windows->image.ximage->height);
7451      (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
7452        exception);
7453      if (windows->image.crop_geometry != (char *) NULL)
7454        windows->image.crop_geometry=(char *) RelinquishMagickMemory(
7455          windows->image.crop_geometry);
7456      windows->image.x=0;
7457      windows->image.y=0;
7458      XConfigureImageColormap(display,resource_info,windows,*image);
7459      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7460      break;
7461    }
7462    case RefreshCommand:
7463    {
7464      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7465      break;
7466    }
7467    case RestoreCommand:
7468    {
7469      /*
7470        Restore Image window to its original size.
7471      */
7472      if ((windows->image.width == (unsigned int) (*image)->columns) &&
7473          (windows->image.height == (unsigned int) (*image)->rows) &&
7474          (windows->image.crop_geometry == (char *) NULL))
7475        {
7476          (void) XBell(display,0);
7477          break;
7478        }
7479      windows->image.window_changes.width=(int) (*image)->columns;
7480      windows->image.window_changes.height=(int) (*image)->rows;
7481      if (windows->image.crop_geometry != (char *) NULL)
7482        {
7483          windows->image.crop_geometry=(char *)
7484            RelinquishMagickMemory(windows->image.crop_geometry);
7485          windows->image.crop_geometry=(char *) NULL;
7486          windows->image.x=0;
7487          windows->image.y=0;
7488        }
7489      XConfigureImageColormap(display,resource_info,windows,*image);
7490      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7491      break;
7492    }
7493    case CropCommand:
7494    {
7495      /*
7496        Crop image.
7497      */
7498      (void) XCropImage(display,resource_info,windows,*image,CropMode,
7499        exception);
7500      break;
7501    }
7502    case ChopCommand:
7503    {
7504      /*
7505        Chop image.
7506      */
7507      status=XChopImage(display,resource_info,windows,image,exception);
7508      if (status == MagickFalse)
7509        {
7510          XNoticeWidget(display,windows,"Unable to cut X image",
7511            (*image)->filename);
7512          break;
7513        }
7514      break;
7515    }
7516    case FlopCommand:
7517    {
7518      Image
7519        *flop_image;
7520
7521      /*
7522        Flop image scanlines.
7523      */
7524      XSetCursorState(display,windows,MagickTrue);
7525      XCheckRefreshWindows(display,windows);
7526      flop_image=FlopImage(*image,exception);
7527      if (flop_image != (Image *) NULL)
7528        {
7529          *image=DestroyImage(*image);
7530          *image=flop_image;
7531        }
7532      CatchException(exception);
7533      XSetCursorState(display,windows,MagickFalse);
7534      if (windows->image.crop_geometry != (char *) NULL)
7535        {
7536          /*
7537            Flop crop geometry.
7538          */
7539          width=(unsigned int) (*image)->columns;
7540          height=(unsigned int) (*image)->rows;
7541          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7542            &width,&height);
7543          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7544            "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7545        }
7546      if (windows->image.orphan != MagickFalse)
7547        break;
7548      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7549      break;
7550    }
7551    case FlipCommand:
7552    {
7553      Image
7554        *flip_image;
7555
7556      /*
7557        Flip image scanlines.
7558      */
7559      XSetCursorState(display,windows,MagickTrue);
7560      XCheckRefreshWindows(display,windows);
7561      flip_image=FlipImage(*image,exception);
7562      if (flip_image != (Image *) NULL)
7563        {
7564          *image=DestroyImage(*image);
7565          *image=flip_image;
7566        }
7567      CatchException(exception);
7568      XSetCursorState(display,windows,MagickFalse);
7569      if (windows->image.crop_geometry != (char *) NULL)
7570        {
7571          /*
7572            Flip crop geometry.
7573          */
7574          width=(unsigned int) (*image)->columns;
7575          height=(unsigned int) (*image)->rows;
7576          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7577            &width,&height);
7578          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7579            "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7580        }
7581      if (windows->image.orphan != MagickFalse)
7582        break;
7583      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7584      break;
7585    }
7586    case RotateRightCommand:
7587    {
7588      /*
7589        Rotate image 90 degrees clockwise.
7590      */
7591      status=XRotateImage(display,resource_info,windows,90.0,image,exception);
7592      if (status == MagickFalse)
7593        {
7594          XNoticeWidget(display,windows,"Unable to rotate X image",
7595            (*image)->filename);
7596          break;
7597        }
7598      break;
7599    }
7600    case RotateLeftCommand:
7601    {
7602      /*
7603        Rotate image 90 degrees counter-clockwise.
7604      */
7605      status=XRotateImage(display,resource_info,windows,-90.0,image,exception);
7606      if (status == MagickFalse)
7607        {
7608          XNoticeWidget(display,windows,"Unable to rotate X image",
7609            (*image)->filename);
7610          break;
7611        }
7612      break;
7613    }
7614    case RotateCommand:
7615    {
7616      /*
7617        Rotate image.
7618      */
7619      status=XRotateImage(display,resource_info,windows,0.0,image,exception);
7620      if (status == MagickFalse)
7621        {
7622          XNoticeWidget(display,windows,"Unable to rotate X image",
7623            (*image)->filename);
7624          break;
7625        }
7626      break;
7627    }
7628    case ShearCommand:
7629    {
7630      Image
7631        *shear_image;
7632
7633      static char
7634        geometry[MaxTextExtent] = "45.0x45.0";
7635
7636      /*
7637        Query user for shear color and geometry.
7638      */
7639      XColorBrowserWidget(display,windows,"Select",color);
7640      if (*color == '\0')
7641        break;
7642      (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7643        geometry);
7644      if (*geometry == '\0')
7645        break;
7646      /*
7647        Shear image.
7648      */
7649      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7650        exception);
7651      XSetCursorState(display,windows,MagickTrue);
7652      XCheckRefreshWindows(display,windows);
7653      (void) QueryColorCompliance(color,AllCompliance,
7654        &(*image)->background_color,exception);
7655      flags=ParseGeometry(geometry,&geometry_info);
7656      if ((flags & SigmaValue) == 0)
7657        geometry_info.sigma=geometry_info.rho;
7658      shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7659        exception);
7660      if (shear_image != (Image *) NULL)
7661        {
7662          *image=DestroyImage(*image);
7663          *image=shear_image;
7664        }
7665      CatchException(exception);
7666      XSetCursorState(display,windows,MagickFalse);
7667      if (windows->image.orphan != MagickFalse)
7668        break;
7669      windows->image.window_changes.width=(int) (*image)->columns;
7670      windows->image.window_changes.height=(int) (*image)->rows;
7671      XConfigureImageColormap(display,resource_info,windows,*image);
7672      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7673      break;
7674    }
7675    case RollCommand:
7676    {
7677      Image
7678        *roll_image;
7679
7680      static char
7681        geometry[MaxTextExtent] = "+2+2";
7682
7683      /*
7684        Query user for the roll geometry.
7685      */
7686      (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7687        geometry);
7688      if (*geometry == '\0')
7689        break;
7690      /*
7691        Roll image.
7692      */
7693      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7694        exception);
7695      XSetCursorState(display,windows,MagickTrue);
7696      XCheckRefreshWindows(display,windows);
7697      (void) ParsePageGeometry(*image,geometry,&page_geometry,
7698        exception);
7699      roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7700        exception);
7701      if (roll_image != (Image *) NULL)
7702        {
7703          *image=DestroyImage(*image);
7704          *image=roll_image;
7705        }
7706      CatchException(exception);
7707      XSetCursorState(display,windows,MagickFalse);
7708      if (windows->image.orphan != MagickFalse)
7709        break;
7710      windows->image.window_changes.width=(int) (*image)->columns;
7711      windows->image.window_changes.height=(int) (*image)->rows;
7712      XConfigureImageColormap(display,resource_info,windows,*image);
7713      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7714      break;
7715    }
7716    case TrimCommand:
7717    {
7718      static char
7719        fuzz[MaxTextExtent];
7720
7721      /*
7722        Query user for the fuzz factor.
7723      */
7724      (void) FormatLocaleString(fuzz,MaxTextExtent,"%g%%",100.0*
7725        (*image)->fuzz/(QuantumRange+1.0));
7726      (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7727      if (*fuzz == '\0')
7728        break;
7729      (*image)->fuzz=SiPrefixToDouble(fuzz,(double) QuantumRange+1.0);
7730      /*
7731        Trim image.
7732      */
7733      status=XTrimImage(display,resource_info,windows,*image,exception);
7734      if (status == MagickFalse)
7735        {
7736          XNoticeWidget(display,windows,"Unable to trim X image",
7737            (*image)->filename);
7738          break;
7739        }
7740      break;
7741    }
7742    case HueCommand:
7743    {
7744      static char
7745        hue_percent[MaxTextExtent] = "110";
7746
7747      /*
7748        Query user for percent hue change.
7749      */
7750      (void) XDialogWidget(display,windows,"Apply",
7751        "Enter percent change in image hue (0-200):",hue_percent);
7752      if (*hue_percent == '\0')
7753        break;
7754      /*
7755        Vary the image hue.
7756      */
7757      XSetCursorState(display,windows,MagickTrue);
7758      XCheckRefreshWindows(display,windows);
7759      (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7760      (void) ConcatenateMagickString(modulate_factors,hue_percent,
7761        MaxTextExtent);
7762      (void) ModulateImage(*image,modulate_factors,exception);
7763      XSetCursorState(display,windows,MagickFalse);
7764      if (windows->image.orphan != MagickFalse)
7765        break;
7766      XConfigureImageColormap(display,resource_info,windows,*image);
7767      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7768      break;
7769    }
7770    case SaturationCommand:
7771    {
7772      static char
7773        saturation_percent[MaxTextExtent] = "110";
7774
7775      /*
7776        Query user for percent saturation change.
7777      */
7778      (void) XDialogWidget(display,windows,"Apply",
7779        "Enter percent change in color saturation (0-200):",saturation_percent);
7780      if (*saturation_percent == '\0')
7781        break;
7782      /*
7783        Vary color saturation.
7784      */
7785      XSetCursorState(display,windows,MagickTrue);
7786      XCheckRefreshWindows(display,windows);
7787      (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7788      (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7789        MaxTextExtent);
7790      (void) ModulateImage(*image,modulate_factors,exception);
7791      XSetCursorState(display,windows,MagickFalse);
7792      if (windows->image.orphan != MagickFalse)
7793        break;
7794      XConfigureImageColormap(display,resource_info,windows,*image);
7795      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7796      break;
7797    }
7798    case BrightnessCommand:
7799    {
7800      static char
7801        brightness_percent[MaxTextExtent] = "110";
7802
7803      /*
7804        Query user for percent brightness change.
7805      */
7806      (void) XDialogWidget(display,windows,"Apply",
7807        "Enter percent change in color brightness (0-200):",brightness_percent);
7808      if (*brightness_percent == '\0')
7809        break;
7810      /*
7811        Vary the color brightness.
7812      */
7813      XSetCursorState(display,windows,MagickTrue);
7814      XCheckRefreshWindows(display,windows);
7815      (void) CopyMagickString(modulate_factors,brightness_percent,
7816        MaxTextExtent);
7817      (void) ModulateImage(*image,modulate_factors,exception);
7818      XSetCursorState(display,windows,MagickFalse);
7819      if (windows->image.orphan != MagickFalse)
7820        break;
7821      XConfigureImageColormap(display,resource_info,windows,*image);
7822      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7823      break;
7824    }
7825    case GammaCommand:
7826    {
7827      static char
7828        factor[MaxTextExtent] = "1.6";
7829
7830      /*
7831        Query user for gamma value.
7832      */
7833      (void) XDialogWidget(display,windows,"Gamma",
7834        "Enter gamma value (e.g. 1.2):",factor);
7835      if (*factor == '\0')
7836        break;
7837      /*
7838        Gamma correct image.
7839      */
7840      XSetCursorState(display,windows,MagickTrue);
7841      XCheckRefreshWindows(display,windows);
7842      (void) GammaImage(*image,atof(factor),exception);
7843      XSetCursorState(display,windows,MagickFalse);
7844      if (windows->image.orphan != MagickFalse)
7845        break;
7846      XConfigureImageColormap(display,resource_info,windows,*image);
7847      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7848      break;
7849    }
7850    case SpiffCommand:
7851    {
7852      /*
7853        Sharpen the image contrast.
7854      */
7855      XSetCursorState(display,windows,MagickTrue);
7856      XCheckRefreshWindows(display,windows);
7857      (void) ContrastImage(*image,MagickTrue,exception);
7858      XSetCursorState(display,windows,MagickFalse);
7859      if (windows->image.orphan != MagickFalse)
7860        break;
7861      XConfigureImageColormap(display,resource_info,windows,*image);
7862      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7863      break;
7864    }
7865    case DullCommand:
7866    {
7867      /*
7868        Dull the image contrast.
7869      */
7870      XSetCursorState(display,windows,MagickTrue);
7871      XCheckRefreshWindows(display,windows);
7872      (void) ContrastImage(*image,MagickFalse,exception);
7873      XSetCursorState(display,windows,MagickFalse);
7874      if (windows->image.orphan != MagickFalse)
7875        break;
7876      XConfigureImageColormap(display,resource_info,windows,*image);
7877      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7878      break;
7879    }
7880    case ContrastStretchCommand:
7881    {
7882      double
7883        black_point,
7884        white_point;
7885
7886      static char
7887        levels[MaxTextExtent] = "1%";
7888
7889      /*
7890        Query user for gamma value.
7891      */
7892      (void) XDialogWidget(display,windows,"Contrast Stretch",
7893        "Enter black and white points:",levels);
7894      if (*levels == '\0')
7895        break;
7896      /*
7897        Contrast stretch image.
7898      */
7899      XSetCursorState(display,windows,MagickTrue);
7900      XCheckRefreshWindows(display,windows);
7901      flags=ParseGeometry(levels,&geometry_info);
7902      black_point=geometry_info.rho;
7903      white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7904      if ((flags & PercentValue) != 0)
7905        {
7906          black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7907          white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7908        }
7909      white_point=(MagickRealType) (*image)->columns*(*image)->rows-white_point;
7910      (void) ContrastStretchImage(*image,black_point,white_point,
7911        exception);
7912      XSetCursorState(display,windows,MagickFalse);
7913      if (windows->image.orphan != MagickFalse)
7914        break;
7915      XConfigureImageColormap(display,resource_info,windows,*image);
7916      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7917      break;
7918    }
7919    case SigmoidalContrastCommand:
7920    {
7921      GeometryInfo
7922        geometry_info;
7923
7924      MagickStatusType
7925        flags;
7926
7927      static char
7928        levels[MaxTextExtent] = "3x50%";
7929
7930      /*
7931        Query user for gamma value.
7932      */
7933      (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7934        "Enter contrast and midpoint:",levels);
7935      if (*levels == '\0')
7936        break;
7937      /*
7938        Contrast stretch image.
7939      */
7940      XSetCursorState(display,windows,MagickTrue);
7941      XCheckRefreshWindows(display,windows);
7942      flags=ParseGeometry(levels,&geometry_info);
7943      if ((flags & SigmaValue) == 0)
7944        geometry_info.sigma=1.0*QuantumRange/2.0;
7945      if ((flags & PercentValue) != 0)
7946        geometry_info.sigma=1.0*QuantumRange*geometry_info.sigma/100.0;
7947      (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
7948        geometry_info.sigma,exception);
7949      XSetCursorState(display,windows,MagickFalse);
7950      if (windows->image.orphan != MagickFalse)
7951        break;
7952      XConfigureImageColormap(display,resource_info,windows,*image);
7953      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7954      break;
7955    }
7956    case NormalizeCommand:
7957    {
7958      /*
7959        Perform histogram normalization on the image.
7960      */
7961      XSetCursorState(display,windows,MagickTrue);
7962      XCheckRefreshWindows(display,windows);
7963      (void) NormalizeImage(*image,exception);
7964      XSetCursorState(display,windows,MagickFalse);
7965      if (windows->image.orphan != MagickFalse)
7966        break;
7967      XConfigureImageColormap(display,resource_info,windows,*image);
7968      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7969      break;
7970    }
7971    case EqualizeCommand:
7972    {
7973      /*
7974        Perform histogram equalization on the image.
7975      */
7976      XSetCursorState(display,windows,MagickTrue);
7977      XCheckRefreshWindows(display,windows);
7978      (void) EqualizeImage(*image,exception);
7979      XSetCursorState(display,windows,MagickFalse);
7980      if (windows->image.orphan != MagickFalse)
7981        break;
7982      XConfigureImageColormap(display,resource_info,windows,*image);
7983      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7984      break;
7985    }
7986    case NegateCommand:
7987    {
7988      /*
7989        Negate colors in image.
7990      */
7991      XSetCursorState(display,windows,MagickTrue);
7992      XCheckRefreshWindows(display,windows);
7993      (void) NegateImage(*image,MagickFalse,exception);
7994      XSetCursorState(display,windows,MagickFalse);
7995      if (windows->image.orphan != MagickFalse)
7996        break;
7997      XConfigureImageColormap(display,resource_info,windows,*image);
7998      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7999      break;
8000    }
8001    case GrayscaleCommand:
8002    {
8003      /*
8004        Convert image to grayscale.
8005      */
8006      XSetCursorState(display,windows,MagickTrue);
8007      XCheckRefreshWindows(display,windows);
8008      (void) SetImageType(*image,(*image)->matte == MagickFalse ?
8009        GrayscaleType : GrayscaleMatteType,exception);
8010      XSetCursorState(display,windows,MagickFalse);
8011      if (windows->image.orphan != MagickFalse)
8012        break;
8013      XConfigureImageColormap(display,resource_info,windows,*image);
8014      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8015      break;
8016    }
8017    case MapCommand:
8018    {
8019      Image
8020        *affinity_image;
8021
8022      static char
8023        filename[MaxTextExtent] = "\0";
8024
8025      /*
8026        Request image file name from user.
8027      */
8028      XFileBrowserWidget(display,windows,"Map",filename);
8029      if (*filename == '\0')
8030        break;
8031      /*
8032        Map image.
8033      */
8034      XSetCursorState(display,windows,MagickTrue);
8035      XCheckRefreshWindows(display,windows);
8036      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
8037      affinity_image=ReadImage(image_info,exception);
8038      if (affinity_image != (Image *) NULL)
8039        {
8040          (void) RemapImage(&quantize_info,*image,affinity_image,exception);
8041          affinity_image=DestroyImage(affinity_image);
8042        }
8043      CatchException(exception);
8044      XSetCursorState(display,windows,MagickFalse);
8045      if (windows->image.orphan != MagickFalse)
8046        break;
8047      XConfigureImageColormap(display,resource_info,windows,*image);
8048      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8049      break;
8050    }
8051    case QuantizeCommand:
8052    {
8053      int
8054        status;
8055
8056      static char
8057        colors[MaxTextExtent] = "256";
8058
8059      /*
8060        Query user for maximum number of colors.
8061      */
8062      status=XDialogWidget(display,windows,"Quantize",
8063        "Maximum number of colors:",colors);
8064      if (*colors == '\0')
8065        break;
8066      /*
8067        Color reduce the image.
8068      */
8069      XSetCursorState(display,windows,MagickTrue);
8070      XCheckRefreshWindows(display,windows);
8071      quantize_info.number_colors=StringToUnsignedLong(colors);
8072      quantize_info.dither=status != 0 ? MagickTrue : MagickFalse;
8073      (void) QuantizeImage(&quantize_info,*image,exception);
8074      XSetCursorState(display,windows,MagickFalse);
8075      if (windows->image.orphan != MagickFalse)
8076        break;
8077      XConfigureImageColormap(display,resource_info,windows,*image);
8078      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8079      break;
8080    }
8081    case DespeckleCommand:
8082    {
8083      Image
8084        *despeckle_image;
8085
8086      /*
8087        Despeckle image.
8088      */
8089      XSetCursorState(display,windows,MagickTrue);
8090      XCheckRefreshWindows(display,windows);
8091      despeckle_image=DespeckleImage(*image,exception);
8092      if (despeckle_image != (Image *) NULL)
8093        {
8094          *image=DestroyImage(*image);
8095          *image=despeckle_image;
8096        }
8097      CatchException(exception);
8098      XSetCursorState(display,windows,MagickFalse);
8099      if (windows->image.orphan != MagickFalse)
8100        break;
8101      XConfigureImageColormap(display,resource_info,windows,*image);
8102      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8103      break;
8104    }
8105    case EmbossCommand:
8106    {
8107      Image
8108        *emboss_image;
8109
8110      static char
8111        radius[MaxTextExtent] = "0.0x1.0";
8112
8113      /*
8114        Query user for emboss radius.
8115      */
8116      (void) XDialogWidget(display,windows,"Emboss",
8117        "Enter the emboss radius and standard deviation:",radius);
8118      if (*radius == '\0')
8119        break;
8120      /*
8121        Reduce noise in the image.
8122      */
8123      XSetCursorState(display,windows,MagickTrue);
8124      XCheckRefreshWindows(display,windows);
8125      flags=ParseGeometry(radius,&geometry_info);
8126      if ((flags & SigmaValue) == 0)
8127        geometry_info.sigma=1.0;
8128      emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8129        exception);
8130      if (emboss_image != (Image *) NULL)
8131        {
8132          *image=DestroyImage(*image);
8133          *image=emboss_image;
8134        }
8135      CatchException(exception);
8136      XSetCursorState(display,windows,MagickFalse);
8137      if (windows->image.orphan != MagickFalse)
8138        break;
8139      XConfigureImageColormap(display,resource_info,windows,*image);
8140      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8141      break;
8142    }
8143    case ReduceNoiseCommand:
8144    {
8145      Image
8146        *noise_image;
8147
8148      static char
8149        radius[MaxTextExtent] = "0";
8150
8151      /*
8152        Query user for noise radius.
8153      */
8154      (void) XDialogWidget(display,windows,"Reduce Noise",
8155        "Enter the noise radius:",radius);
8156      if (*radius == '\0')
8157        break;
8158      /*
8159        Reduce noise in the image.
8160      */
8161      XSetCursorState(display,windows,MagickTrue);
8162      XCheckRefreshWindows(display,windows);
8163      flags=ParseGeometry(radius,&geometry_info);
8164      noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8165        geometry_info.rho,(size_t) geometry_info.rho,exception);
8166      if (noise_image != (Image *) NULL)
8167        {
8168          *image=DestroyImage(*image);
8169          *image=noise_image;
8170        }
8171      CatchException(exception);
8172      XSetCursorState(display,windows,MagickFalse);
8173      if (windows->image.orphan != MagickFalse)
8174        break;
8175      XConfigureImageColormap(display,resource_info,windows,*image);
8176      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8177      break;
8178    }
8179    case AddNoiseCommand:
8180    {
8181      char
8182        **noises;
8183
8184      Image
8185        *noise_image;
8186
8187      static char
8188        noise_type[MaxTextExtent] = "Gaussian";
8189
8190      /*
8191        Add noise to the image.
8192      */
8193      noises=GetCommandOptions(MagickNoiseOptions);
8194      if (noises == (char **) NULL)
8195        break;
8196      XListBrowserWidget(display,windows,&windows->widget,
8197        (const char **) noises,"Add Noise",
8198        "Select a type of noise to add to your image:",noise_type);
8199      noises=DestroyStringList(noises);
8200      if (*noise_type == '\0')
8201        break;
8202      XSetCursorState(display,windows,MagickTrue);
8203      XCheckRefreshWindows(display,windows);
8204      noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8205        MagickNoiseOptions,MagickFalse,noise_type),1.0,exception);
8206      if (noise_image != (Image *) NULL)
8207        {
8208          *image=DestroyImage(*image);
8209          *image=noise_image;
8210        }
8211      CatchException(exception);
8212      XSetCursorState(display,windows,MagickFalse);
8213      if (windows->image.orphan != MagickFalse)
8214        break;
8215      XConfigureImageColormap(display,resource_info,windows,*image);
8216      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8217      break;
8218    }
8219    case SharpenCommand:
8220    {
8221      Image
8222        *sharp_image;
8223
8224      static char
8225        radius[MaxTextExtent] = "0.0x1.0";
8226
8227      /*
8228        Query user for sharpen radius.
8229      */
8230      (void) XDialogWidget(display,windows,"Sharpen",
8231        "Enter the sharpen radius and standard deviation:",radius);
8232      if (*radius == '\0')
8233        break;
8234      /*
8235        Sharpen image scanlines.
8236      */
8237      XSetCursorState(display,windows,MagickTrue);
8238      XCheckRefreshWindows(display,windows);
8239      flags=ParseGeometry(radius,&geometry_info);
8240      sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8241        geometry_info.xi,exception);
8242      if (sharp_image != (Image *) NULL)
8243        {
8244          *image=DestroyImage(*image);
8245          *image=sharp_image;
8246        }
8247      CatchException(exception);
8248      XSetCursorState(display,windows,MagickFalse);
8249      if (windows->image.orphan != MagickFalse)
8250        break;
8251      XConfigureImageColormap(display,resource_info,windows,*image);
8252      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8253      break;
8254    }
8255    case BlurCommand:
8256    {
8257      Image
8258        *blur_image;
8259
8260      static char
8261        radius[MaxTextExtent] = "0.0x1.0";
8262
8263      /*
8264        Query user for blur radius.
8265      */
8266      (void) XDialogWidget(display,windows,"Blur",
8267        "Enter the blur radius and standard deviation:",radius);
8268      if (*radius == '\0')
8269        break;
8270      /*
8271        Blur an image.
8272      */
8273      XSetCursorState(display,windows,MagickTrue);
8274      XCheckRefreshWindows(display,windows);
8275      flags=ParseGeometry(radius,&geometry_info);
8276      blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8277        geometry_info.xi,exception);
8278      if (blur_image != (Image *) NULL)
8279        {
8280          *image=DestroyImage(*image);
8281          *image=blur_image;
8282        }
8283      CatchException(exception);
8284      XSetCursorState(display,windows,MagickFalse);
8285      if (windows->image.orphan != MagickFalse)
8286        break;
8287      XConfigureImageColormap(display,resource_info,windows,*image);
8288      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8289      break;
8290    }
8291    case ThresholdCommand:
8292    {
8293      double
8294        threshold;
8295
8296      static char
8297        factor[MaxTextExtent] = "128";
8298
8299      /*
8300        Query user for threshold value.
8301      */
8302      (void) XDialogWidget(display,windows,"Threshold",
8303        "Enter threshold value:",factor);
8304      if (*factor == '\0')
8305        break;
8306      /*
8307        Gamma correct image.
8308      */
8309      XSetCursorState(display,windows,MagickTrue);
8310      XCheckRefreshWindows(display,windows);
8311      threshold=SiPrefixToDouble(factor,QuantumRange);
8312      (void) BilevelImage(*image,threshold,exception);
8313      XSetCursorState(display,windows,MagickFalse);
8314      if (windows->image.orphan != MagickFalse)
8315        break;
8316      XConfigureImageColormap(display,resource_info,windows,*image);
8317      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8318      break;
8319    }
8320    case EdgeDetectCommand:
8321    {
8322      Image
8323        *edge_image;
8324
8325      static char
8326        radius[MaxTextExtent] = "0";
8327
8328      /*
8329        Query user for edge factor.
8330      */
8331      (void) XDialogWidget(display,windows,"Detect Edges",
8332        "Enter the edge detect radius:",radius);
8333      if (*radius == '\0')
8334        break;
8335      /*
8336        Detect edge in image.
8337      */
8338      XSetCursorState(display,windows,MagickTrue);
8339      XCheckRefreshWindows(display,windows);
8340      flags=ParseGeometry(radius,&geometry_info);
8341      edge_image=EdgeImage(*image,geometry_info.rho,geometry_info.sigma,
8342        exception);
8343      if (edge_image != (Image *) NULL)
8344        {
8345          *image=DestroyImage(*image);
8346          *image=edge_image;
8347        }
8348      CatchException(exception);
8349      XSetCursorState(display,windows,MagickFalse);
8350      if (windows->image.orphan != MagickFalse)
8351        break;
8352      XConfigureImageColormap(display,resource_info,windows,*image);
8353      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8354      break;
8355    }
8356    case SpreadCommand:
8357    {
8358      Image
8359        *spread_image;
8360
8361      static char
8362        amount[MaxTextExtent] = "2";
8363
8364      /*
8365        Query user for spread amount.
8366      */
8367      (void) XDialogWidget(display,windows,"Spread",
8368        "Enter the displacement amount:",amount);
8369      if (*amount == '\0')
8370        break;
8371      /*
8372        Displace image pixels by a random amount.
8373      */
8374      XSetCursorState(display,windows,MagickTrue);
8375      XCheckRefreshWindows(display,windows);
8376      flags=ParseGeometry(amount,&geometry_info);
8377      spread_image=EdgeImage(*image,geometry_info.rho,geometry_info.sigma,
8378        exception);
8379      if (spread_image != (Image *) NULL)
8380        {
8381          *image=DestroyImage(*image);
8382          *image=spread_image;
8383        }
8384      CatchException(exception);
8385      XSetCursorState(display,windows,MagickFalse);
8386      if (windows->image.orphan != MagickFalse)
8387        break;
8388      XConfigureImageColormap(display,resource_info,windows,*image);
8389      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8390      break;
8391    }
8392    case ShadeCommand:
8393    {
8394      Image
8395        *shade_image;
8396
8397      int
8398        status;
8399
8400      static char
8401        geometry[MaxTextExtent] = "30x30";
8402
8403      /*
8404        Query user for the shade geometry.
8405      */
8406      status=XDialogWidget(display,windows,"Shade",
8407        "Enter the azimuth and elevation of the light source:",geometry);
8408      if (*geometry == '\0')
8409        break;
8410      /*
8411        Shade image pixels.
8412      */
8413      XSetCursorState(display,windows,MagickTrue);
8414      XCheckRefreshWindows(display,windows);
8415      flags=ParseGeometry(geometry,&geometry_info);
8416      if ((flags & SigmaValue) == 0)
8417        geometry_info.sigma=1.0;
8418      shade_image=ShadeImage(*image,status != 0 ? MagickFalse : MagickTrue,
8419        geometry_info.rho,geometry_info.sigma,exception);
8420      if (shade_image != (Image *) NULL)
8421        {
8422          *image=DestroyImage(*image);
8423          *image=shade_image;
8424        }
8425      CatchException(exception);
8426      XSetCursorState(display,windows,MagickFalse);
8427      if (windows->image.orphan != MagickFalse)
8428        break;
8429      XConfigureImageColormap(display,resource_info,windows,*image);
8430      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8431      break;
8432    }
8433    case RaiseCommand:
8434    {
8435      static char
8436        bevel_width[MaxTextExtent] = "10";
8437
8438      /*
8439        Query user for bevel width.
8440      */
8441      (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8442      if (*bevel_width == '\0')
8443        break;
8444      /*
8445        Raise an image.
8446      */
8447      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8448        exception);
8449      XSetCursorState(display,windows,MagickTrue);
8450      XCheckRefreshWindows(display,windows);
8451      (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8452        exception);
8453      (void) RaiseImage(*image,&page_geometry,MagickTrue,exception);
8454      XSetCursorState(display,windows,MagickFalse);
8455      if (windows->image.orphan != MagickFalse)
8456        break;
8457      XConfigureImageColormap(display,resource_info,windows,*image);
8458      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8459      break;
8460    }
8461    case SegmentCommand:
8462    {
8463      static char
8464        threshold[MaxTextExtent] = "1.0x1.5";
8465
8466      /*
8467        Query user for smoothing threshold.
8468      */
8469      (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8470        threshold);
8471      if (*threshold == '\0')
8472        break;
8473      /*
8474        Segment an image.
8475      */
8476      XSetCursorState(display,windows,MagickTrue);
8477      XCheckRefreshWindows(display,windows);
8478      flags=ParseGeometry(threshold,&geometry_info);
8479      if ((flags & SigmaValue) == 0)
8480        geometry_info.sigma=1.0;
8481      (void) SegmentImage(*image,RGBColorspace,MagickFalse,geometry_info.rho,
8482        geometry_info.sigma,exception);
8483      XSetCursorState(display,windows,MagickFalse);
8484      if (windows->image.orphan != MagickFalse)
8485        break;
8486      XConfigureImageColormap(display,resource_info,windows,*image);
8487      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8488      break;
8489    }
8490    case SepiaToneCommand:
8491    {
8492      double
8493        threshold;
8494
8495      Image
8496        *sepia_image;
8497
8498      static char
8499        factor[MaxTextExtent] = "80%";
8500
8501      /*
8502        Query user for sepia-tone factor.
8503      */
8504      (void) XDialogWidget(display,windows,"Sepia Tone",
8505        "Enter the sepia tone factor (0 - 99.9%):",factor);
8506      if (*factor == '\0')
8507        break;
8508      /*
8509        Sepia tone image pixels.
8510      */
8511      XSetCursorState(display,windows,MagickTrue);
8512      XCheckRefreshWindows(display,windows);
8513      threshold=SiPrefixToDouble(factor,QuantumRange);
8514      sepia_image=SepiaToneImage(*image,threshold,exception);
8515      if (sepia_image != (Image *) NULL)
8516        {
8517          *image=DestroyImage(*image);
8518          *image=sepia_image;
8519        }
8520      CatchException(exception);
8521      XSetCursorState(display,windows,MagickFalse);
8522      if (windows->image.orphan != MagickFalse)
8523        break;
8524      XConfigureImageColormap(display,resource_info,windows,*image);
8525      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8526      break;
8527    }
8528    case SolarizeCommand:
8529    {
8530      double
8531        threshold;
8532
8533      static char
8534        factor[MaxTextExtent] = "60%";
8535
8536      /*
8537        Query user for solarize factor.
8538      */
8539      (void) XDialogWidget(display,windows,"Solarize",
8540        "Enter the solarize factor (0 - 99.9%):",factor);
8541      if (*factor == '\0')
8542        break;
8543      /*
8544        Solarize image pixels.
8545      */
8546      XSetCursorState(display,windows,MagickTrue);
8547      XCheckRefreshWindows(display,windows);
8548      threshold=SiPrefixToDouble(factor,QuantumRange);
8549      (void) SolarizeImage(*image,threshold,exception);
8550      XSetCursorState(display,windows,MagickFalse);
8551      if (windows->image.orphan != MagickFalse)
8552        break;
8553      XConfigureImageColormap(display,resource_info,windows,*image);
8554      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8555      break;
8556    }
8557    case SwirlCommand:
8558    {
8559      Image
8560        *swirl_image;
8561
8562      static char
8563        degrees[MaxTextExtent] = "60";
8564
8565      /*
8566        Query user for swirl angle.
8567      */
8568      (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8569        degrees);
8570      if (*degrees == '\0')
8571        break;
8572      /*
8573        Swirl image pixels about the center.
8574      */
8575      XSetCursorState(display,windows,MagickTrue);
8576      XCheckRefreshWindows(display,windows);
8577      flags=ParseGeometry(degrees,&geometry_info);
8578      swirl_image=SwirlImage(*image,geometry_info.rho,(*image)->interpolate,
8579        exception);
8580      if (swirl_image != (Image *) NULL)
8581        {
8582          *image=DestroyImage(*image);
8583          *image=swirl_image;
8584        }
8585      CatchException(exception);
8586      XSetCursorState(display,windows,MagickFalse);
8587      if (windows->image.orphan != MagickFalse)
8588        break;
8589      XConfigureImageColormap(display,resource_info,windows,*image);
8590      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8591      break;
8592    }
8593    case ImplodeCommand:
8594    {
8595      Image
8596        *implode_image;
8597
8598      static char
8599        factor[MaxTextExtent] = "0.3";
8600
8601      /*
8602        Query user for implode factor.
8603      */
8604      (void) XDialogWidget(display,windows,"Implode",
8605        "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8606      if (*factor == '\0')
8607        break;
8608      /*
8609        Implode image pixels about the center.
8610      */
8611      XSetCursorState(display,windows,MagickTrue);
8612      XCheckRefreshWindows(display,windows);
8613      flags=ParseGeometry(factor,&geometry_info);
8614      implode_image=ImplodeImage(*image,geometry_info.rho,(*image)->interpolate,
8615        exception);
8616      if (implode_image != (Image *) NULL)
8617        {
8618          *image=DestroyImage(*image);
8619          *image=implode_image;
8620        }
8621      CatchException(exception);
8622      XSetCursorState(display,windows,MagickFalse);
8623      if (windows->image.orphan != MagickFalse)
8624        break;
8625      XConfigureImageColormap(display,resource_info,windows,*image);
8626      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8627      break;
8628    }
8629    case VignetteCommand:
8630    {
8631      Image
8632        *vignette_image;
8633
8634      static char
8635        geometry[MaxTextExtent] = "0x20";
8636
8637      /*
8638        Query user for the vignette geometry.
8639      */
8640      (void) XDialogWidget(display,windows,"Vignette",
8641        "Enter the radius, sigma, and x and y offsets:",geometry);
8642      if (*geometry == '\0')
8643        break;
8644      /*
8645        Soften the edges of the image in vignette style
8646      */
8647      XSetCursorState(display,windows,MagickTrue);
8648      XCheckRefreshWindows(display,windows);
8649      flags=ParseGeometry(geometry,&geometry_info);
8650      if ((flags & SigmaValue) == 0)
8651        geometry_info.sigma=1.0;
8652      if ((flags & XiValue) == 0)
8653        geometry_info.xi=0.1*(*image)->columns;
8654      if ((flags & PsiValue) == 0)
8655        geometry_info.psi=0.1*(*image)->rows;
8656      vignette_image=VignetteImage(*image,geometry_info.rho,geometry_info.sigma,
8657        (ssize_t) ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-
8658        0.5),exception);
8659      if (vignette_image != (Image *) NULL)
8660        {
8661          *image=DestroyImage(*image);
8662          *image=vignette_image;
8663        }
8664      CatchException(exception);
8665      XSetCursorState(display,windows,MagickFalse);
8666      if (windows->image.orphan != MagickFalse)
8667        break;
8668      XConfigureImageColormap(display,resource_info,windows,*image);
8669      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8670      break;
8671    }
8672    case WaveCommand:
8673    {
8674      Image
8675        *wave_image;
8676
8677      static char
8678        geometry[MaxTextExtent] = "25x150";
8679
8680      /*
8681        Query user for the wave geometry.
8682      */
8683      (void) XDialogWidget(display,windows,"Wave",
8684        "Enter the amplitude and length of the wave:",geometry);
8685      if (*geometry == '\0')
8686        break;
8687      /*
8688        Alter an image along a sine wave.
8689      */
8690      XSetCursorState(display,windows,MagickTrue);
8691      XCheckRefreshWindows(display,windows);
8692      flags=ParseGeometry(geometry,&geometry_info);
8693      if ((flags & SigmaValue) == 0)
8694        geometry_info.sigma=1.0;
8695      wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8696        (*image)->interpolate,exception);
8697      if (wave_image != (Image *) NULL)
8698        {
8699          *image=DestroyImage(*image);
8700          *image=wave_image;
8701        }
8702      CatchException(exception);
8703      XSetCursorState(display,windows,MagickFalse);
8704      if (windows->image.orphan != MagickFalse)
8705        break;
8706      XConfigureImageColormap(display,resource_info,windows,*image);
8707      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8708      break;
8709    }
8710    case OilPaintCommand:
8711    {
8712      Image
8713        *paint_image;
8714
8715      static char
8716        radius[MaxTextExtent] = "0";
8717
8718      /*
8719        Query user for circular neighborhood radius.
8720      */
8721      (void) XDialogWidget(display,windows,"Oil Paint",
8722        "Enter the mask radius:",radius);
8723      if (*radius == '\0')
8724        break;
8725      /*
8726        OilPaint image scanlines.
8727      */
8728      XSetCursorState(display,windows,MagickTrue);
8729      XCheckRefreshWindows(display,windows);
8730      flags=ParseGeometry(radius,&geometry_info);
8731      paint_image=OilPaintImage(*image,geometry_info.rho,geometry_info.sigma,
8732        exception);
8733      if (paint_image != (Image *) NULL)
8734        {
8735          *image=DestroyImage(*image);
8736          *image=paint_image;
8737        }
8738      CatchException(exception);
8739      XSetCursorState(display,windows,MagickFalse);
8740      if (windows->image.orphan != MagickFalse)
8741        break;
8742      XConfigureImageColormap(display,resource_info,windows,*image);
8743      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8744      break;
8745    }
8746    case CharcoalDrawCommand:
8747    {
8748      Image
8749        *charcoal_image;
8750
8751      static char
8752        radius[MaxTextExtent] = "0x1";
8753
8754      /*
8755        Query user for charcoal radius.
8756      */
8757      (void) XDialogWidget(display,windows,"Charcoal Draw",
8758        "Enter the charcoal radius and sigma:",radius);
8759      if (*radius == '\0')
8760        break;
8761      /*
8762        Charcoal the image.
8763      */
8764      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8765        exception);
8766      XSetCursorState(display,windows,MagickTrue);
8767      XCheckRefreshWindows(display,windows);
8768      flags=ParseGeometry(radius,&geometry_info);
8769      if ((flags & SigmaValue) == 0)
8770        geometry_info.sigma=geometry_info.rho;
8771      charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8772        geometry_info.xi,exception);
8773      if (charcoal_image != (Image *) NULL)
8774        {
8775          *image=DestroyImage(*image);
8776          *image=charcoal_image;
8777        }
8778      CatchException(exception);
8779      XSetCursorState(display,windows,MagickFalse);
8780      if (windows->image.orphan != MagickFalse)
8781        break;
8782      XConfigureImageColormap(display,resource_info,windows,*image);
8783      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8784      break;
8785    }
8786    case AnnotateCommand:
8787    {
8788      /*
8789        Annotate the image with text.
8790      */
8791      status=XAnnotateEditImage(display,resource_info,windows,*image,exception);
8792      if (status == MagickFalse)
8793        {
8794          XNoticeWidget(display,windows,"Unable to annotate X image",
8795            (*image)->filename);
8796          break;
8797        }
8798      break;
8799    }
8800    case DrawCommand:
8801    {
8802      /*
8803        Draw image.
8804      */
8805      status=XDrawEditImage(display,resource_info,windows,image,exception);
8806      if (status == MagickFalse)
8807        {
8808          XNoticeWidget(display,windows,"Unable to draw on the X image",
8809            (*image)->filename);
8810          break;
8811        }
8812      break;
8813    }
8814    case ColorCommand:
8815    {
8816      /*
8817        Color edit.
8818      */
8819      status=XColorEditImage(display,resource_info,windows,image,exception);
8820      if (status == MagickFalse)
8821        {
8822          XNoticeWidget(display,windows,"Unable to pixel edit X image",
8823            (*image)->filename);
8824          break;
8825        }
8826      break;
8827    }
8828    case MatteCommand:
8829    {
8830      /*
8831        Matte edit.
8832      */
8833      status=XMatteEditImage(display,resource_info,windows,image,exception);
8834      if (status == MagickFalse)
8835        {
8836          XNoticeWidget(display,windows,"Unable to matte edit X image",
8837            (*image)->filename);
8838          break;
8839        }
8840      break;
8841    }
8842    case CompositeCommand:
8843    {
8844      /*
8845        Composite image.
8846      */
8847      status=XCompositeImage(display,resource_info,windows,*image,
8848        exception);
8849      if (status == MagickFalse)
8850        {
8851          XNoticeWidget(display,windows,"Unable to composite X image",
8852            (*image)->filename);
8853          break;
8854        }
8855      break;
8856    }
8857    case AddBorderCommand:
8858    {
8859      Image
8860        *border_image;
8861
8862      static char
8863        geometry[MaxTextExtent] = "6x6";
8864
8865      /*
8866        Query user for border color and geometry.
8867      */
8868      XColorBrowserWidget(display,windows,"Select",color);
8869      if (*color == '\0')
8870        break;
8871      (void) XDialogWidget(display,windows,"Add Border",
8872        "Enter border geometry:",geometry);
8873      if (*geometry == '\0')
8874        break;
8875      /*
8876        Add a border to the image.
8877      */
8878      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8879        exception);
8880      XSetCursorState(display,windows,MagickTrue);
8881      XCheckRefreshWindows(display,windows);
8882      (void) QueryColorCompliance(color,AllCompliance,&(*image)->border_color,
8883        exception);
8884      (void) ParsePageGeometry(*image,geometry,&page_geometry,
8885        exception);
8886      border_image=BorderImage(*image,&page_geometry,(*image)->compose,
8887        exception);
8888      if (border_image != (Image *) NULL)
8889        {
8890          *image=DestroyImage(*image);
8891          *image=border_image;
8892        }
8893      CatchException(exception);
8894      XSetCursorState(display,windows,MagickFalse);
8895      if (windows->image.orphan != MagickFalse)
8896        break;
8897      windows->image.window_changes.width=(int) (*image)->columns;
8898      windows->image.window_changes.height=(int) (*image)->rows;
8899      XConfigureImageColormap(display,resource_info,windows,*image);
8900      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8901      break;
8902    }
8903    case AddFrameCommand:
8904    {
8905      FrameInfo
8906        frame_info;
8907
8908      Image
8909        *frame_image;
8910
8911      static char
8912        geometry[MaxTextExtent] = "6x6";
8913
8914      /*
8915        Query user for frame color and geometry.
8916      */
8917      XColorBrowserWidget(display,windows,"Select",color);
8918      if (*color == '\0')
8919        break;
8920      (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8921        geometry);
8922      if (*geometry == '\0')
8923        break;
8924      /*
8925        Surround image with an ornamental border.
8926      */
8927      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8928        exception);
8929      XSetCursorState(display,windows,MagickTrue);
8930      XCheckRefreshWindows(display,windows);
8931      (void) QueryColorCompliance(color,AllCompliance,&(*image)->matte_color,
8932        exception);
8933      (void) ParsePageGeometry(*image,geometry,&page_geometry,
8934        exception);
8935      frame_info.width=page_geometry.width;
8936      frame_info.height=page_geometry.height;
8937      frame_info.outer_bevel=page_geometry.x;
8938      frame_info.inner_bevel=page_geometry.y;
8939      frame_info.x=(ssize_t) frame_info.width;
8940      frame_info.y=(ssize_t) frame_info.height;
8941      frame_info.width=(*image)->columns+2*frame_info.width;
8942      frame_info.height=(*image)->rows+2*frame_info.height;
8943      frame_image=FrameImage(*image,&frame_info,(*image)->compose,exception);
8944      if (frame_image != (Image *) NULL)
8945        {
8946          *image=DestroyImage(*image);
8947          *image=frame_image;
8948        }
8949      CatchException(exception);
8950      XSetCursorState(display,windows,MagickFalse);
8951      if (windows->image.orphan != MagickFalse)
8952        break;
8953      windows->image.window_changes.width=(int) (*image)->columns;
8954      windows->image.window_changes.height=(int) (*image)->rows;
8955      XConfigureImageColormap(display,resource_info,windows,*image);
8956      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8957      break;
8958    }
8959    case CommentCommand:
8960    {
8961      const char
8962        *value;
8963
8964      FILE
8965        *file;
8966
8967      int
8968        unique_file;
8969
8970      /*
8971        Edit image comment.
8972      */
8973      unique_file=AcquireUniqueFileResource(image_info->filename);
8974      if (unique_file == -1)
8975        XNoticeWidget(display,windows,"Unable to edit image comment",
8976          image_info->filename);
8977      value=GetImageProperty(*image,"comment",exception);
8978      if (value == (char *) NULL)
8979        unique_file=close(unique_file)-1;
8980      else
8981        {
8982          register const char
8983            *p;
8984
8985          file=fdopen(unique_file,"w");
8986          if (file == (FILE *) NULL)
8987            {
8988              XNoticeWidget(display,windows,"Unable to edit image comment",
8989                image_info->filename);
8990              break;
8991            }
8992          for (p=value; *p != '\0'; p++)
8993            (void) fputc((int) *p,file);
8994          (void) fputc('\n',file);
8995          (void) fclose(file);
8996        }
8997      XSetCursorState(display,windows,MagickTrue);
8998      XCheckRefreshWindows(display,windows);
8999      status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
9000        exception);
9001      if (status == MagickFalse)
9002        XNoticeWidget(display,windows,"Unable to edit image comment",
9003          (char *) NULL);
9004      else
9005        {
9006          char
9007            *comment;
9008
9009          comment=FileToString(image_info->filename,~0UL,exception);
9010          if (comment != (char *) NULL)
9011            {
9012              (void) SetImageProperty(*image,"comment",comment,exception);
9013              (*image)->taint=MagickTrue;
9014            }
9015        }
9016      (void) RelinquishUniqueFileResource(image_info->filename);
9017      XSetCursorState(display,windows,MagickFalse);
9018      break;
9019    }
9020    case LaunchCommand:
9021    {
9022      /*
9023        Launch program.
9024      */
9025      XSetCursorState(display,windows,MagickTrue);
9026      XCheckRefreshWindows(display,windows);
9027      (void) AcquireUniqueFilename(filename);
9028      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"launch:%s",
9029        filename);
9030      status=WriteImage(image_info,*image,exception);
9031      if (status == MagickFalse)
9032        XNoticeWidget(display,windows,"Unable to launch image editor",
9033          (char *) NULL);
9034      else
9035        {
9036          nexus=ReadImage(resource_info->image_info,exception);
9037          CatchException(exception);
9038          XClientMessage(display,windows->image.id,windows->im_protocols,
9039            windows->im_next_image,CurrentTime);
9040        }
9041      (void) RelinquishUniqueFileResource(filename);
9042      XSetCursorState(display,windows,MagickFalse);
9043      break;
9044    }
9045    case RegionofInterestCommand:
9046    {
9047      /*
9048        Apply an image processing technique to a region of interest.
9049      */
9050      (void) XROIImage(display,resource_info,windows,image,exception);
9051      break;
9052    }
9053    case InfoCommand:
9054      break;
9055    case ZoomCommand:
9056    {
9057      /*
9058        Zoom image.
9059      */
9060      if (windows->magnify.mapped != MagickFalse)
9061        (void) XRaiseWindow(display,windows->magnify.id);
9062      else
9063        {
9064          /*
9065            Make magnify image.
9066          */
9067          XSetCursorState(display,windows,MagickTrue);
9068          (void) XMapRaised(display,windows->magnify.id);
9069          XSetCursorState(display,windows,MagickFalse);
9070        }
9071      break;
9072    }
9073    case ShowPreviewCommand:
9074    {
9075      char
9076        **previews;
9077
9078      Image
9079        *preview_image;
9080
9081      static char
9082        preview_type[MaxTextExtent] = "Gamma";
9083
9084      /*
9085        Select preview type from menu.
9086      */
9087      previews=GetCommandOptions(MagickPreviewOptions);
9088      if (previews == (char **) NULL)
9089        break;
9090      XListBrowserWidget(display,windows,&windows->widget,
9091        (const char **) previews,"Preview",
9092        "Select an enhancement, effect, or F/X:",preview_type);
9093      previews=DestroyStringList(previews);
9094      if (*preview_type == '\0')
9095        break;
9096      /*
9097        Show image preview.
9098      */
9099      XSetCursorState(display,windows,MagickTrue);
9100      XCheckRefreshWindows(display,windows);
9101      image_info->preview_type=(PreviewType)
9102        ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
9103      image_info->group=(ssize_t) windows->image.id;
9104      (void) DeleteImageProperty(*image,"label");
9105      (void) SetImageProperty(*image,"label","Preview",exception);
9106      (void) AcquireUniqueFilename(filename);
9107      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"preview:%s",
9108        filename);
9109      status=WriteImage(image_info,*image,exception);
9110      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9111      preview_image=ReadImage(image_info,exception);
9112      (void) RelinquishUniqueFileResource(filename);
9113      if (preview_image == (Image *) NULL)
9114        break;
9115      (void) FormatLocaleString(preview_image->filename,MaxTextExtent,"show:%s",
9116        filename);
9117      status=WriteImage(image_info,preview_image,exception);
9118      preview_image=DestroyImage(preview_image);
9119      if (status == MagickFalse)
9120        XNoticeWidget(display,windows,"Unable to show image preview",
9121          (*image)->filename);
9122      XDelay(display,1500);
9123      XSetCursorState(display,windows,MagickFalse);
9124      break;
9125    }
9126    case ShowHistogramCommand:
9127    {
9128      Image
9129        *histogram_image;
9130
9131      /*
9132        Show image histogram.
9133      */
9134      XSetCursorState(display,windows,MagickTrue);
9135      XCheckRefreshWindows(display,windows);
9136      image_info->group=(ssize_t) windows->image.id;
9137      (void) DeleteImageProperty(*image,"label");
9138      (void) SetImageProperty(*image,"label","Histogram",exception);
9139      (void) AcquireUniqueFilename(filename);
9140      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"histogram:%s",
9141        filename);
9142      status=WriteImage(image_info,*image,exception);
9143      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9144      histogram_image=ReadImage(image_info,exception);
9145      (void) RelinquishUniqueFileResource(filename);
9146      if (histogram_image == (Image *) NULL)
9147        break;
9148      (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
9149        "show:%s",filename);
9150      status=WriteImage(image_info,histogram_image,exception);
9151      histogram_image=DestroyImage(histogram_image);
9152      if (status == MagickFalse)
9153        XNoticeWidget(display,windows,"Unable to show histogram",
9154          (*image)->filename);
9155      XDelay(display,1500);
9156      XSetCursorState(display,windows,MagickFalse);
9157      break;
9158    }
9159    case ShowMatteCommand:
9160    {
9161      Image
9162        *matte_image;
9163
9164      if ((*image)->matte == MagickFalse)
9165        {
9166          XNoticeWidget(display,windows,
9167            "Image does not have any matte information",(*image)->filename);
9168          break;
9169        }
9170      /*
9171        Show image matte.
9172      */
9173      XSetCursorState(display,windows,MagickTrue);
9174      XCheckRefreshWindows(display,windows);
9175      image_info->group=(ssize_t) windows->image.id;
9176      (void) DeleteImageProperty(*image,"label");
9177      (void) SetImageProperty(*image,"label","Matte",exception);
9178      (void) AcquireUniqueFilename(filename);
9179      (void) FormatLocaleString((*image)->filename,MaxTextExtent,"matte:%s",
9180        filename);
9181      status=WriteImage(image_info,*image,exception);
9182      (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9183      matte_image=ReadImage(image_info,exception);
9184      (void) RelinquishUniqueFileResource(filename);
9185      if (matte_image == (Image *) NULL)
9186        break;
9187      (void) FormatLocaleString(matte_image->filename,MaxTextExtent,"show:%s",
9188        filename);
9189      status=WriteImage(image_info,matte_image,exception);
9190      matte_image=DestroyImage(matte_image);
9191      if (status == MagickFalse)
9192        XNoticeWidget(display,windows,"Unable to show matte",
9193          (*image)->filename);
9194      XDelay(display,1500);
9195      XSetCursorState(display,windows,MagickFalse);
9196      break;
9197    }
9198    case BackgroundCommand:
9199    {
9200      /*
9201        Background image.
9202      */
9203      status=XBackgroundImage(display,resource_info,windows,image,exception);
9204      if (status == MagickFalse)
9205        break;
9206      nexus=CloneImage(*image,0,0,MagickTrue,exception);
9207      if (nexus != (Image *) NULL)
9208        XClientMessage(display,windows->image.id,windows->im_protocols,
9209          windows->im_next_image,CurrentTime);
9210      break;
9211    }
9212    case SlideShowCommand:
9213    {
9214      static char
9215        delay[MaxTextExtent] = "5";
9216
9217      /*
9218        Display next image after pausing.
9219      */
9220      (void) XDialogWidget(display,windows,"Slide Show",
9221        "Pause how many 1/100ths of a second between images:",delay);
9222      if (*delay == '\0')
9223        break;
9224      resource_info->delay=StringToUnsignedLong(delay);
9225      XClientMessage(display,windows->image.id,windows->im_protocols,
9226        windows->im_next_image,CurrentTime);
9227      break;
9228    }
9229    case PreferencesCommand:
9230    {
9231      /*
9232        Set user preferences.
9233      */
9234      status=XPreferencesWidget(display,resource_info,windows);
9235      if (status == MagickFalse)
9236        break;
9237      nexus=CloneImage(*image,0,0,MagickTrue,exception);
9238      if (nexus != (Image *) NULL)
9239        XClientMessage(display,windows->image.id,windows->im_protocols,
9240          windows->im_next_image,CurrentTime);
9241      break;
9242    }
9243    case HelpCommand:
9244    {
9245      /*
9246        User requested help.
9247      */
9248      XTextViewWidget(display,resource_info,windows,MagickFalse,
9249        "Help Viewer - Display",DisplayHelp);
9250      break;
9251    }
9252    case BrowseDocumentationCommand:
9253    {
9254      Atom
9255        mozilla_atom;
9256
9257      Window
9258        mozilla_window,
9259        root_window;
9260
9261      /*
9262        Browse the ImageMagick documentation.
9263      */
9264      root_window=XRootWindow(display,XDefaultScreen(display));
9265      mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9266      mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9267      if (mozilla_window != (Window) NULL)
9268        {
9269          char
9270            command[MaxTextExtent],
9271            *url;
9272
9273          /*
9274            Display documentation using Netscape remote control.
9275          */
9276          url=GetMagickHomeURL();
9277          (void) FormatLocaleString(command,MaxTextExtent,
9278            "openurl(%s,new-tab)",url);
9279          url=DestroyString(url);
9280          mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9281          (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9282            8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9283          XSetCursorState(display,windows,MagickFalse);
9284          break;
9285        }
9286      XSetCursorState(display,windows,MagickTrue);
9287      XCheckRefreshWindows(display,windows);
9288      status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9289        exception);
9290      if (status == MagickFalse)
9291        XNoticeWidget(display,windows,"Unable to browse documentation",
9292          (char *) NULL);
9293      XDelay(display,1500);
9294      XSetCursorState(display,windows,MagickFalse);
9295      break;
9296    }
9297    case VersionCommand:
9298    {
9299      XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9300        GetMagickCopyright());
9301      break;
9302    }
9303    case SaveToUndoBufferCommand:
9304      break;
9305    default:
9306    {
9307      (void) XBell(display,0);
9308      break;
9309    }
9310  }
9311  image_info=DestroyImageInfo(image_info);
9312  return(nexus);
9313}
9314
9315/*
9316%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9317%                                                                             %
9318%                                                                             %
9319%                                                                             %
9320+   X M a g n i f y I m a g e                                                 %
9321%                                                                             %
9322%                                                                             %
9323%                                                                             %
9324%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9325%
9326%  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9327%  The magnified portion is displayed in a separate window.
9328%
9329%  The format of the XMagnifyImage method is:
9330%
9331%      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9332%
9333%  A description of each parameter follows:
9334%
9335%    o display: Specifies a connection to an X server;  returned from
9336%      XOpenDisplay.
9337%
9338%    o windows: Specifies a pointer to a XWindows structure.
9339%
9340%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
9341%      the entire image is refreshed.
9342%
9343*/
9344static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9345{
9346  char
9347    text[MaxTextExtent];
9348
9349  register int
9350    x,
9351    y;
9352
9353  size_t
9354    state;
9355
9356  /*
9357    Update magnified image until the mouse button is released.
9358  */
9359  (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9360  state=DefaultState;
9361  x=event->xbutton.x;
9362  y=event->xbutton.y;
9363  windows->magnify.x=(int) windows->image.x+x;
9364  windows->magnify.y=(int) windows->image.y+y;
9365  do
9366  {
9367    /*
9368      Map and unmap Info widget as text cursor crosses its boundaries.
9369    */
9370    if (windows->info.mapped != MagickFalse)
9371      {
9372        if ((x < (int) (windows->info.x+windows->info.width)) &&
9373            (y < (int) (windows->info.y+windows->info.height)))
9374          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9375      }
9376    else
9377      if ((x > (int) (windows->info.x+windows->info.width)) ||
9378          (y > (int) (windows->info.y+windows->info.height)))
9379        (void) XMapWindow(display,windows->info.id);
9380    if (windows->info.mapped != MagickFalse)
9381      {
9382        /*
9383          Display pointer position.
9384        */
9385        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9386          windows->magnify.x,windows->magnify.y);
9387        XInfoWidget(display,windows,text);
9388      }
9389    /*
9390      Wait for next event.
9391    */
9392    XScreenEvent(display,windows,event);
9393    switch (event->type)
9394    {
9395      case ButtonPress:
9396        break;
9397      case ButtonRelease:
9398      {
9399        /*
9400          User has finished magnifying image.
9401        */
9402        x=event->xbutton.x;
9403        y=event->xbutton.y;
9404        state|=ExitState;
9405        break;
9406      }
9407      case Expose:
9408        break;
9409      case MotionNotify:
9410      {
9411        x=event->xmotion.x;
9412        y=event->xmotion.y;
9413        break;
9414      }
9415      default:
9416        break;
9417    }
9418    /*
9419      Check boundary conditions.
9420    */
9421    if (x < 0)
9422      x=0;
9423    else
9424      if (x >= (int) windows->image.width)
9425        x=(int) windows->image.width-1;
9426    if (y < 0)
9427      y=0;
9428    else
9429     if (y >= (int) windows->image.height)
9430       y=(int) windows->image.height-1;
9431  } while ((state & ExitState) == 0);
9432  /*
9433    Display magnified image.
9434  */
9435  XSetCursorState(display,windows,MagickFalse);
9436}
9437
9438/*
9439%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9440%                                                                             %
9441%                                                                             %
9442%                                                                             %
9443+   X M a g n i f y W i n d o w C o m m a n d                                 %
9444%                                                                             %
9445%                                                                             %
9446%                                                                             %
9447%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9448%
9449%  XMagnifyWindowCommand() moves the image within an Magnify window by one
9450%  pixel as specified by the key symbol.
9451%
9452%  The format of the XMagnifyWindowCommand method is:
9453%
9454%      void XMagnifyWindowCommand(Display *display,XWindows *windows,
9455%        const MagickStatusType state,const KeySym key_symbol)
9456%
9457%  A description of each parameter follows:
9458%
9459%    o display: Specifies a connection to an X server; returned from
9460%      XOpenDisplay.
9461%
9462%    o windows: Specifies a pointer to a XWindows structure.
9463%
9464%    o state: key mask.
9465%
9466%    o key_symbol: Specifies a KeySym which indicates which side of the image
9467%      to trim.
9468%
9469*/
9470static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9471  const MagickStatusType state,const KeySym key_symbol)
9472{
9473  unsigned int
9474    quantum;
9475
9476  /*
9477    User specified a magnify factor or position.
9478  */
9479  quantum=1;
9480  if ((state & Mod1Mask) != 0)
9481    quantum=10;
9482  switch ((int) key_symbol)
9483  {
9484    case QuitCommand:
9485    {
9486      (void) XWithdrawWindow(display,windows->magnify.id,
9487        windows->magnify.screen);
9488      break;
9489    }
9490    case XK_Home:
9491    case XK_KP_Home:
9492    {
9493      windows->magnify.x=(int) windows->image.width/2;
9494      windows->magnify.y=(int) windows->image.height/2;
9495      break;
9496    }
9497    case XK_Left:
9498    case XK_KP_Left:
9499    {
9500      if (windows->magnify.x > 0)
9501        windows->magnify.x-=quantum;
9502      break;
9503    }
9504    case XK_Up:
9505    case XK_KP_Up:
9506    {
9507      if (windows->magnify.y > 0)
9508        windows->magnify.y-=quantum;
9509      break;
9510    }
9511    case XK_Right:
9512    case XK_KP_Right:
9513    {
9514      if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9515        windows->magnify.x+=quantum;
9516      break;
9517    }
9518    case XK_Down:
9519    case XK_KP_Down:
9520    {
9521      if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9522        windows->magnify.y+=quantum;
9523      break;
9524    }
9525    case XK_0:
9526    case XK_1:
9527    case XK_2:
9528    case XK_3:
9529    case XK_4:
9530    case XK_5:
9531    case XK_6:
9532    case XK_7:
9533    case XK_8:
9534    case XK_9:
9535    {
9536      windows->magnify.data=(key_symbol-XK_0);
9537      break;
9538    }
9539    case XK_KP_0:
9540    case XK_KP_1:
9541    case XK_KP_2:
9542    case XK_KP_3:
9543    case XK_KP_4:
9544    case XK_KP_5:
9545    case XK_KP_6:
9546    case XK_KP_7:
9547    case XK_KP_8:
9548    case XK_KP_9:
9549    {
9550      windows->magnify.data=(key_symbol-XK_KP_0);
9551      break;
9552    }
9553    default:
9554      break;
9555  }
9556  XMakeMagnifyImage(display,windows);
9557}
9558
9559/*
9560%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9561%                                                                             %
9562%                                                                             %
9563%                                                                             %
9564+   X M a k e P a n I m a g e                                                 %
9565%                                                                             %
9566%                                                                             %
9567%                                                                             %
9568%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9569%
9570%  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9571%  icon window.
9572%
9573%  The format of the XMakePanImage method is:
9574%
9575%        void XMakePanImage(Display *display,XResourceInfo *resource_info,
9576%          XWindows *windows,Image *image,ExceptionInfo *exception)
9577%
9578%  A description of each parameter follows:
9579%
9580%    o display: Specifies a connection to an X server;  returned from
9581%      XOpenDisplay.
9582%
9583%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9584%
9585%    o windows: Specifies a pointer to a XWindows structure.
9586%
9587%    o image: the image.
9588%
9589%    o exception: return any errors or warnings in this structure.
9590%
9591*/
9592static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9593  XWindows *windows,Image *image,ExceptionInfo *exception)
9594{
9595  MagickStatusType
9596    status;
9597
9598  /*
9599    Create and display image for panning icon.
9600  */
9601  XSetCursorState(display,windows,MagickTrue);
9602  XCheckRefreshWindows(display,windows);
9603  windows->pan.x=(int) windows->image.x;
9604  windows->pan.y=(int) windows->image.y;
9605  status=XMakeImage(display,resource_info,&windows->pan,image,
9606    windows->pan.width,windows->pan.height,exception);
9607  if (status == MagickFalse)
9608    ThrowXWindowFatalException(ResourceLimitError,
9609     "MemoryAllocationFailed",image->filename);
9610  (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9611    windows->pan.pixmap);
9612  (void) XClearWindow(display,windows->pan.id);
9613  XDrawPanRectangle(display,windows);
9614  XSetCursorState(display,windows,MagickFalse);
9615}
9616
9617/*
9618%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9619%                                                                             %
9620%                                                                             %
9621%                                                                             %
9622+   X M a t t a E d i t I m a g e                                             %
9623%                                                                             %
9624%                                                                             %
9625%                                                                             %
9626%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9627%
9628%  XMatteEditImage() allows the user to interactively change the Matte channel
9629%  of an image.  If the image is PseudoClass it is promoted to DirectClass
9630%  before the matte information is stored.
9631%
9632%  The format of the XMatteEditImage method is:
9633%
9634%      MagickBooleanType XMatteEditImage(Display *display,
9635%        XResourceInfo *resource_info,XWindows *windows,Image **image,
9636%        ExceptionInfo *exception)
9637%
9638%  A description of each parameter follows:
9639%
9640%    o display: Specifies a connection to an X server;  returned from
9641%      XOpenDisplay.
9642%
9643%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9644%
9645%    o windows: Specifies a pointer to a XWindows structure.
9646%
9647%    o image: the image; returned from ReadImage.
9648%
9649%    o exception: return any errors or warnings in this structure.
9650%
9651*/
9652static MagickBooleanType XMatteEditImage(Display *display,
9653  XResourceInfo *resource_info,XWindows *windows,Image **image,
9654  ExceptionInfo *exception)
9655{
9656  static char
9657    matte[MaxTextExtent] = "0";
9658
9659  static const char
9660    *MatteEditMenu[] =
9661    {
9662      "Method",
9663      "Border Color",
9664      "Fuzz",
9665      "Matte Value",
9666      "Undo",
9667      "Help",
9668      "Dismiss",
9669      (char *) NULL
9670    };
9671
9672  static const ModeType
9673    MatteEditCommands[] =
9674    {
9675      MatteEditMethod,
9676      MatteEditBorderCommand,
9677      MatteEditFuzzCommand,
9678      MatteEditValueCommand,
9679      MatteEditUndoCommand,
9680      MatteEditHelpCommand,
9681      MatteEditDismissCommand
9682    };
9683
9684  static PaintMethod
9685    method = PointMethod;
9686
9687  static XColor
9688    border_color = { 0, 0, 0, 0, 0, 0 };
9689
9690  char
9691    command[MaxTextExtent],
9692    text[MaxTextExtent];
9693
9694  Cursor
9695    cursor;
9696
9697  int
9698    entry,
9699    id,
9700    x,
9701    x_offset,
9702    y,
9703    y_offset;
9704
9705  register int
9706    i;
9707
9708  register Quantum
9709    *q;
9710
9711  unsigned int
9712    height,
9713    width;
9714
9715  size_t
9716    state;
9717
9718  XEvent
9719    event;
9720
9721  /*
9722    Map Command widget.
9723  */
9724  (void) CloneString(&windows->command.name,"Matte Edit");
9725  windows->command.data=4;
9726  (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9727  (void) XMapRaised(display,windows->command.id);
9728  XClientMessage(display,windows->image.id,windows->im_protocols,
9729    windows->im_update_widget,CurrentTime);
9730  /*
9731    Make cursor.
9732  */
9733  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9734    resource_info->background_color,resource_info->foreground_color);
9735  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9736  /*
9737    Track pointer until button 1 is pressed.
9738  */
9739  XQueryPosition(display,windows->image.id,&x,&y);
9740  (void) XSelectInput(display,windows->image.id,
9741    windows->image.attributes.event_mask | PointerMotionMask);
9742  state=DefaultState;
9743  do
9744  {
9745    if (windows->info.mapped != MagickFalse)
9746      {
9747        /*
9748          Display pointer position.
9749        */
9750        (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9751          x+windows->image.x,y+windows->image.y);
9752        XInfoWidget(display,windows,text);
9753      }
9754    /*
9755      Wait for next event.
9756    */
9757    XScreenEvent(display,windows,&event);
9758    if (event.xany.window == windows->command.id)
9759      {
9760        /*
9761          Select a command from the Command widget.
9762        */
9763        id=XCommandWidget(display,windows,MatteEditMenu,&event);
9764        if (id < 0)
9765          {
9766            (void) XCheckDefineCursor(display,windows->image.id,cursor);
9767            continue;
9768          }
9769        switch (MatteEditCommands[id])
9770        {
9771          case MatteEditMethod:
9772          {
9773            char
9774              **methods;
9775
9776            /*
9777              Select a method from the pop-up menu.
9778            */
9779            methods=GetCommandOptions(MagickMethodOptions);
9780            if (methods == (char **) NULL)
9781              break;
9782            entry=XMenuWidget(display,windows,MatteEditMenu[id],
9783              (const char **) methods,command);
9784            if (entry >= 0)
9785              method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9786                MagickFalse,methods[entry]);
9787            methods=DestroyStringList(methods);
9788            break;
9789          }
9790          case MatteEditBorderCommand:
9791          {
9792            const char
9793              *ColorMenu[MaxNumberPens];
9794
9795            int
9796              pen_number;
9797
9798            /*
9799              Initialize menu selections.
9800            */
9801            for (i=0; i < (int) (MaxNumberPens-2); i++)
9802              ColorMenu[i]=resource_info->pen_colors[i];
9803            ColorMenu[MaxNumberPens-2]="Browser...";
9804            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9805            /*
9806              Select a pen color from the pop-up menu.
9807            */
9808            pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9809              (const char **) ColorMenu,command);
9810            if (pen_number < 0)
9811              break;
9812            if (pen_number == (MaxNumberPens-2))
9813              {
9814                static char
9815                  color_name[MaxTextExtent] = "gray";
9816
9817                /*
9818                  Select a pen color from a dialog.
9819                */
9820                resource_info->pen_colors[pen_number]=color_name;
9821                XColorBrowserWidget(display,windows,"Select",color_name);
9822                if (*color_name == '\0')
9823                  break;
9824              }
9825            /*
9826              Set border color.
9827            */
9828            (void) XParseColor(display,windows->map_info->colormap,
9829              resource_info->pen_colors[pen_number],&border_color);
9830            break;
9831          }
9832          case MatteEditFuzzCommand:
9833          {
9834            static char
9835              fuzz[MaxTextExtent];
9836
9837            static const char
9838              *FuzzMenu[] =
9839              {
9840                "0%",
9841                "2%",
9842                "5%",
9843                "10%",
9844                "15%",
9845                "Dialog...",
9846                (char *) NULL,
9847              };
9848
9849            /*
9850              Select a command from the pop-up menu.
9851            */
9852            entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9853              command);
9854            if (entry < 0)
9855              break;
9856            if (entry != 5)
9857              {
9858                (*image)->fuzz=SiPrefixToDouble(FuzzMenu[entry],(double)
9859                  QuantumRange+1.0);
9860                break;
9861              }
9862            (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9863            (void) XDialogWidget(display,windows,"Ok",
9864              "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9865            if (*fuzz == '\0')
9866              break;
9867            (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
9868            (*image)->fuzz=SiPrefixToDouble(fuzz,(double) QuantumRange+1.0);
9869            break;
9870          }
9871          case MatteEditValueCommand:
9872          {
9873            static char
9874              message[MaxTextExtent];
9875
9876            static const char
9877              *MatteMenu[] =
9878              {
9879                "Opaque",
9880                "Transparent",
9881                "Dialog...",
9882                (char *) NULL,
9883              };
9884
9885            /*
9886              Select a command from the pop-up menu.
9887            */
9888            entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9889              command);
9890            if (entry < 0)
9891              break;
9892            if (entry != 2)
9893              {
9894                (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9895                  OpaqueAlpha);
9896                if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9897                  (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9898                    (Quantum) TransparentAlpha);
9899                break;
9900              }
9901            (void) FormatLocaleString(message,MaxTextExtent,
9902              "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9903              QuantumRange);
9904            (void) XDialogWidget(display,windows,"Matte",message,matte);
9905            if (*matte == '\0')
9906              break;
9907            break;
9908          }
9909          case MatteEditUndoCommand:
9910          {
9911            (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9912              image,exception);
9913            break;
9914          }
9915          case MatteEditHelpCommand:
9916          {
9917            XTextViewWidget(display,resource_info,windows,MagickFalse,
9918              "Help Viewer - Matte Edit",ImageMatteEditHelp);
9919            break;
9920          }
9921          case MatteEditDismissCommand:
9922          {
9923            /*
9924              Prematurely exit.
9925            */
9926            state|=EscapeState;
9927            state|=ExitState;
9928            break;
9929          }
9930          default:
9931            break;
9932        }
9933        (void) XCheckDefineCursor(display,windows->image.id,cursor);
9934        continue;
9935      }
9936    switch (event.type)
9937    {
9938      case ButtonPress:
9939      {
9940        if (event.xbutton.button != Button1)
9941          break;
9942        if ((event.xbutton.window != windows->image.id) &&
9943            (event.xbutton.window != windows->magnify.id))
9944          break;
9945        /*
9946          Update matte data.
9947        */
9948        x=event.xbutton.x;
9949        y=event.xbutton.y;
9950        (void) XMagickCommand(display,resource_info,windows,
9951          SaveToUndoBufferCommand,image,exception);
9952        state|=UpdateConfigurationState;
9953        break;
9954      }
9955      case ButtonRelease:
9956      {
9957        if (event.xbutton.button != Button1)
9958          break;
9959        if ((event.xbutton.window != windows->image.id) &&
9960            (event.xbutton.window != windows->magnify.id))
9961          break;
9962        /*
9963          Update colormap information.
9964        */
9965        x=event.xbutton.x;
9966        y=event.xbutton.y;
9967        XConfigureImageColormap(display,resource_info,windows,*image);
9968        (void) XConfigureImage(display,resource_info,windows,*image,exception);
9969        XInfoWidget(display,windows,text);
9970        (void) XCheckDefineCursor(display,windows->image.id,cursor);
9971        state&=(~UpdateConfigurationState);
9972        break;
9973      }
9974      case Expose:
9975        break;
9976      case KeyPress:
9977      {
9978        char
9979          command[MaxTextExtent];
9980
9981        KeySym
9982          key_symbol;
9983
9984        if (event.xkey.window == windows->magnify.id)
9985          {
9986            Window
9987              window;
9988
9989            window=windows->magnify.id;
9990            while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9991          }
9992        if (event.xkey.window != windows->image.id)
9993          break;
9994        /*
9995          Respond to a user key press.
9996        */
9997        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
9998          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
9999        switch ((int) key_symbol)
10000        {
10001          case XK_Escape:
10002          case XK_F20:
10003          {
10004            /*
10005              Prematurely exit.
10006            */
10007            state|=ExitState;
10008            break;
10009          }
10010          case XK_F1:
10011          case XK_Help:
10012          {
10013            XTextViewWidget(display,resource_info,windows,MagickFalse,
10014              "Help Viewer - Matte Edit",ImageMatteEditHelp);
10015            break;
10016          }
10017          default:
10018          {
10019            (void) XBell(display,0);
10020            break;
10021          }
10022        }
10023        break;
10024      }
10025      case MotionNotify:
10026      {
10027        /*
10028          Map and unmap Info widget as cursor crosses its boundaries.
10029        */
10030        x=event.xmotion.x;
10031        y=event.xmotion.y;
10032        if (windows->info.mapped != MagickFalse)
10033          {
10034            if ((x < (int) (windows->info.x+windows->info.width)) &&
10035                (y < (int) (windows->info.y+windows->info.height)))
10036              (void) XWithdrawWindow(display,windows->info.id,
10037                windows->info.screen);
10038          }
10039        else
10040          if ((x > (int) (windows->info.x+windows->info.width)) ||
10041              (y > (int) (windows->info.y+windows->info.height)))
10042            (void) XMapWindow(display,windows->info.id);
10043        break;
10044      }
10045      default:
10046        break;
10047    }
10048    if (event.xany.window == windows->magnify.id)
10049      {
10050        x=windows->magnify.x-windows->image.x;
10051        y=windows->magnify.y-windows->image.y;
10052      }
10053    x_offset=x;
10054    y_offset=y;
10055    if ((state & UpdateConfigurationState) != 0)
10056      {
10057        CacheView
10058          *image_view;
10059
10060        int
10061          x,
10062          y;
10063
10064        /*
10065          Matte edit is relative to image configuration.
10066        */
10067        (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
10068          MagickTrue);
10069        XPutPixel(windows->image.ximage,x_offset,y_offset,
10070          windows->pixel_info->background_color.pixel);
10071        width=(unsigned int) (*image)->columns;
10072        height=(unsigned int) (*image)->rows;
10073        x=0;
10074        y=0;
10075        if (windows->image.crop_geometry != (char *) NULL)
10076          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
10077            &height);
10078        x_offset=(int) (width*(windows->image.x+x_offset)/
10079          windows->image.ximage->width+x);
10080        y_offset=(int) (height*(windows->image.y+y_offset)/
10081          windows->image.ximage->height+y);
10082        if ((x_offset < 0) || (y_offset < 0))
10083          continue;
10084        if ((x_offset >= (int) (*image)->columns) ||
10085            (y_offset >= (int) (*image)->rows))
10086          continue;
10087        if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10088          return(MagickFalse);
10089        (*image)->matte=MagickTrue;
10090        image_view=AcquireCacheView(*image);
10091        switch (method)
10092        {
10093          case PointMethod:
10094          default:
10095          {
10096            /*
10097              Update matte information using point algorithm.
10098            */
10099            q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10100              (ssize_t) y_offset,1,1,exception);
10101            if (q == (Quantum *) NULL)
10102              break;
10103            SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10104            (void) SyncCacheViewAuthenticPixels(image_view,exception);
10105            break;
10106          }
10107          case ReplaceMethod:
10108          {
10109            PixelInfo
10110              pixel,
10111              target;
10112
10113            Quantum
10114              virtual_pixel[MaxPixelChannels];
10115
10116            /*
10117              Update matte information using replace algorithm.
10118            */
10119            (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
10120              (ssize_t) y_offset,virtual_pixel,exception);
10121            target.red=virtual_pixel[RedPixelChannel];
10122            target.green=virtual_pixel[GreenPixelChannel];
10123            target.blue=virtual_pixel[BluePixelChannel];
10124            target.alpha=virtual_pixel[AlphaPixelChannel];
10125            for (y=0; y < (int) (*image)->rows; y++)
10126            {
10127              q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10128                (*image)->columns,1,exception);
10129              if (q == (Quantum *) NULL)
10130                break;
10131              for (x=0; x < (int) (*image)->columns; x++)
10132              {
10133                GetPixelInfoPixel(*image,q,&pixel);
10134                if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
10135                  SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10136                q+=GetPixelChannels(*image);
10137              }
10138              if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10139                break;
10140            }
10141            break;
10142          }
10143          case FloodfillMethod:
10144          case FillToBorderMethod:
10145          {
10146            ChannelType
10147              channel_mask;
10148
10149            DrawInfo
10150              *draw_info;
10151
10152            PixelInfo
10153              target;
10154
10155            /*
10156              Update matte information using floodfill algorithm.
10157            */
10158            (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
10159              (ssize_t) y_offset,&target,exception);
10160            if (method == FillToBorderMethod)
10161              {
10162                target.red=(MagickRealType) ScaleShortToQuantum(
10163                  border_color.red);
10164                target.green=(MagickRealType) ScaleShortToQuantum(
10165                  border_color.green);
10166                target.blue=(MagickRealType) ScaleShortToQuantum(
10167                  border_color.blue);
10168              }
10169            draw_info=CloneDrawInfo(resource_info->image_info,
10170              (DrawInfo *) NULL);
10171            draw_info->fill.alpha=ClampToQuantum(InterpretLocaleValue(matte,
10172              (char **) NULL));
10173            channel_mask=SetPixelChannelMask(*image,AlphaChannel);
10174            (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10175              x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
10176              MagickFalse : MagickTrue,exception);
10177            (void) SetPixelChannelMap(*image,channel_mask);
10178            draw_info=DestroyDrawInfo(draw_info);
10179            break;
10180          }
10181          case ResetMethod:
10182          {
10183            /*
10184              Update matte information using reset algorithm.
10185            */
10186            if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10187              return(MagickFalse);
10188            for (y=0; y < (int) (*image)->rows; y++)
10189            {
10190              q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10191                (*image)->columns,1,exception);
10192              if (q == (Quantum *) NULL)
10193                break;
10194              for (x=0; x < (int) (*image)->columns; x++)
10195              {
10196                SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10197                q+=GetPixelChannels(*image);
10198              }
10199              if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10200                break;
10201            }
10202            if (StringToLong(matte) == (long) OpaqueAlpha)
10203              (*image)->matte=MagickFalse;
10204            break;
10205          }
10206        }
10207        image_view=DestroyCacheView(image_view);
10208        state&=(~UpdateConfigurationState);
10209      }
10210  } while ((state & ExitState) == 0);
10211  (void) XSelectInput(display,windows->image.id,
10212    windows->image.attributes.event_mask);
10213  XSetCursorState(display,windows,MagickFalse);
10214  (void) XFreeCursor(display,cursor);
10215  return(MagickTrue);
10216}
10217
10218/*
10219%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10220%                                                                             %
10221%                                                                             %
10222%                                                                             %
10223+   X O p e n I m a g e                                                       %
10224%                                                                             %
10225%                                                                             %
10226%                                                                             %
10227%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10228%
10229%  XOpenImage() loads an image from a file.
10230%
10231%  The format of the XOpenImage method is:
10232%
10233%     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10234%       XWindows *windows,const unsigned int command)
10235%
10236%  A description of each parameter follows:
10237%
10238%    o display: Specifies a connection to an X server; returned from
10239%      XOpenDisplay.
10240%
10241%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10242%
10243%    o windows: Specifies a pointer to a XWindows structure.
10244%
10245%    o command: A value other than zero indicates that the file is selected
10246%      from the command line argument list.
10247%
10248*/
10249static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10250  XWindows *windows,const MagickBooleanType command)
10251{
10252  const MagickInfo
10253    *magick_info;
10254
10255  ExceptionInfo
10256    *exception;
10257
10258  Image
10259    *nexus;
10260
10261  ImageInfo
10262    *image_info;
10263
10264  static char
10265    filename[MaxTextExtent] = "\0";
10266
10267  /*
10268    Request file name from user.
10269  */
10270  if (command == MagickFalse)
10271    XFileBrowserWidget(display,windows,"Open",filename);
10272  else
10273    {
10274      char
10275        **filelist,
10276        **files;
10277
10278      int
10279        count,
10280        status;
10281
10282      register int
10283        i,
10284        j;
10285
10286      /*
10287        Select next image from the command line.
10288      */
10289      status=XGetCommand(display,windows->image.id,&files,&count);
10290      if (status == 0)
10291        {
10292          ThrowXWindowFatalException(XServerError,"UnableToGetProperty","...");
10293          return((Image *) NULL);
10294        }
10295      filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10296      if (filelist == (char **) NULL)
10297        {
10298          ThrowXWindowFatalException(ResourceLimitError,
10299            "MemoryAllocationFailed","...");
10300          (void) XFreeStringList(files);
10301          return((Image *) NULL);
10302        }
10303      j=0;
10304      for (i=1; i < count; i++)
10305        if (*files[i] != '-')
10306          filelist[j++]=files[i];
10307      filelist[j]=(char *) NULL;
10308      XListBrowserWidget(display,windows,&windows->widget,
10309        (const char **) filelist,"Load","Select Image to Load:",filename);
10310      filelist=(char **) RelinquishMagickMemory(filelist);
10311      (void) XFreeStringList(files);
10312    }
10313  if (*filename == '\0')
10314    return((Image *) NULL);
10315  image_info=CloneImageInfo(resource_info->image_info);
10316  (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10317    (void *) NULL);
10318  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10319  exception=AcquireExceptionInfo();
10320  (void) SetImageInfo(image_info,0,exception);
10321  if (LocaleCompare(image_info->magick,"X") == 0)
10322    {
10323      char
10324        seconds[MaxTextExtent];
10325
10326      /*
10327        User may want to delay the X server screen grab.
10328      */
10329      (void) CopyMagickString(seconds,"0",MaxTextExtent);
10330      (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10331        seconds);
10332      if (*seconds == '\0')
10333        return((Image *) NULL);
10334      XDelay(display,(size_t) (1000*StringToLong(seconds)));
10335    }
10336  magick_info=GetMagickInfo(image_info->magick,exception);
10337  if ((magick_info != (const MagickInfo *) NULL) &&
10338      (magick_info->raw != MagickFalse))
10339    {
10340      char
10341        geometry[MaxTextExtent];
10342
10343      /*
10344        Request image size from the user.
10345      */
10346      (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10347      if (image_info->size != (char *) NULL)
10348        (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10349      (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10350        geometry);
10351      (void) CloneString(&image_info->size,geometry);
10352    }
10353  /*
10354    Load the image.
10355  */
10356  XSetCursorState(display,windows,MagickTrue);
10357  XCheckRefreshWindows(display,windows);
10358  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10359  nexus=ReadImage(image_info,exception);
10360  CatchException(exception);
10361  XSetCursorState(display,windows,MagickFalse);
10362  if (nexus != (Image *) NULL)
10363    XClientMessage(display,windows->image.id,windows->im_protocols,
10364      windows->im_next_image,CurrentTime);
10365  else
10366    {
10367      char
10368        *text,
10369        **textlist;
10370
10371      /*
10372        Unknown image format.
10373      */
10374      text=FileToString(filename,~0,exception);
10375      if (text == (char *) NULL)
10376        return((Image *) NULL);
10377      textlist=StringToList(text);
10378      if (textlist != (char **) NULL)
10379        {
10380          char
10381            title[MaxTextExtent];
10382
10383          register int
10384            i;
10385
10386          (void) FormatLocaleString(title,MaxTextExtent,
10387            "Unknown format: %s",filename);
10388          XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10389            (const char **) textlist);
10390          for (i=0; textlist[i] != (char *) NULL; i++)
10391            textlist[i]=DestroyString(textlist[i]);
10392          textlist=(char **) RelinquishMagickMemory(textlist);
10393        }
10394      text=DestroyString(text);
10395    }
10396  exception=DestroyExceptionInfo(exception);
10397  image_info=DestroyImageInfo(image_info);
10398  return(nexus);
10399}
10400
10401/*
10402%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10403%                                                                             %
10404%                                                                             %
10405%                                                                             %
10406+   X P a n I m a g e                                                         %
10407%                                                                             %
10408%                                                                             %
10409%                                                                             %
10410%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10411%
10412%  XPanImage() pans the image until the mouse button is released.
10413%
10414%  The format of the XPanImage method is:
10415%
10416%      void XPanImage(Display *display,XWindows *windows,XEvent *event)
10417%
10418%  A description of each parameter follows:
10419%
10420%    o display: Specifies a connection to an X server;  returned from
10421%      XOpenDisplay.
10422%
10423%    o windows: Specifies a pointer to a XWindows structure.
10424%
10425%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10426%      the entire image is refreshed.
10427%
10428*/
10429static void XPanImage(Display *display,XWindows *windows,XEvent *event)
10430{
10431  char
10432    text[MaxTextExtent];
10433
10434  Cursor
10435    cursor;
10436
10437  MagickRealType
10438    x_factor,
10439    y_factor;
10440
10441  RectangleInfo
10442    pan_info;
10443
10444  size_t
10445    state;
10446
10447  /*
10448    Define cursor.
10449  */
10450  if ((windows->image.ximage->width > (int) windows->image.width) &&
10451      (windows->image.ximage->height > (int) windows->image.height))
10452    cursor=XCreateFontCursor(display,XC_fleur);
10453  else
10454    if (windows->image.ximage->width > (int) windows->image.width)
10455      cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10456    else
10457      if (windows->image.ximage->height > (int) windows->image.height)
10458        cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10459      else
10460        cursor=XCreateFontCursor(display,XC_arrow);
10461  (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10462  /*
10463    Pan image as pointer moves until the mouse button is released.
10464  */
10465  x_factor=(MagickRealType) windows->image.ximage->width/windows->pan.width;
10466  y_factor=(MagickRealType) windows->image.ximage->height/windows->pan.height;
10467  pan_info.width=windows->pan.width*windows->image.width/
10468    windows->image.ximage->width;
10469  pan_info.height=windows->pan.height*windows->image.height/
10470    windows->image.ximage->height;
10471  pan_info.x=0;
10472  pan_info.y=0;
10473  state=UpdateConfigurationState;
10474  do
10475  {
10476    switch (event->type)
10477    {
10478      case ButtonPress:
10479      {
10480        /*
10481          User choose an initial pan location.
10482        */
10483        pan_info.x=(ssize_t) event->xbutton.x;
10484        pan_info.y=(ssize_t) event->xbutton.y;
10485        state|=UpdateConfigurationState;
10486        break;
10487      }
10488      case ButtonRelease:
10489      {
10490        /*
10491          User has finished panning the image.
10492        */
10493        pan_info.x=(ssize_t) event->xbutton.x;
10494        pan_info.y=(ssize_t) event->xbutton.y;
10495        state|=UpdateConfigurationState | ExitState;
10496        break;
10497      }
10498      case MotionNotify:
10499      {
10500        pan_info.x=(ssize_t) event->xmotion.x;
10501        pan_info.y=(ssize_t) event->xmotion.y;
10502        state|=UpdateConfigurationState;
10503      }
10504      default:
10505        break;
10506    }
10507    if ((state & UpdateConfigurationState) != 0)
10508      {
10509        /*
10510          Check boundary conditions.
10511        */
10512        if (pan_info.x < (ssize_t) (pan_info.width/2))
10513          pan_info.x=0;
10514        else
10515          pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10516        if (pan_info.x < 0)
10517          pan_info.x=0;
10518        else
10519          if ((int) (pan_info.x+windows->image.width) >
10520              windows->image.ximage->width)
10521            pan_info.x=(ssize_t)
10522              (windows->image.ximage->width-windows->image.width);
10523        if (pan_info.y < (ssize_t) (pan_info.height/2))
10524          pan_info.y=0;
10525        else
10526          pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10527        if (pan_info.y < 0)
10528          pan_info.y=0;
10529        else
10530          if ((int) (pan_info.y+windows->image.height) >
10531              windows->image.ximage->height)
10532            pan_info.y=(ssize_t)
10533              (windows->image.ximage->height-windows->image.height);
10534        if ((windows->image.x != (int) pan_info.x) ||
10535            (windows->image.y != (int) pan_info.y))
10536          {
10537            /*
10538              Display image pan offset.
10539            */
10540            windows->image.x=(int) pan_info.x;
10541            windows->image.y=(int) pan_info.y;
10542            (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
10543              windows->image.width,windows->image.height,windows->image.x,
10544              windows->image.y);
10545            XInfoWidget(display,windows,text);
10546            /*
10547              Refresh Image window.
10548            */
10549            XDrawPanRectangle(display,windows);
10550            XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10551          }
10552        state&=(~UpdateConfigurationState);
10553      }
10554    /*
10555      Wait for next event.
10556    */
10557    if ((state & ExitState) == 0)
10558      XScreenEvent(display,windows,event);
10559  } while ((state & ExitState) == 0);
10560  /*
10561    Restore cursor.
10562  */
10563  (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10564  (void) XFreeCursor(display,cursor);
10565  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10566}
10567
10568/*
10569%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10570%                                                                             %
10571%                                                                             %
10572%                                                                             %
10573+   X P a s t e I m a g e                                                     %
10574%                                                                             %
10575%                                                                             %
10576%                                                                             %
10577%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10578%
10579%  XPasteImage() pastes an image previously saved with XCropImage in the X
10580%  window image at a location the user chooses with the pointer.
10581%
10582%  The format of the XPasteImage method is:
10583%
10584%      MagickBooleanType XPasteImage(Display *display,
10585%        XResourceInfo *resource_info,XWindows *windows,Image *image,
10586%        ExceptionInfo *exception)
10587%
10588%  A description of each parameter follows:
10589%
10590%    o display: Specifies a connection to an X server;  returned from
10591%      XOpenDisplay.
10592%
10593%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10594%
10595%    o windows: Specifies a pointer to a XWindows structure.
10596%
10597%    o image: the image; returned from ReadImage.
10598%
10599%    o exception: return any errors or warnings in this structure.
10600%
10601*/
10602static MagickBooleanType XPasteImage(Display *display,
10603  XResourceInfo *resource_info,XWindows *windows,Image *image,
10604  ExceptionInfo *exception)
10605{
10606  static const char
10607    *PasteMenu[] =
10608    {
10609      "Operator",
10610      "Help",
10611      "Dismiss",
10612      (char *) NULL
10613    };
10614
10615  static const ModeType
10616    PasteCommands[] =
10617    {
10618      PasteOperatorsCommand,
10619      PasteHelpCommand,
10620      PasteDismissCommand
10621    };
10622
10623  static CompositeOperator
10624    compose = CopyCompositeOp;
10625
10626  char
10627    text[MaxTextExtent];
10628
10629  Cursor
10630    cursor;
10631
10632  Image
10633    *paste_image;
10634
10635  int
10636    entry,
10637    id,
10638    x,
10639    y;
10640
10641  MagickRealType
10642    scale_factor;
10643
10644  RectangleInfo
10645    highlight_info,
10646    paste_info;
10647
10648  unsigned int
10649    height,
10650    width;
10651
10652  size_t
10653    state;
10654
10655  XEvent
10656    event;
10657
10658  /*
10659    Copy image.
10660  */
10661  if (resource_info->copy_image == (Image *) NULL)
10662    return(MagickFalse);
10663  paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,exception);
10664  /*
10665    Map Command widget.
10666  */
10667  (void) CloneString(&windows->command.name,"Paste");
10668  windows->command.data=1;
10669  (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10670  (void) XMapRaised(display,windows->command.id);
10671  XClientMessage(display,windows->image.id,windows->im_protocols,
10672    windows->im_update_widget,CurrentTime);
10673  /*
10674    Track pointer until button 1 is pressed.
10675  */
10676  XSetCursorState(display,windows,MagickFalse);
10677  XQueryPosition(display,windows->image.id,&x,&y);
10678  (void) XSelectInput(display,windows->image.id,
10679    windows->image.attributes.event_mask | PointerMotionMask);
10680  paste_info.x=(ssize_t) windows->image.x+x;
10681  paste_info.y=(ssize_t) windows->image.y+y;
10682  paste_info.width=0;
10683  paste_info.height=0;
10684  cursor=XCreateFontCursor(display,XC_ul_angle);
10685  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10686  state=DefaultState;
10687  do
10688  {
10689    if (windows->info.mapped != MagickFalse)
10690      {
10691        /*
10692          Display pointer position.
10693        */
10694        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
10695          (long) paste_info.x,(long) paste_info.y);
10696        XInfoWidget(display,windows,text);
10697      }
10698    highlight_info=paste_info;
10699    highlight_info.x=paste_info.x-windows->image.x;
10700    highlight_info.y=paste_info.y-windows->image.y;
10701    XHighlightRectangle(display,windows->image.id,
10702      windows->image.highlight_context,&highlight_info);
10703    /*
10704      Wait for next event.
10705    */
10706    XScreenEvent(display,windows,&event);
10707    XHighlightRectangle(display,windows->image.id,
10708      windows->image.highlight_context,&highlight_info);
10709    if (event.xany.window == windows->command.id)
10710      {
10711        /*
10712          Select a command from the Command widget.
10713        */
10714        id=XCommandWidget(display,windows,PasteMenu,&event);
10715        if (id < 0)
10716          continue;
10717        switch (PasteCommands[id])
10718        {
10719          case PasteOperatorsCommand:
10720          {
10721            char
10722              command[MaxTextExtent],
10723              **operators;
10724
10725            /*
10726              Select a command from the pop-up menu.
10727            */
10728            operators=GetCommandOptions(MagickComposeOptions);
10729            if (operators == (char **) NULL)
10730              break;
10731            entry=XMenuWidget(display,windows,PasteMenu[id],
10732              (const char **) operators,command);
10733            if (entry >= 0)
10734              compose=(CompositeOperator) ParseCommandOption(
10735                MagickComposeOptions,MagickFalse,operators[entry]);
10736            operators=DestroyStringList(operators);
10737            break;
10738          }
10739          case PasteHelpCommand:
10740          {
10741            XTextViewWidget(display,resource_info,windows,MagickFalse,
10742              "Help Viewer - Image Composite",ImagePasteHelp);
10743            break;
10744          }
10745          case PasteDismissCommand:
10746          {
10747            /*
10748              Prematurely exit.
10749            */
10750            state|=EscapeState;
10751            state|=ExitState;
10752            break;
10753          }
10754          default:
10755            break;
10756        }
10757        continue;
10758      }
10759    switch (event.type)
10760    {
10761      case ButtonPress:
10762      {
10763        if (image->debug != MagickFalse)
10764          (void) LogMagickEvent(X11Event,GetMagickModule(),
10765            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10766            event.xbutton.button,event.xbutton.x,event.xbutton.y);
10767        if (event.xbutton.button != Button1)
10768          break;
10769        if (event.xbutton.window != windows->image.id)
10770          break;
10771        /*
10772          Paste rectangle is relative to image configuration.
10773        */
10774        width=(unsigned int) image->columns;
10775        height=(unsigned int) image->rows;
10776        x=0;
10777        y=0;
10778        if (windows->image.crop_geometry != (char *) NULL)
10779          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10780            &width,&height);
10781        scale_factor=(MagickRealType) windows->image.ximage->width/width;
10782        paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10783        scale_factor=(MagickRealType) windows->image.ximage->height/height;
10784        paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10785        (void) XCheckDefineCursor(display,windows->image.id,cursor);
10786        paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10787        paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10788        break;
10789      }
10790      case ButtonRelease:
10791      {
10792        if (image->debug != MagickFalse)
10793          (void) LogMagickEvent(X11Event,GetMagickModule(),
10794            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10795            event.xbutton.button,event.xbutton.x,event.xbutton.y);
10796        if (event.xbutton.button != Button1)
10797          break;
10798        if (event.xbutton.window != windows->image.id)
10799          break;
10800        if ((paste_info.width != 0) && (paste_info.height != 0))
10801          {
10802            /*
10803              User has selected the location of the paste image.
10804            */
10805            paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10806            paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10807            state|=ExitState;
10808          }
10809        break;
10810      }
10811      case Expose:
10812        break;
10813      case KeyPress:
10814      {
10815        char
10816          command[MaxTextExtent];
10817
10818        KeySym
10819          key_symbol;
10820
10821        int
10822          length;
10823
10824        if (event.xkey.window != windows->image.id)
10825          break;
10826        /*
10827          Respond to a user key press.
10828        */
10829        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10830          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10831        *(command+length)='\0';
10832        if (image->debug != MagickFalse)
10833          (void) LogMagickEvent(X11Event,GetMagickModule(),
10834            "Key press: 0x%lx (%s)",(long) key_symbol,command);
10835        switch ((int) key_symbol)
10836        {
10837          case XK_Escape:
10838          case XK_F20:
10839          {
10840            /*
10841              Prematurely exit.
10842            */
10843            paste_image=DestroyImage(paste_image);
10844            state|=EscapeState;
10845            state|=ExitState;
10846            break;
10847          }
10848          case XK_F1:
10849          case XK_Help:
10850          {
10851            (void) XSetFunction(display,windows->image.highlight_context,
10852              GXcopy);
10853            XTextViewWidget(display,resource_info,windows,MagickFalse,
10854              "Help Viewer - Image Composite",ImagePasteHelp);
10855            (void) XSetFunction(display,windows->image.highlight_context,
10856              GXinvert);
10857            break;
10858          }
10859          default:
10860          {
10861            (void) XBell(display,0);
10862            break;
10863          }
10864        }
10865        break;
10866      }
10867      case MotionNotify:
10868      {
10869        /*
10870          Map and unmap Info widget as text cursor crosses its boundaries.
10871        */
10872        x=event.xmotion.x;
10873        y=event.xmotion.y;
10874        if (windows->info.mapped != MagickFalse)
10875          {
10876            if ((x < (int) (windows->info.x+windows->info.width)) &&
10877                (y < (int) (windows->info.y+windows->info.height)))
10878              (void) XWithdrawWindow(display,windows->info.id,
10879                windows->info.screen);
10880          }
10881        else
10882          if ((x > (int) (windows->info.x+windows->info.width)) ||
10883              (y > (int) (windows->info.y+windows->info.height)))
10884            (void) XMapWindow(display,windows->info.id);
10885        paste_info.x=(ssize_t) windows->image.x+x;
10886        paste_info.y=(ssize_t) windows->image.y+y;
10887        break;
10888      }
10889      default:
10890      {
10891        if (image->debug != MagickFalse)
10892          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10893            event.type);
10894        break;
10895      }
10896    }
10897  } while ((state & ExitState) == 0);
10898  (void) XSelectInput(display,windows->image.id,
10899    windows->image.attributes.event_mask);
10900  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10901  XSetCursorState(display,windows,MagickFalse);
10902  (void) XFreeCursor(display,cursor);
10903  if ((state & EscapeState) != 0)
10904    return(MagickTrue);
10905  /*
10906    Image pasting is relative to image configuration.
10907  */
10908  XSetCursorState(display,windows,MagickTrue);
10909  XCheckRefreshWindows(display,windows);
10910  width=(unsigned int) image->columns;
10911  height=(unsigned int) image->rows;
10912  x=0;
10913  y=0;
10914  if (windows->image.crop_geometry != (char *) NULL)
10915    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10916  scale_factor=(MagickRealType) width/windows->image.ximage->width;
10917  paste_info.x+=x;
10918  paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10919  paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10920  scale_factor=(MagickRealType) height/windows->image.ximage->height;
10921  paste_info.y+=y;
10922  paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10923  paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10924  /*
10925    Paste image with X Image window.
10926  */
10927  (void) CompositeImage(image,compose,paste_image,paste_info.x,paste_info.y,
10928    exception);
10929  paste_image=DestroyImage(paste_image);
10930  XSetCursorState(display,windows,MagickFalse);
10931  /*
10932    Update image colormap.
10933  */
10934  XConfigureImageColormap(display,resource_info,windows,image);
10935  (void) XConfigureImage(display,resource_info,windows,image,exception);
10936  return(MagickTrue);
10937}
10938
10939/*
10940%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10941%                                                                             %
10942%                                                                             %
10943%                                                                             %
10944+   X P r i n t I m a g e                                                     %
10945%                                                                             %
10946%                                                                             %
10947%                                                                             %
10948%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10949%
10950%  XPrintImage() prints an image to a Postscript printer.
10951%
10952%  The format of the XPrintImage method is:
10953%
10954%      MagickBooleanType XPrintImage(Display *display,
10955%        XResourceInfo *resource_info,XWindows *windows,Image *image,
10956%        ExceptionInfo *exception)
10957%
10958%  A description of each parameter follows:
10959%
10960%    o display: Specifies a connection to an X server; returned from
10961%      XOpenDisplay.
10962%
10963%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10964%
10965%    o windows: Specifies a pointer to a XWindows structure.
10966%
10967%    o image: the image.
10968%
10969%    o exception: return any errors or warnings in this structure.
10970%
10971*/
10972static MagickBooleanType XPrintImage(Display *display,
10973  XResourceInfo *resource_info,XWindows *windows,Image *image,
10974  ExceptionInfo *exception)
10975{
10976  char
10977    filename[MaxTextExtent],
10978    geometry[MaxTextExtent];
10979
10980  Image
10981    *print_image;
10982
10983  ImageInfo
10984    *image_info;
10985
10986  MagickStatusType
10987    status;
10988
10989  /*
10990    Request Postscript page geometry from user.
10991  */
10992  image_info=CloneImageInfo(resource_info->image_info);
10993  (void) FormatLocaleString(geometry,MaxTextExtent,"Letter");
10994  if (image_info->page != (char *) NULL)
10995    (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
10996  XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
10997    "Select Postscript Page Geometry:",geometry);
10998  if (*geometry == '\0')
10999    return(MagickTrue);
11000  image_info->page=GetPageGeometry(geometry);
11001  /*
11002    Apply image transforms.
11003  */
11004  XSetCursorState(display,windows,MagickTrue);
11005  XCheckRefreshWindows(display,windows);
11006  print_image=CloneImage(image,0,0,MagickTrue,exception);
11007  if (print_image == (Image *) NULL)
11008    return(MagickFalse);
11009  (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
11010    windows->image.ximage->width,windows->image.ximage->height);
11011  (void) TransformImage(&print_image,windows->image.crop_geometry,geometry,
11012    exception);
11013  /*
11014    Print image.
11015  */
11016  (void) AcquireUniqueFilename(filename);
11017  (void) FormatLocaleString(print_image->filename,MaxTextExtent,"print:%s",
11018    filename);
11019  status=WriteImage(image_info,print_image,exception);
11020  (void) RelinquishUniqueFileResource(filename);
11021  print_image=DestroyImage(print_image);
11022  image_info=DestroyImageInfo(image_info);
11023  XSetCursorState(display,windows,MagickFalse);
11024  return(status != 0 ? MagickTrue : MagickFalse);
11025}
11026
11027/*
11028%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11029%                                                                             %
11030%                                                                             %
11031%                                                                             %
11032+   X R O I I m a g e                                                         %
11033%                                                                             %
11034%                                                                             %
11035%                                                                             %
11036%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11037%
11038%  XROIImage() applies an image processing technique to a region of interest.
11039%
11040%  The format of the XROIImage method is:
11041%
11042%      MagickBooleanType XROIImage(Display *display,
11043%        XResourceInfo *resource_info,XWindows *windows,Image **image,
11044%        ExceptionInfo *exception)
11045%
11046%  A description of each parameter follows:
11047%
11048%    o display: Specifies a connection to an X server; returned from
11049%      XOpenDisplay.
11050%
11051%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11052%
11053%    o windows: Specifies a pointer to a XWindows structure.
11054%
11055%    o image: the image; returned from ReadImage.
11056%
11057%    o exception: return any errors or warnings in this structure.
11058%
11059*/
11060static MagickBooleanType XROIImage(Display *display,
11061  XResourceInfo *resource_info,XWindows *windows,Image **image,
11062  ExceptionInfo *exception)
11063{
11064#define ApplyMenus  7
11065
11066  static const char
11067    *ROIMenu[] =
11068    {
11069      "Help",
11070      "Dismiss",
11071      (char *) NULL
11072    },
11073    *ApplyMenu[] =
11074    {
11075      "File",
11076      "Edit",
11077      "Transform",
11078      "Enhance",
11079      "Effects",
11080      "F/X",
11081      "Miscellany",
11082      "Help",
11083      "Dismiss",
11084      (char *) NULL
11085    },
11086    *FileMenu[] =
11087    {
11088      "Save...",
11089      "Print...",
11090      (char *) NULL
11091    },
11092    *EditMenu[] =
11093    {
11094      "Undo",
11095      "Redo",
11096      (char *) NULL
11097    },
11098    *TransformMenu[] =
11099    {
11100      "Flop",
11101      "Flip",
11102      "Rotate Right",
11103      "Rotate Left",
11104      (char *) NULL
11105    },
11106    *EnhanceMenu[] =
11107    {
11108      "Hue...",
11109      "Saturation...",
11110      "Brightness...",
11111      "Gamma...",
11112      "Spiff",
11113      "Dull",
11114      "Contrast Stretch...",
11115      "Sigmoidal Contrast...",
11116      "Normalize",
11117      "Equalize",
11118      "Negate",
11119      "Grayscale",
11120      "Map...",
11121      "Quantize...",
11122      (char *) NULL
11123    },
11124    *EffectsMenu[] =
11125    {
11126      "Despeckle",
11127      "Emboss",
11128      "Reduce Noise",
11129      "Add Noise",
11130      "Sharpen...",
11131      "Blur...",
11132      "Threshold...",
11133      "Edge Detect...",
11134      "Spread...",
11135      "Shade...",
11136      "Raise...",
11137      "Segment...",
11138      (char *) NULL
11139    },
11140    *FXMenu[] =
11141    {
11142      "Solarize...",
11143      "Sepia Tone...",
11144      "Swirl...",
11145      "Implode...",
11146      "Vignette...",
11147      "Wave...",
11148      "Oil Paint...",
11149      "Charcoal Draw...",
11150      (char *) NULL
11151    },
11152    *MiscellanyMenu[] =
11153    {
11154      "Image Info",
11155      "Zoom Image",
11156      "Show Preview...",
11157      "Show Histogram",
11158      "Show Matte",
11159      (char *) NULL
11160    };
11161
11162  static const char
11163    **Menus[ApplyMenus] =
11164    {
11165      FileMenu,
11166      EditMenu,
11167      TransformMenu,
11168      EnhanceMenu,
11169      EffectsMenu,
11170      FXMenu,
11171      MiscellanyMenu
11172    };
11173
11174  static const CommandType
11175    ApplyCommands[] =
11176    {
11177      NullCommand,
11178      NullCommand,
11179      NullCommand,
11180      NullCommand,
11181      NullCommand,
11182      NullCommand,
11183      NullCommand,
11184      HelpCommand,
11185      QuitCommand
11186    },
11187    FileCommands[] =
11188    {
11189      SaveCommand,
11190      PrintCommand
11191    },
11192    EditCommands[] =
11193    {
11194      UndoCommand,
11195      RedoCommand
11196    },
11197    TransformCommands[] =
11198    {
11199      FlopCommand,
11200      FlipCommand,
11201      RotateRightCommand,
11202      RotateLeftCommand
11203    },
11204    EnhanceCommands[] =
11205    {
11206      HueCommand,
11207      SaturationCommand,
11208      BrightnessCommand,
11209      GammaCommand,
11210      SpiffCommand,
11211      DullCommand,
11212      ContrastStretchCommand,
11213      SigmoidalContrastCommand,
11214      NormalizeCommand,
11215      EqualizeCommand,
11216      NegateCommand,
11217      GrayscaleCommand,
11218      MapCommand,
11219      QuantizeCommand
11220    },
11221    EffectsCommands[] =
11222    {
11223      DespeckleCommand,
11224      EmbossCommand,
11225      ReduceNoiseCommand,
11226      AddNoiseCommand,
11227      SharpenCommand,
11228      BlurCommand,
11229      EdgeDetectCommand,
11230      SpreadCommand,
11231      ShadeCommand,
11232      RaiseCommand,
11233      SegmentCommand
11234    },
11235    FXCommands[] =
11236    {
11237      SolarizeCommand,
11238      SepiaToneCommand,
11239      SwirlCommand,
11240      ImplodeCommand,
11241      VignetteCommand,
11242      WaveCommand,
11243      OilPaintCommand,
11244      CharcoalDrawCommand
11245    },
11246    MiscellanyCommands[] =
11247    {
11248      InfoCommand,
11249      ZoomCommand,
11250      ShowPreviewCommand,
11251      ShowHistogramCommand,
11252      ShowMatteCommand
11253    },
11254    ROICommands[] =
11255    {
11256      ROIHelpCommand,
11257      ROIDismissCommand
11258    };
11259
11260  static const CommandType
11261    *Commands[ApplyMenus] =
11262    {
11263      FileCommands,
11264      EditCommands,
11265      TransformCommands,
11266      EnhanceCommands,
11267      EffectsCommands,
11268      FXCommands,
11269      MiscellanyCommands
11270    };
11271
11272  char
11273    command[MaxTextExtent],
11274    text[MaxTextExtent];
11275
11276  CommandType
11277    command_type;
11278
11279  Cursor
11280    cursor;
11281
11282  Image
11283    *roi_image;
11284
11285  int
11286    entry,
11287    id,
11288    x,
11289    y;
11290
11291  MagickRealType
11292    scale_factor;
11293
11294  MagickProgressMonitor
11295    progress_monitor;
11296
11297  RectangleInfo
11298    crop_info,
11299    highlight_info,
11300    roi_info;
11301
11302  unsigned int
11303    height,
11304    width;
11305
11306  size_t
11307    state;
11308
11309  XEvent
11310    event;
11311
11312  /*
11313    Map Command widget.
11314  */
11315  (void) CloneString(&windows->command.name,"ROI");
11316  windows->command.data=0;
11317  (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11318  (void) XMapRaised(display,windows->command.id);
11319  XClientMessage(display,windows->image.id,windows->im_protocols,
11320    windows->im_update_widget,CurrentTime);
11321  /*
11322    Track pointer until button 1 is pressed.
11323  */
11324  XQueryPosition(display,windows->image.id,&x,&y);
11325  (void) XSelectInput(display,windows->image.id,
11326    windows->image.attributes.event_mask | PointerMotionMask);
11327  roi_info.x=(ssize_t) windows->image.x+x;
11328  roi_info.y=(ssize_t) windows->image.y+y;
11329  roi_info.width=0;
11330  roi_info.height=0;
11331  cursor=XCreateFontCursor(display,XC_fleur);
11332  state=DefaultState;
11333  do
11334  {
11335    if (windows->info.mapped != MagickFalse)
11336      {
11337        /*
11338          Display pointer position.
11339        */
11340        (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
11341          (long) roi_info.x,(long) roi_info.y);
11342        XInfoWidget(display,windows,text);
11343      }
11344    /*
11345      Wait for next event.
11346    */
11347    XScreenEvent(display,windows,&event);
11348    if (event.xany.window == windows->command.id)
11349      {
11350        /*
11351          Select a command from the Command widget.
11352        */
11353        id=XCommandWidget(display,windows,ROIMenu,&event);
11354        if (id < 0)
11355          continue;
11356        switch (ROICommands[id])
11357        {
11358          case ROIHelpCommand:
11359          {
11360            XTextViewWidget(display,resource_info,windows,MagickFalse,
11361              "Help Viewer - Region of Interest",ImageROIHelp);
11362            break;
11363          }
11364          case ROIDismissCommand:
11365          {
11366            /*
11367              Prematurely exit.
11368            */
11369            state|=EscapeState;
11370            state|=ExitState;
11371            break;
11372          }
11373          default:
11374            break;
11375        }
11376        continue;
11377      }
11378    switch (event.type)
11379    {
11380      case ButtonPress:
11381      {
11382        if (event.xbutton.button != Button1)
11383          break;
11384        if (event.xbutton.window != windows->image.id)
11385          break;
11386        /*
11387          Note first corner of region of interest rectangle-- exit loop.
11388        */
11389        (void) XCheckDefineCursor(display,windows->image.id,cursor);
11390        roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11391        roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11392        state|=ExitState;
11393        break;
11394      }
11395      case ButtonRelease:
11396        break;
11397      case Expose:
11398        break;
11399      case KeyPress:
11400      {
11401        KeySym
11402          key_symbol;
11403
11404        if (event.xkey.window != windows->image.id)
11405          break;
11406        /*
11407          Respond to a user key press.
11408        */
11409        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11410          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11411        switch ((int) key_symbol)
11412        {
11413          case XK_Escape:
11414          case XK_F20:
11415          {
11416            /*
11417              Prematurely exit.
11418            */
11419            state|=EscapeState;
11420            state|=ExitState;
11421            break;
11422          }
11423          case XK_F1:
11424          case XK_Help:
11425          {
11426            XTextViewWidget(display,resource_info,windows,MagickFalse,
11427              "Help Viewer - Region of Interest",ImageROIHelp);
11428            break;
11429          }
11430          default:
11431          {
11432            (void) XBell(display,0);
11433            break;
11434          }
11435        }
11436        break;
11437      }
11438      case MotionNotify:
11439      {
11440        /*
11441          Map and unmap Info widget as text cursor crosses its boundaries.
11442        */
11443        x=event.xmotion.x;
11444        y=event.xmotion.y;
11445        if (windows->info.mapped != MagickFalse)
11446          {
11447            if ((x < (int) (windows->info.x+windows->info.width)) &&
11448                (y < (int) (windows->info.y+windows->info.height)))
11449              (void) XWithdrawWindow(display,windows->info.id,
11450                windows->info.screen);
11451          }
11452        else
11453          if ((x > (int) (windows->info.x+windows->info.width)) ||
11454              (y > (int) (windows->info.y+windows->info.height)))
11455            (void) XMapWindow(display,windows->info.id);
11456        roi_info.x=(ssize_t) windows->image.x+x;
11457        roi_info.y=(ssize_t) windows->image.y+y;
11458        break;
11459      }
11460      default:
11461        break;
11462    }
11463  } while ((state & ExitState) == 0);
11464  (void) XSelectInput(display,windows->image.id,
11465    windows->image.attributes.event_mask);
11466  if ((state & EscapeState) != 0)
11467    {
11468      /*
11469        User want to exit without region of interest.
11470      */
11471      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11472      (void) XFreeCursor(display,cursor);
11473      return(MagickTrue);
11474    }
11475  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11476  do
11477  {
11478    /*
11479      Size rectangle as pointer moves until the mouse button is released.
11480    */
11481    x=(int) roi_info.x;
11482    y=(int) roi_info.y;
11483    roi_info.width=0;
11484    roi_info.height=0;
11485    state=DefaultState;
11486    do
11487    {
11488      highlight_info=roi_info;
11489      highlight_info.x=roi_info.x-windows->image.x;
11490      highlight_info.y=roi_info.y-windows->image.y;
11491      if ((highlight_info.width > 3) && (highlight_info.height > 3))
11492        {
11493          /*
11494            Display info and draw region of interest rectangle.
11495          */
11496          if (windows->info.mapped == MagickFalse)
11497            (void) XMapWindow(display,windows->info.id);
11498          (void) FormatLocaleString(text,MaxTextExtent,
11499            " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11500            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11501          XInfoWidget(display,windows,text);
11502          XHighlightRectangle(display,windows->image.id,
11503            windows->image.highlight_context,&highlight_info);
11504        }
11505      else
11506        if (windows->info.mapped != MagickFalse)
11507          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11508      /*
11509        Wait for next event.
11510      */
11511      XScreenEvent(display,windows,&event);
11512      if ((highlight_info.width > 3) && (highlight_info.height > 3))
11513        XHighlightRectangle(display,windows->image.id,
11514          windows->image.highlight_context,&highlight_info);
11515      switch (event.type)
11516      {
11517        case ButtonPress:
11518        {
11519          roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11520          roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11521          break;
11522        }
11523        case ButtonRelease:
11524        {
11525          /*
11526            User has committed to region of interest rectangle.
11527          */
11528          roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11529          roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11530          XSetCursorState(display,windows,MagickFalse);
11531          state|=ExitState;
11532          if (LocaleCompare(windows->command.name,"Apply") == 0)
11533            break;
11534          (void) CloneString(&windows->command.name,"Apply");
11535          windows->command.data=ApplyMenus;
11536          (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11537          break;
11538        }
11539        case Expose:
11540          break;
11541        case MotionNotify:
11542        {
11543          roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11544          roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11545        }
11546        default:
11547          break;
11548      }
11549      if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11550          ((state & ExitState) != 0))
11551        {
11552          /*
11553            Check boundary conditions.
11554          */
11555          if (roi_info.x < 0)
11556            roi_info.x=0;
11557          else
11558            if (roi_info.x > (ssize_t) windows->image.ximage->width)
11559              roi_info.x=(ssize_t) windows->image.ximage->width;
11560          if ((int) roi_info.x < x)
11561            roi_info.width=(unsigned int) (x-roi_info.x);
11562          else
11563            {
11564              roi_info.width=(unsigned int) (roi_info.x-x);
11565              roi_info.x=(ssize_t) x;
11566            }
11567          if (roi_info.y < 0)
11568            roi_info.y=0;
11569          else
11570            if (roi_info.y > (ssize_t) windows->image.ximage->height)
11571              roi_info.y=(ssize_t) windows->image.ximage->height;
11572          if ((int) roi_info.y < y)
11573            roi_info.height=(unsigned int) (y-roi_info.y);
11574          else
11575            {
11576              roi_info.height=(unsigned int) (roi_info.y-y);
11577              roi_info.y=(ssize_t) y;
11578            }
11579        }
11580    } while ((state & ExitState) == 0);
11581    /*
11582      Wait for user to grab a corner of the rectangle or press return.
11583    */
11584    state=DefaultState;
11585    command_type=NullCommand;
11586    (void) XMapWindow(display,windows->info.id);
11587    do
11588    {
11589      if (windows->info.mapped != MagickFalse)
11590        {
11591          /*
11592            Display pointer position.
11593          */
11594          (void) FormatLocaleString(text,MaxTextExtent,
11595            " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11596            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11597          XInfoWidget(display,windows,text);
11598        }
11599      highlight_info=roi_info;
11600      highlight_info.x=roi_info.x-windows->image.x;
11601      highlight_info.y=roi_info.y-windows->image.y;
11602      if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11603        {
11604          state|=EscapeState;
11605          state|=ExitState;
11606          break;
11607        }
11608      if ((state & UpdateRegionState) != 0)
11609        {
11610          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11611          switch (command_type)
11612          {
11613            case UndoCommand:
11614            case RedoCommand:
11615            {
11616              (void) XMagickCommand(display,resource_info,windows,command_type,
11617                image,exception);
11618              break;
11619            }
11620            default:
11621            {
11622              /*
11623                Region of interest is relative to image configuration.
11624              */
11625              progress_monitor=SetImageProgressMonitor(*image,
11626                (MagickProgressMonitor) NULL,(*image)->client_data);
11627              crop_info=roi_info;
11628              width=(unsigned int) (*image)->columns;
11629              height=(unsigned int) (*image)->rows;
11630              x=0;
11631              y=0;
11632              if (windows->image.crop_geometry != (char *) NULL)
11633                (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11634                  &width,&height);
11635              scale_factor=(MagickRealType) width/windows->image.ximage->width;
11636              crop_info.x+=x;
11637              crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11638              crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11639              scale_factor=(MagickRealType)
11640                height/windows->image.ximage->height;
11641              crop_info.y+=y;
11642              crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11643              crop_info.height=(unsigned int)
11644                (scale_factor*crop_info.height+0.5);
11645              roi_image=CropImage(*image,&crop_info,exception);
11646              (void) SetImageProgressMonitor(*image,progress_monitor,
11647                (*image)->client_data);
11648              if (roi_image == (Image *) NULL)
11649                continue;
11650              /*
11651                Apply image processing technique to the region of interest.
11652              */
11653              windows->image.orphan=MagickTrue;
11654              (void) XMagickCommand(display,resource_info,windows,command_type,
11655                &roi_image,exception);
11656              progress_monitor=SetImageProgressMonitor(*image,
11657                (MagickProgressMonitor) NULL,(*image)->client_data);
11658              (void) XMagickCommand(display,resource_info,windows,
11659                SaveToUndoBufferCommand,image,exception);
11660              windows->image.orphan=MagickFalse;
11661              (void) CompositeImage(*image,CopyCompositeOp,roi_image,
11662                crop_info.x,crop_info.y,exception);
11663              roi_image=DestroyImage(roi_image);
11664              (void) SetImageProgressMonitor(*image,progress_monitor,
11665                (*image)->client_data);
11666              break;
11667            }
11668          }
11669          if (command_type != InfoCommand)
11670            {
11671              XConfigureImageColormap(display,resource_info,windows,*image);
11672              (void) XConfigureImage(display,resource_info,windows,*image,exception);
11673            }
11674          XCheckRefreshWindows(display,windows);
11675          XInfoWidget(display,windows,text);
11676          (void) XSetFunction(display,windows->image.highlight_context,
11677            GXinvert);
11678          state&=(~UpdateRegionState);
11679        }
11680      XHighlightRectangle(display,windows->image.id,
11681        windows->image.highlight_context,&highlight_info);
11682      XScreenEvent(display,windows,&event);
11683      if (event.xany.window == windows->command.id)
11684        {
11685          /*
11686            Select a command from the Command widget.
11687          */
11688          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11689          command_type=NullCommand;
11690          id=XCommandWidget(display,windows,ApplyMenu,&event);
11691          if (id >= 0)
11692            {
11693              (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11694              command_type=ApplyCommands[id];
11695              if (id < ApplyMenus)
11696                {
11697                  /*
11698                    Select a command from a pop-up menu.
11699                  */
11700                  entry=XMenuWidget(display,windows,ApplyMenu[id],
11701                    (const char **) Menus[id],command);
11702                  if (entry >= 0)
11703                    {
11704                      (void) CopyMagickString(command,Menus[id][entry],
11705                        MaxTextExtent);
11706                      command_type=Commands[id][entry];
11707                    }
11708                }
11709            }
11710          (void) XSetFunction(display,windows->image.highlight_context,
11711            GXinvert);
11712          XHighlightRectangle(display,windows->image.id,
11713            windows->image.highlight_context,&highlight_info);
11714          if (command_type == HelpCommand)
11715            {
11716              (void) XSetFunction(display,windows->image.highlight_context,
11717                GXcopy);
11718              XTextViewWidget(display,resource_info,windows,MagickFalse,
11719                "Help Viewer - Region of Interest",ImageROIHelp);
11720              (void) XSetFunction(display,windows->image.highlight_context,
11721                GXinvert);
11722              continue;
11723            }
11724          if (command_type == QuitCommand)
11725            {
11726              /*
11727                exit.
11728              */
11729              state|=EscapeState;
11730              state|=ExitState;
11731              continue;
11732            }
11733          if (command_type != NullCommand)
11734            state|=UpdateRegionState;
11735          continue;
11736        }
11737      XHighlightRectangle(display,windows->image.id,
11738        windows->image.highlight_context,&highlight_info);
11739      switch (event.type)
11740      {
11741        case ButtonPress:
11742        {
11743          x=windows->image.x;
11744          y=windows->image.y;
11745          if (event.xbutton.button != Button1)
11746            break;
11747          if (event.xbutton.window != windows->image.id)
11748            break;
11749          x=windows->image.x+event.xbutton.x;
11750          y=windows->image.y+event.xbutton.y;
11751          if ((x < (int) (roi_info.x+RoiDelta)) &&
11752              (x > (int) (roi_info.x-RoiDelta)) &&
11753              (y < (int) (roi_info.y+RoiDelta)) &&
11754              (y > (int) (roi_info.y-RoiDelta)))
11755            {
11756              roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11757              roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11758              state|=UpdateConfigurationState;
11759              break;
11760            }
11761          if ((x < (int) (roi_info.x+RoiDelta)) &&
11762              (x > (int) (roi_info.x-RoiDelta)) &&
11763              (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11764              (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11765            {
11766              roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11767              state|=UpdateConfigurationState;
11768              break;
11769            }
11770          if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11771              (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11772              (y < (int) (roi_info.y+RoiDelta)) &&
11773              (y > (int) (roi_info.y-RoiDelta)))
11774            {
11775              roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11776              state|=UpdateConfigurationState;
11777              break;
11778            }
11779          if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11780              (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11781              (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11782              (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11783            {
11784              state|=UpdateConfigurationState;
11785              break;
11786            }
11787        }
11788        case ButtonRelease:
11789        {
11790          if (event.xbutton.window == windows->pan.id)
11791            if ((highlight_info.x != crop_info.x-windows->image.x) ||
11792                (highlight_info.y != crop_info.y-windows->image.y))
11793              XHighlightRectangle(display,windows->image.id,
11794                windows->image.highlight_context,&highlight_info);
11795          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11796            event.xbutton.time);
11797          break;
11798        }
11799        case Expose:
11800        {
11801          if (event.xexpose.window == windows->image.id)
11802            if (event.xexpose.count == 0)
11803              {
11804                event.xexpose.x=(int) highlight_info.x;
11805                event.xexpose.y=(int) highlight_info.y;
11806                event.xexpose.width=(int) highlight_info.width;
11807                event.xexpose.height=(int) highlight_info.height;
11808                XRefreshWindow(display,&windows->image,&event);
11809              }
11810          if (event.xexpose.window == windows->info.id)
11811            if (event.xexpose.count == 0)
11812              XInfoWidget(display,windows,text);
11813          break;
11814        }
11815        case KeyPress:
11816        {
11817          KeySym
11818            key_symbol;
11819
11820          if (event.xkey.window != windows->image.id)
11821            break;
11822          /*
11823            Respond to a user key press.
11824          */
11825          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11826            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11827          switch ((int) key_symbol)
11828          {
11829            case XK_Shift_L:
11830            case XK_Shift_R:
11831              break;
11832            case XK_Escape:
11833            case XK_F20:
11834              state|=EscapeState;
11835            case XK_Return:
11836            {
11837              state|=ExitState;
11838              break;
11839            }
11840            case XK_Home:
11841            case XK_KP_Home:
11842            {
11843              roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11844              roi_info.y=(ssize_t) (windows->image.height/2L-
11845                roi_info.height/2L);
11846              break;
11847            }
11848            case XK_Left:
11849            case XK_KP_Left:
11850            {
11851              roi_info.x--;
11852              break;
11853            }
11854            case XK_Up:
11855            case XK_KP_Up:
11856            case XK_Next:
11857            {
11858              roi_info.y--;
11859              break;
11860            }
11861            case XK_Right:
11862            case XK_KP_Right:
11863            {
11864              roi_info.x++;
11865              break;
11866            }
11867            case XK_Prior:
11868            case XK_Down:
11869            case XK_KP_Down:
11870            {
11871              roi_info.y++;
11872              break;
11873            }
11874            case XK_F1:
11875            case XK_Help:
11876            {
11877              (void) XSetFunction(display,windows->image.highlight_context,
11878                GXcopy);
11879              XTextViewWidget(display,resource_info,windows,MagickFalse,
11880                "Help Viewer - Region of Interest",ImageROIHelp);
11881              (void) XSetFunction(display,windows->image.highlight_context,
11882                GXinvert);
11883              break;
11884            }
11885            default:
11886            {
11887              command_type=XImageWindowCommand(display,resource_info,windows,
11888                event.xkey.state,key_symbol,image,exception);
11889              if (command_type != NullCommand)
11890                state|=UpdateRegionState;
11891              break;
11892            }
11893          }
11894          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11895            event.xkey.time);
11896          break;
11897        }
11898        case KeyRelease:
11899          break;
11900        case MotionNotify:
11901        {
11902          if (event.xbutton.window != windows->image.id)
11903            break;
11904          /*
11905            Map and unmap Info widget as text cursor crosses its boundaries.
11906          */
11907          x=event.xmotion.x;
11908          y=event.xmotion.y;
11909          if (windows->info.mapped != MagickFalse)
11910            {
11911              if ((x < (int) (windows->info.x+windows->info.width)) &&
11912                  (y < (int) (windows->info.y+windows->info.height)))
11913                (void) XWithdrawWindow(display,windows->info.id,
11914                  windows->info.screen);
11915            }
11916          else
11917            if ((x > (int) (windows->info.x+windows->info.width)) ||
11918                (y > (int) (windows->info.y+windows->info.height)))
11919              (void) XMapWindow(display,windows->info.id);
11920          roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11921          roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11922          break;
11923        }
11924        case SelectionRequest:
11925        {
11926          XSelectionEvent
11927            notify;
11928
11929          XSelectionRequestEvent
11930            *request;
11931
11932          /*
11933            Set primary selection.
11934          */
11935          (void) FormatLocaleString(text,MaxTextExtent,
11936            "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11937            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11938          request=(&(event.xselectionrequest));
11939          (void) XChangeProperty(request->display,request->requestor,
11940            request->property,request->target,8,PropModeReplace,
11941            (unsigned char *) text,(int) strlen(text));
11942          notify.type=SelectionNotify;
11943          notify.display=request->display;
11944          notify.requestor=request->requestor;
11945          notify.selection=request->selection;
11946          notify.target=request->target;
11947          notify.time=request->time;
11948          if (request->property == None)
11949            notify.property=request->target;
11950          else
11951            notify.property=request->property;
11952          (void) XSendEvent(request->display,request->requestor,False,0,
11953            (XEvent *) &notify);
11954        }
11955        default:
11956          break;
11957      }
11958      if ((state & UpdateConfigurationState) != 0)
11959        {
11960          (void) XPutBackEvent(display,&event);
11961          (void) XCheckDefineCursor(display,windows->image.id,cursor);
11962          break;
11963        }
11964    } while ((state & ExitState) == 0);
11965  } while ((state & ExitState) == 0);
11966  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11967  XSetCursorState(display,windows,MagickFalse);
11968  if ((state & EscapeState) != 0)
11969    return(MagickTrue);
11970  return(MagickTrue);
11971}
11972
11973/*
11974%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11975%                                                                             %
11976%                                                                             %
11977%                                                                             %
11978+   X R o t a t e I m a g e                                                   %
11979%                                                                             %
11980%                                                                             %
11981%                                                                             %
11982%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11983%
11984%  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
11985%  rotation angle is computed from the slope of a line drawn by the user.
11986%
11987%  The format of the XRotateImage method is:
11988%
11989%      MagickBooleanType XRotateImage(Display *display,
11990%        XResourceInfo *resource_info,XWindows *windows,double degrees,
11991%        Image **image,ExceptionInfo *exception)
11992%
11993%  A description of each parameter follows:
11994%
11995%    o display: Specifies a connection to an X server; returned from
11996%      XOpenDisplay.
11997%
11998%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11999%
12000%    o windows: Specifies a pointer to a XWindows structure.
12001%
12002%    o degrees: Specifies the number of degrees to rotate the image.
12003%
12004%    o image: the image.
12005%
12006%    o exception: return any errors or warnings in this structure.
12007%
12008*/
12009static MagickBooleanType XRotateImage(Display *display,
12010  XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
12011  ExceptionInfo *exception)
12012{
12013  static const char
12014    *RotateMenu[] =
12015    {
12016      "Pixel Color",
12017      "Direction",
12018      "Help",
12019      "Dismiss",
12020      (char *) NULL
12021    };
12022
12023  static ModeType
12024    direction = HorizontalRotateCommand;
12025
12026  static const ModeType
12027    DirectionCommands[] =
12028    {
12029      HorizontalRotateCommand,
12030      VerticalRotateCommand
12031    },
12032    RotateCommands[] =
12033    {
12034      RotateColorCommand,
12035      RotateDirectionCommand,
12036      RotateHelpCommand,
12037      RotateDismissCommand
12038    };
12039
12040  static unsigned int
12041    pen_id = 0;
12042
12043  char
12044    command[MaxTextExtent],
12045    text[MaxTextExtent];
12046
12047  Image
12048    *rotate_image;
12049
12050  int
12051    id,
12052    x,
12053    y;
12054
12055  MagickRealType
12056    normalized_degrees;
12057
12058  register int
12059    i;
12060
12061  unsigned int
12062    height,
12063    rotations,
12064    width;
12065
12066  if (degrees == 0.0)
12067    {
12068      unsigned int
12069        distance;
12070
12071      size_t
12072        state;
12073
12074      XEvent
12075        event;
12076
12077      XSegment
12078        rotate_info;
12079
12080      /*
12081        Map Command widget.
12082      */
12083      (void) CloneString(&windows->command.name,"Rotate");
12084      windows->command.data=2;
12085      (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12086      (void) XMapRaised(display,windows->command.id);
12087      XClientMessage(display,windows->image.id,windows->im_protocols,
12088        windows->im_update_widget,CurrentTime);
12089      /*
12090        Wait for first button press.
12091      */
12092      (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12093      XQueryPosition(display,windows->image.id,&x,&y);
12094      rotate_info.x1=x;
12095      rotate_info.y1=y;
12096      rotate_info.x2=x;
12097      rotate_info.y2=y;
12098      state=DefaultState;
12099      do
12100      {
12101        XHighlightLine(display,windows->image.id,
12102          windows->image.highlight_context,&rotate_info);
12103        /*
12104          Wait for next event.
12105        */
12106        XScreenEvent(display,windows,&event);
12107        XHighlightLine(display,windows->image.id,
12108          windows->image.highlight_context,&rotate_info);
12109        if (event.xany.window == windows->command.id)
12110          {
12111            /*
12112              Select a command from the Command widget.
12113            */
12114            id=XCommandWidget(display,windows,RotateMenu,&event);
12115            if (id < 0)
12116              continue;
12117            (void) XSetFunction(display,windows->image.highlight_context,
12118              GXcopy);
12119            switch (RotateCommands[id])
12120            {
12121              case RotateColorCommand:
12122              {
12123                const char
12124                  *ColorMenu[MaxNumberPens];
12125
12126                int
12127                  pen_number;
12128
12129                XColor
12130                  color;
12131
12132                /*
12133                  Initialize menu selections.
12134                */
12135                for (i=0; i < (int) (MaxNumberPens-2); i++)
12136                  ColorMenu[i]=resource_info->pen_colors[i];
12137                ColorMenu[MaxNumberPens-2]="Browser...";
12138                ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12139                /*
12140                  Select a pen color from the pop-up menu.
12141                */
12142                pen_number=XMenuWidget(display,windows,RotateMenu[id],
12143                  (const char **) ColorMenu,command);
12144                if (pen_number < 0)
12145                  break;
12146                if (pen_number == (MaxNumberPens-2))
12147                  {
12148                    static char
12149                      color_name[MaxTextExtent] = "gray";
12150
12151                    /*
12152                      Select a pen color from a dialog.
12153                    */
12154                    resource_info->pen_colors[pen_number]=color_name;
12155                    XColorBrowserWidget(display,windows,"Select",color_name);
12156                    if (*color_name == '\0')
12157                      break;
12158                  }
12159                /*
12160                  Set pen color.
12161                */
12162                (void) XParseColor(display,windows->map_info->colormap,
12163                  resource_info->pen_colors[pen_number],&color);
12164                XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12165                  (unsigned int) MaxColors,&color);
12166                windows->pixel_info->pen_colors[pen_number]=color;
12167                pen_id=(unsigned int) pen_number;
12168                break;
12169              }
12170              case RotateDirectionCommand:
12171              {
12172                static const char
12173                  *Directions[] =
12174                  {
12175                    "horizontal",
12176                    "vertical",
12177                    (char *) NULL,
12178                  };
12179
12180                /*
12181                  Select a command from the pop-up menu.
12182                */
12183                id=XMenuWidget(display,windows,RotateMenu[id],
12184                  Directions,command);
12185                if (id >= 0)
12186                  direction=DirectionCommands[id];
12187                break;
12188              }
12189              case RotateHelpCommand:
12190              {
12191                XTextViewWidget(display,resource_info,windows,MagickFalse,
12192                  "Help Viewer - Image Rotation",ImageRotateHelp);
12193                break;
12194              }
12195              case RotateDismissCommand:
12196              {
12197                /*
12198                  Prematurely exit.
12199                */
12200                state|=EscapeState;
12201                state|=ExitState;
12202                break;
12203              }
12204              default:
12205                break;
12206            }
12207            (void) XSetFunction(display,windows->image.highlight_context,
12208              GXinvert);
12209            continue;
12210          }
12211        switch (event.type)
12212        {
12213          case ButtonPress:
12214          {
12215            if (event.xbutton.button != Button1)
12216              break;
12217            if (event.xbutton.window != windows->image.id)
12218              break;
12219            /*
12220              exit loop.
12221            */
12222            (void) XSetFunction(display,windows->image.highlight_context,
12223              GXcopy);
12224            rotate_info.x1=event.xbutton.x;
12225            rotate_info.y1=event.xbutton.y;
12226            state|=ExitState;
12227            break;
12228          }
12229          case ButtonRelease:
12230            break;
12231          case Expose:
12232            break;
12233          case KeyPress:
12234          {
12235            char
12236              command[MaxTextExtent];
12237
12238            KeySym
12239              key_symbol;
12240
12241            if (event.xkey.window != windows->image.id)
12242              break;
12243            /*
12244              Respond to a user key press.
12245            */
12246            (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12247              sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12248            switch ((int) key_symbol)
12249            {
12250              case XK_Escape:
12251              case XK_F20:
12252              {
12253                /*
12254                  Prematurely exit.
12255                */
12256                state|=EscapeState;
12257                state|=ExitState;
12258                break;
12259              }
12260              case XK_F1:
12261              case XK_Help:
12262              {
12263                (void) XSetFunction(display,windows->image.highlight_context,
12264                  GXcopy);
12265                XTextViewWidget(display,resource_info,windows,MagickFalse,
12266                  "Help Viewer - Image Rotation",ImageRotateHelp);
12267                (void) XSetFunction(display,windows->image.highlight_context,
12268                  GXinvert);
12269                break;
12270              }
12271              default:
12272              {
12273                (void) XBell(display,0);
12274                break;
12275              }
12276            }
12277            break;
12278          }
12279          case MotionNotify:
12280          {
12281            rotate_info.x1=event.xmotion.x;
12282            rotate_info.y1=event.xmotion.y;
12283          }
12284        }
12285        rotate_info.x2=rotate_info.x1;
12286        rotate_info.y2=rotate_info.y1;
12287        if (direction == HorizontalRotateCommand)
12288          rotate_info.x2+=32;
12289        else
12290          rotate_info.y2-=32;
12291      } while ((state & ExitState) == 0);
12292      (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12293      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12294      if ((state & EscapeState) != 0)
12295        return(MagickTrue);
12296      /*
12297        Draw line as pointer moves until the mouse button is released.
12298      */
12299      distance=0;
12300      (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12301      state=DefaultState;
12302      do
12303      {
12304        if (distance > 9)
12305          {
12306            /*
12307              Display info and draw rotation line.
12308            */
12309            if (windows->info.mapped == MagickFalse)
12310              (void) XMapWindow(display,windows->info.id);
12311            (void) FormatLocaleString(text,MaxTextExtent," %g",
12312              direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12313            XInfoWidget(display,windows,text);
12314            XHighlightLine(display,windows->image.id,
12315              windows->image.highlight_context,&rotate_info);
12316          }
12317        else
12318          if (windows->info.mapped != MagickFalse)
12319            (void) XWithdrawWindow(display,windows->info.id,
12320              windows->info.screen);
12321        /*
12322          Wait for next event.
12323        */
12324        XScreenEvent(display,windows,&event);
12325        if (distance > 9)
12326          XHighlightLine(display,windows->image.id,
12327            windows->image.highlight_context,&rotate_info);
12328        switch (event.type)
12329        {
12330          case ButtonPress:
12331            break;
12332          case ButtonRelease:
12333          {
12334            /*
12335              User has committed to rotation line.
12336            */
12337            rotate_info.x2=event.xbutton.x;
12338            rotate_info.y2=event.xbutton.y;
12339            state|=ExitState;
12340            break;
12341          }
12342          case Expose:
12343            break;
12344          case MotionNotify:
12345          {
12346            rotate_info.x2=event.xmotion.x;
12347            rotate_info.y2=event.xmotion.y;
12348          }
12349          default:
12350            break;
12351        }
12352        /*
12353          Check boundary conditions.
12354        */
12355        if (rotate_info.x2 < 0)
12356          rotate_info.x2=0;
12357        else
12358          if (rotate_info.x2 > (int) windows->image.width)
12359            rotate_info.x2=(short) windows->image.width;
12360        if (rotate_info.y2 < 0)
12361          rotate_info.y2=0;
12362        else
12363          if (rotate_info.y2 > (int) windows->image.height)
12364            rotate_info.y2=(short) windows->image.height;
12365        /*
12366          Compute rotation angle from the slope of the line.
12367        */
12368        degrees=0.0;
12369        distance=(unsigned int)
12370          ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12371          ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12372        if (distance > 9)
12373          degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12374            rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12375      } while ((state & ExitState) == 0);
12376      (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12377      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12378      if (distance <= 9)
12379        return(MagickTrue);
12380    }
12381  if (direction == VerticalRotateCommand)
12382    degrees-=90.0;
12383  if (degrees == 0.0)
12384    return(MagickTrue);
12385  /*
12386    Rotate image.
12387  */
12388  normalized_degrees=degrees;
12389  while (normalized_degrees < -45.0)
12390    normalized_degrees+=360.0;
12391  for (rotations=0; normalized_degrees > 45.0; rotations++)
12392    normalized_degrees-=90.0;
12393  if (normalized_degrees != 0.0)
12394    (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12395      exception);
12396  XSetCursorState(display,windows,MagickTrue);
12397  XCheckRefreshWindows(display,windows);
12398  (*image)->background_color.red=ScaleShortToQuantum(
12399    windows->pixel_info->pen_colors[pen_id].red);
12400  (*image)->background_color.green=ScaleShortToQuantum(
12401    windows->pixel_info->pen_colors[pen_id].green);
12402  (*image)->background_color.blue=ScaleShortToQuantum(
12403    windows->pixel_info->pen_colors[pen_id].blue);
12404  rotate_image=RotateImage(*image,degrees,exception);
12405  XSetCursorState(display,windows,MagickFalse);
12406  if (rotate_image == (Image *) NULL)
12407    return(MagickFalse);
12408  *image=DestroyImage(*image);
12409  *image=rotate_image;
12410  if (windows->image.crop_geometry != (char *) NULL)
12411    {
12412      /*
12413        Rotate crop geometry.
12414      */
12415      width=(unsigned int) (*image)->columns;
12416      height=(unsigned int) (*image)->rows;
12417      (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12418      switch (rotations % 4)
12419      {
12420        default:
12421        case 0:
12422          break;
12423        case 1:
12424        {
12425          /*
12426            Rotate 90 degrees.
12427          */
12428          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12429            "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12430            (int) height-y,x);
12431          break;
12432        }
12433        case 2:
12434        {
12435          /*
12436            Rotate 180 degrees.
12437          */
12438          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12439            "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12440          break;
12441        }
12442        case 3:
12443        {
12444          /*
12445            Rotate 270 degrees.
12446          */
12447          (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12448            "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12449          break;
12450        }
12451      }
12452    }
12453  if (windows->image.orphan != MagickFalse)
12454    return(MagickTrue);
12455  if (normalized_degrees != 0.0)
12456    {
12457      /*
12458        Update image colormap.
12459      */
12460      windows->image.window_changes.width=(int) (*image)->columns;
12461      windows->image.window_changes.height=(int) (*image)->rows;
12462      if (windows->image.crop_geometry != (char *) NULL)
12463        {
12464          /*
12465            Obtain dimensions of image from crop geometry.
12466          */
12467          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12468            &width,&height);
12469          windows->image.window_changes.width=(int) width;
12470          windows->image.window_changes.height=(int) height;
12471        }
12472      XConfigureImageColormap(display,resource_info,windows,*image);
12473    }
12474  else
12475    if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12476      {
12477        windows->image.window_changes.width=windows->image.ximage->height;
12478        windows->image.window_changes.height=windows->image.ximage->width;
12479      }
12480  /*
12481    Update image configuration.
12482  */
12483  (void) XConfigureImage(display,resource_info,windows,*image,exception);
12484  return(MagickTrue);
12485}
12486
12487/*
12488%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12489%                                                                             %
12490%                                                                             %
12491%                                                                             %
12492+   X S a v e I m a g e                                                       %
12493%                                                                             %
12494%                                                                             %
12495%                                                                             %
12496%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12497%
12498%  XSaveImage() saves an image to a file.
12499%
12500%  The format of the XSaveImage method is:
12501%
12502%      MagickBooleanType XSaveImage(Display *display,
12503%        XResourceInfo *resource_info,XWindows *windows,Image *image,
12504%        ExceptionInfo *exception)
12505%
12506%  A description of each parameter follows:
12507%
12508%    o display: Specifies a connection to an X server; returned from
12509%      XOpenDisplay.
12510%
12511%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12512%
12513%    o windows: Specifies a pointer to a XWindows structure.
12514%
12515%    o image: the image.
12516%
12517%    o exception: return any errors or warnings in this structure.
12518%
12519*/
12520static MagickBooleanType XSaveImage(Display *display,
12521  XResourceInfo *resource_info,XWindows *windows,Image *image,
12522  ExceptionInfo *exception)
12523{
12524  char
12525    filename[MaxTextExtent],
12526    geometry[MaxTextExtent];
12527
12528  Image
12529    *save_image;
12530
12531  ImageInfo
12532    *image_info;
12533
12534  MagickStatusType
12535    status;
12536
12537  /*
12538    Request file name from user.
12539  */
12540  if (resource_info->write_filename != (char *) NULL)
12541    (void) CopyMagickString(filename,resource_info->write_filename,
12542      MaxTextExtent);
12543  else
12544    {
12545      char
12546        path[MaxTextExtent];
12547
12548      int
12549        status;
12550
12551      GetPathComponent(image->filename,HeadPath,path);
12552      GetPathComponent(image->filename,TailPath,filename);
12553      if (*path != '\0')
12554        {
12555          status=chdir(path);
12556          if (status == -1)
12557            (void) ThrowMagickException(exception,GetMagickModule(),
12558              FileOpenError,"UnableToOpenFile","%s",path);
12559        }
12560    }
12561  XFileBrowserWidget(display,windows,"Save",filename);
12562  if (*filename == '\0')
12563    return(MagickTrue);
12564  if (IsPathAccessible(filename) != MagickFalse)
12565    {
12566      int
12567        status;
12568
12569      /*
12570        File exists-- seek user's permission before overwriting.
12571      */
12572      status=XConfirmWidget(display,windows,"Overwrite",filename);
12573      if (status <= 0)
12574        return(MagickTrue);
12575    }
12576  image_info=CloneImageInfo(resource_info->image_info);
12577  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12578  (void) SetImageInfo(image_info,1,exception);
12579  if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12580      (LocaleCompare(image_info->magick,"JPG") == 0))
12581    {
12582      char
12583        quality[MaxTextExtent];
12584
12585      int
12586        status;
12587
12588      /*
12589        Request JPEG quality from user.
12590      */
12591      (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
12592        image->quality);
12593      status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12594        quality);
12595      if (*quality == '\0')
12596        return(MagickTrue);
12597      image->quality=StringToUnsignedLong(quality);
12598      image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12599    }
12600  if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12601      (LocaleCompare(image_info->magick,"PDF") == 0) ||
12602      (LocaleCompare(image_info->magick,"PS") == 0) ||
12603      (LocaleCompare(image_info->magick,"PS2") == 0))
12604    {
12605      char
12606        geometry[MaxTextExtent];
12607
12608      /*
12609        Request page geometry from user.
12610      */
12611      (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12612      if (LocaleCompare(image_info->magick,"PDF") == 0)
12613        (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12614      if (image_info->page != (char *) NULL)
12615        (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12616      XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12617        "Select page geometry:",geometry);
12618      if (*geometry != '\0')
12619        image_info->page=GetPageGeometry(geometry);
12620    }
12621  /*
12622    Apply image transforms.
12623  */
12624  XSetCursorState(display,windows,MagickTrue);
12625  XCheckRefreshWindows(display,windows);
12626  save_image=CloneImage(image,0,0,MagickTrue,exception);
12627  if (save_image == (Image *) NULL)
12628    return(MagickFalse);
12629  (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
12630    windows->image.ximage->width,windows->image.ximage->height);
12631  (void) TransformImage(&save_image,windows->image.crop_geometry,geometry,
12632    exception);
12633  /*
12634    Write image.
12635  */
12636  (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12637  status=WriteImage(image_info,save_image,exception);
12638  if (status != MagickFalse)
12639    image->taint=MagickFalse;
12640  save_image=DestroyImage(save_image);
12641  image_info=DestroyImageInfo(image_info);
12642  XSetCursorState(display,windows,MagickFalse);
12643  return(status != 0 ? MagickTrue : MagickFalse);
12644}
12645
12646/*
12647%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12648%                                                                             %
12649%                                                                             %
12650%                                                                             %
12651+   X S c r e e n E v e n t                                                   %
12652%                                                                             %
12653%                                                                             %
12654%                                                                             %
12655%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12656%
12657%  XScreenEvent() handles global events associated with the Pan and Magnify
12658%  windows.
12659%
12660%  The format of the XScreenEvent function is:
12661%
12662%      void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12663%
12664%  A description of each parameter follows:
12665%
12666%    o display: Specifies a pointer to the Display structure;  returned from
12667%      XOpenDisplay.
12668%
12669%    o windows: Specifies a pointer to a XWindows structure.
12670%
12671%    o event: Specifies a pointer to a X11 XEvent structure.
12672%
12673%
12674*/
12675
12676#if defined(__cplusplus) || defined(c_plusplus)
12677extern "C" {
12678#endif
12679
12680static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12681{
12682  register XWindows
12683    *windows;
12684
12685  windows=(XWindows *) data;
12686  if ((event->type == ClientMessage) &&
12687      (event->xclient.window == windows->image.id))
12688    return(MagickFalse);
12689  return(MagickTrue);
12690}
12691
12692#if defined(__cplusplus) || defined(c_plusplus)
12693}
12694#endif
12695
12696static void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12697{
12698  register int
12699    x,
12700    y;
12701
12702  (void) XIfEvent(display,event,XPredicate,(char *) windows);
12703  if (event->xany.window == windows->command.id)
12704    return;
12705  switch (event->type)
12706  {
12707    case ButtonPress:
12708    case ButtonRelease:
12709    {
12710      if ((event->xbutton.button == Button3) &&
12711          (event->xbutton.state & Mod1Mask))
12712        {
12713          /*
12714            Convert Alt-Button3 to Button2.
12715          */
12716          event->xbutton.button=Button2;
12717          event->xbutton.state&=(~Mod1Mask);
12718        }
12719      if (event->xbutton.window == windows->backdrop.id)
12720        {
12721          (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12722            event->xbutton.time);
12723          break;
12724        }
12725      if (event->xbutton.window == windows->pan.id)
12726        {
12727          XPanImage(display,windows,event);
12728          break;
12729        }
12730      if (event->xbutton.window == windows->image.id)
12731        if (event->xbutton.button == Button2)
12732          {
12733            /*
12734              Update magnified image.
12735            */
12736            x=event->xbutton.x;
12737            y=event->xbutton.y;
12738            if (x < 0)
12739              x=0;
12740            else
12741              if (x >= (int) windows->image.width)
12742                x=(int) (windows->image.width-1);
12743            windows->magnify.x=(int) windows->image.x+x;
12744            if (y < 0)
12745              y=0;
12746            else
12747             if (y >= (int) windows->image.height)
12748               y=(int) (windows->image.height-1);
12749            windows->magnify.y=windows->image.y+y;
12750            if (windows->magnify.mapped == MagickFalse)
12751              (void) XMapRaised(display,windows->magnify.id);
12752            XMakeMagnifyImage(display,windows);
12753            if (event->type == ButtonRelease)
12754              (void) XWithdrawWindow(display,windows->info.id,
12755                windows->info.screen);
12756            break;
12757          }
12758      break;
12759    }
12760    case ClientMessage:
12761    {
12762      /*
12763        If client window delete message, exit.
12764      */
12765      if (event->xclient.message_type != windows->wm_protocols)
12766        break;
12767      if (*event->xclient.data.l != (long) windows->wm_delete_window)
12768        break;
12769      if (event->xclient.window == windows->magnify.id)
12770        {
12771          (void) XWithdrawWindow(display,windows->magnify.id,
12772            windows->magnify.screen);
12773          break;
12774        }
12775      break;
12776    }
12777    case ConfigureNotify:
12778    {
12779      if (event->xconfigure.window == windows->magnify.id)
12780        {
12781          unsigned int
12782            magnify;
12783
12784          /*
12785            Magnify window has a new configuration.
12786          */
12787          windows->magnify.width=(unsigned int) event->xconfigure.width;
12788          windows->magnify.height=(unsigned int) event->xconfigure.height;
12789          if (windows->magnify.mapped == MagickFalse)
12790            break;
12791          magnify=1;
12792          while ((int) magnify <= event->xconfigure.width)
12793            magnify<<=1;
12794          while ((int) magnify <= event->xconfigure.height)
12795            magnify<<=1;
12796          magnify>>=1;
12797          if (((int) magnify != event->xconfigure.width) ||
12798              ((int) magnify != event->xconfigure.height))
12799            {
12800              XWindowChanges
12801                window_changes;
12802
12803              window_changes.width=(int) magnify;
12804              window_changes.height=(int) magnify;
12805              (void) XReconfigureWMWindow(display,windows->magnify.id,
12806                windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12807                &window_changes);
12808              break;
12809            }
12810          XMakeMagnifyImage(display,windows);
12811          break;
12812        }
12813      break;
12814    }
12815    case Expose:
12816    {
12817      if (event->xexpose.window == windows->image.id)
12818        {
12819          XRefreshWindow(display,&windows->image,event);
12820          break;
12821        }
12822      if (event->xexpose.window == windows->pan.id)
12823        if (event->xexpose.count == 0)
12824          {
12825            XDrawPanRectangle(display,windows);
12826            break;
12827          }
12828      if (event->xexpose.window == windows->magnify.id)
12829        if (event->xexpose.count == 0)
12830          {
12831            XMakeMagnifyImage(display,windows);
12832            break;
12833          }
12834      break;
12835    }
12836    case KeyPress:
12837    {
12838      char
12839        command[MaxTextExtent];
12840
12841      KeySym
12842        key_symbol;
12843
12844      if (event->xkey.window != windows->magnify.id)
12845        break;
12846      /*
12847        Respond to a user key press.
12848      */
12849      (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12850        sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12851      XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol);
12852      break;
12853    }
12854    case MapNotify:
12855    {
12856      if (event->xmap.window == windows->magnify.id)
12857        {
12858          windows->magnify.mapped=MagickTrue;
12859          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12860          break;
12861        }
12862      if (event->xmap.window == windows->info.id)
12863        {
12864          windows->info.mapped=MagickTrue;
12865          break;
12866        }
12867      break;
12868    }
12869    case MotionNotify:
12870    {
12871      while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12872      if (event->xmotion.window == windows->image.id)
12873        if (windows->magnify.mapped != MagickFalse)
12874          {
12875            /*
12876              Update magnified image.
12877            */
12878            x=event->xmotion.x;
12879            y=event->xmotion.y;
12880            if (x < 0)
12881              x=0;
12882            else
12883              if (x >= (int) windows->image.width)
12884                x=(int) (windows->image.width-1);
12885            windows->magnify.x=(int) windows->image.x+x;
12886            if (y < 0)
12887              y=0;
12888            else
12889             if (y >= (int) windows->image.height)
12890               y=(int) (windows->image.height-1);
12891            windows->magnify.y=windows->image.y+y;
12892            XMakeMagnifyImage(display,windows);
12893          }
12894      break;
12895    }
12896    case UnmapNotify:
12897    {
12898      if (event->xunmap.window == windows->magnify.id)
12899        {
12900          windows->magnify.mapped=MagickFalse;
12901          break;
12902        }
12903      if (event->xunmap.window == windows->info.id)
12904        {
12905          windows->info.mapped=MagickFalse;
12906          break;
12907        }
12908      break;
12909    }
12910    default:
12911      break;
12912  }
12913}
12914
12915/*
12916%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12917%                                                                             %
12918%                                                                             %
12919%                                                                             %
12920+   X S e t C r o p G e o m e t r y                                           %
12921%                                                                             %
12922%                                                                             %
12923%                                                                             %
12924%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12925%
12926%  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12927%  and translates it to a cropping geometry relative to the image.
12928%
12929%  The format of the XSetCropGeometry method is:
12930%
12931%      void XSetCropGeometry(Display *display,XWindows *windows,
12932%        RectangleInfo *crop_info,Image *image)
12933%
12934%  A description of each parameter follows:
12935%
12936%    o display: Specifies a connection to an X server; returned from
12937%      XOpenDisplay.
12938%
12939%    o windows: Specifies a pointer to a XWindows structure.
12940%
12941%    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12942%      Image window to crop.
12943%
12944%    o image: the image.
12945%
12946*/
12947static void XSetCropGeometry(Display *display,XWindows *windows,
12948  RectangleInfo *crop_info,Image *image)
12949{
12950  char
12951    text[MaxTextExtent];
12952
12953  int
12954    x,
12955    y;
12956
12957  MagickRealType
12958    scale_factor;
12959
12960  unsigned int
12961    height,
12962    width;
12963
12964  if (windows->info.mapped != MagickFalse)
12965    {
12966      /*
12967        Display info on cropping rectangle.
12968      */
12969      (void) FormatLocaleString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
12970        (double) crop_info->width,(double) crop_info->height,(double)
12971        crop_info->x,(double) crop_info->y);
12972      XInfoWidget(display,windows,text);
12973    }
12974  /*
12975    Cropping geometry is relative to any previous crop geometry.
12976  */
12977  x=0;
12978  y=0;
12979  width=(unsigned int) image->columns;
12980  height=(unsigned int) image->rows;
12981  if (windows->image.crop_geometry != (char *) NULL)
12982    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12983  else
12984    windows->image.crop_geometry=AcquireString((char *) NULL);
12985  /*
12986    Define the crop geometry string from the cropping rectangle.
12987  */
12988  scale_factor=(MagickRealType) width/windows->image.ximage->width;
12989  if (crop_info->x > 0)
12990    x+=(int) (scale_factor*crop_info->x+0.5);
12991  width=(unsigned int) (scale_factor*crop_info->width+0.5);
12992  if (width == 0)
12993    width=1;
12994  scale_factor=(MagickRealType) height/windows->image.ximage->height;
12995  if (crop_info->y > 0)
12996    y+=(int) (scale_factor*crop_info->y+0.5);
12997  height=(unsigned int) (scale_factor*crop_info->height+0.5);
12998  if (height == 0)
12999    height=1;
13000  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
13001    "%ux%u%+d%+d",width,height,x,y);
13002}
13003
13004/*
13005%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13006%                                                                             %
13007%                                                                             %
13008%                                                                             %
13009+   X T i l e I m a g e                                                       %
13010%                                                                             %
13011%                                                                             %
13012%                                                                             %
13013%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13014%
13015%  XTileImage() loads or deletes a selected tile from a visual image directory.
13016%  The load or delete command is chosen from a menu.
13017%
13018%  The format of the XTileImage method is:
13019%
13020%      Image *XTileImage(Display *display,XResourceInfo *resource_info,
13021%        XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13022%
13023%  A description of each parameter follows:
13024%
13025%    o tile_image:  XTileImage reads or deletes the tile image
13026%      and returns it.  A null image is returned if an error occurs.
13027%
13028%    o display: Specifies a connection to an X server;  returned from
13029%      XOpenDisplay.
13030%
13031%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13032%
13033%    o windows: Specifies a pointer to a XWindows structure.
13034%
13035%    o image: the image; returned from ReadImage.
13036%
13037%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
13038%      the entire image is refreshed.
13039%
13040%    o exception: return any errors or warnings in this structure.
13041%
13042*/
13043static Image *XTileImage(Display *display,XResourceInfo *resource_info,
13044  XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13045{
13046  static const char
13047    *VerbMenu[] =
13048    {
13049      "Load",
13050      "Next",
13051      "Former",
13052      "Delete",
13053      "Update",
13054      (char *) NULL,
13055    };
13056
13057  static const ModeType
13058    TileCommands[] =
13059    {
13060      TileLoadCommand,
13061      TileNextCommand,
13062      TileFormerCommand,
13063      TileDeleteCommand,
13064      TileUpdateCommand
13065    };
13066
13067  char
13068    command[MaxTextExtent],
13069    filename[MaxTextExtent];
13070
13071  Image
13072    *tile_image;
13073
13074  int
13075    id,
13076    status,
13077    tile,
13078    x,
13079    y;
13080
13081  MagickRealType
13082    scale_factor;
13083
13084  register char
13085    *p,
13086    *q;
13087
13088  register int
13089    i;
13090
13091  unsigned int
13092    height,
13093    width;
13094
13095  /*
13096    Tile image is relative to montage image configuration.
13097  */
13098  x=0;
13099  y=0;
13100  width=(unsigned int) image->columns;
13101  height=(unsigned int) image->rows;
13102  if (windows->image.crop_geometry != (char *) NULL)
13103    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13104  scale_factor=(MagickRealType) width/windows->image.ximage->width;
13105  event->xbutton.x+=windows->image.x;
13106  event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13107  scale_factor=(MagickRealType) height/windows->image.ximage->height;
13108  event->xbutton.y+=windows->image.y;
13109  event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13110  /*
13111    Determine size and location of each tile in the visual image directory.
13112  */
13113  width=(unsigned int) image->columns;
13114  height=(unsigned int) image->rows;
13115  x=0;
13116  y=0;
13117  (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13118  tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13119    (event->xbutton.x-x)/width;
13120  if (tile < 0)
13121    {
13122      /*
13123        Button press is outside any tile.
13124      */
13125      (void) XBell(display,0);
13126      return((Image *) NULL);
13127    }
13128  /*
13129    Determine file name from the tile directory.
13130  */
13131  p=image->directory;
13132  for (i=tile; (i != 0) && (*p != '\0'); )
13133  {
13134    if (*p == '\n')
13135      i--;
13136    p++;
13137  }
13138  if (*p == '\0')
13139    {
13140      /*
13141        Button press is outside any tile.
13142      */
13143      (void) XBell(display,0);
13144      return((Image *) NULL);
13145    }
13146  /*
13147    Select a command from the pop-up menu.
13148  */
13149  id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13150  if (id < 0)
13151    return((Image *) NULL);
13152  q=p;
13153  while ((*q != '\n') && (*q != '\0'))
13154    q++;
13155  (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13156  /*
13157    Perform command for the selected tile.
13158  */
13159  XSetCursorState(display,windows,MagickTrue);
13160  XCheckRefreshWindows(display,windows);
13161  tile_image=NewImageList();
13162  switch (TileCommands[id])
13163  {
13164    case TileLoadCommand:
13165    {
13166      /*
13167        Load tile image.
13168      */
13169      XCheckRefreshWindows(display,windows);
13170      (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13171        MaxTextExtent);
13172      (void) CopyMagickString(resource_info->image_info->filename,filename,
13173        MaxTextExtent);
13174      tile_image=ReadImage(resource_info->image_info,exception);
13175      CatchException(exception);
13176      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13177      break;
13178    }
13179    case TileNextCommand:
13180    {
13181      /*
13182        Display next image.
13183      */
13184      XClientMessage(display,windows->image.id,windows->im_protocols,
13185        windows->im_next_image,CurrentTime);
13186      break;
13187    }
13188    case TileFormerCommand:
13189    {
13190      /*
13191        Display former image.
13192      */
13193      XClientMessage(display,windows->image.id,windows->im_protocols,
13194        windows->im_former_image,CurrentTime);
13195      break;
13196    }
13197    case TileDeleteCommand:
13198    {
13199      /*
13200        Delete tile image.
13201      */
13202      if (IsPathAccessible(filename) == MagickFalse)
13203        {
13204          XNoticeWidget(display,windows,"Image file does not exist:",filename);
13205          break;
13206        }
13207      status=XConfirmWidget(display,windows,"Really delete tile",filename);
13208      if (status <= 0)
13209        break;
13210      status=remove_utf8(filename) != 0 ? MagickTrue : MagickFalse;
13211      if (status != MagickFalse)
13212        {
13213          XNoticeWidget(display,windows,"Unable to delete image file:",
13214            filename);
13215          break;
13216        }
13217    }
13218    case TileUpdateCommand:
13219    {
13220      int
13221        x_offset,
13222        y_offset;
13223
13224      PixelInfo
13225        pixel;
13226
13227      Quantum
13228        virtual_pixel[MaxPixelChannels];
13229
13230      register int
13231        j;
13232
13233      register Quantum
13234        *s;
13235
13236      /*
13237        Ensure all the images exist.
13238      */
13239      tile=0;
13240      GetPixelInfo(image,&pixel);
13241      for (p=image->directory; *p != '\0'; p++)
13242      {
13243        CacheView
13244          *image_view;
13245
13246        q=p;
13247        while ((*q != '\n') && (*q != '\0'))
13248          q++;
13249        (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13250        p=q;
13251        if (IsPathAccessible(filename) != MagickFalse)
13252          {
13253            tile++;
13254            continue;
13255          }
13256        /*
13257          Overwrite tile with background color.
13258        */
13259        x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13260        y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13261        image_view=AcquireCacheView(image);
13262        (void) GetOneCacheViewVirtualPixel(image_view,0,0,virtual_pixel,
13263          exception);
13264        pixel.red=virtual_pixel[RedPixelChannel];
13265        pixel.green=virtual_pixel[GreenPixelChannel];
13266        pixel.blue=virtual_pixel[BluePixelChannel];
13267        pixel.alpha=virtual_pixel[AlphaPixelChannel];
13268        for (i=0; i < (int) height; i++)
13269        {
13270          s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13271            y_offset+i,width,1,exception);
13272          if (s == (Quantum *) NULL)
13273            break;
13274          for (j=0; j < (int) width; j++)
13275          {
13276            SetPixelPixelInfo(image,&pixel,s);
13277            s+=GetPixelChannels(image);
13278          }
13279          if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13280            break;
13281        }
13282        image_view=DestroyCacheView(image_view);
13283        tile++;
13284      }
13285      windows->image.window_changes.width=(int) image->columns;
13286      windows->image.window_changes.height=(int) image->rows;
13287      XConfigureImageColormap(display,resource_info,windows,image);
13288      (void) XConfigureImage(display,resource_info,windows,image,exception);
13289      break;
13290    }
13291    default:
13292      break;
13293  }
13294  XSetCursorState(display,windows,MagickFalse);
13295  return(tile_image);
13296}
13297
13298/*
13299%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13300%                                                                             %
13301%                                                                             %
13302%                                                                             %
13303+   X T r a n s l a t e I m a g e                                             %
13304%                                                                             %
13305%                                                                             %
13306%                                                                             %
13307%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13308%
13309%  XTranslateImage() translates the image within an Image window by one pixel
13310%  as specified by the key symbol.  If the image has a `montage string the
13311%  translation is respect to the width and height contained within the string.
13312%
13313%  The format of the XTranslateImage method is:
13314%
13315%      void XTranslateImage(Display *display,XWindows *windows,
13316%        Image *image,const KeySym key_symbol)
13317%
13318%  A description of each parameter follows:
13319%
13320%    o display: Specifies a connection to an X server; returned from
13321%      XOpenDisplay.
13322%
13323%    o windows: Specifies a pointer to a XWindows structure.
13324%
13325%    o image: the image.
13326%
13327%    o key_symbol: Specifies a KeySym which indicates which side of the image
13328%      to trim.
13329%
13330*/
13331static void XTranslateImage(Display *display,XWindows *windows,
13332  Image *image,const KeySym key_symbol)
13333{
13334  char
13335    text[MaxTextExtent];
13336
13337  int
13338    x,
13339    y;
13340
13341  unsigned int
13342    x_offset,
13343    y_offset;
13344
13345  /*
13346    User specified a pan position offset.
13347  */
13348  x_offset=windows->image.width;
13349  y_offset=windows->image.height;
13350  if (image->montage != (char *) NULL)
13351    (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13352  switch ((int) key_symbol)
13353  {
13354    case XK_Home:
13355    case XK_KP_Home:
13356    {
13357      windows->image.x=(int) windows->image.width/2;
13358      windows->image.y=(int) windows->image.height/2;
13359      break;
13360    }
13361    case XK_Left:
13362    case XK_KP_Left:
13363    {
13364      windows->image.x-=x_offset;
13365      break;
13366    }
13367    case XK_Next:
13368    case XK_Up:
13369    case XK_KP_Up:
13370    {
13371      windows->image.y-=y_offset;
13372      break;
13373    }
13374    case XK_Right:
13375    case XK_KP_Right:
13376    {
13377      windows->image.x+=x_offset;
13378      break;
13379    }
13380    case XK_Prior:
13381    case XK_Down:
13382    case XK_KP_Down:
13383    {
13384      windows->image.y+=y_offset;
13385      break;
13386    }
13387    default:
13388      return;
13389  }
13390  /*
13391    Check boundary conditions.
13392  */
13393  if (windows->image.x < 0)
13394    windows->image.x=0;
13395  else
13396    if ((int) (windows->image.x+windows->image.width) >
13397        windows->image.ximage->width)
13398      windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13399  if (windows->image.y < 0)
13400    windows->image.y=0;
13401  else
13402    if ((int) (windows->image.y+windows->image.height) >
13403        windows->image.ximage->height)
13404      windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13405  /*
13406    Refresh Image window.
13407  */
13408  (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
13409    windows->image.width,windows->image.height,windows->image.x,
13410    windows->image.y);
13411  XInfoWidget(display,windows,text);
13412  XCheckRefreshWindows(display,windows);
13413  XDrawPanRectangle(display,windows);
13414  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13415  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13416}
13417
13418/*
13419%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13420%                                                                             %
13421%                                                                             %
13422%                                                                             %
13423+   X T r i m I m a g e                                                       %
13424%                                                                             %
13425%                                                                             %
13426%                                                                             %
13427%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13428%
13429%  XTrimImage() trims the edges from the Image window.
13430%
13431%  The format of the XTrimImage method is:
13432%
13433%      MagickBooleanType XTrimImage(Display *display,
13434%        XResourceInfo *resource_info,XWindows *windows,Image *image,
13435%        ExceptionInfo *exception)
13436%
13437%  A description of each parameter follows:
13438%
13439%    o display: Specifies a connection to an X server; returned from
13440%      XOpenDisplay.
13441%
13442%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13443%
13444%    o windows: Specifies a pointer to a XWindows structure.
13445%
13446%    o image: the image.
13447%
13448%    o exception: return any errors or warnings in this structure.
13449%
13450*/
13451static MagickBooleanType XTrimImage(Display *display,
13452  XResourceInfo *resource_info,XWindows *windows,Image *image,
13453  ExceptionInfo *exception)
13454{
13455  RectangleInfo
13456    trim_info;
13457
13458  register int
13459    x,
13460    y;
13461
13462  size_t
13463    background,
13464    pixel;
13465
13466  /*
13467    Trim edges from image.
13468  */
13469  XSetCursorState(display,windows,MagickTrue);
13470  XCheckRefreshWindows(display,windows);
13471  /*
13472    Crop the left edge.
13473  */
13474  background=XGetPixel(windows->image.ximage,0,0);
13475  trim_info.width=(size_t) windows->image.ximage->width;
13476  for (x=0; x < windows->image.ximage->width; x++)
13477  {
13478    for (y=0; y < windows->image.ximage->height; y++)
13479    {
13480      pixel=XGetPixel(windows->image.ximage,x,y);
13481      if (pixel != background)
13482        break;
13483    }
13484    if (y < windows->image.ximage->height)
13485      break;
13486  }
13487  trim_info.x=(ssize_t) x;
13488  if (trim_info.x == (ssize_t) windows->image.ximage->width)
13489    {
13490      XSetCursorState(display,windows,MagickFalse);
13491      return(MagickFalse);
13492    }
13493  /*
13494    Crop the right edge.
13495  */
13496  background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13497  for (x=windows->image.ximage->width-1; x != 0; x--)
13498  {
13499    for (y=0; y < windows->image.ximage->height; y++)
13500    {
13501      pixel=XGetPixel(windows->image.ximage,x,y);
13502      if (pixel != background)
13503        break;
13504    }
13505    if (y < windows->image.ximage->height)
13506      break;
13507  }
13508  trim_info.width=(size_t) (x-trim_info.x+1);
13509  /*
13510    Crop the top edge.
13511  */
13512  background=XGetPixel(windows->image.ximage,0,0);
13513  trim_info.height=(size_t) windows->image.ximage->height;
13514  for (y=0; y < windows->image.ximage->height; y++)
13515  {
13516    for (x=0; x < windows->image.ximage->width; x++)
13517    {
13518      pixel=XGetPixel(windows->image.ximage,x,y);
13519      if (pixel != background)
13520        break;
13521    }
13522    if (x < windows->image.ximage->width)
13523      break;
13524  }
13525  trim_info.y=(ssize_t) y;
13526  /*
13527    Crop the bottom edge.
13528  */
13529  background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13530  for (y=windows->image.ximage->height-1; y != 0; y--)
13531  {
13532    for (x=0; x < windows->image.ximage->width; x++)
13533    {
13534      pixel=XGetPixel(windows->image.ximage,x,y);
13535      if (pixel != background)
13536        break;
13537    }
13538    if (x < windows->image.ximage->width)
13539      break;
13540  }
13541  trim_info.height=(size_t) y-trim_info.y+1;
13542  if (((unsigned int) trim_info.width != windows->image.width) ||
13543      ((unsigned int) trim_info.height != windows->image.height))
13544    {
13545      /*
13546        Reconfigure Image window as defined by the trimming rectangle.
13547      */
13548      XSetCropGeometry(display,windows,&trim_info,image);
13549      windows->image.window_changes.width=(int) trim_info.width;
13550      windows->image.window_changes.height=(int) trim_info.height;
13551      (void) XConfigureImage(display,resource_info,windows,image,exception);
13552    }
13553  XSetCursorState(display,windows,MagickFalse);
13554  return(MagickTrue);
13555}
13556
13557/*
13558%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13559%                                                                             %
13560%                                                                             %
13561%                                                                             %
13562+   X V i s u a l D i r e c t o r y I m a g e                                 %
13563%                                                                             %
13564%                                                                             %
13565%                                                                             %
13566%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13567%
13568%  XVisualDirectoryImage() creates a Visual Image Directory.
13569%
13570%  The format of the XVisualDirectoryImage method is:
13571%
13572%      Image *XVisualDirectoryImage(Display *display,
13573%        XResourceInfo *resource_info,XWindows *windows,
13574%        ExceptionInfo *exception)
13575%
13576%  A description of each parameter follows:
13577%
13578%    o display: Specifies a connection to an X server; returned from
13579%      XOpenDisplay.
13580%
13581%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13582%
13583%    o windows: Specifies a pointer to a XWindows structure.
13584%
13585%    o exception: return any errors or warnings in this structure.
13586%
13587*/
13588static Image *XVisualDirectoryImage(Display *display,
13589  XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
13590{
13591#define TileImageTag  "Scale/Image"
13592#define XClientName  "montage"
13593
13594  char
13595    **filelist;
13596
13597  Image
13598    *images,
13599    *montage_image,
13600    *next_image,
13601    *thumbnail_image;
13602
13603  ImageInfo
13604    *read_info;
13605
13606  int
13607    number_files;
13608
13609  MagickBooleanType
13610    backdrop;
13611
13612  MagickStatusType
13613    status;
13614
13615  MontageInfo
13616    *montage_info;
13617
13618  RectangleInfo
13619    geometry;
13620
13621  register int
13622    i;
13623
13624  static char
13625    filename[MaxTextExtent] = "\0",
13626    filenames[MaxTextExtent] = "*";
13627
13628  XResourceInfo
13629    background_resources;
13630
13631  /*
13632    Request file name from user.
13633  */
13634  XFileBrowserWidget(display,windows,"Directory",filenames);
13635  if (*filenames == '\0')
13636    return((Image *) NULL);
13637  /*
13638    Expand the filenames.
13639  */
13640  filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13641  if (filelist == (char **) NULL)
13642    {
13643      ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13644        filenames);
13645      return((Image *) NULL);
13646    }
13647  number_files=1;
13648  filelist[0]=filenames;
13649  status=ExpandFilenames(&number_files,&filelist);
13650  if ((status == MagickFalse) || (number_files == 0))
13651    {
13652      if (number_files == 0)
13653        ThrowXWindowFatalException(ImageError,"NoImagesWereFound",filenames)
13654      else
13655        ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13656          filenames);
13657      return((Image *) NULL);
13658    }
13659  /*
13660    Set image background resources.
13661  */
13662  background_resources=(*resource_info);
13663  background_resources.window_id=AcquireString("");
13664  (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
13665    "0x%lx",windows->image.id);
13666  background_resources.backdrop=MagickTrue;
13667  /*
13668    Read each image and convert them to a tile.
13669  */
13670  backdrop=(windows->visual_info->klass == TrueColor) ||
13671    (windows->visual_info->klass == DirectColor) ? MagickTrue : MagickFalse;
13672  read_info=CloneImageInfo(resource_info->image_info);
13673  (void) SetImageOption(read_info,"jpeg:size","120x120");
13674  (void) CloneString(&read_info->size,DefaultTileGeometry);
13675  (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13676    (void *) NULL);
13677  images=NewImageList();
13678  XSetCursorState(display,windows,MagickTrue);
13679  XCheckRefreshWindows(display,windows);
13680  for (i=0; i < (int) number_files; i++)
13681  {
13682    (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13683    filelist[i]=DestroyString(filelist[i]);
13684    *read_info->magick='\0';
13685    next_image=ReadImage(read_info,exception);
13686    CatchException(exception);
13687    if (next_image != (Image *) NULL)
13688      {
13689        (void) DeleteImageProperty(next_image,"label");
13690        (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13691          read_info,next_image,DefaultTileLabel,exception),exception);
13692        (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13693          exception);
13694        thumbnail_image=ThumbnailImage(next_image,geometry.width,
13695          geometry.height,exception);
13696        if (thumbnail_image != (Image *) NULL)
13697          {
13698            next_image=DestroyImage(next_image);
13699            next_image=thumbnail_image;
13700          }
13701        if (backdrop)
13702          {
13703            (void) XDisplayBackgroundImage(display,&background_resources,
13704              next_image,exception);
13705            XSetCursorState(display,windows,MagickTrue);
13706          }
13707        AppendImageToList(&images,next_image);
13708        if (images->progress_monitor != (MagickProgressMonitor) NULL)
13709          {
13710            MagickBooleanType
13711              proceed;
13712
13713            proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13714              (MagickSizeType) number_files);
13715            if (proceed == MagickFalse)
13716              break;
13717          }
13718      }
13719  }
13720  filelist=(char **) RelinquishMagickMemory(filelist);
13721  if (images == (Image *) NULL)
13722    {
13723      read_info=DestroyImageInfo(read_info);
13724      XSetCursorState(display,windows,MagickFalse);
13725      ThrowXWindowFatalException(ImageError,"NoImagesWereLoaded",filenames);
13726      return((Image *) NULL);
13727    }
13728  /*
13729    Create the Visual Image Directory.
13730  */
13731  montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13732  montage_info->pointsize=10;
13733  if (resource_info->font != (char *) NULL)
13734    (void) CloneString(&montage_info->font,resource_info->font);
13735  (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13736  montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13737    images),exception);
13738  images=DestroyImageList(images);
13739  montage_info=DestroyMontageInfo(montage_info);
13740  read_info=DestroyImageInfo(read_info);
13741  XSetCursorState(display,windows,MagickFalse);
13742  if (montage_image == (Image *) NULL)
13743    return(montage_image);
13744  XClientMessage(display,windows->image.id,windows->im_protocols,
13745    windows->im_next_image,CurrentTime);
13746  return(montage_image);
13747}
13748
13749/*
13750%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13751%                                                                             %
13752%                                                                             %
13753%                                                                             %
13754%   X D i s p l a y B a c k g r o u n d I m a g e                             %
13755%                                                                             %
13756%                                                                             %
13757%                                                                             %
13758%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13759%
13760%  XDisplayBackgroundImage() displays an image in the background of a window.
13761%
13762%  The format of the XDisplayBackgroundImage method is:
13763%
13764%      MagickBooleanType XDisplayBackgroundImage(Display *display,
13765%        XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13766%
13767%  A description of each parameter follows:
13768%
13769%    o display: Specifies a connection to an X server;  returned from
13770%      XOpenDisplay.
13771%
13772%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13773%
13774%    o image: the image.
13775%
13776%    o exception: return any errors or warnings in this structure.
13777%
13778*/
13779MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13780  XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13781{
13782  char
13783    geometry[MaxTextExtent],
13784    visual_type[MaxTextExtent];
13785
13786  int
13787    height,
13788    status,
13789    width;
13790
13791  RectangleInfo
13792    geometry_info;
13793
13794  static XPixelInfo
13795    pixel;
13796
13797  static XStandardColormap
13798    *map_info;
13799
13800  static XVisualInfo
13801    *visual_info = (XVisualInfo *) NULL;
13802
13803  static XWindowInfo
13804    window_info;
13805
13806  size_t
13807    delay;
13808
13809  Window
13810    root_window;
13811
13812  XGCValues
13813    context_values;
13814
13815  XResourceInfo
13816    resources;
13817
13818  XWindowAttributes
13819    window_attributes;
13820
13821  /*
13822    Determine target window.
13823  */
13824  assert(image != (Image *) NULL);
13825  assert(image->signature == MagickSignature);
13826  if (image->debug != MagickFalse)
13827    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13828  resources=(*resource_info);
13829  window_info.id=(Window) NULL;
13830  root_window=XRootWindow(display,XDefaultScreen(display));
13831  if (LocaleCompare(resources.window_id,"root") == 0)
13832    window_info.id=root_window;
13833  else
13834    {
13835      if (isdigit((unsigned char) *resources.window_id) != 0)
13836        window_info.id=XWindowByID(display,root_window,
13837          (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13838      if (window_info.id == (Window) NULL)
13839        window_info.id=XWindowByName(display,root_window,resources.window_id);
13840    }
13841  if (window_info.id == (Window) NULL)
13842    {
13843      ThrowXWindowFatalException(XServerError,"NoWindowWithSpecifiedIDExists",
13844        resources.window_id);
13845      return(MagickFalse);
13846    }
13847  /*
13848    Determine window visual id.
13849  */
13850  window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13851  window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13852  (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13853  status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13854  if (status != 0)
13855    (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
13856      XVisualIDFromVisual(window_attributes.visual));
13857  if (visual_info == (XVisualInfo *) NULL)
13858    {
13859      /*
13860        Allocate standard colormap.
13861      */
13862      map_info=XAllocStandardColormap();
13863      if (map_info == (XStandardColormap *) NULL)
13864        ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13865          image->filename);
13866      map_info->colormap=(Colormap) NULL;
13867      pixel.pixels=(unsigned long *) NULL;
13868      /*
13869        Initialize visual info.
13870      */
13871      resources.map_type=(char *) NULL;
13872      resources.visual_type=visual_type;
13873      visual_info=XBestVisualInfo(display,map_info,&resources);
13874      if (visual_info == (XVisualInfo *) NULL)
13875        ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13876          resources.visual_type);
13877      /*
13878        Initialize window info.
13879      */
13880      window_info.ximage=(XImage *) NULL;
13881      window_info.matte_image=(XImage *) NULL;
13882      window_info.pixmap=(Pixmap) NULL;
13883      window_info.matte_pixmap=(Pixmap) NULL;
13884    }
13885  /*
13886    Free previous root colors.
13887  */
13888  if (window_info.id == root_window)
13889    (void) XDestroyWindowColors(display,root_window);
13890  /*
13891    Initialize Standard Colormap.
13892  */
13893  resources.colormap=SharedColormap;
13894  XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel);
13895  /*
13896    Graphic context superclass.
13897  */
13898  context_values.background=pixel.background_color.pixel;
13899  context_values.foreground=pixel.foreground_color.pixel;
13900  pixel.annotate_context=XCreateGC(display,window_info.id,
13901    (size_t) (GCBackground | GCForeground),&context_values);
13902  if (pixel.annotate_context == (GC) NULL)
13903    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13904      image->filename);
13905  /*
13906    Initialize Image window attributes.
13907  */
13908  window_info.name=AcquireString("\0");
13909  window_info.icon_name=AcquireString("\0");
13910  XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13911    &resources,&window_info);
13912  /*
13913    Create the X image.
13914  */
13915  window_info.width=(unsigned int) image->columns;
13916  window_info.height=(unsigned int) image->rows;
13917  if ((image->columns != window_info.width) ||
13918      (image->rows != window_info.height))
13919    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13920      image->filename);
13921  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
13922    window_attributes.width,window_attributes.height);
13923  geometry_info.width=window_info.width;
13924  geometry_info.height=window_info.height;
13925  geometry_info.x=(ssize_t) window_info.x;
13926  geometry_info.y=(ssize_t) window_info.y;
13927  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13928    &geometry_info.width,&geometry_info.height);
13929  window_info.width=(unsigned int) geometry_info.width;
13930  window_info.height=(unsigned int) geometry_info.height;
13931  window_info.x=(int) geometry_info.x;
13932  window_info.y=(int) geometry_info.y;
13933  status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13934    window_info.height,exception);
13935  if (status == MagickFalse)
13936    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13937      image->filename);
13938  window_info.x=0;
13939  window_info.y=0;
13940  if (image->debug != MagickFalse)
13941    {
13942      (void) LogMagickEvent(X11Event,GetMagickModule(),
13943        "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13944        (double) image->columns,(double) image->rows);
13945      if (image->colors != 0)
13946        (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13947          image->colors);
13948      (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13949    }
13950  /*
13951    Adjust image dimensions as specified by backdrop or geometry options.
13952  */
13953  width=(int) window_info.width;
13954  height=(int) window_info.height;
13955  if (resources.backdrop != MagickFalse)
13956    {
13957      /*
13958        Center image on window.
13959      */
13960      window_info.x=(window_attributes.width/2)-
13961        (window_info.ximage->width/2);
13962      window_info.y=(window_attributes.height/2)-
13963        (window_info.ximage->height/2);
13964      width=window_attributes.width;
13965      height=window_attributes.height;
13966    }
13967  if ((resources.image_geometry != (char *) NULL) &&
13968      (*resources.image_geometry != '\0'))
13969    {
13970      char
13971        default_geometry[MaxTextExtent];
13972
13973      int
13974        flags,
13975        gravity;
13976
13977      XSizeHints
13978        *size_hints;
13979
13980      /*
13981        User specified geometry.
13982      */
13983      size_hints=XAllocSizeHints();
13984      if (size_hints == (XSizeHints *) NULL)
13985        ThrowXWindowFatalException(ResourceLimitFatalError,
13986          "MemoryAllocationFailed",image->filename);
13987      size_hints->flags=0L;
13988      (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
13989        width,height);
13990      flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
13991        default_geometry,window_info.border_width,size_hints,&window_info.x,
13992        &window_info.y,&width,&height,&gravity);
13993      if (flags & (XValue | YValue))
13994        {
13995          width=window_attributes.width;
13996          height=window_attributes.height;
13997        }
13998      (void) XFree((void *) size_hints);
13999    }
14000  /*
14001    Create the X pixmap.
14002  */
14003  window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
14004    (unsigned int) height,window_info.depth);
14005  if (window_info.pixmap == (Pixmap) NULL)
14006    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
14007      image->filename);
14008  /*
14009    Display pixmap on the window.
14010  */
14011  if (((unsigned int) width > window_info.width) ||
14012      ((unsigned int) height > window_info.height))
14013    (void) XFillRectangle(display,window_info.pixmap,
14014      window_info.annotate_context,0,0,(unsigned int) width,
14015      (unsigned int) height);
14016  (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
14017    window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
14018    window_info.width,(unsigned int) window_info.height);
14019  (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
14020  (void) XClearWindow(display,window_info.id);
14021  delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
14022  XDelay(display,delay == 0UL ? 10UL : delay);
14023  (void) XSync(display,MagickFalse);
14024  return(window_info.id == root_window ? MagickTrue : MagickFalse);
14025}
14026
14027/*
14028%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14029%                                                                             %
14030%                                                                             %
14031%                                                                             %
14032+   X D i s p l a y I m a g e                                                 %
14033%                                                                             %
14034%                                                                             %
14035%                                                                             %
14036%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14037%
14038%  XDisplayImage() displays an image via X11.  A new image is created and
14039%  returned if the user interactively transforms the displayed image.
14040%
14041%  The format of the XDisplayImage method is:
14042%
14043%      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14044%        char **argv,int argc,Image **image,size_t *state,
14045%        ExceptionInfo *exception)
14046%
14047%  A description of each parameter follows:
14048%
14049%    o nexus:  Method XDisplayImage returns an image when the
14050%      user chooses 'Open Image' from the command menu or picks a tile
14051%      from the image directory.  Otherwise a null image is returned.
14052%
14053%    o display: Specifies a connection to an X server;  returned from
14054%      XOpenDisplay.
14055%
14056%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14057%
14058%    o argv: Specifies the application's argument list.
14059%
14060%    o argc: Specifies the number of arguments.
14061%
14062%    o image: Specifies an address to an address of an Image structure;
14063%
14064%    o exception: return any errors or warnings in this structure.
14065%
14066*/
14067MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14068  char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
14069{
14070#define MagnifySize  256  /* must be a power of 2 */
14071#define MagickMenus  10
14072#define MagickTitle  "Commands"
14073
14074  static const char
14075    *CommandMenu[] =
14076    {
14077      "File",
14078      "Edit",
14079      "View",
14080      "Transform",
14081      "Enhance",
14082      "Effects",
14083      "F/X",
14084      "Image Edit",
14085      "Miscellany",
14086      "Help",
14087      (char *) NULL
14088    },
14089    *FileMenu[] =
14090    {
14091      "Open...",
14092      "Next",
14093      "Former",
14094      "Select...",
14095      "Save...",
14096      "Print...",
14097      "Delete...",
14098      "New...",
14099      "Visual Directory...",
14100      "Quit",
14101      (char *) NULL
14102    },
14103    *EditMenu[] =
14104    {
14105      "Undo",
14106      "Redo",
14107      "Cut",
14108      "Copy",
14109      "Paste",
14110      (char *) NULL
14111    },
14112    *ViewMenu[] =
14113    {
14114      "Half Size",
14115      "Original Size",
14116      "Double Size",
14117      "Resize...",
14118      "Apply",
14119      "Refresh",
14120      "Restore",
14121      (char *) NULL
14122    },
14123    *TransformMenu[] =
14124    {
14125      "Crop",
14126      "Chop",
14127      "Flop",
14128      "Flip",
14129      "Rotate Right",
14130      "Rotate Left",
14131      "Rotate...",
14132      "Shear...",
14133      "Roll...",
14134      "Trim Edges",
14135      (char *) NULL
14136    },
14137    *EnhanceMenu[] =
14138    {
14139      "Hue...",
14140      "Saturation...",
14141      "Brightness...",
14142      "Gamma...",
14143      "Spiff",
14144      "Dull",
14145      "Contrast Stretch...",
14146      "Sigmoidal Contrast...",
14147      "Normalize",
14148      "Equalize",
14149      "Negate",
14150      "Grayscale",
14151      "Map...",
14152      "Quantize...",
14153      (char *) NULL
14154    },
14155    *EffectsMenu[] =
14156    {
14157      "Despeckle",
14158      "Emboss",
14159      "Reduce Noise",
14160      "Add Noise...",
14161      "Sharpen...",
14162      "Blur...",
14163      "Threshold...",
14164      "Edge Detect...",
14165      "Spread...",
14166      "Shade...",
14167      "Raise...",
14168      "Segment...",
14169      (char *) NULL
14170    },
14171    *FXMenu[] =
14172    {
14173      "Solarize...",
14174      "Sepia Tone...",
14175      "Swirl...",
14176      "Implode...",
14177      "Vignette...",
14178      "Wave...",
14179      "Oil Paint...",
14180      "Charcoal Draw...",
14181      (char *) NULL
14182    },
14183    *ImageEditMenu[] =
14184    {
14185      "Annotate...",
14186      "Draw...",
14187      "Color...",
14188      "Matte...",
14189      "Composite...",
14190      "Add Border...",
14191      "Add Frame...",
14192      "Comment...",
14193      "Launch...",
14194      "Region of Interest...",
14195      (char *) NULL
14196    },
14197    *MiscellanyMenu[] =
14198    {
14199      "Image Info",
14200      "Zoom Image",
14201      "Show Preview...",
14202      "Show Histogram",
14203      "Show Matte",
14204      "Background...",
14205      "Slide Show...",
14206      "Preferences...",
14207      (char *) NULL
14208    },
14209    *HelpMenu[] =
14210    {
14211      "Overview",
14212      "Browse Documentation",
14213      "About Display",
14214      (char *) NULL
14215    },
14216    *ShortCutsMenu[] =
14217    {
14218      "Next",
14219      "Former",
14220      "Open...",
14221      "Save...",
14222      "Print...",
14223      "Undo",
14224      "Restore",
14225      "Image Info",
14226      "Quit",
14227      (char *) NULL
14228    },
14229    *VirtualMenu[] =
14230    {
14231      "Image Info",
14232      "Print",
14233      "Next",
14234      "Quit",
14235      (char *) NULL
14236    };
14237
14238  static const char
14239    **Menus[MagickMenus] =
14240    {
14241      FileMenu,
14242      EditMenu,
14243      ViewMenu,
14244      TransformMenu,
14245      EnhanceMenu,
14246      EffectsMenu,
14247      FXMenu,
14248      ImageEditMenu,
14249      MiscellanyMenu,
14250      HelpMenu
14251    };
14252
14253  static CommandType
14254    CommandMenus[] =
14255    {
14256      NullCommand,
14257      NullCommand,
14258      NullCommand,
14259      NullCommand,
14260      NullCommand,
14261      NullCommand,
14262      NullCommand,
14263      NullCommand,
14264      NullCommand,
14265      NullCommand,
14266    },
14267    FileCommands[] =
14268    {
14269      OpenCommand,
14270      NextCommand,
14271      FormerCommand,
14272      SelectCommand,
14273      SaveCommand,
14274      PrintCommand,
14275      DeleteCommand,
14276      NewCommand,
14277      VisualDirectoryCommand,
14278      QuitCommand
14279    },
14280    EditCommands[] =
14281    {
14282      UndoCommand,
14283      RedoCommand,
14284      CutCommand,
14285      CopyCommand,
14286      PasteCommand
14287    },
14288    ViewCommands[] =
14289    {
14290      HalfSizeCommand,
14291      OriginalSizeCommand,
14292      DoubleSizeCommand,
14293      ResizeCommand,
14294      ApplyCommand,
14295      RefreshCommand,
14296      RestoreCommand
14297    },
14298    TransformCommands[] =
14299    {
14300      CropCommand,
14301      ChopCommand,
14302      FlopCommand,
14303      FlipCommand,
14304      RotateRightCommand,
14305      RotateLeftCommand,
14306      RotateCommand,
14307      ShearCommand,
14308      RollCommand,
14309      TrimCommand
14310    },
14311    EnhanceCommands[] =
14312    {
14313      HueCommand,
14314      SaturationCommand,
14315      BrightnessCommand,
14316      GammaCommand,
14317      SpiffCommand,
14318      DullCommand,
14319      ContrastStretchCommand,
14320      SigmoidalContrastCommand,
14321      NormalizeCommand,
14322      EqualizeCommand,
14323      NegateCommand,
14324      GrayscaleCommand,
14325      MapCommand,
14326      QuantizeCommand
14327    },
14328    EffectsCommands[] =
14329    {
14330      DespeckleCommand,
14331      EmbossCommand,
14332      ReduceNoiseCommand,
14333      AddNoiseCommand,
14334      SharpenCommand,
14335      BlurCommand,
14336      ThresholdCommand,
14337      EdgeDetectCommand,
14338      SpreadCommand,
14339      ShadeCommand,
14340      RaiseCommand,
14341      SegmentCommand
14342    },
14343    FXCommands[] =
14344    {
14345      SolarizeCommand,
14346      SepiaToneCommand,
14347      SwirlCommand,
14348      ImplodeCommand,
14349      VignetteCommand,
14350      WaveCommand,
14351      OilPaintCommand,
14352      CharcoalDrawCommand
14353    },
14354    ImageEditCommands[] =
14355    {
14356      AnnotateCommand,
14357      DrawCommand,
14358      ColorCommand,
14359      MatteCommand,
14360      CompositeCommand,
14361      AddBorderCommand,
14362      AddFrameCommand,
14363      CommentCommand,
14364      LaunchCommand,
14365      RegionofInterestCommand
14366    },
14367    MiscellanyCommands[] =
14368    {
14369      InfoCommand,
14370      ZoomCommand,
14371      ShowPreviewCommand,
14372      ShowHistogramCommand,
14373      ShowMatteCommand,
14374      BackgroundCommand,
14375      SlideShowCommand,
14376      PreferencesCommand
14377    },
14378    HelpCommands[] =
14379    {
14380      HelpCommand,
14381      BrowseDocumentationCommand,
14382      VersionCommand
14383    },
14384    ShortCutsCommands[] =
14385    {
14386      NextCommand,
14387      FormerCommand,
14388      OpenCommand,
14389      SaveCommand,
14390      PrintCommand,
14391      UndoCommand,
14392      RestoreCommand,
14393      InfoCommand,
14394      QuitCommand
14395    },
14396    VirtualCommands[] =
14397    {
14398      InfoCommand,
14399      PrintCommand,
14400      NextCommand,
14401      QuitCommand
14402    };
14403
14404  static CommandType
14405    *Commands[MagickMenus] =
14406    {
14407      FileCommands,
14408      EditCommands,
14409      ViewCommands,
14410      TransformCommands,
14411      EnhanceCommands,
14412      EffectsCommands,
14413      FXCommands,
14414      ImageEditCommands,
14415      MiscellanyCommands,
14416      HelpCommands
14417    };
14418
14419  char
14420    command[MaxTextExtent],
14421    *directory,
14422    geometry[MaxTextExtent],
14423    resource_name[MaxTextExtent];
14424
14425  CommandType
14426    command_type;
14427
14428  Image
14429    *display_image,
14430    *nexus;
14431
14432  int
14433    entry,
14434    id;
14435
14436  KeySym
14437    key_symbol;
14438
14439  MagickStatusType
14440    context_mask,
14441    status;
14442
14443  RectangleInfo
14444    geometry_info;
14445
14446  register int
14447    i;
14448
14449  static char
14450    working_directory[MaxTextExtent];
14451
14452  static XPoint
14453    vid_info;
14454
14455  static XWindowInfo
14456    *magick_windows[MaxXWindows];
14457
14458  static unsigned int
14459    number_windows;
14460
14461  struct stat
14462    attributes;
14463
14464  time_t
14465    timer,
14466    timestamp,
14467    update_time;
14468
14469  unsigned int
14470    height,
14471    width;
14472
14473  size_t
14474    delay;
14475
14476  WarningHandler
14477    warning_handler;
14478
14479  Window
14480    root_window;
14481
14482  XClassHint
14483    *class_hints;
14484
14485  XEvent
14486    event;
14487
14488  XFontStruct
14489    *font_info;
14490
14491  XGCValues
14492    context_values;
14493
14494  XPixelInfo
14495    *icon_pixel,
14496    *pixel;
14497
14498  XResourceInfo
14499    *icon_resources;
14500
14501  XStandardColormap
14502    *icon_map,
14503    *map_info;
14504
14505  XVisualInfo
14506    *icon_visual,
14507    *visual_info;
14508
14509  XWindowChanges
14510    window_changes;
14511
14512  XWindows
14513    *windows;
14514
14515  XWMHints
14516    *manager_hints;
14517
14518  assert(image != (Image **) NULL);
14519  assert((*image)->signature == MagickSignature);
14520  if ((*image)->debug != MagickFalse)
14521    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14522  display_image=(*image);
14523  warning_handler=(WarningHandler) NULL;
14524  windows=XSetWindows((XWindows *) ~0);
14525  if (windows != (XWindows *) NULL)
14526    {
14527      int
14528        status;
14529
14530      status=chdir(working_directory);
14531      if (status == -1)
14532        (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14533          "UnableToOpenFile","%s",working_directory);
14534      warning_handler=resource_info->display_warnings ?
14535        SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14536      warning_handler=resource_info->display_warnings ?
14537        SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14538    }
14539  else
14540    {
14541      /*
14542        Allocate windows structure.
14543      */
14544      resource_info->colors=display_image->colors;
14545      windows=XSetWindows(XInitializeWindows(display,resource_info));
14546      if (windows == (XWindows *) NULL)
14547        ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14548          (*image)->filename);
14549      /*
14550        Initialize window id's.
14551      */
14552      number_windows=0;
14553      magick_windows[number_windows++]=(&windows->icon);
14554      magick_windows[number_windows++]=(&windows->backdrop);
14555      magick_windows[number_windows++]=(&windows->image);
14556      magick_windows[number_windows++]=(&windows->info);
14557      magick_windows[number_windows++]=(&windows->command);
14558      magick_windows[number_windows++]=(&windows->widget);
14559      magick_windows[number_windows++]=(&windows->popup);
14560      magick_windows[number_windows++]=(&windows->magnify);
14561      magick_windows[number_windows++]=(&windows->pan);
14562      for (i=0; i < (int) number_windows; i++)
14563        magick_windows[i]->id=(Window) NULL;
14564      vid_info.x=0;
14565      vid_info.y=0;
14566    }
14567  /*
14568    Initialize font info.
14569  */
14570  if (windows->font_info != (XFontStruct *) NULL)
14571    (void) XFreeFont(display,windows->font_info);
14572  windows->font_info=XBestFont(display,resource_info,MagickFalse);
14573  if (windows->font_info == (XFontStruct *) NULL)
14574    ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14575      resource_info->font);
14576  /*
14577    Initialize Standard Colormap.
14578  */
14579  map_info=windows->map_info;
14580  icon_map=windows->icon_map;
14581  visual_info=windows->visual_info;
14582  icon_visual=windows->icon_visual;
14583  pixel=windows->pixel_info;
14584  icon_pixel=windows->icon_pixel;
14585  font_info=windows->font_info;
14586  icon_resources=windows->icon_resources;
14587  class_hints=windows->class_hints;
14588  manager_hints=windows->manager_hints;
14589  root_window=XRootWindow(display,visual_info->screen);
14590  nexus=NewImageList();
14591  if (display_image->debug != MagickFalse)
14592    {
14593      (void) LogMagickEvent(X11Event,GetMagickModule(),
14594        "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14595        (double) display_image->scene,(double) display_image->columns,
14596        (double) display_image->rows);
14597      if (display_image->colors != 0)
14598        (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14599          display_image->colors);
14600      (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14601        display_image->magick);
14602    }
14603  XMakeStandardColormap(display,visual_info,resource_info,display_image,
14604    map_info,pixel);
14605  display_image->taint=MagickFalse;
14606  /*
14607    Initialize graphic context.
14608  */
14609  windows->context.id=(Window) NULL;
14610  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14611    resource_info,&windows->context);
14612  (void) CloneString(&class_hints->res_name,resource_info->client_name);
14613  (void) CloneString(&class_hints->res_class,resource_info->client_name);
14614  class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14615  manager_hints->flags=InputHint | StateHint;
14616  manager_hints->input=MagickFalse;
14617  manager_hints->initial_state=WithdrawnState;
14618  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14619    &windows->context);
14620  if (display_image->debug != MagickFalse)
14621    (void) LogMagickEvent(X11Event,GetMagickModule(),
14622      "Window id: 0x%lx (context)",windows->context.id);
14623  context_values.background=pixel->background_color.pixel;
14624  context_values.font=font_info->fid;
14625  context_values.foreground=pixel->foreground_color.pixel;
14626  context_values.graphics_exposures=MagickFalse;
14627  context_mask=(MagickStatusType)
14628    (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14629  if (pixel->annotate_context != (GC) NULL)
14630    (void) XFreeGC(display,pixel->annotate_context);
14631  pixel->annotate_context=XCreateGC(display,windows->context.id,
14632    context_mask,&context_values);
14633  if (pixel->annotate_context == (GC) NULL)
14634    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14635      display_image->filename);
14636  context_values.background=pixel->depth_color.pixel;
14637  if (pixel->widget_context != (GC) NULL)
14638    (void) XFreeGC(display,pixel->widget_context);
14639  pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14640    &context_values);
14641  if (pixel->widget_context == (GC) NULL)
14642    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14643      display_image->filename);
14644  context_values.background=pixel->foreground_color.pixel;
14645  context_values.foreground=pixel->background_color.pixel;
14646  context_values.plane_mask=context_values.background ^
14647    context_values.foreground;
14648  if (pixel->highlight_context != (GC) NULL)
14649    (void) XFreeGC(display,pixel->highlight_context);
14650  pixel->highlight_context=XCreateGC(display,windows->context.id,
14651    (size_t) (context_mask | GCPlaneMask),&context_values);
14652  if (pixel->highlight_context == (GC) NULL)
14653    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14654      display_image->filename);
14655  (void) XDestroyWindow(display,windows->context.id);
14656  /*
14657    Initialize icon window.
14658  */
14659  XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14660    icon_resources,&windows->icon);
14661  windows->icon.geometry=resource_info->icon_geometry;
14662  XBestIconSize(display,&windows->icon,display_image);
14663  windows->icon.attributes.colormap=XDefaultColormap(display,
14664    icon_visual->screen);
14665  windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14666  manager_hints->flags=InputHint | StateHint;
14667  manager_hints->input=MagickFalse;
14668  manager_hints->initial_state=IconicState;
14669  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14670    &windows->icon);
14671  if (display_image->debug != MagickFalse)
14672    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14673      windows->icon.id);
14674  /*
14675    Initialize graphic context for icon window.
14676  */
14677  if (icon_pixel->annotate_context != (GC) NULL)
14678    (void) XFreeGC(display,icon_pixel->annotate_context);
14679  context_values.background=icon_pixel->background_color.pixel;
14680  context_values.foreground=icon_pixel->foreground_color.pixel;
14681  icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14682    (size_t) (GCBackground | GCForeground),&context_values);
14683  if (icon_pixel->annotate_context == (GC) NULL)
14684    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14685      display_image->filename);
14686  windows->icon.annotate_context=icon_pixel->annotate_context;
14687  /*
14688    Initialize Image window.
14689  */
14690  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14691    &windows->image);
14692  windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14693  if (resource_info->use_shared_memory == MagickFalse)
14694    windows->image.shared_memory=MagickFalse;
14695  if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14696    {
14697      char
14698        *title;
14699
14700      title=InterpretImageProperties(resource_info->image_info,display_image,
14701        resource_info->title,exception);
14702      (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
14703      (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
14704      title=DestroyString(title);
14705    }
14706  else
14707    {
14708      char
14709        filename[MaxTextExtent];
14710
14711      /*
14712        Window name is the base of the filename.
14713      */
14714      GetPathComponent(display_image->magick_filename,TailPath,filename);
14715      if (display_image->scene == 0)
14716        (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14717          "%s: %s",MagickPackageName,filename);
14718      else
14719        (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14720          "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14721          (double) display_image->scene,(double) GetImageListLength(
14722          display_image));
14723      (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
14724    }
14725  if (resource_info->immutable)
14726    windows->image.immutable=MagickTrue;
14727  windows->image.use_pixmap=resource_info->use_pixmap;
14728  windows->image.geometry=resource_info->image_geometry;
14729  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14730    XDisplayWidth(display,visual_info->screen),
14731    XDisplayHeight(display,visual_info->screen));
14732  geometry_info.width=display_image->columns;
14733  geometry_info.height=display_image->rows;
14734  geometry_info.x=0;
14735  geometry_info.y=0;
14736  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14737    &geometry_info.width,&geometry_info.height);
14738  windows->image.width=(unsigned int) geometry_info.width;
14739  windows->image.height=(unsigned int) geometry_info.height;
14740  windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14741    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14742    KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14743    PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14744  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14745    resource_info,&windows->backdrop);
14746  if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14747    {
14748      /*
14749        Initialize backdrop window.
14750      */
14751      windows->backdrop.x=0;
14752      windows->backdrop.y=0;
14753      (void) CloneString(&windows->backdrop.name,"Backdrop");
14754      windows->backdrop.flags=(size_t) (USSize | USPosition);
14755      windows->backdrop.width=(unsigned int)
14756        XDisplayWidth(display,visual_info->screen);
14757      windows->backdrop.height=(unsigned int)
14758        XDisplayHeight(display,visual_info->screen);
14759      windows->backdrop.border_width=0;
14760      windows->backdrop.immutable=MagickTrue;
14761      windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14762        ButtonReleaseMask;
14763      windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14764        StructureNotifyMask;
14765      manager_hints->flags=IconWindowHint | InputHint | StateHint;
14766      manager_hints->icon_window=windows->icon.id;
14767      manager_hints->input=MagickTrue;
14768      manager_hints->initial_state=resource_info->iconic ? IconicState :
14769        NormalState;
14770      XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14771        &windows->backdrop);
14772      if (display_image->debug != MagickFalse)
14773        (void) LogMagickEvent(X11Event,GetMagickModule(),
14774          "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14775      (void) XMapWindow(display,windows->backdrop.id);
14776      (void) XClearWindow(display,windows->backdrop.id);
14777      if (windows->image.id != (Window) NULL)
14778        {
14779          (void) XDestroyWindow(display,windows->image.id);
14780          windows->image.id=(Window) NULL;
14781        }
14782      /*
14783        Position image in the center the backdrop.
14784      */
14785      windows->image.flags|=USPosition;
14786      windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14787        (windows->image.width/2);
14788      windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14789        (windows->image.height/2);
14790    }
14791  manager_hints->flags=IconWindowHint | InputHint | StateHint;
14792  manager_hints->icon_window=windows->icon.id;
14793  manager_hints->input=MagickTrue;
14794  manager_hints->initial_state=resource_info->iconic ? IconicState :
14795    NormalState;
14796  if (windows->group_leader.id != (Window) NULL)
14797    {
14798      /*
14799        Follow the leader.
14800      */
14801      manager_hints->flags|=WindowGroupHint;
14802      manager_hints->window_group=windows->group_leader.id;
14803      (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14804      if (display_image->debug != MagickFalse)
14805        (void) LogMagickEvent(X11Event,GetMagickModule(),
14806          "Window id: 0x%lx (group leader)",windows->group_leader.id);
14807    }
14808  XMakeWindow(display,
14809    (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14810    argv,argc,class_hints,manager_hints,&windows->image);
14811  (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14812    XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14813  if (windows->group_leader.id != (Window) NULL)
14814    (void) XSetTransientForHint(display,windows->image.id,
14815      windows->group_leader.id);
14816  if (display_image->debug != MagickFalse)
14817    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14818      windows->image.id);
14819  /*
14820    Initialize Info widget.
14821  */
14822  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14823    &windows->info);
14824  (void) CloneString(&windows->info.name,"Info");
14825  (void) CloneString(&windows->info.icon_name,"Info");
14826  windows->info.border_width=1;
14827  windows->info.x=2;
14828  windows->info.y=2;
14829  windows->info.flags|=PPosition;
14830  windows->info.attributes.win_gravity=UnmapGravity;
14831  windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14832    StructureNotifyMask;
14833  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14834  manager_hints->input=MagickFalse;
14835  manager_hints->initial_state=NormalState;
14836  manager_hints->window_group=windows->image.id;
14837  XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14838    &windows->info);
14839  windows->info.highlight_stipple=XCreateBitmapFromData(display,
14840    windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14841  windows->info.shadow_stipple=XCreateBitmapFromData(display,
14842    windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14843  (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14844  if (windows->image.mapped != MagickFalse)
14845    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14846  if (display_image->debug != MagickFalse)
14847    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14848      windows->info.id);
14849  /*
14850    Initialize Command widget.
14851  */
14852  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14853    resource_info,&windows->command);
14854  windows->command.data=MagickMenus;
14855  (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14856  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
14857    resource_info->client_name);
14858  windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14859    resource_name,"geometry",(char *) NULL);
14860  (void) CloneString(&windows->command.name,MagickTitle);
14861  windows->command.border_width=0;
14862  windows->command.flags|=PPosition;
14863  windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14864    ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14865    OwnerGrabButtonMask | StructureNotifyMask;
14866  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14867  manager_hints->input=MagickTrue;
14868  manager_hints->initial_state=NormalState;
14869  manager_hints->window_group=windows->image.id;
14870  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14871    &windows->command);
14872  windows->command.highlight_stipple=XCreateBitmapFromData(display,
14873    windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14874    HighlightHeight);
14875  windows->command.shadow_stipple=XCreateBitmapFromData(display,
14876    windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14877  (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14878  if (windows->command.mapped != MagickFalse)
14879    (void) XMapRaised(display,windows->command.id);
14880  if (display_image->debug != MagickFalse)
14881    (void) LogMagickEvent(X11Event,GetMagickModule(),
14882      "Window id: 0x%lx (command)",windows->command.id);
14883  /*
14884    Initialize Widget window.
14885  */
14886  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14887    resource_info,&windows->widget);
14888  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
14889    resource_info->client_name);
14890  windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14891    resource_name,"geometry",(char *) NULL);
14892  windows->widget.border_width=0;
14893  windows->widget.flags|=PPosition;
14894  windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14895    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14896    KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14897    StructureNotifyMask;
14898  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14899  manager_hints->input=MagickTrue;
14900  manager_hints->initial_state=NormalState;
14901  manager_hints->window_group=windows->image.id;
14902  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14903    &windows->widget);
14904  windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14905    windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14906  windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14907    windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14908  (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14909  if (display_image->debug != MagickFalse)
14910    (void) LogMagickEvent(X11Event,GetMagickModule(),
14911      "Window id: 0x%lx (widget)",windows->widget.id);
14912  /*
14913    Initialize popup window.
14914  */
14915  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14916    resource_info,&windows->popup);
14917  windows->popup.border_width=0;
14918  windows->popup.flags|=PPosition;
14919  windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14920    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14921    KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14922  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14923  manager_hints->input=MagickTrue;
14924  manager_hints->initial_state=NormalState;
14925  manager_hints->window_group=windows->image.id;
14926  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14927    &windows->popup);
14928  windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14929    windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14930  windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14931    windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14932  (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14933  if (display_image->debug != MagickFalse)
14934    (void) LogMagickEvent(X11Event,GetMagickModule(),
14935      "Window id: 0x%lx (pop up)",windows->popup.id);
14936  /*
14937    Initialize Magnify window and cursor.
14938  */
14939  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14940    resource_info,&windows->magnify);
14941  if (resource_info->use_shared_memory == MagickFalse)
14942    windows->magnify.shared_memory=MagickFalse;
14943  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
14944    resource_info->client_name);
14945  windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14946    resource_name,"geometry",(char *) NULL);
14947  (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14948    resource_info->magnify);
14949  if (windows->magnify.cursor != (Cursor) NULL)
14950    (void) XFreeCursor(display,windows->magnify.cursor);
14951  windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14952    map_info->colormap,resource_info->background_color,
14953    resource_info->foreground_color);
14954  if (windows->magnify.cursor == (Cursor) NULL)
14955    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14956      display_image->filename);
14957  windows->magnify.width=MagnifySize;
14958  windows->magnify.height=MagnifySize;
14959  windows->magnify.flags|=PPosition;
14960  windows->magnify.min_width=MagnifySize;
14961  windows->magnify.min_height=MagnifySize;
14962  windows->magnify.width_inc=MagnifySize;
14963  windows->magnify.height_inc=MagnifySize;
14964  windows->magnify.data=resource_info->magnify;
14965  windows->magnify.attributes.cursor=windows->magnify.cursor;
14966  windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14967    ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14968    StructureNotifyMask;
14969  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14970  manager_hints->input=MagickTrue;
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->magnify);
14975  if (display_image->debug != MagickFalse)
14976    (void) LogMagickEvent(X11Event,GetMagickModule(),
14977      "Window id: 0x%lx (magnify)",windows->magnify.id);
14978  (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14979  /*
14980    Initialize panning window.
14981  */
14982  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14983    resource_info,&windows->pan);
14984  (void) CloneString(&windows->pan.name,"Pan Icon");
14985  windows->pan.width=windows->icon.width;
14986  windows->pan.height=windows->icon.height;
14987  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
14988    resource_info->client_name);
14989  windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
14990    resource_name,"geometry",(char *) NULL);
14991  (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
14992    &windows->pan.width,&windows->pan.height);
14993  windows->pan.flags|=PPosition;
14994  windows->pan.immutable=MagickTrue;
14995  windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14996    ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
14997    StructureNotifyMask;
14998  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14999  manager_hints->input=MagickFalse;
15000  manager_hints->initial_state=NormalState;
15001  manager_hints->window_group=windows->image.id;
15002  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
15003    &windows->pan);
15004  if (display_image->debug != MagickFalse)
15005    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
15006      windows->pan.id);
15007  (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
15008  if (windows->info.mapped != MagickFalse)
15009    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15010  if ((windows->image.mapped == MagickFalse) ||
15011      (windows->backdrop.id != (Window) NULL))
15012    (void) XMapWindow(display,windows->image.id);
15013  /*
15014    Set our progress monitor and warning handlers.
15015  */
15016  if (warning_handler == (WarningHandler) NULL)
15017    {
15018      warning_handler=resource_info->display_warnings ?
15019        SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
15020      warning_handler=resource_info->display_warnings ?
15021        SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
15022    }
15023  /*
15024    Initialize Image and Magnify X images.
15025  */
15026  windows->image.x=0;
15027  windows->image.y=0;
15028  windows->magnify.shape=MagickFalse;
15029  width=(unsigned int) display_image->columns;
15030  height=(unsigned int) display_image->rows;
15031  if ((display_image->columns != width) || (display_image->rows != height))
15032    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15033      display_image->filename);
15034  status=XMakeImage(display,resource_info,&windows->image,display_image,
15035    width,height,exception);
15036  if (status == MagickFalse)
15037    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15038      display_image->filename);
15039  status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
15040    windows->magnify.width,windows->magnify.height,exception);
15041  if (status == MagickFalse)
15042    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15043      display_image->filename);
15044  if (windows->magnify.mapped != MagickFalse)
15045    (void) XMapRaised(display,windows->magnify.id);
15046  if (windows->pan.mapped != MagickFalse)
15047    (void) XMapRaised(display,windows->pan.id);
15048  windows->image.window_changes.width=(int) display_image->columns;
15049  windows->image.window_changes.height=(int) display_image->rows;
15050  (void) XConfigureImage(display,resource_info,windows,display_image,exception);
15051  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15052  (void) XSync(display,MagickFalse);
15053  /*
15054    Respond to events.
15055  */
15056  delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
15057  timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15058  update_time=0;
15059  if (resource_info->update != MagickFalse)
15060    {
15061      MagickBooleanType
15062        status;
15063
15064      /*
15065        Determine when file data was last modified.
15066      */
15067      status=GetPathAttributes(display_image->filename,&attributes);
15068      if (status != MagickFalse)
15069        update_time=attributes.st_mtime;
15070    }
15071  *state&=(~FormerImageState);
15072  *state&=(~MontageImageState);
15073  *state&=(~NextImageState);
15074  do
15075  {
15076    /*
15077      Handle a window event.
15078    */
15079    if (windows->image.mapped != MagickFalse)
15080      if ((display_image->delay != 0) || (resource_info->update != 0))
15081        {
15082          if (timer < time((time_t *) NULL))
15083            {
15084              if (resource_info->update == MagickFalse)
15085                *state|=NextImageState | ExitState;
15086              else
15087                {
15088                  MagickBooleanType
15089                    status;
15090
15091                  /*
15092                    Determine if image file was modified.
15093                  */
15094                  status=GetPathAttributes(display_image->filename,&attributes);
15095                  if (status != MagickFalse)
15096                    if (update_time != attributes.st_mtime)
15097                      {
15098                        /*
15099                          Redisplay image.
15100                        */
15101                        (void) FormatLocaleString(
15102                          resource_info->image_info->filename,MaxTextExtent,
15103                          "%s:%s",display_image->magick,
15104                          display_image->filename);
15105                        nexus=ReadImage(resource_info->image_info,exception);
15106                        if (nexus != (Image *) NULL)
15107                          {
15108                            nexus=DestroyImage(nexus);
15109                            *state|=NextImageState | ExitState;
15110                          }
15111                      }
15112                  delay=display_image->delay/MagickMax(
15113                    display_image->ticks_per_second,1L);
15114                  timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15115                }
15116            }
15117          if (XEventsQueued(display,QueuedAfterFlush) == 0)
15118            {
15119              /*
15120                Do not block if delay > 0.
15121              */
15122              XDelay(display,SuspendTime << 2);
15123              continue;
15124            }
15125        }
15126    timestamp=time((time_t *) NULL);
15127    (void) XNextEvent(display,&event);
15128    if (windows->image.stasis == MagickFalse)
15129      windows->image.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15130        MagickTrue : MagickFalse;
15131    if (windows->magnify.stasis == MagickFalse)
15132      windows->magnify.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15133        MagickTrue : MagickFalse;
15134    if (event.xany.window == windows->command.id)
15135      {
15136        /*
15137          Select a command from the Command widget.
15138        */
15139        id=XCommandWidget(display,windows,CommandMenu,&event);
15140        if (id < 0)
15141          continue;
15142        (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15143        command_type=CommandMenus[id];
15144        if (id < MagickMenus)
15145          {
15146            /*
15147              Select a command from a pop-up menu.
15148            */
15149            entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15150              command);
15151            if (entry < 0)
15152              continue;
15153            (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15154            command_type=Commands[id][entry];
15155          }
15156        if (command_type != NullCommand)
15157          nexus=XMagickCommand(display,resource_info,windows,command_type,
15158            &display_image,exception);
15159        continue;
15160      }
15161    switch (event.type)
15162    {
15163      case ButtonPress:
15164      {
15165        if (display_image->debug != MagickFalse)
15166          (void) LogMagickEvent(X11Event,GetMagickModule(),
15167            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15168            event.xbutton.button,event.xbutton.x,event.xbutton.y);
15169        if ((event.xbutton.button == Button3) &&
15170            (event.xbutton.state & Mod1Mask))
15171          {
15172            /*
15173              Convert Alt-Button3 to Button2.
15174            */
15175            event.xbutton.button=Button2;
15176            event.xbutton.state&=(~Mod1Mask);
15177          }
15178        if (event.xbutton.window == windows->backdrop.id)
15179          {
15180            (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15181              event.xbutton.time);
15182            break;
15183          }
15184        if (event.xbutton.window == windows->image.id)
15185          {
15186            switch (event.xbutton.button)
15187            {
15188              case Button1:
15189              {
15190                if (resource_info->immutable)
15191                  {
15192                    /*
15193                      Select a command from the Virtual menu.
15194                    */
15195                    entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15196                      command);
15197                    if (entry >= 0)
15198                      nexus=XMagickCommand(display,resource_info,windows,
15199                        VirtualCommands[entry],&display_image,exception);
15200                    break;
15201                  }
15202                /*
15203                  Map/unmap Command widget.
15204                */
15205                if (windows->command.mapped != MagickFalse)
15206                  (void) XWithdrawWindow(display,windows->command.id,
15207                    windows->command.screen);
15208                else
15209                  {
15210                    (void) XCommandWidget(display,windows,CommandMenu,
15211                      (XEvent *) NULL);
15212                    (void) XMapRaised(display,windows->command.id);
15213                  }
15214                break;
15215              }
15216              case Button2:
15217              {
15218                /*
15219                  User pressed the image magnify button.
15220                */
15221                (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15222                  &display_image,exception);
15223                XMagnifyImage(display,windows,&event);
15224                break;
15225              }
15226              case Button3:
15227              {
15228                if (resource_info->immutable)
15229                  {
15230                    /*
15231                      Select a command from the Virtual menu.
15232                    */
15233                    entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15234                      command);
15235                    if (entry >= 0)
15236                      nexus=XMagickCommand(display,resource_info,windows,
15237                        VirtualCommands[entry],&display_image,exception);
15238                    break;
15239                  }
15240                if (display_image->montage != (char *) NULL)
15241                  {
15242                    /*
15243                      Open or delete a tile from a visual image directory.
15244                    */
15245                    nexus=XTileImage(display,resource_info,windows,
15246                      display_image,&event,exception);
15247                    if (nexus != (Image *) NULL)
15248                      *state|=MontageImageState | NextImageState | ExitState;
15249                    vid_info.x=(short int) windows->image.x;
15250                    vid_info.y=(short int) windows->image.y;
15251                    break;
15252                  }
15253                /*
15254                  Select a command from the Short Cuts menu.
15255                */
15256                entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15257                  command);
15258                if (entry >= 0)
15259                  nexus=XMagickCommand(display,resource_info,windows,
15260                    ShortCutsCommands[entry],&display_image,exception);
15261                break;
15262              }
15263              case Button4:
15264              {
15265                /*
15266                  Wheel up.
15267                */
15268                XTranslateImage(display,windows,*image,XK_Up);
15269                break;
15270              }
15271              case Button5:
15272              {
15273                /*
15274                  Wheel down.
15275                */
15276                XTranslateImage(display,windows,*image,XK_Down);
15277                break;
15278              }
15279              default:
15280                break;
15281            }
15282            break;
15283          }
15284        if (event.xbutton.window == windows->magnify.id)
15285          {
15286            int
15287              factor;
15288
15289            static const char
15290              *MagnifyMenu[] =
15291              {
15292                "2",
15293                "4",
15294                "5",
15295                "6",
15296                "7",
15297                "8",
15298                "9",
15299                "3",
15300                (char *) NULL,
15301              };
15302
15303            static KeySym
15304              MagnifyCommands[] =
15305              {
15306                XK_2,
15307                XK_4,
15308                XK_5,
15309                XK_6,
15310                XK_7,
15311                XK_8,
15312                XK_9,
15313                XK_3
15314              };
15315
15316            /*
15317              Select a magnify factor from the pop-up menu.
15318            */
15319            factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15320            if (factor >= 0)
15321              XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor]);
15322            break;
15323          }
15324        if (event.xbutton.window == windows->pan.id)
15325          {
15326            switch (event.xbutton.button)
15327            {
15328              case Button4:
15329              {
15330                /*
15331                  Wheel up.
15332                */
15333                XTranslateImage(display,windows,*image,XK_Up);
15334                break;
15335              }
15336              case Button5:
15337              {
15338                /*
15339                  Wheel down.
15340                */
15341                XTranslateImage(display,windows,*image,XK_Down);
15342                break;
15343              }
15344              default:
15345              {
15346                XPanImage(display,windows,&event);
15347                break;
15348              }
15349            }
15350            break;
15351          }
15352        delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15353          1L);
15354        timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15355        break;
15356      }
15357      case ButtonRelease:
15358      {
15359        if (display_image->debug != MagickFalse)
15360          (void) LogMagickEvent(X11Event,GetMagickModule(),
15361            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15362            event.xbutton.button,event.xbutton.x,event.xbutton.y);
15363        break;
15364      }
15365      case ClientMessage:
15366      {
15367        if (display_image->debug != MagickFalse)
15368          (void) LogMagickEvent(X11Event,GetMagickModule(),
15369            "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15370            event.xclient.message_type,event.xclient.format,(unsigned long)
15371            event.xclient.data.l[0]);
15372        if (event.xclient.message_type == windows->im_protocols)
15373          {
15374            if (*event.xclient.data.l == (long) windows->im_update_widget)
15375              {
15376                (void) CloneString(&windows->command.name,MagickTitle);
15377                windows->command.data=MagickMenus;
15378                (void) XCommandWidget(display,windows,CommandMenu,
15379                  (XEvent *) NULL);
15380                break;
15381              }
15382            if (*event.xclient.data.l == (long) windows->im_update_colormap)
15383              {
15384                /*
15385                  Update graphic context and window colormap.
15386                */
15387                for (i=0; i < (int) number_windows; i++)
15388                {
15389                  if (magick_windows[i]->id == windows->icon.id)
15390                    continue;
15391                  context_values.background=pixel->background_color.pixel;
15392                  context_values.foreground=pixel->foreground_color.pixel;
15393                  (void) XChangeGC(display,magick_windows[i]->annotate_context,
15394                    context_mask,&context_values);
15395                  (void) XChangeGC(display,magick_windows[i]->widget_context,
15396                    context_mask,&context_values);
15397                  context_values.background=pixel->foreground_color.pixel;
15398                  context_values.foreground=pixel->background_color.pixel;
15399                  context_values.plane_mask=context_values.background ^
15400                    context_values.foreground;
15401                  (void) XChangeGC(display,magick_windows[i]->highlight_context,
15402                    (size_t) (context_mask | GCPlaneMask),
15403                    &context_values);
15404                  magick_windows[i]->attributes.background_pixel=
15405                    pixel->background_color.pixel;
15406                  magick_windows[i]->attributes.border_pixel=
15407                    pixel->border_color.pixel;
15408                  magick_windows[i]->attributes.colormap=map_info->colormap;
15409                  (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15410                    (unsigned long) magick_windows[i]->mask,
15411                    &magick_windows[i]->attributes);
15412                }
15413                if (windows->pan.mapped != MagickFalse)
15414                  {
15415                    (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15416                      windows->pan.pixmap);
15417                    (void) XClearWindow(display,windows->pan.id);
15418                    XDrawPanRectangle(display,windows);
15419                  }
15420                if (windows->backdrop.id != (Window) NULL)
15421                  (void) XInstallColormap(display,map_info->colormap);
15422                break;
15423              }
15424            if (*event.xclient.data.l == (long) windows->im_former_image)
15425              {
15426                *state|=FormerImageState | ExitState;
15427                break;
15428              }
15429            if (*event.xclient.data.l == (long) windows->im_next_image)
15430              {
15431                *state|=NextImageState | ExitState;
15432                break;
15433              }
15434            if (*event.xclient.data.l == (long) windows->im_retain_colors)
15435              {
15436                *state|=RetainColorsState;
15437                break;
15438              }
15439            if (*event.xclient.data.l == (long) windows->im_exit)
15440              {
15441                *state|=ExitState;
15442                break;
15443              }
15444            break;
15445          }
15446        if (event.xclient.message_type == windows->dnd_protocols)
15447          {
15448            Atom
15449              selection,
15450              type;
15451
15452            int
15453              format,
15454              status;
15455
15456            unsigned char
15457              *data;
15458
15459            unsigned long
15460              after,
15461              length;
15462
15463            /*
15464              Display image named by the Drag-and-Drop selection.
15465            */
15466            if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15467              break;
15468            selection=XInternAtom(display,"DndSelection",MagickFalse);
15469            status=XGetWindowProperty(display,root_window,selection,0L,(long)
15470              MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15471              &length,&after,&data);
15472            if ((status != Success) || (length == 0))
15473              break;
15474            if (*event.xclient.data.l == 2)
15475              {
15476                /*
15477                  Offix DND.
15478                */
15479                (void) CopyMagickString(resource_info->image_info->filename,
15480                  (char *) data,MaxTextExtent);
15481              }
15482            else
15483              {
15484                /*
15485                  XDND.
15486                */
15487                if (strncmp((char *) data, "file:", 5) != 0)
15488                  {
15489                    (void) XFree((void *) data);
15490                    break;
15491                  }
15492                (void) CopyMagickString(resource_info->image_info->filename,
15493                  ((char *) data)+5,MaxTextExtent);
15494              }
15495            nexus=ReadImage(resource_info->image_info,exception);
15496            CatchException(exception);
15497            if (nexus != (Image *) NULL)
15498              *state|=NextImageState | ExitState;
15499            (void) XFree((void *) data);
15500            break;
15501          }
15502        /*
15503          If client window delete message, exit.
15504        */
15505        if (event.xclient.message_type != windows->wm_protocols)
15506          break;
15507        if (*event.xclient.data.l != (long) windows->wm_delete_window)
15508          break;
15509        (void) XWithdrawWindow(display,event.xclient.window,
15510          visual_info->screen);
15511        if (event.xclient.window == windows->image.id)
15512          {
15513            *state|=ExitState;
15514            break;
15515          }
15516        if (event.xclient.window == windows->pan.id)
15517          {
15518            /*
15519              Restore original image size when pan window is deleted.
15520            */
15521            windows->image.window_changes.width=windows->image.ximage->width;
15522            windows->image.window_changes.height=windows->image.ximage->height;
15523            (void) XConfigureImage(display,resource_info,windows,
15524              display_image,exception);
15525          }
15526        break;
15527      }
15528      case ConfigureNotify:
15529      {
15530        if (display_image->debug != MagickFalse)
15531          (void) LogMagickEvent(X11Event,GetMagickModule(),
15532            "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15533            event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15534            event.xconfigure.y,event.xconfigure.send_event);
15535        if (event.xconfigure.window == windows->image.id)
15536          {
15537            /*
15538              Image window has a new configuration.
15539            */
15540            if (event.xconfigure.send_event != 0)
15541              {
15542                XWindowChanges
15543                  window_changes;
15544
15545                /*
15546                  Position the transient windows relative of the Image window.
15547                */
15548                if (windows->command.geometry == (char *) NULL)
15549                  if (windows->command.mapped == MagickFalse)
15550                    {
15551                      windows->command.x=event.xconfigure.x-
15552                        windows->command.width-25;
15553                      windows->command.y=event.xconfigure.y;
15554                      XConstrainWindowPosition(display,&windows->command);
15555                      window_changes.x=windows->command.x;
15556                      window_changes.y=windows->command.y;
15557                      (void) XReconfigureWMWindow(display,windows->command.id,
15558                        windows->command.screen,(unsigned int) (CWX | CWY),
15559                        &window_changes);
15560                    }
15561                if (windows->widget.geometry == (char *) NULL)
15562                  if (windows->widget.mapped == MagickFalse)
15563                    {
15564                      windows->widget.x=event.xconfigure.x+
15565                        event.xconfigure.width/10;
15566                      windows->widget.y=event.xconfigure.y+
15567                        event.xconfigure.height/10;
15568                      XConstrainWindowPosition(display,&windows->widget);
15569                      window_changes.x=windows->widget.x;
15570                      window_changes.y=windows->widget.y;
15571                      (void) XReconfigureWMWindow(display,windows->widget.id,
15572                        windows->widget.screen,(unsigned int) (CWX | CWY),
15573                        &window_changes);
15574                    }
15575                if (windows->magnify.geometry == (char *) NULL)
15576                  if (windows->magnify.mapped == MagickFalse)
15577                    {
15578                      windows->magnify.x=event.xconfigure.x+
15579                        event.xconfigure.width+25;
15580                      windows->magnify.y=event.xconfigure.y;
15581                      XConstrainWindowPosition(display,&windows->magnify);
15582                      window_changes.x=windows->magnify.x;
15583                      window_changes.y=windows->magnify.y;
15584                      (void) XReconfigureWMWindow(display,windows->magnify.id,
15585                        windows->magnify.screen,(unsigned int) (CWX | CWY),
15586                        &window_changes);
15587                    }
15588                if (windows->pan.geometry == (char *) NULL)
15589                  if (windows->pan.mapped == MagickFalse)
15590                    {
15591                      windows->pan.x=event.xconfigure.x+
15592                        event.xconfigure.width+25;
15593                      windows->pan.y=event.xconfigure.y+
15594                        windows->magnify.height+50;
15595                      XConstrainWindowPosition(display,&windows->pan);
15596                      window_changes.x=windows->pan.x;
15597                      window_changes.y=windows->pan.y;
15598                      (void) XReconfigureWMWindow(display,windows->pan.id,
15599                        windows->pan.screen,(unsigned int) (CWX | CWY),
15600                        &window_changes);
15601                    }
15602              }
15603            if ((event.xconfigure.width == (int) windows->image.width) &&
15604                (event.xconfigure.height == (int) windows->image.height))
15605              break;
15606            windows->image.width=(unsigned int) event.xconfigure.width;
15607            windows->image.height=(unsigned int) event.xconfigure.height;
15608            windows->image.x=0;
15609            windows->image.y=0;
15610            if (display_image->montage != (char *) NULL)
15611              {
15612                windows->image.x=vid_info.x;
15613                windows->image.y=vid_info.y;
15614              }
15615            if ((windows->image.mapped != MagickFalse) &&
15616                (windows->image.stasis != MagickFalse))
15617              {
15618                /*
15619                  Update image window configuration.
15620                */
15621                windows->image.window_changes.width=event.xconfigure.width;
15622                windows->image.window_changes.height=event.xconfigure.height;
15623                (void) XConfigureImage(display,resource_info,windows,
15624                  display_image,exception);
15625              }
15626            /*
15627              Update pan window configuration.
15628            */
15629            if ((event.xconfigure.width < windows->image.ximage->width) ||
15630                (event.xconfigure.height < windows->image.ximage->height))
15631              {
15632                (void) XMapRaised(display,windows->pan.id);
15633                XDrawPanRectangle(display,windows);
15634              }
15635            else
15636              if (windows->pan.mapped != MagickFalse)
15637                (void) XWithdrawWindow(display,windows->pan.id,
15638                  windows->pan.screen);
15639            break;
15640          }
15641        if (event.xconfigure.window == windows->magnify.id)
15642          {
15643            unsigned int
15644              magnify;
15645
15646            /*
15647              Magnify window has a new configuration.
15648            */
15649            windows->magnify.width=(unsigned int) event.xconfigure.width;
15650            windows->magnify.height=(unsigned int) event.xconfigure.height;
15651            if (windows->magnify.mapped == MagickFalse)
15652              break;
15653            magnify=1;
15654            while ((int) magnify <= event.xconfigure.width)
15655              magnify<<=1;
15656            while ((int) magnify <= event.xconfigure.height)
15657              magnify<<=1;
15658            magnify>>=1;
15659            if (((int) magnify != event.xconfigure.width) ||
15660                ((int) magnify != event.xconfigure.height))
15661              {
15662                window_changes.width=(int) magnify;
15663                window_changes.height=(int) magnify;
15664                (void) XReconfigureWMWindow(display,windows->magnify.id,
15665                  windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15666                  &window_changes);
15667                break;
15668              }
15669            if ((windows->magnify.mapped != MagickFalse) &&
15670                (windows->magnify.stasis != MagickFalse))
15671              {
15672                status=XMakeImage(display,resource_info,&windows->magnify,
15673                  display_image,windows->magnify.width,windows->magnify.height,
15674                  exception);
15675                XMakeMagnifyImage(display,windows);
15676              }
15677            break;
15678          }
15679        if ((windows->magnify.mapped != MagickFalse) &&
15680            (event.xconfigure.window == windows->pan.id))
15681          {
15682            /*
15683              Pan icon window has a new configuration.
15684            */
15685            if (event.xconfigure.send_event != 0)
15686              {
15687                windows->pan.x=event.xconfigure.x;
15688                windows->pan.y=event.xconfigure.y;
15689              }
15690            windows->pan.width=(unsigned int) event.xconfigure.width;
15691            windows->pan.height=(unsigned int) event.xconfigure.height;
15692            break;
15693          }
15694        if (event.xconfigure.window == windows->icon.id)
15695          {
15696            /*
15697              Icon window has a new configuration.
15698            */
15699            windows->icon.width=(unsigned int) event.xconfigure.width;
15700            windows->icon.height=(unsigned int) event.xconfigure.height;
15701            break;
15702          }
15703        break;
15704      }
15705      case DestroyNotify:
15706      {
15707        /*
15708          Group leader has exited.
15709        */
15710        if (display_image->debug != MagickFalse)
15711          (void) LogMagickEvent(X11Event,GetMagickModule(),
15712            "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15713        if (event.xdestroywindow.window == windows->group_leader.id)
15714          {
15715            *state|=ExitState;
15716            break;
15717          }
15718        break;
15719      }
15720      case EnterNotify:
15721      {
15722        /*
15723          Selectively install colormap.
15724        */
15725        if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15726          if (event.xcrossing.mode != NotifyUngrab)
15727            XInstallColormap(display,map_info->colormap);
15728        break;
15729      }
15730      case Expose:
15731      {
15732        if (display_image->debug != MagickFalse)
15733          (void) LogMagickEvent(X11Event,GetMagickModule(),
15734            "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15735            event.xexpose.width,event.xexpose.height,event.xexpose.x,
15736            event.xexpose.y);
15737        /*
15738          Refresh windows that are now exposed.
15739        */
15740        if ((event.xexpose.window == windows->image.id) &&
15741            (windows->image.mapped != MagickFalse))
15742          {
15743            XRefreshWindow(display,&windows->image,&event);
15744            delay=display_image->delay/MagickMax(
15745              display_image->ticks_per_second,1L);
15746            timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15747            break;
15748          }
15749        if ((event.xexpose.window == windows->magnify.id) &&
15750            (windows->magnify.mapped != MagickFalse))
15751          {
15752            XMakeMagnifyImage(display,windows);
15753            break;
15754          }
15755        if (event.xexpose.window == windows->pan.id)
15756          {
15757            XDrawPanRectangle(display,windows);
15758            break;
15759          }
15760        if (event.xexpose.window == windows->icon.id)
15761          {
15762            XRefreshWindow(display,&windows->icon,&event);
15763            break;
15764          }
15765        break;
15766      }
15767      case KeyPress:
15768      {
15769        int
15770          length;
15771
15772        /*
15773          Respond to a user key press.
15774        */
15775        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15776          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15777        *(command+length)='\0';
15778        if (display_image->debug != MagickFalse)
15779          (void) LogMagickEvent(X11Event,GetMagickModule(),
15780            "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15781            key_symbol,command);
15782        if (event.xkey.window == windows->image.id)
15783          {
15784            command_type=XImageWindowCommand(display,resource_info,windows,
15785              event.xkey.state,key_symbol,&display_image,exception);
15786            if (command_type != NullCommand)
15787              nexus=XMagickCommand(display,resource_info,windows,command_type,
15788                &display_image,exception);
15789          }
15790        if (event.xkey.window == windows->magnify.id)
15791          XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol);
15792        if (event.xkey.window == windows->pan.id)
15793          {
15794            if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15795              (void) XWithdrawWindow(display,windows->pan.id,
15796                windows->pan.screen);
15797            else
15798              if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15799                XTextViewWidget(display,resource_info,windows,MagickFalse,
15800                  "Help Viewer - Image Pan",ImagePanHelp);
15801              else
15802                XTranslateImage(display,windows,*image,key_symbol);
15803          }
15804        delay=display_image->delay/MagickMax(
15805          display_image->ticks_per_second,1L);
15806        timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15807        break;
15808      }
15809      case KeyRelease:
15810      {
15811        /*
15812          Respond to a user key release.
15813        */
15814        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15815          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15816        if (display_image->debug != MagickFalse)
15817          (void) LogMagickEvent(X11Event,GetMagickModule(),
15818            "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15819        break;
15820      }
15821      case LeaveNotify:
15822      {
15823        /*
15824          Selectively uninstall colormap.
15825        */
15826        if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15827          if (event.xcrossing.mode != NotifyUngrab)
15828            XUninstallColormap(display,map_info->colormap);
15829        break;
15830      }
15831      case MapNotify:
15832      {
15833        if (display_image->debug != MagickFalse)
15834          (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15835            event.xmap.window);
15836        if (event.xmap.window == windows->backdrop.id)
15837          {
15838            (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15839              CurrentTime);
15840            windows->backdrop.mapped=MagickTrue;
15841            break;
15842          }
15843        if (event.xmap.window == windows->image.id)
15844          {
15845            if (windows->backdrop.id != (Window) NULL)
15846              (void) XInstallColormap(display,map_info->colormap);
15847            if (LocaleCompare(display_image->magick,"LOGO") == 0)
15848              {
15849                if (LocaleCompare(display_image->filename,"LOGO") == 0)
15850                  nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15851              }
15852            if (((int) windows->image.width < windows->image.ximage->width) ||
15853                ((int) windows->image.height < windows->image.ximage->height))
15854              (void) XMapRaised(display,windows->pan.id);
15855            windows->image.mapped=MagickTrue;
15856            break;
15857          }
15858        if (event.xmap.window == windows->magnify.id)
15859          {
15860            XMakeMagnifyImage(display,windows);
15861            windows->magnify.mapped=MagickTrue;
15862            (void) XWithdrawWindow(display,windows->info.id,
15863              windows->info.screen);
15864            break;
15865          }
15866        if (event.xmap.window == windows->pan.id)
15867          {
15868            XMakePanImage(display,resource_info,windows,display_image,
15869              exception);
15870            windows->pan.mapped=MagickTrue;
15871            break;
15872          }
15873        if (event.xmap.window == windows->info.id)
15874          {
15875            windows->info.mapped=MagickTrue;
15876            break;
15877          }
15878        if (event.xmap.window == windows->icon.id)
15879          {
15880            MagickBooleanType
15881              taint;
15882
15883            /*
15884              Create an icon image.
15885            */
15886            taint=display_image->taint;
15887            XMakeStandardColormap(display,icon_visual,icon_resources,
15888              display_image,icon_map,icon_pixel);
15889            (void) XMakeImage(display,icon_resources,&windows->icon,
15890              display_image,windows->icon.width,windows->icon.height,
15891              exception);
15892            display_image->taint=taint;
15893            (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15894              windows->icon.pixmap);
15895            (void) XClearWindow(display,windows->icon.id);
15896            (void) XWithdrawWindow(display,windows->info.id,
15897              windows->info.screen);
15898            windows->icon.mapped=MagickTrue;
15899            break;
15900          }
15901        if (event.xmap.window == windows->command.id)
15902          {
15903            windows->command.mapped=MagickTrue;
15904            break;
15905          }
15906        if (event.xmap.window == windows->popup.id)
15907          {
15908            windows->popup.mapped=MagickTrue;
15909            break;
15910          }
15911        if (event.xmap.window == windows->widget.id)
15912          {
15913            windows->widget.mapped=MagickTrue;
15914            break;
15915          }
15916        break;
15917      }
15918      case MappingNotify:
15919      {
15920        (void) XRefreshKeyboardMapping(&event.xmapping);
15921        break;
15922      }
15923      case NoExpose:
15924        break;
15925      case PropertyNotify:
15926      {
15927        Atom
15928          type;
15929
15930        int
15931          format,
15932          status;
15933
15934        unsigned char
15935          *data;
15936
15937        unsigned long
15938          after,
15939          length;
15940
15941        if (display_image->debug != MagickFalse)
15942          (void) LogMagickEvent(X11Event,GetMagickModule(),
15943            "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15944            event.xproperty.atom,event.xproperty.state);
15945        if (event.xproperty.atom != windows->im_remote_command)
15946          break;
15947        /*
15948          Display image named by the remote command protocol.
15949        */
15950        status=XGetWindowProperty(display,event.xproperty.window,
15951          event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15952          AnyPropertyType,&type,&format,&length,&after,&data);
15953        if ((status != Success) || (length == 0))
15954          break;
15955        if (LocaleCompare((char *) data,"-quit") == 0)
15956          {
15957            XClientMessage(display,windows->image.id,windows->im_protocols,
15958              windows->im_exit,CurrentTime);
15959            (void) XFree((void *) data);
15960            break;
15961          }
15962        (void) CopyMagickString(resource_info->image_info->filename,
15963          (char *) data,MaxTextExtent);
15964        (void) XFree((void *) data);
15965        nexus=ReadImage(resource_info->image_info,exception);
15966        CatchException(exception);
15967        if (nexus != (Image *) NULL)
15968          *state|=NextImageState | ExitState;
15969        break;
15970      }
15971      case ReparentNotify:
15972      {
15973        if (display_image->debug != MagickFalse)
15974          (void) LogMagickEvent(X11Event,GetMagickModule(),
15975            "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15976            event.xreparent.window);
15977        break;
15978      }
15979      case UnmapNotify:
15980      {
15981        if (display_image->debug != MagickFalse)
15982          (void) LogMagickEvent(X11Event,GetMagickModule(),
15983            "Unmap Notify: 0x%lx",event.xunmap.window);
15984        if (event.xunmap.window == windows->backdrop.id)
15985          {
15986            windows->backdrop.mapped=MagickFalse;
15987            break;
15988          }
15989        if (event.xunmap.window == windows->image.id)
15990          {
15991            windows->image.mapped=MagickFalse;
15992            break;
15993          }
15994        if (event.xunmap.window == windows->magnify.id)
15995          {
15996            windows->magnify.mapped=MagickFalse;
15997            break;
15998          }
15999        if (event.xunmap.window == windows->pan.id)
16000          {
16001            windows->pan.mapped=MagickFalse;
16002            break;
16003          }
16004        if (event.xunmap.window == windows->info.id)
16005          {
16006            windows->info.mapped=MagickFalse;
16007            break;
16008          }
16009        if (event.xunmap.window == windows->icon.id)
16010          {
16011            if (map_info->colormap == icon_map->colormap)
16012              XConfigureImageColormap(display,resource_info,windows,
16013                display_image);
16014            (void) XFreeStandardColormap(display,icon_visual,icon_map,
16015              icon_pixel);
16016            windows->icon.mapped=MagickFalse;
16017            break;
16018          }
16019        if (event.xunmap.window == windows->command.id)
16020          {
16021            windows->command.mapped=MagickFalse;
16022            break;
16023          }
16024        if (event.xunmap.window == windows->popup.id)
16025          {
16026            if (windows->backdrop.id != (Window) NULL)
16027              (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16028                CurrentTime);
16029            windows->popup.mapped=MagickFalse;
16030            break;
16031          }
16032        if (event.xunmap.window == windows->widget.id)
16033          {
16034            if (windows->backdrop.id != (Window) NULL)
16035              (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16036                CurrentTime);
16037            windows->widget.mapped=MagickFalse;
16038            break;
16039          }
16040        break;
16041      }
16042      default:
16043      {
16044        if (display_image->debug != MagickFalse)
16045          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
16046            event.type);
16047        break;
16048      }
16049    }
16050  } while (!(*state & ExitState));
16051  if ((*state & ExitState) == 0)
16052    (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
16053      &display_image,exception);
16054  else
16055    if (resource_info->confirm_edit != MagickFalse)
16056      {
16057        /*
16058          Query user if image has changed.
16059        */
16060        if ((resource_info->immutable == MagickFalse) &&
16061            (display_image->taint != MagickFalse))
16062          {
16063            int
16064              status;
16065
16066            status=XConfirmWidget(display,windows,"Your image changed.",
16067              "Do you want to save it");
16068            if (status == 0)
16069              *state&=(~ExitState);
16070            else
16071              if (status > 0)
16072                (void) XMagickCommand(display,resource_info,windows,SaveCommand,
16073                  &display_image,exception);
16074          }
16075      }
16076  if ((windows->visual_info->klass == GrayScale) ||
16077      (windows->visual_info->klass == PseudoColor) ||
16078      (windows->visual_info->klass == DirectColor))
16079    {
16080      /*
16081        Withdraw pan and Magnify window.
16082      */
16083      if (windows->info.mapped != MagickFalse)
16084        (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16085      if (windows->magnify.mapped != MagickFalse)
16086        (void) XWithdrawWindow(display,windows->magnify.id,
16087          windows->magnify.screen);
16088      if (windows->command.mapped != MagickFalse)
16089        (void) XWithdrawWindow(display,windows->command.id,
16090          windows->command.screen);
16091    }
16092  if (windows->pan.mapped != MagickFalse)
16093    (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16094  if (resource_info->backdrop == MagickFalse)
16095    if (windows->backdrop.mapped)
16096      {
16097        (void) XWithdrawWindow(display,windows->backdrop.id,
16098          windows->backdrop.screen);
16099        (void) XDestroyWindow(display,windows->backdrop.id);
16100        windows->backdrop.id=(Window) NULL;
16101        (void) XWithdrawWindow(display,windows->image.id,
16102          windows->image.screen);
16103        (void) XDestroyWindow(display,windows->image.id);
16104        windows->image.id=(Window) NULL;
16105      }
16106  XSetCursorState(display,windows,MagickTrue);
16107  XCheckRefreshWindows(display,windows);
16108  if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16109    *state&=(~ExitState);
16110  if (*state & ExitState)
16111    {
16112      /*
16113        Free Standard Colormap.
16114      */
16115      (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16116      if (resource_info->map_type == (char *) NULL)
16117        (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16118      /*
16119        Free X resources.
16120      */
16121      if (resource_info->copy_image != (Image *) NULL)
16122        {
16123          resource_info->copy_image=DestroyImage(resource_info->copy_image);
16124          resource_info->copy_image=NewImageList();
16125        }
16126      DestroyXResources();
16127    }
16128  (void) XSync(display,MagickFalse);
16129  /*
16130    Restore our progress monitor and warning handlers.
16131  */
16132  (void) SetErrorHandler(warning_handler);
16133  (void) SetWarningHandler(warning_handler);
16134  /*
16135    Change to home directory.
16136  */
16137  directory=getcwd(working_directory,MaxTextExtent);
16138  (void) directory;
16139  {
16140    int
16141      status;
16142
16143    status=chdir(resource_info->home_directory);
16144    if (status == -1)
16145      (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
16146        "UnableToOpenFile","%s",resource_info->home_directory);
16147  }
16148  *image=display_image;
16149  return(nexus);
16150}
16151#else
16152
16153/*
16154%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16155%                                                                             %
16156%                                                                             %
16157%                                                                             %
16158+   D i s p l a y I m a g e s                                                 %
16159%                                                                             %
16160%                                                                             %
16161%                                                                             %
16162%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16163%
16164%  DisplayImages() displays an image sequence to any X window screen.  It
16165%  returns a value other than 0 if successful.  Check the exception member
16166%  of image to determine the reason for any failure.
16167%
16168%  The format of the DisplayImages method is:
16169%
16170%      MagickBooleanType DisplayImages(const ImageInfo *image_info,
16171%        Image *images,ExceptionInfo *exception)
16172%
16173%  A description of each parameter follows:
16174%
16175%    o image_info: the image info.
16176%
16177%    o image: the image.
16178%
16179%    o exception: return any errors or warnings in this structure.
16180%
16181*/
16182MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16183  Image *image,ExceptionInfo *exception)
16184{
16185  assert(image_info != (const ImageInfo *) NULL);
16186  assert(image_info->signature == MagickSignature);
16187  assert(image != (Image *) NULL);
16188  assert(image->signature == MagickSignature);
16189  if (image->debug != MagickFalse)
16190    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16191  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16192    "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image->filename);
16193  return(MagickFalse);
16194}
16195
16196/*
16197%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16198%                                                                             %
16199%                                                                             %
16200%                                                                             %
16201+   R e m o t e D i s p l a y C o m m a n d                                   %
16202%                                                                             %
16203%                                                                             %
16204%                                                                             %
16205%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16206%
16207%  RemoteDisplayCommand() encourages a remote display program to display the
16208%  specified image filename.
16209%
16210%  The format of the RemoteDisplayCommand method is:
16211%
16212%      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16213%        const char *window,const char *filename,ExceptionInfo *exception)
16214%
16215%  A description of each parameter follows:
16216%
16217%    o image_info: the image info.
16218%
16219%    o window: Specifies the name or id of an X window.
16220%
16221%    o filename: the name of the image filename to display.
16222%
16223%    o exception: return any errors or warnings in this structure.
16224%
16225*/
16226MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16227  const char *window,const char *filename,ExceptionInfo *exception)
16228{
16229  assert(image_info != (const ImageInfo *) NULL);
16230  assert(image_info->signature == MagickSignature);
16231  assert(filename != (char *) NULL);
16232  (void) window;
16233  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16234  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16235    "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image_info->filename);
16236  return(MagickFalse);
16237}
16238#endif
16239