display.c revision 3bab5f10a58a7e206557cb465d63aa6dffa6ed48
1a05814398202c4147a5e3f28474830ec0a9a0a90Jay Srinivasan/*
2f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson%                                                                             %
4f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson%                                                                             %
5f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson%                                                                             %
6f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson%               DDDD   IIIII  SSSSS  PPPP   L       AAA   Y   Y               %
7c4e92af85ac2bfd90ab82c0b13bb0041595a0affBill Richardson%               D   D    I    SS     P   P  L      A   A   Y Y                %
8f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson%               D   D    I     SSS   PPPP   L      AAAAA    Y                 %
90c3ba249abb1dc60f5ebabccf84ff13206440b83Bill Richardson%               D   D    I       SS  P      L      A   A    Y                 %
100c3ba249abb1dc60f5ebabccf84ff13206440b83Bill Richardson%               DDDD   IIIII  SSSSS  P      LLLLL  A   A    Y                 %
110c3ba249abb1dc60f5ebabccf84ff13206440b83Bill Richardson%                                                                             %
12f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson%                                                                             %
134cb5497984642b8cbd592c14cb1912a787b2d4d7Bill Richardson%        MagickCore Methods to Interactively Display and Edit an Image        %
144cb5497984642b8cbd592c14cb1912a787b2d4d7Bill Richardson%                                                                             %
15f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson%                             Software Design                                 %
16f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson%                                  Cristy                                     %
17f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson%                                July 1992                                    %
18f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson%                                                                             %
19f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson%                                                                             %
20ab899591808dd3e5f955ab7693b54a83389cd35fNam T. Nguyen%  Copyright 1999-2015 ImageMagick Studio LLC, a non-profit organization      %
21ab899591808dd3e5f955ab7693b54a83389cd35fNam T. Nguyen%  dedicated to making software imaging solutions freely available.           %
22ab899591808dd3e5f955ab7693b54a83389cd35fNam T. Nguyen%                                                                             %
23f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson%  You may not use this file except in compliance with the License.  You may  %
24f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson%  obtain a copy of the License at                                            %
25f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson%                                                                             %
26f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson%    http://www.imagemagick.org/script/license.php                            %
27f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson%                                                                             %
28f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson%  Unless required by applicable law or agreed to in writing, software        %
29f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson%  distributed under the License is distributed on an "AS IS" BASIS,          %
30f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson%  See the License for the specific language governing permissions and        %
32f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson%  limitations under the License.                                             %
33f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson%                                                                             %
34f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson%
36455b119dc0f3cd5538c4a53f0e2ab38534bcdf14Louis Yung-Chieh Lo%
37f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson*/
38f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson
39f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson/*
40f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson  Include declarations.
41a05814398202c4147a5e3f28474830ec0a9a0a90Jay Srinivasan*/
42a05814398202c4147a5e3f28474830ec0a9a0a90Jay Srinivasan#include "MagickCore/studio.h"
43f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson#include "MagickCore/artifact.h"
44f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson#include "MagickCore/attribute.h"
45f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson#include "MagickCore/blob.h"
46f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson#include "MagickCore/cache.h"
47f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson#include "MagickCore/cache-private.h"
48f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson#include "MagickCore/channel.h"
49ab899591808dd3e5f955ab7693b54a83389cd35fNam T. Nguyen#include "MagickCore/client.h"
50f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson#include "MagickCore/color.h"
51f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson#include "MagickCore/colorspace.h"
52f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson#include "MagickCore/composite.h"
53ab899591808dd3e5f955ab7693b54a83389cd35fNam T. Nguyen#include "MagickCore/constitute.h"
54ab899591808dd3e5f955ab7693b54a83389cd35fNam T. Nguyen#include "MagickCore/decorate.h"
55ab899591808dd3e5f955ab7693b54a83389cd35fNam T. Nguyen#include "MagickCore/delegate.h"
56ab899591808dd3e5f955ab7693b54a83389cd35fNam T. Nguyen#include "MagickCore/display.h"
57ab899591808dd3e5f955ab7693b54a83389cd35fNam T. Nguyen#include "MagickCore/display-private.h"
58ab899591808dd3e5f955ab7693b54a83389cd35fNam T. Nguyen#include "MagickCore/distort.h"
59ab899591808dd3e5f955ab7693b54a83389cd35fNam T. Nguyen#include "MagickCore/draw.h"
60ab899591808dd3e5f955ab7693b54a83389cd35fNam T. Nguyen#include "MagickCore/effect.h"
61f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson#include "MagickCore/enhance.h"
62a05814398202c4147a5e3f28474830ec0a9a0a90Jay Srinivasan#include "MagickCore/exception.h"
63f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson#include "MagickCore/exception-private.h"
64f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson#include "MagickCore/fx.h"
65a05814398202c4147a5e3f28474830ec0a9a0a90Jay Srinivasan#include "MagickCore/geometry.h"
66f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson#include "MagickCore/image.h"
67f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson#include "MagickCore/image-private.h"
68a05814398202c4147a5e3f28474830ec0a9a0a90Jay Srinivasan#include "MagickCore/list.h"
69f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson#include "MagickCore/log.h"
70f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson#include "MagickCore/magick.h"
71a05814398202c4147a5e3f28474830ec0a9a0a90Jay Srinivasan#include "MagickCore/memory_.h"
72f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson#include "MagickCore/monitor.h"
73f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson#include "MagickCore/monitor-private.h"
74f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson#include "MagickCore/montage.h"
75f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson#include "MagickCore/nt-base-private.h"
76f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson#include "MagickCore/option.h"
77f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson#include "MagickCore/paint.h"
78f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson#include "MagickCore/pixel.h"
79f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson#include "MagickCore/pixel-accessor.h"
80f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson#include "MagickCore/property.h"
81f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson#include "MagickCore/quantum.h"
82f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson#include "MagickCore/quantum-private.h"
83f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson#include "MagickCore/resize.h"
84f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson#include "MagickCore/resource_.h"
85f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson#include "MagickCore/shear.h"
86f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson#include "MagickCore/segment.h"
87a05814398202c4147a5e3f28474830ec0a9a0a90Jay Srinivasan#include "MagickCore/statistic.h"
88f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson#include "MagickCore/string_.h"
89f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson#include "MagickCore/string-private.h"
90455b119dc0f3cd5538c4a53f0e2ab38534bcdf14Louis Yung-Chieh Lo#include "MagickCore/transform.h"
91455b119dc0f3cd5538c4a53f0e2ab38534bcdf14Louis Yung-Chieh Lo#include "MagickCore/threshold.h"
92455b119dc0f3cd5538c4a53f0e2ab38534bcdf14Louis Yung-Chieh Lo#include "MagickCore/utility.h"
93455b119dc0f3cd5538c4a53f0e2ab38534bcdf14Louis Yung-Chieh Lo#include "MagickCore/utility-private.h"
94f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson#include "MagickCore/version.h"
95f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson#include "MagickCore/widget.h"
96f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson#include "MagickCore/widget-private.h"
97f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson#include "MagickCore/xwindow.h"
98f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson#include "MagickCore/xwindow-private.h"
99f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson
100f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson#if defined(MAGICKCORE_X11_DELEGATE)
101f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson/*
102f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson  Define declarations.
103f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson*/
104f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson#define MaxColors  MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
105f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson
106f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson/*
107f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson  Constant declarations.
108f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson*/
109f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardsonstatic const unsigned char
110f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson  HighlightBitmap[8] =
111f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson  {
112f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson    0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
113f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson  },
114f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson  OpaqueBitmap[8] =
115f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson  {
116f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
117f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson  },
118f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson  ShadowBitmap[8] =
119f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson  {
120f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
121f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson  };
122250549d3e742cddaf72b4f53d5739e54faf5db96Jay Srinivasan
123f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardsonstatic const char
1243f806a2abf07d7b801852a4a6f3a9080a4b5c427Bill Richardson  *PageSizes[] =
125f1372d9109d638fbb1a177a89ebaf64e7ee0637eBill Richardson  {
126    "Letter",
127    "Tabloid",
128    "Ledger",
129    "Legal",
130    "Statement",
131    "Executive",
132    "A3",
133    "A4",
134    "A5",
135    "B4",
136    "B5",
137    "Folio",
138    "Quarto",
139    "10x14",
140    (char *) NULL
141  };
142
143/*
144  Help widget declarations.
145*/
146static const char
147  *ImageAnnotateHelp[] =
148  {
149    "In annotate mode, the Command widget has these options:",
150    "",
151    "    Font Name",
152    "      fixed",
153    "      variable",
154    "      5x8",
155    "      6x10",
156    "      7x13bold",
157    "      8x13bold",
158    "      9x15bold",
159    "      10x20",
160    "      12x24",
161    "      Browser...",
162    "    Font Color",
163    "      black",
164    "      blue",
165    "      cyan",
166    "      green",
167    "      gray",
168    "      red",
169    "      magenta",
170    "      yellow",
171    "      white",
172    "      transparent",
173    "      Browser...",
174    "    Font Color",
175    "      black",
176    "      blue",
177    "      cyan",
178    "      green",
179    "      gray",
180    "      red",
181    "      magenta",
182    "      yellow",
183    "      white",
184    "      transparent",
185    "      Browser...",
186    "    Rotate Text",
187    "      -90",
188    "      -45",
189    "      -30",
190    "      0",
191    "      30",
192    "      45",
193    "      90",
194    "      180",
195    "      Dialog...",
196    "    Help",
197    "    Dismiss",
198    "",
199    "Choose a font name from the Font Name sub-menu.  Additional",
200    "font names can be specified with the font browser.  You can",
201    "change the menu names by setting the X resources font1",
202    "through font9.",
203    "",
204    "Choose a font color from the Font Color sub-menu.",
205    "Additional font colors can be specified with the color",
206    "browser.  You can change the menu colors by setting the X",
207    "resources pen1 through pen9.",
208    "",
209    "If you select the color browser and press Grab, you can",
210    "choose the font color by moving the pointer to the desired",
211    "color on the screen and press any button.",
212    "",
213    "If you choose to rotate the text, choose Rotate Text from the",
214    "menu and select an angle.  Typically you will only want to",
215    "rotate one line of text at a time.  Depending on the angle you",
216    "choose, subsequent lines may end up overwriting each other.",
217    "",
218    "Choosing a font and its color is optional.  The default font",
219    "is fixed and the default color is black.  However, you must",
220    "choose a location to begin entering text and press button 1.",
221    "An underscore character will appear at the location of the",
222    "pointer.  The cursor changes to a pencil to indicate you are",
223    "in text mode.  To exit immediately, press Dismiss.",
224    "",
225    "In text mode, any key presses will display the character at",
226    "the location of the underscore and advance the underscore",
227    "cursor.  Enter your text and once completed press Apply to",
228    "finish your image annotation.  To correct errors press BACK",
229    "SPACE.  To delete an entire line of text, press DELETE.  Any",
230    "text that exceeds the boundaries of the image window is",
231    "automagically continued onto the next line.",
232    "",
233    "The actual color you request for the font is saved in the",
234    "image.  However, the color that appears in your image window",
235    "may be different.  For example, on a monochrome screen the",
236    "text will appear black or white even if you choose the color",
237    "red as the font color.  However, the image saved to a file",
238    "with -write is written with red lettering.  To assure the",
239    "correct color text in the final image, any PseudoClass image",
240    "is promoted to DirectClass (see miff(5)).  To force a",
241    "PseudoClass image to remain PseudoClass, use -colors.",
242    (char *) NULL,
243  },
244  *ImageChopHelp[] =
245  {
246    "In chop mode, the Command widget has these options:",
247    "",
248    "    Direction",
249    "      horizontal",
250    "      vertical",
251    "    Help",
252    "    Dismiss",
253    "",
254    "If the you choose the horizontal direction (this the",
255    "default), the area of the image between the two horizontal",
256    "endpoints of the chop line is removed.  Otherwise, the area",
257    "of the image between the two vertical endpoints of the chop",
258    "line is removed.",
259    "",
260    "Select a location within the image window to begin your chop,",
261    "press and hold any button.  Next, move the pointer to",
262    "another location in the image.  As you move a line will",
263    "connect the initial location and the pointer.  When you",
264    "release the button, the area within the image to chop is",
265    "determined by which direction you choose from the Command",
266    "widget.",
267    "",
268    "To cancel the image chopping, move the pointer back to the",
269    "starting point of the line and release the button.",
270    (char *) NULL,
271  },
272  *ImageColorEditHelp[] =
273  {
274    "In color edit mode, the Command widget has these options:",
275    "",
276    "    Method",
277    "      point",
278    "      replace",
279    "      floodfill",
280    "      filltoborder",
281    "      reset",
282    "    Pixel Color",
283    "      black",
284    "      blue",
285    "      cyan",
286    "      green",
287    "      gray",
288    "      red",
289    "      magenta",
290    "      yellow",
291    "      white",
292    "      Browser...",
293    "    Border Color",
294    "      black",
295    "      blue",
296    "      cyan",
297    "      green",
298    "      gray",
299    "      red",
300    "      magenta",
301    "      yellow",
302    "      white",
303    "      Browser...",
304    "    Fuzz",
305    "      0%",
306    "      2%",
307    "      5%",
308    "      10%",
309    "      15%",
310    "      Dialog...",
311    "    Undo",
312    "    Help",
313    "    Dismiss",
314    "",
315    "Choose a color editing method from the Method sub-menu",
316    "of the Command widget.  The point method recolors any pixel",
317    "selected with the pointer until the button is released.  The",
318    "replace method recolors any pixel that matches the color of",
319    "the pixel you select with a button press.  Floodfill recolors",
320    "any pixel that matches the color of the pixel you select with",
321    "a button press and is a neighbor.  Whereas filltoborder recolors",
322    "any neighbor pixel that is not the border color.  Finally reset",
323    "changes the entire image to the designated color.",
324    "",
325    "Next, choose a pixel color from the Pixel Color sub-menu.",
326    "Additional pixel colors can be specified with the color",
327    "browser.  You can change the menu colors by setting the X",
328    "resources pen1 through pen9.",
329    "",
330    "Now press button 1 to select a pixel within the image window",
331    "to change its color.  Additional pixels may be recolored as",
332    "prescribed by the method you choose.",
333    "",
334    "If the Magnify widget is mapped, it can be helpful in positioning",
335    "your pointer within the image (refer to button 2).",
336    "",
337    "The actual color you request for the pixels is saved in the",
338    "image.  However, the color that appears in your image window",
339    "may be different.  For example, on a monochrome screen the",
340    "pixel will appear black or white even if you choose the",
341    "color red as the pixel color.  However, the image saved to a",
342    "file with -write is written with red pixels.  To assure the",
343    "correct color text in the final image, any PseudoClass image",
344    "is promoted to DirectClass (see miff(5)).  To force a",
345    "PseudoClass image to remain PseudoClass, use -colors.",
346    (char *) NULL,
347  },
348  *ImageCompositeHelp[] =
349  {
350    "First a widget window is displayed requesting you to enter an",
351    "image name. Press Composite, Grab or type a file name.",
352    "Press Cancel if you choose not to create a composite image.",
353    "When you choose Grab, move the pointer to the desired window",
354    "and press any button.",
355    "",
356    "If the Composite image does not have any matte information,",
357    "you are informed and the file browser is displayed again.",
358    "Enter the name of a mask image.  The image is typically",
359    "grayscale and the same size as the composite image.  If the",
360    "image is not grayscale, it is converted to grayscale and the",
361    "resulting intensities are used as matte information.",
362    "",
363    "A small window appears showing the location of the cursor in",
364    "the image window. You are now in composite mode.  To exit",
365    "immediately, press Dismiss.  In composite mode, the Command",
366    "widget has these options:",
367    "",
368    "    Operators",
369    "      Over",
370    "      In",
371    "      Out",
372    "      Atop",
373    "      Xor",
374    "      Plus",
375    "      Minus",
376    "      Add",
377    "      Subtract",
378    "      Difference",
379    "      Multiply",
380    "      Bumpmap",
381    "      Copy",
382    "      CopyRed",
383    "      CopyGreen",
384    "      CopyBlue",
385    "      CopyOpacity",
386    "      Clear",
387    "    Dissolve",
388    "    Displace",
389    "    Help",
390    "    Dismiss",
391    "",
392    "Choose a composite operation from the Operators sub-menu of",
393    "the Command widget.  How each operator behaves is described",
394    "below.  Image window is the image currently displayed on",
395    "your X server and image is the image obtained with the File",
396    "Browser widget.",
397    "",
398    "Over     The result is the union of the two image shapes,",
399    "         with image obscuring image window in the region of",
400    "         overlap.",
401    "",
402    "In       The result is simply image cut by the shape of",
403    "         image window.  None of the image data of image",
404    "         window is in the result.",
405    "",
406    "Out      The resulting image is image with the shape of",
407    "         image window cut out.",
408    "",
409    "Atop     The result is the same shape as image image window,",
410    "         with image obscuring image window where the image",
411    "         shapes overlap.  Note this differs from over",
412    "         because the portion of image outside image window's",
413    "         shape does not appear in the result.",
414    "",
415    "Xor      The result is the image data from both image and",
416    "         image window that is outside the overlap region.",
417    "         The overlap region is blank.",
418    "",
419    "Plus     The result is just the sum of the image data.",
420    "         Output values are cropped to QuantumRange (no overflow).",
421    "",
422    "Minus    The result of image - image window, with underflow",
423    "         cropped to zero.",
424    "",
425    "Add      The result of image + image window, with overflow",
426    "         wrapping around (mod 256).",
427    "",
428    "Subtract The result of image - image window, with underflow",
429    "         wrapping around (mod 256).  The add and subtract",
430    "         operators can be used to perform reversible",
431    "         transformations.",
432    "",
433    "Difference",
434    "         The result of abs(image - image window).  This",
435    "         useful for comparing two very similar images.",
436    "",
437    "Multiply",
438    "         The result of image * image window.  This",
439    "         useful for the creation of drop-shadows.",
440    "",
441    "Bumpmap  The result of surface normals from image * image",
442    "         window.",
443    "",
444    "Copy     The resulting image is image window replaced with",
445    "         image.  Here the matte information is ignored.",
446    "",
447    "CopyRed  The red layer of the image window is replace with",
448    "         the red layer of the image.  The other layers are",
449    "         untouched.",
450    "",
451    "CopyGreen",
452    "         The green layer of the image window is replace with",
453    "         the green layer of the image.  The other layers are",
454    "         untouched.",
455    "",
456    "CopyBlue The blue layer of the image window is replace with",
457    "         the blue layer of the image.  The other layers are",
458    "         untouched.",
459    "",
460    "CopyOpacity",
461    "         The matte layer of the image window is replace with",
462    "         the matte layer of the image.  The other layers are",
463    "         untouched.",
464    "",
465    "The image compositor requires a matte, or alpha channel in",
466    "the image for some operations.  This extra channel usually",
467    "defines a mask which represents a sort of a cookie-cutter",
468    "for the image.  This the case when matte is opaque (full",
469    "coverage) for pixels inside the shape, zero outside, and",
470    "between 0 and QuantumRange on the boundary.  If image does not",
471    "have a matte channel, it is initialized with 0 for any pixel",
472    "matching in color to pixel location (0,0), otherwise QuantumRange.",
473    "",
474    "If you choose Dissolve, the composite operator becomes Over.  The",
475    "image matte channel percent transparency is initialized to factor.",
476    "The image window is initialized to (100-factor). Where factor is the",
477    "value you specify in the Dialog widget.",
478    "",
479    "Displace shifts the image pixels as defined by a displacement",
480    "map.  With this option, image is used as a displacement map.",
481    "Black, within the displacement map, is a maximum positive",
482    "displacement.  White is a maximum negative displacement and",
483    "middle gray is neutral.  The displacement is scaled to determine",
484    "the pixel shift.  By default, the displacement applies in both the",
485    "horizontal and vertical directions.  However, if you specify a mask,",
486    "image is the horizontal X displacement and mask the vertical Y",
487    "displacement.",
488    "",
489    "Note that matte information for image window is not retained",
490    "for colormapped X server visuals (e.g. StaticColor,",
491    "StaticColor, GrayScale, PseudoColor).  Correct compositing",
492    "behavior may require a TrueColor or DirectColor visual or a",
493    "Standard Colormap.",
494    "",
495    "Choosing a composite operator is optional.  The default",
496    "operator is replace.  However, you must choose a location to",
497    "composite your image and press button 1.  Press and hold the",
498    "button before releasing and an outline of the image will",
499    "appear to help you identify your location.",
500    "",
501    "The actual colors of the composite image is saved.  However,",
502    "the color that appears in image window may be different.",
503    "For example, on a monochrome screen image window will appear",
504    "black or white even though your composited image may have",
505    "many colors.  If the image is saved to a file it is written",
506    "with the correct colors.  To assure the correct colors are",
507    "saved in the final image, any PseudoClass image is promoted",
508    "to DirectClass (see miff(5)).  To force a PseudoClass image",
509    "to remain PseudoClass, use -colors.",
510    (char *) NULL,
511  },
512  *ImageCutHelp[] =
513  {
514    "In cut mode, the Command widget has these options:",
515    "",
516    "    Help",
517    "    Dismiss",
518    "",
519    "To define a cut region, press button 1 and drag.  The",
520    "cut region is defined by a highlighted rectangle that",
521    "expands or contracts as it follows the pointer.  Once you",
522    "are satisfied with the cut region, release the button.",
523    "You are now in rectify mode.  In rectify mode, the Command",
524    "widget has these options:",
525    "",
526    "    Cut",
527    "    Help",
528    "    Dismiss",
529    "",
530    "You can make adjustments by moving the pointer to one of the",
531    "cut rectangle corners, pressing a button, and dragging.",
532    "Finally, press Cut to commit your copy region.  To",
533    "exit without cutting the image, press Dismiss.",
534    (char *) NULL,
535  },
536  *ImageCopyHelp[] =
537  {
538    "In copy mode, the Command widget has these options:",
539    "",
540    "    Help",
541    "    Dismiss",
542    "",
543    "To define a copy region, press button 1 and drag.  The",
544    "copy region is defined by a highlighted rectangle that",
545    "expands or contracts as it follows the pointer.  Once you",
546    "are satisfied with the copy region, release the button.",
547    "You are now in rectify mode.  In rectify mode, the Command",
548    "widget has these options:",
549    "",
550    "    Copy",
551    "    Help",
552    "    Dismiss",
553    "",
554    "You can make adjustments by moving the pointer to one of the",
555    "copy rectangle corners, pressing a button, and dragging.",
556    "Finally, press Copy to commit your copy region.  To",
557    "exit without copying the image, press Dismiss.",
558    (char *) NULL,
559  },
560  *ImageCropHelp[] =
561  {
562    "In crop mode, the Command widget has these options:",
563    "",
564    "    Help",
565    "    Dismiss",
566    "",
567    "To define a cropping region, press button 1 and drag.  The",
568    "cropping region is defined by a highlighted rectangle that",
569    "expands or contracts as it follows the pointer.  Once you",
570    "are satisfied with the cropping region, release the button.",
571    "You are now in rectify mode.  In rectify mode, the Command",
572    "widget has these options:",
573    "",
574    "    Crop",
575    "    Help",
576    "    Dismiss",
577    "",
578    "You can make adjustments by moving the pointer to one of the",
579    "cropping rectangle corners, pressing a button, and dragging.",
580    "Finally, press Crop to commit your cropping region.  To",
581    "exit without cropping the image, press Dismiss.",
582    (char *) NULL,
583  },
584  *ImageDrawHelp[] =
585  {
586    "The cursor changes to a crosshair to indicate you are in",
587    "draw mode.  To exit immediately, press Dismiss.  In draw mode,",
588    "the Command widget has these options:",
589    "",
590    "    Element",
591    "      point",
592    "      line",
593    "      rectangle",
594    "      fill rectangle",
595    "      circle",
596    "      fill circle",
597    "      ellipse",
598    "      fill ellipse",
599    "      polygon",
600    "      fill polygon",
601    "    Color",
602    "      black",
603    "      blue",
604    "      cyan",
605    "      green",
606    "      gray",
607    "      red",
608    "      magenta",
609    "      yellow",
610    "      white",
611    "      transparent",
612    "      Browser...",
613    "    Stipple",
614    "      Brick",
615    "      Diagonal",
616    "      Scales",
617    "      Vertical",
618    "      Wavy",
619    "      Translucent",
620    "      Opaque",
621    "      Open...",
622    "    Width",
623    "      1",
624    "      2",
625    "      4",
626    "      8",
627    "      16",
628    "      Dialog...",
629    "    Undo",
630    "    Help",
631    "    Dismiss",
632    "",
633    "Choose a drawing primitive from the Element sub-menu.",
634    "",
635    "Choose a color from the Color sub-menu.  Additional",
636    "colors can be specified with the color browser.",
637    "",
638    "If you choose the color browser and press Grab, you can",
639    "select the color by moving the pointer to the desired",
640    "color on the screen and press any button.  The transparent",
641    "color updates the image matte channel and is useful for",
642    "image compositing.",
643    "",
644    "Choose a stipple, if appropriate, from the Stipple sub-menu.",
645    "Additional stipples can be specified with the file browser.",
646    "Stipples obtained from the file browser must be on disk in the",
647    "X11 bitmap format.",
648    "",
649    "Choose a width, if appropriate, from the Width sub-menu.  To",
650    "choose a specific width select the Dialog widget.",
651    "",
652    "Choose a point in the Image window and press button 1 and",
653    "hold.  Next, move the pointer to another location in the",
654    "image.  As you move, a line connects the initial location and",
655    "the pointer.  When you release the button, the image is",
656    "updated with the primitive you just drew.  For polygons, the",
657    "image is updated when you press and release the button without",
658    "moving the pointer.",
659    "",
660    "To cancel image drawing, move the pointer back to the",
661    "starting point of the line and release the button.",
662    (char *) NULL,
663  },
664  *DisplayHelp[] =
665  {
666    "BUTTONS",
667    "  The effects of each button press is described below.  Three",
668    "  buttons are required.  If you have a two button mouse,",
669    "  button 1 and 3 are returned.  Press ALT and button 3 to",
670    "  simulate button 2.",
671    "",
672    "  1    Press this button to map or unmap the Command widget.",
673    "",
674    "  2    Press and drag to define a region of the image to",
675    "       magnify.",
676    "",
677    "  3    Press and drag to choose from a select set of commands.",
678    "       This button behaves differently if the image being",
679    "       displayed is a visual image directory.  Here, choose a",
680    "       particular tile of the directory and press this button and",
681    "       drag to select a command from a pop-up menu.  Choose from",
682    "       these menu items:",
683    "",
684    "           Open",
685    "           Next",
686    "           Former",
687    "           Delete",
688    "           Update",
689    "",
690    "       If you choose Open, the image represented by the tile is",
691    "       displayed.  To return to the visual image directory, choose",
692    "       Next from the Command widget.  Next and Former moves to the",
693    "       next or former image respectively.  Choose Delete to delete",
694    "       a particular image tile.  Finally, choose Update to",
695    "       synchronize all the image tiles with their respective",
696    "       images.",
697    "",
698    "COMMAND WIDGET",
699    "  The Command widget lists a number of sub-menus and commands.",
700    "  They are",
701    "",
702    "      File",
703    "        Open...",
704    "        Next",
705    "        Former",
706    "        Select...",
707    "        Save...",
708    "        Print...",
709    "        Delete...",
710    "        New...",
711    "        Visual Directory...",
712    "        Quit",
713    "      Edit",
714    "        Undo",
715    "        Redo",
716    "        Cut",
717    "        Copy",
718    "        Paste",
719    "      View",
720    "        Half Size",
721    "        Original Size",
722    "        Double Size",
723    "        Resize...",
724    "        Apply",
725    "        Refresh",
726    "        Restore",
727    "      Transform",
728    "        Crop",
729    "        Chop",
730    "        Flop",
731    "        Flip",
732    "        Rotate Right",
733    "        Rotate Left",
734    "        Rotate...",
735    "        Shear...",
736    "        Roll...",
737    "        Trim Edges",
738    "      Enhance",
739    "        Brightness...",
740    "        Saturation...",
741    "        Hue...",
742    "        Gamma...",
743    "        Sharpen...",
744    "        Dull",
745    "        Contrast Stretch...",
746    "        Sigmoidal Contrast...",
747    "        Normalize",
748    "        Equalize",
749    "        Negate",
750    "        Grayscale",
751    "        Map...",
752    "        Quantize...",
753    "      Effects",
754    "        Despeckle",
755    "        Emboss",
756    "        Reduce Noise",
757    "        Add Noise",
758    "        Sharpen...",
759    "        Blur...",
760    "        Threshold...",
761    "        Edge Detect...",
762    "        Spread...",
763    "        Shade...",
764    "        Painting...",
765    "        Segment...",
766    "      F/X",
767    "        Solarize...",
768    "        Sepia Tone...",
769    "        Swirl...",
770    "        Implode...",
771    "        Vignette...",
772    "        Wave...",
773    "        Oil Painting...",
774    "        Charcoal Drawing...",
775    "      Image Edit",
776    "        Annotate...",
777    "        Draw...",
778    "        Color...",
779    "        Matte...",
780    "        Composite...",
781    "        Add Border...",
782    "        Add Frame...",
783    "        Comment...",
784    "        Launch...",
785    "        Region of Interest...",
786    "      Miscellany",
787    "        Image Info",
788    "        Zoom Image",
789    "        Show Preview...",
790    "        Show Histogram",
791    "        Show Matte",
792    "        Background...",
793    "        Slide Show",
794    "        Preferences...",
795    "      Help",
796    "        Overview",
797    "        Browse Documentation",
798    "        About Display",
799    "",
800    "  Menu items with a indented triangle have a sub-menu.  They",
801    "  are represented above as the indented items.  To access a",
802    "  sub-menu item, move the pointer to the appropriate menu and",
803    "  press a button and drag.  When you find the desired sub-menu",
804    "  item, release the button and the command is executed.  Move",
805    "  the pointer away from the sub-menu if you decide not to",
806    "  execute a particular command.",
807    "",
808    "KEYBOARD ACCELERATORS",
809    "  Accelerators are one or two key presses that effect a",
810    "  particular command.  The keyboard accelerators that",
811    "  display(1) understands is:",
812    "",
813    "  Ctl+O     Press to open an image from a file.",
814    "",
815    "  space     Press to display the next image.",
816    "",
817    "            If the image is a multi-paged document such as a Postscript",
818    "            document, you can skip ahead several pages by preceding",
819    "            this command with a number.  For example to display the",
820    "            third page beyond the current page, press 3<space>.",
821    "",
822    "  backspace Press to display the former image.",
823    "",
824    "            If the image is a multi-paged document such as a Postscript",
825    "            document, you can skip behind several pages by preceding",
826    "            this command with a number.  For example to display the",
827    "            third page preceding the current page, press 3<backspace>.",
828    "",
829    "  Ctl+S     Press to write the image to a file.",
830    "",
831    "  Ctl+P     Press to print the image to a Postscript printer.",
832    "",
833    "  Ctl+D     Press to delete an image file.",
834    "",
835    "  Ctl+N     Press to create a blank canvas.",
836    "",
837    "  Ctl+Q     Press to discard all images and exit program.",
838    "",
839    "  Ctl+Z     Press to undo last image transformation.",
840    "",
841    "  Ctl+R     Press to redo last image transformation.",
842    "",
843    "  Ctl+X     Press to cut a region of the image.",
844    "",
845    "  Ctl+C     Press to copy a region of the image.",
846    "",
847    "  Ctl+V     Press to paste a region to the image.",
848    "",
849    "  <         Press to half the image size.",
850    "",
851    "  -         Press to return to the original image size.",
852    "",
853    "  >         Press to double the image size.",
854    "",
855    "  %         Press to resize the image to a width and height you",
856    "            specify.",
857    "",
858    "Cmd-A       Press to make any image transformations permanent."
859    "",
860    "            By default, any image size transformations are applied",
861    "            to the original image to create the image displayed on",
862    "            the X server.  However, the transformations are not",
863    "            permanent (i.e. the original image does not change",
864    "            size only the X image does).  For example, if you",
865    "            press > the X image will appear to double in size,",
866    "            but the original image will in fact remain the same size.",
867    "            To force the original image to double in size, press >",
868    "            followed by Cmd-A.",
869    "",
870    "  @         Press to refresh the image window.",
871    "",
872    "  C         Press to cut out a rectangular region of the image.",
873    "",
874    "  [         Press to chop the image.",
875    "",
876    "  H         Press to flop image in the horizontal direction.",
877    "",
878    "  V         Press to flip image in the vertical direction.",
879    "",
880    "  /         Press to rotate the image 90 degrees clockwise.",
881    "",
882    " \\         Press to rotate the image 90 degrees counter-clockwise.",
883    "",
884    "  *         Press to rotate the image the number of degrees you",
885    "            specify.",
886    "",
887    "  S         Press to shear the image the number of degrees you",
888    "            specify.",
889    "",
890    "  R         Press to roll the image.",
891    "",
892    "  T         Press to trim the image edges.",
893    "",
894    "  Shft-H    Press to vary the image hue.",
895    "",
896    "  Shft-S    Press to vary the color saturation.",
897    "",
898    "  Shft-L    Press to vary the color brightness.",
899    "",
900    "  Shft-G    Press to gamma correct the image.",
901    "",
902    "  Shft-C    Press to sharpen the image contrast.",
903    "",
904    "  Shft-Z    Press to dull the image contrast.",
905    "",
906    "  =         Press to perform histogram equalization on the image.",
907    "",
908    "  Shft-N    Press to perform histogram normalization on the image.",
909    "",
910    "  Shft-~    Press to negate the colors of the image.",
911    "",
912    "  .         Press to convert the image colors to gray.",
913    "",
914    "  Shft-#    Press to set the maximum number of unique colors in the",
915    "            image.",
916    "",
917    "  F2        Press to reduce the speckles in an image.",
918    "",
919    "  F3        Press to eliminate peak noise from an image.",
920    "",
921    "  F4        Press to add noise to an image.",
922    "",
923    "  F5        Press to sharpen an image.",
924    "",
925    "  F6        Press to delete an image file.",
926    "",
927    "  F7        Press to threshold the image.",
928    "",
929    "  F8        Press to detect edges within an image.",
930    "",
931    "  F9        Press to emboss an image.",
932    "",
933    "  F10       Press to displace pixels by a random amount.",
934    "",
935    "  F11       Press to negate all pixels above the threshold level.",
936    "",
937    "  F12       Press to shade the image using a distant light source.",
938    "",
939    "  F13       Press to lighten or darken image edges to create a 3-D effect.",
940    "",
941    "  F14       Press to segment the image by color.",
942    "",
943    "  Meta-S    Press to swirl image pixels about the center.",
944    "",
945    "  Meta-I    Press to implode image pixels about the center.",
946    "",
947    "  Meta-W    Press to alter an image along a sine wave.",
948    "",
949    "  Meta-P    Press to simulate an oil painting.",
950    "",
951    "  Meta-C    Press to simulate a charcoal drawing.",
952    "",
953    "  Alt-A     Press to annotate the image with text.",
954    "",
955    "  Alt-D     Press to draw on an image.",
956    "",
957    "  Alt-P     Press to edit an image pixel color.",
958    "",
959    "  Alt-M     Press to edit the image matte information.",
960    "",
961    "  Alt-V     Press to composite the image with another.",
962    "",
963    "  Alt-B     Press to add a border to the image.",
964    "",
965    "  Alt-F     Press to add an ornamental border to the image.",
966    "",
967    "  Alt-Shft-!",
968    "            Press to add an image comment.",
969    "",
970    "  Ctl-A     Press to apply image processing techniques to a region",
971    "            of interest.",
972    "",
973    "  Shft-?    Press to display information about the image.",
974    "",
975    "  Shft-+    Press to map the zoom image window.",
976    "",
977    "  Shft-P    Press to preview an image enhancement, effect, or f/x.",
978    "",
979    "  F1        Press to display helpful information about display(1).",
980    "",
981    "  Find      Press to browse documentation about ImageMagick.",
982    "",
983    "  1-9       Press to change the level of magnification.",
984    "",
985    "  Use the arrow keys to move the image one pixel up, down,",
986    "  left, or right within the magnify window.  Be sure to first",
987    "  map the magnify window by pressing button 2.",
988    "",
989    "  Press ALT and one of the arrow keys to trim off one pixel",
990    "  from any side of the image.",
991    (char *) NULL,
992  },
993  *ImageMatteEditHelp[] =
994  {
995    "Matte information within an image is useful for some",
996    "operations such as image compositing (See IMAGE",
997    "COMPOSITING).  This extra channel usually defines a mask",
998    "which represents a sort of a cookie-cutter for the image.",
999    "This the case when matte is opaque (full coverage) for",
1000    "pixels inside the shape, zero outside, and between 0 and",
1001    "QuantumRange on the boundary.",
1002    "",
1003    "A small window appears showing the location of the cursor in",
1004    "the image window. You are now in matte edit mode.  To exit",
1005    "immediately, press Dismiss.  In matte edit mode, the Command",
1006    "widget has these options:",
1007    "",
1008    "    Method",
1009    "      point",
1010    "      replace",
1011    "      floodfill",
1012    "      filltoborder",
1013    "      reset",
1014    "    Border Color",
1015    "      black",
1016    "      blue",
1017    "      cyan",
1018    "      green",
1019    "      gray",
1020    "      red",
1021    "      magenta",
1022    "      yellow",
1023    "      white",
1024    "      Browser...",
1025    "    Fuzz",
1026    "      0%",
1027    "      2%",
1028    "      5%",
1029    "      10%",
1030    "      15%",
1031    "      Dialog...",
1032    "    Matte",
1033    "      Opaque",
1034    "      Transparent",
1035    "      Dialog...",
1036    "    Undo",
1037    "    Help",
1038    "    Dismiss",
1039    "",
1040    "Choose a matte editing method from the Method sub-menu of",
1041    "the Command widget.  The point method changes the matte value",
1042    "of any pixel selected with the pointer until the button is",
1043    "is released.  The replace method changes the matte value of",
1044    "any pixel that matches the color of the pixel you select with",
1045    "a button press.  Floodfill changes the matte value of any pixel",
1046    "that matches the color of the pixel you select with a button",
1047    "press and is a neighbor.  Whereas filltoborder changes the matte",
1048    "value any neighbor pixel that is not the border color.  Finally",
1049    "reset changes the entire image to the designated matte value.",
1050    "",
1051    "Choose Matte Value and pick Opaque or Transarent.  For other values",
1052    "select the Dialog entry.  Here a dialog appears requesting a matte",
1053    "value.  The value you select is assigned as the opacity value of the",
1054    "selected pixel or pixels.",
1055    "",
1056    "Now, press any button to select a pixel within the image",
1057    "window to change its matte value.",
1058    "",
1059    "If the Magnify widget is mapped, it can be helpful in positioning",
1060    "your pointer within the image (refer to button 2).",
1061    "",
1062    "Matte information is only valid in a DirectClass image.",
1063    "Therefore, any PseudoClass image is promoted to DirectClass",
1064    "(see miff(5)).  Note that matte information for PseudoClass",
1065    "is not retained for colormapped X server visuals (e.g.",
1066    "StaticColor, StaticColor, GrayScale, PseudoColor) unless you",
1067    "immediately save your image to a file (refer to Write).",
1068    "Correct matte editing behavior may require a TrueColor or",
1069    "DirectColor visual or a Standard Colormap.",
1070    (char *) NULL,
1071  },
1072  *ImagePanHelp[] =
1073  {
1074    "When an image exceeds the width or height of the X server",
1075    "screen, display maps a small panning icon.  The rectangle",
1076    "within the panning icon shows the area that is currently",
1077    "displayed in the image window.  To pan about the image,",
1078    "press any button and drag the pointer within the panning",
1079    "icon.  The pan rectangle moves with the pointer and the",
1080    "image window is updated to reflect the location of the",
1081    "rectangle within the panning icon.  When you have selected",
1082    "the area of the image you wish to view, release the button.",
1083    "",
1084    "Use the arrow keys to pan the image one pixel up, down,",
1085    "left, or right within the image window.",
1086    "",
1087    "The panning icon is withdrawn if the image becomes smaller",
1088    "than the dimensions of the X server screen.",
1089    (char *) NULL,
1090  },
1091  *ImagePasteHelp[] =
1092  {
1093    "A small window appears showing the location of the cursor in",
1094    "the image window. You are now in paste mode.  To exit",
1095    "immediately, press Dismiss.  In paste mode, the Command",
1096    "widget has these options:",
1097    "",
1098    "    Operators",
1099    "      over",
1100    "      in",
1101    "      out",
1102    "      atop",
1103    "      xor",
1104    "      plus",
1105    "      minus",
1106    "      add",
1107    "      subtract",
1108    "      difference",
1109    "      replace",
1110    "    Help",
1111    "    Dismiss",
1112    "",
1113    "Choose a composite operation from the Operators sub-menu of",
1114    "the Command widget.  How each operator behaves is described",
1115    "below.  Image window is the image currently displayed on",
1116    "your X server and image is the image obtained with the File",
1117    "Browser widget.",
1118    "",
1119    "Over     The result is the union of the two image shapes,",
1120    "         with image obscuring image window in the region of",
1121    "         overlap.",
1122    "",
1123    "In       The result is simply image cut by the shape of",
1124    "         image window.  None of the image data of image",
1125    "         window is in the result.",
1126    "",
1127    "Out      The resulting image is image with the shape of",
1128    "         image window cut out.",
1129    "",
1130    "Atop     The result is the same shape as image image window,",
1131    "         with image obscuring image window where the image",
1132    "         shapes overlap.  Note this differs from over",
1133    "         because the portion of image outside image window's",
1134    "         shape does not appear in the result.",
1135    "",
1136    "Xor      The result is the image data from both image and",
1137    "         image window that is outside the overlap region.",
1138    "         The overlap region is blank.",
1139    "",
1140    "Plus     The result is just the sum of the image data.",
1141    "         Output values are cropped to QuantumRange (no overflow).",
1142    "         This operation is independent of the matte",
1143    "         channels.",
1144    "",
1145    "Minus    The result of image - image window, with underflow",
1146    "         cropped to zero.",
1147    "",
1148    "Add      The result of image + image window, with overflow",
1149    "         wrapping around (mod 256).",
1150    "",
1151    "Subtract The result of image - image window, with underflow",
1152    "         wrapping around (mod 256).  The add and subtract",
1153    "         operators can be used to perform reversible",
1154    "         transformations.",
1155    "",
1156    "Difference",
1157    "         The result of abs(image - image window).  This",
1158    "         useful for comparing two very similar images.",
1159    "",
1160    "Copy     The resulting image is image window replaced with",
1161    "         image.  Here the matte information is ignored.",
1162    "",
1163    "CopyRed  The red layer of the image window is replace with",
1164    "         the red layer of the image.  The other layers are",
1165    "         untouched.",
1166    "",
1167    "CopyGreen",
1168    "         The green layer of the image window is replace with",
1169    "         the green layer of the image.  The other layers are",
1170    "         untouched.",
1171    "",
1172    "CopyBlue The blue layer of the image window is replace with",
1173    "         the blue layer of the image.  The other layers are",
1174    "         untouched.",
1175    "",
1176    "CopyOpacity",
1177    "         The matte layer of the image window is replace with",
1178    "         the matte layer of the image.  The other layers are",
1179    "         untouched.",
1180    "",
1181    "The image compositor requires a matte, or alpha channel in",
1182    "the image for some operations.  This extra channel usually",
1183    "defines a mask which represents a sort of a cookie-cutter",
1184    "for the image.  This the case when matte is opaque (full",
1185    "coverage) for pixels inside the shape, zero outside, and",
1186    "between 0 and QuantumRange on the boundary.  If image does not",
1187    "have a matte channel, it is initialized with 0 for any pixel",
1188    "matching in color to pixel location (0,0), otherwise QuantumRange.",
1189    "",
1190    "Note that matte information for image window is not retained",
1191    "for colormapped X server visuals (e.g. StaticColor,",
1192    "StaticColor, GrayScale, PseudoColor).  Correct compositing",
1193    "behavior may require a TrueColor or DirectColor visual or a",
1194    "Standard Colormap.",
1195    "",
1196    "Choosing a composite operator is optional.  The default",
1197    "operator is replace.  However, you must choose a location to",
1198    "paste your image and press button 1.  Press and hold the",
1199    "button before releasing and an outline of the image will",
1200    "appear to help you identify your location.",
1201    "",
1202    "The actual colors of the pasted image is saved.  However,",
1203    "the color that appears in image window may be different.",
1204    "For example, on a monochrome screen image window will appear",
1205    "black or white even though your pasted image may have",
1206    "many colors.  If the image is saved to a file it is written",
1207    "with the correct colors.  To assure the correct colors are",
1208    "saved in the final image, any PseudoClass image is promoted",
1209    "to DirectClass (see miff(5)).  To force a PseudoClass image",
1210    "to remain PseudoClass, use -colors.",
1211    (char *) NULL,
1212  },
1213  *ImageROIHelp[] =
1214  {
1215    "In region of interest mode, the Command widget has these",
1216    "options:",
1217    "",
1218    "    Help",
1219    "    Dismiss",
1220    "",
1221    "To define a region of interest, press button 1 and drag.",
1222    "The region of interest is defined by a highlighted rectangle",
1223    "that expands or contracts as it follows the pointer.  Once",
1224    "you are satisfied with the region of interest, release the",
1225    "button.  You are now in apply mode.  In apply mode the",
1226    "Command widget has these options:",
1227    "",
1228    "      File",
1229    "        Save...",
1230    "        Print...",
1231    "      Edit",
1232    "        Undo",
1233    "        Redo",
1234    "      Transform",
1235    "        Flop",
1236    "        Flip",
1237    "        Rotate Right",
1238    "        Rotate Left",
1239    "      Enhance",
1240    "        Hue...",
1241    "        Saturation...",
1242    "        Brightness...",
1243    "        Gamma...",
1244    "        Spiff",
1245    "        Dull",
1246    "        Contrast Stretch",
1247    "        Sigmoidal Contrast...",
1248    "        Normalize",
1249    "        Equalize",
1250    "        Negate",
1251    "        Grayscale",
1252    "        Map...",
1253    "        Quantize...",
1254    "      Effects",
1255    "        Despeckle",
1256    "        Emboss",
1257    "        Reduce Noise",
1258    "        Sharpen...",
1259    "        Blur...",
1260    "        Threshold...",
1261    "        Edge Detect...",
1262    "        Spread...",
1263    "        Shade...",
1264    "        Raise...",
1265    "        Segment...",
1266    "      F/X",
1267    "        Solarize...",
1268    "        Sepia Tone...",
1269    "        Swirl...",
1270    "        Implode...",
1271    "        Vignette...",
1272    "        Wave...",
1273    "        Oil Painting...",
1274    "        Charcoal Drawing...",
1275    "      Miscellany",
1276    "        Image Info",
1277    "        Zoom Image",
1278    "        Show Preview...",
1279    "        Show Histogram",
1280    "        Show Matte",
1281    "      Help",
1282    "      Dismiss",
1283    "",
1284    "You can make adjustments to the region of interest by moving",
1285    "the pointer to one of the rectangle corners, pressing a",
1286    "button, and dragging.  Finally, choose an image processing",
1287    "technique from the Command widget.  You can choose more than",
1288    "one image processing technique to apply to an area.",
1289    "Alternatively, you can move the region of interest before",
1290    "applying another image processing technique.  To exit, press",
1291    "Dismiss.",
1292    (char *) NULL,
1293  },
1294  *ImageRotateHelp[] =
1295  {
1296    "In rotate mode, the Command widget has these options:",
1297    "",
1298    "    Pixel Color",
1299    "      black",
1300    "      blue",
1301    "      cyan",
1302    "      green",
1303    "      gray",
1304    "      red",
1305    "      magenta",
1306    "      yellow",
1307    "      white",
1308    "      Browser...",
1309    "    Direction",
1310    "      horizontal",
1311    "      vertical",
1312    "    Help",
1313    "    Dismiss",
1314    "",
1315    "Choose a background color from the Pixel Color sub-menu.",
1316    "Additional background colors can be specified with the color",
1317    "browser.  You can change the menu colors by setting the X",
1318    "resources pen1 through pen9.",
1319    "",
1320    "If you choose the color browser and press Grab, you can",
1321    "select the background color by moving the pointer to the",
1322    "desired color on the screen and press any button.",
1323    "",
1324    "Choose a point in the image window and press this button and",
1325    "hold.  Next, move the pointer to another location in the",
1326    "image.  As you move a line connects the initial location and",
1327    "the pointer.  When you release the button, the degree of",
1328    "image rotation is determined by the slope of the line you",
1329    "just drew.  The slope is relative to the direction you",
1330    "choose from the Direction sub-menu of the Command widget.",
1331    "",
1332    "To cancel the image rotation, move the pointer back to the",
1333    "starting point of the line and release the button.",
1334    (char *) NULL,
1335  };
1336
1337/*
1338  Enumeration declarations.
1339*/
1340typedef enum
1341{
1342  CopyMode,
1343  CropMode,
1344  CutMode
1345} ClipboardMode;
1346
1347typedef enum
1348{
1349  OpenCommand,
1350  NextCommand,
1351  FormerCommand,
1352  SelectCommand,
1353  SaveCommand,
1354  PrintCommand,
1355  DeleteCommand,
1356  NewCommand,
1357  VisualDirectoryCommand,
1358  QuitCommand,
1359  UndoCommand,
1360  RedoCommand,
1361  CutCommand,
1362  CopyCommand,
1363  PasteCommand,
1364  HalfSizeCommand,
1365  OriginalSizeCommand,
1366  DoubleSizeCommand,
1367  ResizeCommand,
1368  ApplyCommand,
1369  RefreshCommand,
1370  RestoreCommand,
1371  CropCommand,
1372  ChopCommand,
1373  FlopCommand,
1374  FlipCommand,
1375  RotateRightCommand,
1376  RotateLeftCommand,
1377  RotateCommand,
1378  ShearCommand,
1379  RollCommand,
1380  TrimCommand,
1381  HueCommand,
1382  SaturationCommand,
1383  BrightnessCommand,
1384  GammaCommand,
1385  SpiffCommand,
1386  DullCommand,
1387  ContrastStretchCommand,
1388  SigmoidalContrastCommand,
1389  NormalizeCommand,
1390  EqualizeCommand,
1391  NegateCommand,
1392  GrayscaleCommand,
1393  MapCommand,
1394  QuantizeCommand,
1395  DespeckleCommand,
1396  EmbossCommand,
1397  ReduceNoiseCommand,
1398  AddNoiseCommand,
1399  SharpenCommand,
1400  BlurCommand,
1401  ThresholdCommand,
1402  EdgeDetectCommand,
1403  SpreadCommand,
1404  ShadeCommand,
1405  RaiseCommand,
1406  SegmentCommand,
1407  SolarizeCommand,
1408  SepiaToneCommand,
1409  SwirlCommand,
1410  ImplodeCommand,
1411  VignetteCommand,
1412  WaveCommand,
1413  OilPaintCommand,
1414  CharcoalDrawCommand,
1415  AnnotateCommand,
1416  DrawCommand,
1417  ColorCommand,
1418  MatteCommand,
1419  CompositeCommand,
1420  AddBorderCommand,
1421  AddFrameCommand,
1422  CommentCommand,
1423  LaunchCommand,
1424  RegionofInterestCommand,
1425  ROIHelpCommand,
1426  ROIDismissCommand,
1427  InfoCommand,
1428  ZoomCommand,
1429  ShowPreviewCommand,
1430  ShowHistogramCommand,
1431  ShowMatteCommand,
1432  BackgroundCommand,
1433  SlideShowCommand,
1434  PreferencesCommand,
1435  HelpCommand,
1436  BrowseDocumentationCommand,
1437  VersionCommand,
1438  SaveToUndoBufferCommand,
1439  FreeBuffersCommand,
1440  NullCommand
1441} CommandType;
1442
1443typedef enum
1444{
1445  AnnotateNameCommand,
1446  AnnotateFontColorCommand,
1447  AnnotateBackgroundColorCommand,
1448  AnnotateRotateCommand,
1449  AnnotateHelpCommand,
1450  AnnotateDismissCommand,
1451  TextHelpCommand,
1452  TextApplyCommand,
1453  ChopDirectionCommand,
1454  ChopHelpCommand,
1455  ChopDismissCommand,
1456  HorizontalChopCommand,
1457  VerticalChopCommand,
1458  ColorEditMethodCommand,
1459  ColorEditColorCommand,
1460  ColorEditBorderCommand,
1461  ColorEditFuzzCommand,
1462  ColorEditUndoCommand,
1463  ColorEditHelpCommand,
1464  ColorEditDismissCommand,
1465  CompositeOperatorsCommand,
1466  CompositeDissolveCommand,
1467  CompositeDisplaceCommand,
1468  CompositeHelpCommand,
1469  CompositeDismissCommand,
1470  CropHelpCommand,
1471  CropDismissCommand,
1472  RectifyCopyCommand,
1473  RectifyHelpCommand,
1474  RectifyDismissCommand,
1475  DrawElementCommand,
1476  DrawColorCommand,
1477  DrawStippleCommand,
1478  DrawWidthCommand,
1479  DrawUndoCommand,
1480  DrawHelpCommand,
1481  DrawDismissCommand,
1482  MatteEditMethod,
1483  MatteEditBorderCommand,
1484  MatteEditFuzzCommand,
1485  MatteEditValueCommand,
1486  MatteEditUndoCommand,
1487  MatteEditHelpCommand,
1488  MatteEditDismissCommand,
1489  PasteOperatorsCommand,
1490  PasteHelpCommand,
1491  PasteDismissCommand,
1492  RotateColorCommand,
1493  RotateDirectionCommand,
1494  RotateCropCommand,
1495  RotateSharpenCommand,
1496  RotateHelpCommand,
1497  RotateDismissCommand,
1498  HorizontalRotateCommand,
1499  VerticalRotateCommand,
1500  TileLoadCommand,
1501  TileNextCommand,
1502  TileFormerCommand,
1503  TileDeleteCommand,
1504  TileUpdateCommand
1505} ModeType;
1506
1507/*
1508  Stipples.
1509*/
1510#define BricksWidth  20
1511#define BricksHeight  20
1512#define DiagonalWidth  16
1513#define DiagonalHeight  16
1514#define HighlightWidth  8
1515#define HighlightHeight  8
1516#define OpaqueWidth  8
1517#define OpaqueHeight  8
1518#define ScalesWidth  16
1519#define ScalesHeight  16
1520#define ShadowWidth  8
1521#define ShadowHeight  8
1522#define VerticalWidth  16
1523#define VerticalHeight  16
1524#define WavyWidth  16
1525#define WavyHeight  16
1526
1527/*
1528  Constant declaration.
1529*/
1530static const int
1531  RoiDelta = 8;
1532
1533static const unsigned char
1534  BricksBitmap[] =
1535  {
1536    0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1537    0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1538    0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1539    0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1540    0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1541  },
1542  DiagonalBitmap[] =
1543  {
1544    0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1545    0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1546    0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1547  },
1548  ScalesBitmap[] =
1549  {
1550    0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1551    0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1552    0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1553  },
1554  VerticalBitmap[] =
1555  {
1556    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1557    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1558    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1559  },
1560  WavyBitmap[] =
1561  {
1562    0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1563    0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1564    0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1565  };
1566
1567/*
1568  Function prototypes.
1569*/
1570static CommandType
1571  XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1572    const MagickStatusType,KeySym,Image **,ExceptionInfo *);
1573
1574static Image
1575  *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
1576    Image **,ExceptionInfo *),
1577  *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1578  *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *,
1579    ExceptionInfo *),
1580  *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *,
1581    ExceptionInfo *);
1582
1583static MagickBooleanType
1584  XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *,
1585    ExceptionInfo *),
1586  XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **,
1587    ExceptionInfo *),
1588  XChopImage(Display *,XResourceInfo *,XWindows *,Image **,
1589    ExceptionInfo *),
1590  XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode,
1591    ExceptionInfo *),
1592  XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1593    ExceptionInfo *),
1594  XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *,
1595    ExceptionInfo *),
1596  XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1597  XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1598    ExceptionInfo *),
1599  XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1600    ExceptionInfo *),
1601  XPasteImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1602  XPrintImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1603  XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **,
1604    ExceptionInfo *),
1605  XROIImage(Display *,XResourceInfo *,XWindows *,Image **,ExceptionInfo *),
1606  XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1607  XTrimImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
1608
1609static void
1610  XDrawPanRectangle(Display *,XWindows *),
1611  XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **,
1612    ExceptionInfo *),
1613  XMagnifyImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1614  XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1615  XPanImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1616  XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1617    const KeySym,ExceptionInfo *),
1618  XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1619  XScreenEvent(Display *,XWindows *,XEvent *,ExceptionInfo *),
1620  XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1621
1622/*
1623%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1624%                                                                             %
1625%                                                                             %
1626%                                                                             %
1627%   D i s p l a y I m a g e s                                                 %
1628%                                                                             %
1629%                                                                             %
1630%                                                                             %
1631%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1632%
1633%  DisplayImages() displays an image sequence to any X window screen.  It
1634%  returns a value other than 0 if successful.  Check the exception member
1635%  of image to determine the reason for any failure.
1636%
1637%  The format of the DisplayImages method is:
1638%
1639%      MagickBooleanType DisplayImages(const ImageInfo *image_info,
1640%        Image *images,ExceptionInfo *exception)
1641%
1642%  A description of each parameter follows:
1643%
1644%    o image_info: the image info.
1645%
1646%    o image: the image.
1647%
1648%    o exception: return any errors or warnings in this structure.
1649%
1650*/
1651MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1652  Image *images,ExceptionInfo *exception)
1653{
1654  char
1655    *argv[1];
1656
1657  Display
1658    *display;
1659
1660  Image
1661    *image;
1662
1663  register ssize_t
1664    i;
1665
1666  size_t
1667    state;
1668
1669  XrmDatabase
1670    resource_database;
1671
1672  XResourceInfo
1673    resource_info;
1674
1675  assert(image_info != (const ImageInfo *) NULL);
1676  assert(image_info->signature == MagickCoreSignature);
1677  assert(images != (Image *) NULL);
1678  assert(images->signature == MagickCoreSignature);
1679  if (IfMagickTrue(images->debug) )
1680    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1681  display=XOpenDisplay(image_info->server_name);
1682  if (display == (Display *) NULL)
1683    {
1684      (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1685        "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1686      return(MagickFalse);
1687    }
1688  if (exception->severity != UndefinedException)
1689    CatchException(exception);
1690  (void) XSetErrorHandler(XError);
1691  resource_database=XGetResourceDatabase(display,GetClientName());
1692  (void) ResetMagickMemory(&resource_info,0,sizeof(resource_info));
1693  XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1694  if (image_info->page != (char *) NULL)
1695    resource_info.image_geometry=AcquireString(image_info->page);
1696  resource_info.immutable=MagickTrue;
1697  argv[0]=AcquireString(GetClientName());
1698  state=DefaultState;
1699  for (i=0; (state & ExitState) == 0; i++)
1700  {
1701    if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1702      break;
1703    image=GetImageFromList(images,i % GetImageListLength(images));
1704    (void) XDisplayImage(display,&resource_info,argv,1,&image,&state,exception);
1705  }
1706  (void) SetErrorHandler((ErrorHandler) NULL);
1707  (void) SetWarningHandler((WarningHandler) NULL);
1708  argv[0]=DestroyString(argv[0]);
1709  (void) XCloseDisplay(display);
1710  XDestroyResourceInfo(&resource_info);
1711  if (exception->severity != UndefinedException)
1712    return(MagickFalse);
1713  return(MagickTrue);
1714}
1715
1716/*
1717%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1718%                                                                             %
1719%                                                                             %
1720%                                                                             %
1721%   R e m o t e D i s p l a y C o m m a n d                                   %
1722%                                                                             %
1723%                                                                             %
1724%                                                                             %
1725%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1726%
1727%  RemoteDisplayCommand() encourages a remote display program to display the
1728%  specified image filename.
1729%
1730%  The format of the RemoteDisplayCommand method is:
1731%
1732%      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1733%        const char *window,const char *filename,ExceptionInfo *exception)
1734%
1735%  A description of each parameter follows:
1736%
1737%    o image_info: the image info.
1738%
1739%    o window: Specifies the name or id of an X window.
1740%
1741%    o filename: the name of the image filename to display.
1742%
1743%    o exception: return any errors or warnings in this structure.
1744%
1745*/
1746MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1747  const char *window,const char *filename,ExceptionInfo *exception)
1748{
1749  Display
1750    *display;
1751
1752  MagickStatusType
1753    status;
1754
1755  assert(image_info != (const ImageInfo *) NULL);
1756  assert(image_info->signature == MagickCoreSignature);
1757  assert(filename != (char *) NULL);
1758  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1759  display=XOpenDisplay(image_info->server_name);
1760  if (display == (Display *) NULL)
1761    {
1762      (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1763        "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1764      return(MagickFalse);
1765    }
1766  (void) XSetErrorHandler(XError);
1767  status=XRemoteCommand(display,window,filename);
1768  (void) XCloseDisplay(display);
1769  return(status != 0 ? MagickTrue : MagickFalse);
1770}
1771
1772/*
1773%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1774%                                                                             %
1775%                                                                             %
1776%                                                                             %
1777+   X A n n o t a t e E d i t I m a g e                                       %
1778%                                                                             %
1779%                                                                             %
1780%                                                                             %
1781%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1782%
1783%  XAnnotateEditImage() annotates the image with text.
1784%
1785%  The format of the XAnnotateEditImage method is:
1786%
1787%      MagickBooleanType XAnnotateEditImage(Display *display,
1788%        XResourceInfo *resource_info,XWindows *windows,Image *image,
1789%        ExceptionInfo *exception)
1790%
1791%  A description of each parameter follows:
1792%
1793%    o display: Specifies a connection to an X server;  returned from
1794%      XOpenDisplay.
1795%
1796%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1797%
1798%    o windows: Specifies a pointer to a XWindows structure.
1799%
1800%    o image: the image; returned from ReadImage.
1801%
1802*/
1803
1804static MagickBooleanType XAnnotateEditImage(Display *display,
1805  XResourceInfo *resource_info,XWindows *windows,Image *image,
1806  ExceptionInfo *exception)
1807{
1808  static const char
1809    *AnnotateMenu[] =
1810    {
1811      "Font Name",
1812      "Font Color",
1813      "Box Color",
1814      "Rotate Text",
1815      "Help",
1816      "Dismiss",
1817      (char *) NULL
1818    },
1819    *TextMenu[] =
1820    {
1821      "Help",
1822      "Apply",
1823      (char *) NULL
1824    };
1825
1826  static const ModeType
1827    AnnotateCommands[] =
1828    {
1829      AnnotateNameCommand,
1830      AnnotateFontColorCommand,
1831      AnnotateBackgroundColorCommand,
1832      AnnotateRotateCommand,
1833      AnnotateHelpCommand,
1834      AnnotateDismissCommand
1835    },
1836    TextCommands[] =
1837    {
1838      TextHelpCommand,
1839      TextApplyCommand
1840    };
1841
1842  static MagickBooleanType
1843    transparent_box = MagickTrue,
1844    transparent_pen = MagickFalse;
1845
1846  static double
1847    degrees = 0.0;
1848
1849  static unsigned int
1850    box_id = MaxNumberPens-2,
1851    font_id = 0,
1852    pen_id = 0;
1853
1854  char
1855    command[MagickPathExtent],
1856    text[MagickPathExtent];
1857
1858  const char
1859    *ColorMenu[MaxNumberPens+1];
1860
1861  Cursor
1862    cursor;
1863
1864  GC
1865    annotate_context;
1866
1867  int
1868    id,
1869    pen_number,
1870    status,
1871    x,
1872    y;
1873
1874  KeySym
1875    key_symbol;
1876
1877  register char
1878    *p;
1879
1880  register ssize_t
1881    i;
1882
1883  unsigned int
1884    height,
1885    width;
1886
1887  size_t
1888    state;
1889
1890  XAnnotateInfo
1891    *annotate_info,
1892    *previous_info;
1893
1894  XColor
1895    color;
1896
1897  XFontStruct
1898    *font_info;
1899
1900  XEvent
1901    event,
1902    text_event;
1903
1904  /*
1905    Map Command widget.
1906  */
1907  (void) CloneString(&windows->command.name,"Annotate");
1908  windows->command.data=4;
1909  (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1910  (void) XMapRaised(display,windows->command.id);
1911  XClientMessage(display,windows->image.id,windows->im_protocols,
1912    windows->im_update_widget,CurrentTime);
1913  /*
1914    Track pointer until button 1 is pressed.
1915  */
1916  XQueryPosition(display,windows->image.id,&x,&y);
1917  (void) XSelectInput(display,windows->image.id,
1918    windows->image.attributes.event_mask | PointerMotionMask);
1919  cursor=XCreateFontCursor(display,XC_left_side);
1920  (void) XCheckDefineCursor(display,windows->image.id,cursor);
1921  state=DefaultState;
1922  do
1923  {
1924    if (IfMagickTrue(windows->info.mapped) )
1925      {
1926        /*
1927          Display pointer position.
1928        */
1929        (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
1930          x+windows->image.x,y+windows->image.y);
1931        XInfoWidget(display,windows,text);
1932      }
1933    /*
1934      Wait for next event.
1935    */
1936    XScreenEvent(display,windows,&event,exception);
1937    if (event.xany.window == windows->command.id)
1938      {
1939        /*
1940          Select a command from the Command widget.
1941        */
1942        id=XCommandWidget(display,windows,AnnotateMenu,&event);
1943        (void) XCheckDefineCursor(display,windows->image.id,cursor);
1944        if (id < 0)
1945          continue;
1946        switch (AnnotateCommands[id])
1947        {
1948          case AnnotateNameCommand:
1949          {
1950            const char
1951              *FontMenu[MaxNumberFonts];
1952
1953            int
1954              font_number;
1955
1956            /*
1957              Initialize menu selections.
1958            */
1959            for (i=0; i < MaxNumberFonts; i++)
1960              FontMenu[i]=resource_info->font_name[i];
1961            FontMenu[MaxNumberFonts-2]="Browser...";
1962            FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1963            /*
1964              Select a font name from the pop-up menu.
1965            */
1966            font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1967              (const char **) FontMenu,command);
1968            if (font_number < 0)
1969              break;
1970            if (font_number == (MaxNumberFonts-2))
1971              {
1972                static char
1973                  font_name[MagickPathExtent] = "fixed";
1974
1975                /*
1976                  Select a font name from a browser.
1977                */
1978                resource_info->font_name[font_number]=font_name;
1979                XFontBrowserWidget(display,windows,"Select",font_name);
1980                if (*font_name == '\0')
1981                  break;
1982              }
1983            /*
1984              Initialize font info.
1985            */
1986            font_info=XLoadQueryFont(display,resource_info->font_name[
1987              font_number]);
1988            if (font_info == (XFontStruct *) NULL)
1989              {
1990                XNoticeWidget(display,windows,"Unable to load font:",
1991                  resource_info->font_name[font_number]);
1992                break;
1993              }
1994            font_id=(unsigned int) font_number;
1995            (void) XFreeFont(display,font_info);
1996            break;
1997          }
1998          case AnnotateFontColorCommand:
1999          {
2000            /*
2001              Initialize menu selections.
2002            */
2003            for (i=0; i < (int) (MaxNumberPens-2); i++)
2004              ColorMenu[i]=resource_info->pen_colors[i];
2005            ColorMenu[MaxNumberPens-2]="transparent";
2006            ColorMenu[MaxNumberPens-1]="Browser...";
2007            ColorMenu[MaxNumberPens]=(const char *) NULL;
2008            /*
2009              Select a pen color from the pop-up menu.
2010            */
2011            pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2012              (const char **) ColorMenu,command);
2013            if (pen_number < 0)
2014              break;
2015            transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
2016              MagickFalse;
2017            if (IfMagickTrue(transparent_pen) )
2018              break;
2019            if (pen_number == (MaxNumberPens-1))
2020              {
2021                static char
2022                  color_name[MagickPathExtent] = "gray";
2023
2024                /*
2025                  Select a pen color from a dialog.
2026                */
2027                resource_info->pen_colors[pen_number]=color_name;
2028                XColorBrowserWidget(display,windows,"Select",color_name);
2029                if (*color_name == '\0')
2030                  break;
2031              }
2032            /*
2033              Set pen color.
2034            */
2035            (void) XParseColor(display,windows->map_info->colormap,
2036              resource_info->pen_colors[pen_number],&color);
2037            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2038              (unsigned int) MaxColors,&color);
2039            windows->pixel_info->pen_colors[pen_number]=color;
2040            pen_id=(unsigned int) pen_number;
2041            break;
2042          }
2043          case AnnotateBackgroundColorCommand:
2044          {
2045            /*
2046              Initialize menu selections.
2047            */
2048            for (i=0; i < (int) (MaxNumberPens-2); i++)
2049              ColorMenu[i]=resource_info->pen_colors[i];
2050            ColorMenu[MaxNumberPens-2]="transparent";
2051            ColorMenu[MaxNumberPens-1]="Browser...";
2052            ColorMenu[MaxNumberPens]=(const char *) NULL;
2053            /*
2054              Select a pen color from the pop-up menu.
2055            */
2056            pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2057              (const char **) ColorMenu,command);
2058            if (pen_number < 0)
2059              break;
2060            transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2061              MagickFalse;
2062            if (IfMagickTrue(transparent_box) )
2063              break;
2064            if (pen_number == (MaxNumberPens-1))
2065              {
2066                static char
2067                  color_name[MagickPathExtent] = "gray";
2068
2069                /*
2070                  Select a pen color from a dialog.
2071                */
2072                resource_info->pen_colors[pen_number]=color_name;
2073                XColorBrowserWidget(display,windows,"Select",color_name);
2074                if (*color_name == '\0')
2075                  break;
2076              }
2077            /*
2078              Set pen color.
2079            */
2080            (void) XParseColor(display,windows->map_info->colormap,
2081              resource_info->pen_colors[pen_number],&color);
2082            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2083              (unsigned int) MaxColors,&color);
2084            windows->pixel_info->pen_colors[pen_number]=color;
2085            box_id=(unsigned int) pen_number;
2086            break;
2087          }
2088          case AnnotateRotateCommand:
2089          {
2090            int
2091              entry;
2092
2093            static char
2094              angle[MagickPathExtent] = "30.0";
2095
2096            static const char
2097              *RotateMenu[] =
2098              {
2099                "-90",
2100                "-45",
2101                "-30",
2102                "0",
2103                "30",
2104                "45",
2105                "90",
2106                "180",
2107                "Dialog...",
2108                (char *) NULL,
2109              };
2110
2111            /*
2112              Select a command from the pop-up menu.
2113            */
2114            entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2115              command);
2116            if (entry < 0)
2117              break;
2118            if (entry != 8)
2119              {
2120                degrees=StringToDouble(RotateMenu[entry],(char **) NULL);
2121                break;
2122              }
2123            (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2124              angle);
2125            if (*angle == '\0')
2126              break;
2127            degrees=StringToDouble(angle,(char **) NULL);
2128            break;
2129          }
2130          case AnnotateHelpCommand:
2131          {
2132            XTextViewWidget(display,resource_info,windows,MagickFalse,
2133              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2134            break;
2135          }
2136          case AnnotateDismissCommand:
2137          {
2138            /*
2139              Prematurely exit.
2140            */
2141            state|=EscapeState;
2142            state|=ExitState;
2143            break;
2144          }
2145          default:
2146            break;
2147        }
2148        continue;
2149      }
2150    switch (event.type)
2151    {
2152      case ButtonPress:
2153      {
2154        if (event.xbutton.button != Button1)
2155          break;
2156        if (event.xbutton.window != windows->image.id)
2157          break;
2158        /*
2159          Change to text entering mode.
2160        */
2161        x=event.xbutton.x;
2162        y=event.xbutton.y;
2163        state|=ExitState;
2164        break;
2165      }
2166      case ButtonRelease:
2167        break;
2168      case Expose:
2169        break;
2170      case KeyPress:
2171      {
2172        if (event.xkey.window != windows->image.id)
2173          break;
2174        /*
2175          Respond to a user key press.
2176        */
2177        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2178          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2179        switch ((int) key_symbol)
2180        {
2181          case XK_Escape:
2182          case XK_F20:
2183          {
2184            /*
2185              Prematurely exit.
2186            */
2187            state|=EscapeState;
2188            state|=ExitState;
2189            break;
2190          }
2191          case XK_F1:
2192          case XK_Help:
2193          {
2194            XTextViewWidget(display,resource_info,windows,MagickFalse,
2195              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2196            break;
2197          }
2198          default:
2199          {
2200            (void) XBell(display,0);
2201            break;
2202          }
2203        }
2204        break;
2205      }
2206      case MotionNotify:
2207      {
2208        /*
2209          Map and unmap Info widget as cursor crosses its boundaries.
2210        */
2211        x=event.xmotion.x;
2212        y=event.xmotion.y;
2213        if (IfMagickTrue(windows->info.mapped) )
2214          {
2215            if ((x < (int) (windows->info.x+windows->info.width)) &&
2216                (y < (int) (windows->info.y+windows->info.height)))
2217              (void) XWithdrawWindow(display,windows->info.id,
2218                windows->info.screen);
2219          }
2220        else
2221          if ((x > (int) (windows->info.x+windows->info.width)) ||
2222              (y > (int) (windows->info.y+windows->info.height)))
2223            (void) XMapWindow(display,windows->info.id);
2224        break;
2225      }
2226      default:
2227        break;
2228    }
2229  } while ((state & ExitState) == 0);
2230  (void) XSelectInput(display,windows->image.id,
2231    windows->image.attributes.event_mask);
2232  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2233  if ((state & EscapeState) != 0)
2234    return(MagickTrue);
2235  /*
2236    Set font info and check boundary conditions.
2237  */
2238  font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2239  if (font_info == (XFontStruct *) NULL)
2240    {
2241      XNoticeWidget(display,windows,"Unable to load font:",
2242        resource_info->font_name[font_id]);
2243      font_info=windows->font_info;
2244    }
2245  if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2246    x=(int) windows->image.width-font_info->max_bounds.width;
2247  if (y < (int) (font_info->ascent+font_info->descent))
2248    y=(int) font_info->ascent+font_info->descent;
2249  if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2250      ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2251    return(MagickFalse);
2252  /*
2253    Initialize annotate structure.
2254  */
2255  annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2256  if (annotate_info == (XAnnotateInfo *) NULL)
2257    return(MagickFalse);
2258  XGetAnnotateInfo(annotate_info);
2259  annotate_info->x=x;
2260  annotate_info->y=y;
2261  if (IfMagickFalse(transparent_box) && IfMagickFalse(transparent_pen))
2262    annotate_info->stencil=OpaqueStencil;
2263  else
2264    if (IfMagickFalse(transparent_box) )
2265      annotate_info->stencil=BackgroundStencil;
2266    else
2267      annotate_info->stencil=ForegroundStencil;
2268  annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2269  annotate_info->degrees=degrees;
2270  annotate_info->font_info=font_info;
2271  annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2272    windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
2273    sizeof(*annotate_info->text));
2274  if (annotate_info->text == (char *) NULL)
2275    return(MagickFalse);
2276  /*
2277    Create cursor and set graphic context.
2278  */
2279  cursor=XCreateFontCursor(display,XC_pencil);
2280  (void) XCheckDefineCursor(display,windows->image.id,cursor);
2281  annotate_context=windows->image.annotate_context;
2282  (void) XSetFont(display,annotate_context,font_info->fid);
2283  (void) XSetBackground(display,annotate_context,
2284    windows->pixel_info->pen_colors[box_id].pixel);
2285  (void) XSetForeground(display,annotate_context,
2286    windows->pixel_info->pen_colors[pen_id].pixel);
2287  /*
2288    Begin annotating the image with text.
2289  */
2290  (void) CloneString(&windows->command.name,"Text");
2291  windows->command.data=0;
2292  (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2293  state=DefaultState;
2294  (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2295  text_event.xexpose.width=(int) font_info->max_bounds.width;
2296  text_event.xexpose.height=font_info->max_bounds.ascent+
2297    font_info->max_bounds.descent;
2298  p=annotate_info->text;
2299  do
2300  {
2301    /*
2302      Display text cursor.
2303    */
2304    *p='\0';
2305    (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2306    /*
2307      Wait for next event.
2308    */
2309    XScreenEvent(display,windows,&event,exception);
2310    if (event.xany.window == windows->command.id)
2311      {
2312        /*
2313          Select a command from the Command widget.
2314        */
2315        (void) XSetBackground(display,annotate_context,
2316          windows->pixel_info->background_color.pixel);
2317        (void) XSetForeground(display,annotate_context,
2318          windows->pixel_info->foreground_color.pixel);
2319        id=XCommandWidget(display,windows,AnnotateMenu,&event);
2320        (void) XSetBackground(display,annotate_context,
2321          windows->pixel_info->pen_colors[box_id].pixel);
2322        (void) XSetForeground(display,annotate_context,
2323          windows->pixel_info->pen_colors[pen_id].pixel);
2324        if (id < 0)
2325          continue;
2326        switch (TextCommands[id])
2327        {
2328          case TextHelpCommand:
2329          {
2330            XTextViewWidget(display,resource_info,windows,MagickFalse,
2331              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2332            (void) XCheckDefineCursor(display,windows->image.id,cursor);
2333            break;
2334          }
2335          case TextApplyCommand:
2336          {
2337            /*
2338              Finished annotating.
2339            */
2340            annotate_info->width=(unsigned int) XTextWidth(font_info,
2341              annotate_info->text,(int) strlen(annotate_info->text));
2342            XRefreshWindow(display,&windows->image,&text_event);
2343            state|=ExitState;
2344            break;
2345          }
2346          default:
2347            break;
2348        }
2349        continue;
2350      }
2351    /*
2352      Erase text cursor.
2353    */
2354    text_event.xexpose.x=x;
2355    text_event.xexpose.y=y-font_info->max_bounds.ascent;
2356    (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2357      (unsigned int) text_event.xexpose.width,(unsigned int)
2358      text_event.xexpose.height,MagickFalse);
2359    XRefreshWindow(display,&windows->image,&text_event);
2360    switch (event.type)
2361    {
2362      case ButtonPress:
2363      {
2364        if (event.xbutton.window != windows->image.id)
2365          break;
2366        if (event.xbutton.button == Button2)
2367          {
2368            /*
2369              Request primary selection.
2370            */
2371            (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2372              windows->image.id,CurrentTime);
2373            break;
2374          }
2375        break;
2376      }
2377      case Expose:
2378      {
2379        if (event.xexpose.count == 0)
2380          {
2381            XAnnotateInfo
2382              *text_info;
2383
2384            /*
2385              Refresh Image window.
2386            */
2387            XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2388            text_info=annotate_info;
2389            while (text_info != (XAnnotateInfo *) NULL)
2390            {
2391              if (annotate_info->stencil == ForegroundStencil)
2392                (void) XDrawString(display,windows->image.id,annotate_context,
2393                  text_info->x,text_info->y,text_info->text,
2394                  (int) strlen(text_info->text));
2395              else
2396                (void) XDrawImageString(display,windows->image.id,
2397                  annotate_context,text_info->x,text_info->y,text_info->text,
2398                  (int) strlen(text_info->text));
2399              text_info=text_info->previous;
2400            }
2401            (void) XDrawString(display,windows->image.id,annotate_context,
2402              x,y,"_",1);
2403          }
2404        break;
2405      }
2406      case KeyPress:
2407      {
2408        int
2409          length;
2410
2411        if (event.xkey.window != windows->image.id)
2412          break;
2413        /*
2414          Respond to a user key press.
2415        */
2416        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2417          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2418        *(command+length)='\0';
2419        if (((event.xkey.state & ControlMask) != 0) ||
2420            ((event.xkey.state & Mod1Mask) != 0))
2421          state|=ModifierState;
2422        if ((state & ModifierState) != 0)
2423          switch ((int) key_symbol)
2424          {
2425            case XK_u:
2426            case XK_U:
2427            {
2428              key_symbol=DeleteCommand;
2429              break;
2430            }
2431            default:
2432              break;
2433          }
2434        switch ((int) key_symbol)
2435        {
2436          case XK_BackSpace:
2437          {
2438            /*
2439              Erase one character.
2440            */
2441            if (p == annotate_info->text)
2442              {
2443                if (annotate_info->previous == (XAnnotateInfo *) NULL)
2444                  break;
2445                else
2446                  {
2447                    /*
2448                      Go to end of the previous line of text.
2449                    */
2450                    annotate_info=annotate_info->previous;
2451                    p=annotate_info->text;
2452                    x=annotate_info->x+annotate_info->width;
2453                    y=annotate_info->y;
2454                    if (annotate_info->width != 0)
2455                      p+=strlen(annotate_info->text);
2456                    break;
2457                  }
2458              }
2459            p--;
2460            x-=XTextWidth(font_info,p,1);
2461            text_event.xexpose.x=x;
2462            text_event.xexpose.y=y-font_info->max_bounds.ascent;
2463            XRefreshWindow(display,&windows->image,&text_event);
2464            break;
2465          }
2466          case XK_bracketleft:
2467          {
2468            key_symbol=XK_Escape;
2469            break;
2470          }
2471          case DeleteCommand:
2472          {
2473            /*
2474              Erase the entire line of text.
2475            */
2476            while (p != annotate_info->text)
2477            {
2478              p--;
2479              x-=XTextWidth(font_info,p,1);
2480              text_event.xexpose.x=x;
2481              XRefreshWindow(display,&windows->image,&text_event);
2482            }
2483            break;
2484          }
2485          case XK_Escape:
2486          case XK_F20:
2487          {
2488            /*
2489              Finished annotating.
2490            */
2491            annotate_info->width=(unsigned int) XTextWidth(font_info,
2492              annotate_info->text,(int) strlen(annotate_info->text));
2493            XRefreshWindow(display,&windows->image,&text_event);
2494            state|=ExitState;
2495            break;
2496          }
2497          default:
2498          {
2499            /*
2500              Draw a single character on the Image window.
2501            */
2502            if ((state & ModifierState) != 0)
2503              break;
2504            if (*command == '\0')
2505              break;
2506            *p=(*command);
2507            if (annotate_info->stencil == ForegroundStencil)
2508              (void) XDrawString(display,windows->image.id,annotate_context,
2509                x,y,p,1);
2510            else
2511              (void) XDrawImageString(display,windows->image.id,
2512                annotate_context,x,y,p,1);
2513            x+=XTextWidth(font_info,p,1);
2514            p++;
2515            if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2516              break;
2517          }
2518          case XK_Return:
2519          case XK_KP_Enter:
2520          {
2521            /*
2522              Advance to the next line of text.
2523            */
2524            *p='\0';
2525            annotate_info->width=(unsigned int) XTextWidth(font_info,
2526              annotate_info->text,(int) strlen(annotate_info->text));
2527            if (annotate_info->next != (XAnnotateInfo *) NULL)
2528              {
2529                /*
2530                  Line of text already exists.
2531                */
2532                annotate_info=annotate_info->next;
2533                x=annotate_info->x;
2534                y=annotate_info->y;
2535                p=annotate_info->text;
2536                break;
2537              }
2538            annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2539              sizeof(*annotate_info->next));
2540            if (annotate_info->next == (XAnnotateInfo *) NULL)
2541              return(MagickFalse);
2542            *annotate_info->next=(*annotate_info);
2543            annotate_info->next->previous=annotate_info;
2544            annotate_info=annotate_info->next;
2545            annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2546              windows->image.width/MagickMax((ssize_t)
2547              font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2548            if (annotate_info->text == (char *) NULL)
2549              return(MagickFalse);
2550            annotate_info->y+=annotate_info->height;
2551            if (annotate_info->y > (int) windows->image.height)
2552              annotate_info->y=(int) annotate_info->height;
2553            annotate_info->next=(XAnnotateInfo *) NULL;
2554            x=annotate_info->x;
2555            y=annotate_info->y;
2556            p=annotate_info->text;
2557            break;
2558          }
2559        }
2560        break;
2561      }
2562      case KeyRelease:
2563      {
2564        /*
2565          Respond to a user key release.
2566        */
2567        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2568          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2569        state&=(~ModifierState);
2570        break;
2571      }
2572      case SelectionNotify:
2573      {
2574        Atom
2575          type;
2576
2577        int
2578          format;
2579
2580        unsigned char
2581          *data;
2582
2583        unsigned long
2584          after,
2585          length;
2586
2587        /*
2588          Obtain response from primary selection.
2589        */
2590        if (event.xselection.property == (Atom) None)
2591          break;
2592        status=XGetWindowProperty(display,event.xselection.requestor,
2593          event.xselection.property,0L,(long) MagickPathExtent,True,XA_STRING,
2594          &type,&format,&length,&after,&data);
2595        if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2596            (length == 0))
2597          break;
2598        /*
2599          Annotate Image window with primary selection.
2600        */
2601        for (i=0; i < (ssize_t) length; i++)
2602        {
2603          if ((char) data[i] != '\n')
2604            {
2605              /*
2606                Draw a single character on the Image window.
2607              */
2608              *p=(char) data[i];
2609              (void) XDrawString(display,windows->image.id,annotate_context,
2610                x,y,p,1);
2611              x+=XTextWidth(font_info,p,1);
2612              p++;
2613              if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2614                continue;
2615            }
2616          /*
2617            Advance to the next line of text.
2618          */
2619          *p='\0';
2620          annotate_info->width=(unsigned int) XTextWidth(font_info,
2621            annotate_info->text,(int) strlen(annotate_info->text));
2622          if (annotate_info->next != (XAnnotateInfo *) NULL)
2623            {
2624              /*
2625                Line of text already exists.
2626              */
2627              annotate_info=annotate_info->next;
2628              x=annotate_info->x;
2629              y=annotate_info->y;
2630              p=annotate_info->text;
2631              continue;
2632            }
2633          annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2634            sizeof(*annotate_info->next));
2635          if (annotate_info->next == (XAnnotateInfo *) NULL)
2636            return(MagickFalse);
2637          *annotate_info->next=(*annotate_info);
2638          annotate_info->next->previous=annotate_info;
2639          annotate_info=annotate_info->next;
2640          annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2641            windows->image.width/MagickMax((ssize_t)
2642            font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2643          if (annotate_info->text == (char *) NULL)
2644            return(MagickFalse);
2645          annotate_info->y+=annotate_info->height;
2646          if (annotate_info->y > (int) windows->image.height)
2647            annotate_info->y=(int) annotate_info->height;
2648          annotate_info->next=(XAnnotateInfo *) NULL;
2649          x=annotate_info->x;
2650          y=annotate_info->y;
2651          p=annotate_info->text;
2652        }
2653        (void) XFree((void *) data);
2654        break;
2655      }
2656      default:
2657        break;
2658    }
2659  } while ((state & ExitState) == 0);
2660  (void) XFreeCursor(display,cursor);
2661  /*
2662    Annotation is relative to image configuration.
2663  */
2664  width=(unsigned int) image->columns;
2665  height=(unsigned int) image->rows;
2666  x=0;
2667  y=0;
2668  if (windows->image.crop_geometry != (char *) NULL)
2669    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2670  /*
2671    Initialize annotated image.
2672  */
2673  XSetCursorState(display,windows,MagickTrue);
2674  XCheckRefreshWindows(display,windows);
2675  while (annotate_info != (XAnnotateInfo *) NULL)
2676  {
2677    if (annotate_info->width == 0)
2678      {
2679        /*
2680          No text on this line--  go to the next line of text.
2681        */
2682        previous_info=annotate_info->previous;
2683        annotate_info->text=(char *)
2684          RelinquishMagickMemory(annotate_info->text);
2685        annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2686        annotate_info=previous_info;
2687        continue;
2688      }
2689    /*
2690      Determine pixel index for box and pen color.
2691    */
2692    windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2693    if (windows->pixel_info->colors != 0)
2694      for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2695        if (windows->pixel_info->pixels[i] ==
2696            windows->pixel_info->pen_colors[box_id].pixel)
2697          {
2698            windows->pixel_info->box_index=(unsigned short) i;
2699            break;
2700          }
2701    windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_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[pen_id].pixel)
2706          {
2707            windows->pixel_info->pen_index=(unsigned short) i;
2708            break;
2709          }
2710    /*
2711      Define the annotate geometry string.
2712    */
2713    annotate_info->x=(int)
2714      width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2715    annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2716      windows->image.y)/windows->image.ximage->height;
2717    (void) FormatLocaleString(annotate_info->geometry,MagickPathExtent,
2718      "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2719      height*annotate_info->height/windows->image.ximage->height,
2720      annotate_info->x+x,annotate_info->y+y);
2721    /*
2722      Annotate image with text.
2723    */
2724    status=XAnnotateImage(display,windows->pixel_info,annotate_info,image,
2725      exception);
2726    if (status == 0)
2727      return(MagickFalse);
2728    /*
2729      Free up memory.
2730    */
2731    previous_info=annotate_info->previous;
2732    annotate_info->text=DestroyString(annotate_info->text);
2733    annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2734    annotate_info=previous_info;
2735  }
2736  (void) XSetForeground(display,annotate_context,
2737    windows->pixel_info->foreground_color.pixel);
2738  (void) XSetBackground(display,annotate_context,
2739    windows->pixel_info->background_color.pixel);
2740  (void) XSetFont(display,annotate_context,windows->font_info->fid);
2741  XSetCursorState(display,windows,MagickFalse);
2742  (void) XFreeFont(display,font_info);
2743  /*
2744    Update image configuration.
2745  */
2746  XConfigureImageColormap(display,resource_info,windows,image,exception);
2747  (void) XConfigureImage(display,resource_info,windows,image,exception);
2748  return(MagickTrue);
2749}
2750
2751/*
2752%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2753%                                                                             %
2754%                                                                             %
2755%                                                                             %
2756+   X B a c k g r o u n d I m a g e                                           %
2757%                                                                             %
2758%                                                                             %
2759%                                                                             %
2760%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2761%
2762%  XBackgroundImage() displays the image in the background of a window.
2763%
2764%  The format of the XBackgroundImage method is:
2765%
2766%      MagickBooleanType XBackgroundImage(Display *display,
2767%        XResourceInfo *resource_info,XWindows *windows,Image **image,
2768%        ExceptionInfo *exception)
2769%
2770%  A description of each parameter follows:
2771%
2772%    o display: Specifies a connection to an X server; returned from
2773%      XOpenDisplay.
2774%
2775%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2776%
2777%    o windows: Specifies a pointer to a XWindows structure.
2778%
2779%    o image: the image.
2780%
2781%    o exception: return any errors or warnings in this structure.
2782%
2783*/
2784static MagickBooleanType XBackgroundImage(Display *display,
2785  XResourceInfo *resource_info,XWindows *windows,Image **image,
2786  ExceptionInfo *exception)
2787{
2788#define BackgroundImageTag  "Background/Image"
2789
2790  int
2791    status;
2792
2793  static char
2794    window_id[MagickPathExtent] = "root";
2795
2796  XResourceInfo
2797    background_resources;
2798
2799  /*
2800    Put image in background.
2801  */
2802  status=XDialogWidget(display,windows,"Background",
2803    "Enter window id (id 0x00 selects window with pointer):",window_id);
2804  if (*window_id == '\0')
2805    return(MagickFalse);
2806  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
2807    exception);
2808  XInfoWidget(display,windows,BackgroundImageTag);
2809  XSetCursorState(display,windows,MagickTrue);
2810  XCheckRefreshWindows(display,windows);
2811  background_resources=(*resource_info);
2812  background_resources.window_id=window_id;
2813  background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2814  status=XDisplayBackgroundImage(display,&background_resources,*image,
2815    exception);
2816  if (IfMagickTrue(status))
2817    XClientMessage(display,windows->image.id,windows->im_protocols,
2818      windows->im_retain_colors,CurrentTime);
2819  XSetCursorState(display,windows,MagickFalse);
2820  (void) XMagickCommand(display,resource_info,windows,UndoCommand,image,
2821    exception);
2822  return(MagickTrue);
2823}
2824
2825/*
2826%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2827%                                                                             %
2828%                                                                             %
2829%                                                                             %
2830+   X C h o p I m a g e                                                       %
2831%                                                                             %
2832%                                                                             %
2833%                                                                             %
2834%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2835%
2836%  XChopImage() chops the X image.
2837%
2838%  The format of the XChopImage method is:
2839%
2840%    MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2841%      XWindows *windows,Image **image,ExceptionInfo *exception)
2842%
2843%  A description of each parameter follows:
2844%
2845%    o display: Specifies a connection to an X server; returned from
2846%      XOpenDisplay.
2847%
2848%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2849%
2850%    o windows: Specifies a pointer to a XWindows structure.
2851%
2852%    o image: the image.
2853%
2854%    o exception: return any errors or warnings in this structure.
2855%
2856*/
2857static MagickBooleanType XChopImage(Display *display,
2858  XResourceInfo *resource_info,XWindows *windows,Image **image,
2859  ExceptionInfo *exception)
2860{
2861  static const char
2862    *ChopMenu[] =
2863    {
2864      "Direction",
2865      "Help",
2866      "Dismiss",
2867      (char *) NULL
2868    };
2869
2870  static ModeType
2871    direction = HorizontalChopCommand;
2872
2873  static const ModeType
2874    ChopCommands[] =
2875    {
2876      ChopDirectionCommand,
2877      ChopHelpCommand,
2878      ChopDismissCommand
2879    },
2880    DirectionCommands[] =
2881    {
2882      HorizontalChopCommand,
2883      VerticalChopCommand
2884    };
2885
2886  char
2887    text[MagickPathExtent];
2888
2889  Image
2890    *chop_image;
2891
2892  int
2893    id,
2894    x,
2895    y;
2896
2897  double
2898    scale_factor;
2899
2900  RectangleInfo
2901    chop_info;
2902
2903  unsigned int
2904    distance,
2905    height,
2906    width;
2907
2908  size_t
2909    state;
2910
2911  XEvent
2912    event;
2913
2914  XSegment
2915    segment_info;
2916
2917  /*
2918    Map Command widget.
2919  */
2920  (void) CloneString(&windows->command.name,"Chop");
2921  windows->command.data=1;
2922  (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2923  (void) XMapRaised(display,windows->command.id);
2924  XClientMessage(display,windows->image.id,windows->im_protocols,
2925    windows->im_update_widget,CurrentTime);
2926  /*
2927    Track pointer until button 1 is pressed.
2928  */
2929  XQueryPosition(display,windows->image.id,&x,&y);
2930  (void) XSelectInput(display,windows->image.id,
2931    windows->image.attributes.event_mask | PointerMotionMask);
2932  state=DefaultState;
2933  (void) ResetMagickMemory(&segment_info,0,sizeof(segment_info));
2934  do
2935  {
2936    if (IfMagickTrue(windows->info.mapped) )
2937      {
2938        /*
2939          Display pointer position.
2940        */
2941        (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
2942          x+windows->image.x,y+windows->image.y);
2943        XInfoWidget(display,windows,text);
2944      }
2945    /*
2946      Wait for next event.
2947    */
2948    XScreenEvent(display,windows,&event,exception);
2949    if (event.xany.window == windows->command.id)
2950      {
2951        /*
2952          Select a command from the Command widget.
2953        */
2954        id=XCommandWidget(display,windows,ChopMenu,&event);
2955        if (id < 0)
2956          continue;
2957        switch (ChopCommands[id])
2958        {
2959          case ChopDirectionCommand:
2960          {
2961            char
2962              command[MagickPathExtent];
2963
2964            static const char
2965              *Directions[] =
2966              {
2967                "horizontal",
2968                "vertical",
2969                (char *) NULL,
2970              };
2971
2972            /*
2973              Select a command from the pop-up menu.
2974            */
2975            id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2976            if (id >= 0)
2977              direction=DirectionCommands[id];
2978            break;
2979          }
2980          case ChopHelpCommand:
2981          {
2982            XTextViewWidget(display,resource_info,windows,MagickFalse,
2983              "Help Viewer - Image Chop",ImageChopHelp);
2984            break;
2985          }
2986          case ChopDismissCommand:
2987          {
2988            /*
2989              Prematurely exit.
2990            */
2991            state|=EscapeState;
2992            state|=ExitState;
2993            break;
2994          }
2995          default:
2996            break;
2997        }
2998        continue;
2999      }
3000    switch (event.type)
3001    {
3002      case ButtonPress:
3003      {
3004        if (event.xbutton.button != Button1)
3005          break;
3006        if (event.xbutton.window != windows->image.id)
3007          break;
3008        /*
3009          User has committed to start point of chopping line.
3010        */
3011        segment_info.x1=(short int) event.xbutton.x;
3012        segment_info.x2=(short int) event.xbutton.x;
3013        segment_info.y1=(short int) event.xbutton.y;
3014        segment_info.y2=(short int) event.xbutton.y;
3015        state|=ExitState;
3016        break;
3017      }
3018      case ButtonRelease:
3019        break;
3020      case Expose:
3021        break;
3022      case KeyPress:
3023      {
3024        char
3025          command[MagickPathExtent];
3026
3027        KeySym
3028          key_symbol;
3029
3030        if (event.xkey.window != windows->image.id)
3031          break;
3032        /*
3033          Respond to a user key press.
3034        */
3035        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3036          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3037        switch ((int) key_symbol)
3038        {
3039          case XK_Escape:
3040          case XK_F20:
3041          {
3042            /*
3043              Prematurely exit.
3044            */
3045            state|=EscapeState;
3046            state|=ExitState;
3047            break;
3048          }
3049          case XK_F1:
3050          case XK_Help:
3051          {
3052            (void) XSetFunction(display,windows->image.highlight_context,
3053              GXcopy);
3054            XTextViewWidget(display,resource_info,windows,MagickFalse,
3055              "Help Viewer - Image Chop",ImageChopHelp);
3056            (void) XSetFunction(display,windows->image.highlight_context,
3057              GXinvert);
3058            break;
3059          }
3060          default:
3061          {
3062            (void) XBell(display,0);
3063            break;
3064          }
3065        }
3066        break;
3067      }
3068      case MotionNotify:
3069      {
3070        /*
3071          Map and unmap Info widget as text cursor crosses its boundaries.
3072        */
3073        x=event.xmotion.x;
3074        y=event.xmotion.y;
3075        if (IfMagickTrue(windows->info.mapped) )
3076          {
3077            if ((x < (int) (windows->info.x+windows->info.width)) &&
3078                (y < (int) (windows->info.y+windows->info.height)))
3079              (void) XWithdrawWindow(display,windows->info.id,
3080                windows->info.screen);
3081          }
3082        else
3083          if ((x > (int) (windows->info.x+windows->info.width)) ||
3084              (y > (int) (windows->info.y+windows->info.height)))
3085            (void) XMapWindow(display,windows->info.id);
3086      }
3087    }
3088  } while ((state & ExitState) == 0);
3089  (void) XSelectInput(display,windows->image.id,
3090    windows->image.attributes.event_mask);
3091  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3092  if ((state & EscapeState) != 0)
3093    return(MagickTrue);
3094  /*
3095    Draw line as pointer moves until the mouse button is released.
3096  */
3097  chop_info.width=0;
3098  chop_info.height=0;
3099  chop_info.x=0;
3100  chop_info.y=0;
3101  distance=0;
3102  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3103  state=DefaultState;
3104  do
3105  {
3106    if (distance > 9)
3107      {
3108        /*
3109          Display info and draw chopping line.
3110        */
3111        if (IfMagickFalse(windows->info.mapped) )
3112          (void) XMapWindow(display,windows->info.id);
3113        (void) FormatLocaleString(text,MagickPathExtent,
3114          " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3115          chop_info.height,(double) chop_info.x,(double) chop_info.y);
3116        XInfoWidget(display,windows,text);
3117        XHighlightLine(display,windows->image.id,
3118          windows->image.highlight_context,&segment_info);
3119      }
3120    else
3121      if (IfMagickTrue(windows->info.mapped) )
3122        (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3123    /*
3124      Wait for next event.
3125    */
3126    XScreenEvent(display,windows,&event,exception);
3127    if (distance > 9)
3128      XHighlightLine(display,windows->image.id,
3129        windows->image.highlight_context,&segment_info);
3130    switch (event.type)
3131    {
3132      case ButtonPress:
3133      {
3134        segment_info.x2=(short int) event.xmotion.x;
3135        segment_info.y2=(short int) event.xmotion.y;
3136        break;
3137      }
3138      case ButtonRelease:
3139      {
3140        /*
3141          User has committed to chopping line.
3142        */
3143        segment_info.x2=(short int) event.xbutton.x;
3144        segment_info.y2=(short int) event.xbutton.y;
3145        state|=ExitState;
3146        break;
3147      }
3148      case Expose:
3149        break;
3150      case MotionNotify:
3151      {
3152        segment_info.x2=(short int) event.xmotion.x;
3153        segment_info.y2=(short int) event.xmotion.y;
3154      }
3155      default:
3156        break;
3157    }
3158    /*
3159      Check boundary conditions.
3160    */
3161    if (segment_info.x2 < 0)
3162      segment_info.x2=0;
3163    else
3164      if (segment_info.x2 > windows->image.ximage->width)
3165        segment_info.x2=windows->image.ximage->width;
3166    if (segment_info.y2 < 0)
3167      segment_info.y2=0;
3168    else
3169      if (segment_info.y2 > windows->image.ximage->height)
3170        segment_info.y2=windows->image.ximage->height;
3171    distance=(unsigned int)
3172      (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3173       ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3174    /*
3175      Compute chopping geometry.
3176    */
3177    if (direction == HorizontalChopCommand)
3178      {
3179        chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3180        chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3181        chop_info.height=0;
3182        chop_info.y=0;
3183        if (segment_info.x1 > (int) segment_info.x2)
3184          {
3185            chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3186            chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3187          }
3188      }
3189    else
3190      {
3191        chop_info.width=0;
3192        chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3193        chop_info.x=0;
3194        chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3195        if (segment_info.y1 > segment_info.y2)
3196          {
3197            chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3198            chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3199          }
3200      }
3201  } while ((state & ExitState) == 0);
3202  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3203  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3204  if (distance <= 9)
3205    return(MagickTrue);
3206  /*
3207    Image chopping is relative to image configuration.
3208  */
3209  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
3210    exception);
3211  XSetCursorState(display,windows,MagickTrue);
3212  XCheckRefreshWindows(display,windows);
3213  windows->image.window_changes.width=windows->image.ximage->width-
3214    (unsigned int) chop_info.width;
3215  windows->image.window_changes.height=windows->image.ximage->height-
3216    (unsigned int) chop_info.height;
3217  width=(unsigned int) (*image)->columns;
3218  height=(unsigned int) (*image)->rows;
3219  x=0;
3220  y=0;
3221  if (windows->image.crop_geometry != (char *) NULL)
3222    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3223  scale_factor=(double) width/windows->image.ximage->width;
3224  chop_info.x+=x;
3225  chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3226  chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3227  scale_factor=(double) height/windows->image.ximage->height;
3228  chop_info.y+=y;
3229  chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3230  chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3231  /*
3232    Chop image.
3233  */
3234  chop_image=ChopImage(*image,&chop_info,exception);
3235  XSetCursorState(display,windows,MagickFalse);
3236  if (chop_image == (Image *) NULL)
3237    return(MagickFalse);
3238  *image=DestroyImage(*image);
3239  *image=chop_image;
3240  /*
3241    Update image configuration.
3242  */
3243  XConfigureImageColormap(display,resource_info,windows,*image,exception);
3244  (void) XConfigureImage(display,resource_info,windows,*image,exception);
3245  return(MagickTrue);
3246}
3247
3248/*
3249%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3250%                                                                             %
3251%                                                                             %
3252%                                                                             %
3253+   X C o l o r E d i t I m a g e                                             %
3254%                                                                             %
3255%                                                                             %
3256%                                                                             %
3257%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3258%
3259%  XColorEditImage() allows the user to interactively change the color of one
3260%  pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3261%
3262%  The format of the XColorEditImage method is:
3263%
3264%      MagickBooleanType XColorEditImage(Display *display,
3265%        XResourceInfo *resource_info,XWindows *windows,Image **image,
3266%          ExceptionInfo *exception)
3267%
3268%  A description of each parameter follows:
3269%
3270%    o display: Specifies a connection to an X server;  returned from
3271%      XOpenDisplay.
3272%
3273%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3274%
3275%    o windows: Specifies a pointer to a XWindows structure.
3276%
3277%    o image: the image; returned from ReadImage.
3278%
3279%    o exception: return any errors or warnings in this structure.
3280%
3281*/
3282static MagickBooleanType XColorEditImage(Display *display,
3283  XResourceInfo *resource_info,XWindows *windows,Image **image,
3284  ExceptionInfo *exception)
3285{
3286  static const char
3287    *ColorEditMenu[] =
3288    {
3289      "Method",
3290      "Pixel Color",
3291      "Border Color",
3292      "Fuzz",
3293      "Undo",
3294      "Help",
3295      "Dismiss",
3296      (char *) NULL
3297    };
3298
3299  static const ModeType
3300    ColorEditCommands[] =
3301    {
3302      ColorEditMethodCommand,
3303      ColorEditColorCommand,
3304      ColorEditBorderCommand,
3305      ColorEditFuzzCommand,
3306      ColorEditUndoCommand,
3307      ColorEditHelpCommand,
3308      ColorEditDismissCommand
3309    };
3310
3311  static PaintMethod
3312    method = PointMethod;
3313
3314  static unsigned int
3315    pen_id = 0;
3316
3317  static XColor
3318    border_color = { 0, 0, 0, 0, 0, 0 };
3319
3320  char
3321    command[MagickPathExtent],
3322    text[MagickPathExtent];
3323
3324  Cursor
3325    cursor;
3326
3327  int
3328    entry,
3329    id,
3330    x,
3331    x_offset,
3332    y,
3333    y_offset;
3334
3335  register Quantum
3336    *q;
3337
3338  register ssize_t
3339    i;
3340
3341  unsigned int
3342    height,
3343    width;
3344
3345  size_t
3346    state;
3347
3348  XColor
3349    color;
3350
3351  XEvent
3352    event;
3353
3354  /*
3355    Map Command widget.
3356  */
3357  (void) CloneString(&windows->command.name,"Color Edit");
3358  windows->command.data=4;
3359  (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3360  (void) XMapRaised(display,windows->command.id);
3361  XClientMessage(display,windows->image.id,windows->im_protocols,
3362    windows->im_update_widget,CurrentTime);
3363  /*
3364    Make cursor.
3365  */
3366  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3367    resource_info->background_color,resource_info->foreground_color);
3368  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3369  /*
3370    Track pointer until button 1 is pressed.
3371  */
3372  XQueryPosition(display,windows->image.id,&x,&y);
3373  (void) XSelectInput(display,windows->image.id,
3374    windows->image.attributes.event_mask | PointerMotionMask);
3375  state=DefaultState;
3376  do
3377  {
3378    if (IfMagickTrue(windows->info.mapped) )
3379      {
3380        /*
3381          Display pointer position.
3382        */
3383        (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
3384          x+windows->image.x,y+windows->image.y);
3385        XInfoWidget(display,windows,text);
3386      }
3387    /*
3388      Wait for next event.
3389    */
3390    XScreenEvent(display,windows,&event,exception);
3391    if (event.xany.window == windows->command.id)
3392      {
3393        /*
3394          Select a command from the Command widget.
3395        */
3396        id=XCommandWidget(display,windows,ColorEditMenu,&event);
3397        if (id < 0)
3398          {
3399            (void) XCheckDefineCursor(display,windows->image.id,cursor);
3400            continue;
3401          }
3402        switch (ColorEditCommands[id])
3403        {
3404          case ColorEditMethodCommand:
3405          {
3406            char
3407              **methods;
3408
3409            /*
3410              Select a method from the pop-up menu.
3411            */
3412            methods=(char **) GetCommandOptions(MagickMethodOptions);
3413            if (methods == (char **) NULL)
3414              break;
3415            entry=XMenuWidget(display,windows,ColorEditMenu[id],
3416              (const char **) methods,command);
3417            if (entry >= 0)
3418              method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3419                MagickFalse,methods[entry]);
3420            methods=DestroyStringList(methods);
3421            break;
3422          }
3423          case ColorEditColorCommand:
3424          {
3425            const char
3426              *ColorMenu[MaxNumberPens];
3427
3428            int
3429              pen_number;
3430
3431            /*
3432              Initialize menu selections.
3433            */
3434            for (i=0; i < (int) (MaxNumberPens-2); i++)
3435              ColorMenu[i]=resource_info->pen_colors[i];
3436            ColorMenu[MaxNumberPens-2]="Browser...";
3437            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3438            /*
3439              Select a pen color from the pop-up menu.
3440            */
3441            pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3442              (const char **) ColorMenu,command);
3443            if (pen_number < 0)
3444              break;
3445            if (pen_number == (MaxNumberPens-2))
3446              {
3447                static char
3448                  color_name[MagickPathExtent] = "gray";
3449
3450                /*
3451                  Select a pen color from a dialog.
3452                */
3453                resource_info->pen_colors[pen_number]=color_name;
3454                XColorBrowserWidget(display,windows,"Select",color_name);
3455                if (*color_name == '\0')
3456                  break;
3457              }
3458            /*
3459              Set pen color.
3460            */
3461            (void) XParseColor(display,windows->map_info->colormap,
3462              resource_info->pen_colors[pen_number],&color);
3463            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3464              (unsigned int) MaxColors,&color);
3465            windows->pixel_info->pen_colors[pen_number]=color;
3466            pen_id=(unsigned int) pen_number;
3467            break;
3468          }
3469          case ColorEditBorderCommand:
3470          {
3471            const char
3472              *ColorMenu[MaxNumberPens];
3473
3474            int
3475              pen_number;
3476
3477            /*
3478              Initialize menu selections.
3479            */
3480            for (i=0; i < (int) (MaxNumberPens-2); i++)
3481              ColorMenu[i]=resource_info->pen_colors[i];
3482            ColorMenu[MaxNumberPens-2]="Browser...";
3483            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3484            /*
3485              Select a pen color from the pop-up menu.
3486            */
3487            pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3488              (const char **) ColorMenu,command);
3489            if (pen_number < 0)
3490              break;
3491            if (pen_number == (MaxNumberPens-2))
3492              {
3493                static char
3494                  color_name[MagickPathExtent] = "gray";
3495
3496                /*
3497                  Select a pen color from a dialog.
3498                */
3499                resource_info->pen_colors[pen_number]=color_name;
3500                XColorBrowserWidget(display,windows,"Select",color_name);
3501                if (*color_name == '\0')
3502                  break;
3503              }
3504            /*
3505              Set border color.
3506            */
3507            (void) XParseColor(display,windows->map_info->colormap,
3508              resource_info->pen_colors[pen_number],&border_color);
3509            break;
3510          }
3511          case ColorEditFuzzCommand:
3512          {
3513            static char
3514              fuzz[MagickPathExtent];
3515
3516            static const char
3517              *FuzzMenu[] =
3518              {
3519                "0%",
3520                "2%",
3521                "5%",
3522                "10%",
3523                "15%",
3524                "Dialog...",
3525                (char *) NULL,
3526              };
3527
3528            /*
3529              Select a command from the pop-up menu.
3530            */
3531            entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3532              command);
3533            if (entry < 0)
3534              break;
3535            if (entry != 5)
3536              {
3537                (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
3538                  QuantumRange+1.0);
3539                break;
3540              }
3541            (void) (void) CopyMagickString(fuzz,"20%",MagickPathExtent);
3542            (void) XDialogWidget(display,windows,"Ok",
3543              "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3544            if (*fuzz == '\0')
3545              break;
3546            (void) ConcatenateMagickString(fuzz,"%",MagickPathExtent);
3547            (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
3548              1.0);
3549            break;
3550          }
3551          case ColorEditUndoCommand:
3552          {
3553            (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3554              image,exception);
3555            break;
3556          }
3557          case ColorEditHelpCommand:
3558          default:
3559          {
3560            XTextViewWidget(display,resource_info,windows,MagickFalse,
3561              "Help Viewer - Image Annotation",ImageColorEditHelp);
3562            break;
3563          }
3564          case ColorEditDismissCommand:
3565          {
3566            /*
3567              Prematurely exit.
3568            */
3569            state|=EscapeState;
3570            state|=ExitState;
3571            break;
3572          }
3573        }
3574        (void) XCheckDefineCursor(display,windows->image.id,cursor);
3575        continue;
3576      }
3577    switch (event.type)
3578    {
3579      case ButtonPress:
3580      {
3581        if (event.xbutton.button != Button1)
3582          break;
3583        if ((event.xbutton.window != windows->image.id) &&
3584            (event.xbutton.window != windows->magnify.id))
3585          break;
3586        /*
3587          exit loop.
3588        */
3589        x=event.xbutton.x;
3590        y=event.xbutton.y;
3591        (void) XMagickCommand(display,resource_info,windows,
3592          SaveToUndoBufferCommand,image,exception);
3593        state|=UpdateConfigurationState;
3594        break;
3595      }
3596      case ButtonRelease:
3597      {
3598        if (event.xbutton.button != Button1)
3599          break;
3600        if ((event.xbutton.window != windows->image.id) &&
3601            (event.xbutton.window != windows->magnify.id))
3602          break;
3603        /*
3604          Update colormap information.
3605        */
3606        x=event.xbutton.x;
3607        y=event.xbutton.y;
3608        XConfigureImageColormap(display,resource_info,windows,*image,exception);
3609        (void) XConfigureImage(display,resource_info,windows,*image,exception);
3610        XInfoWidget(display,windows,text);
3611        (void) XCheckDefineCursor(display,windows->image.id,cursor);
3612        state&=(~UpdateConfigurationState);
3613        break;
3614      }
3615      case Expose:
3616        break;
3617      case KeyPress:
3618      {
3619        KeySym
3620          key_symbol;
3621
3622        if (event.xkey.window == windows->magnify.id)
3623          {
3624            Window
3625              window;
3626
3627            window=windows->magnify.id;
3628            while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3629          }
3630        if (event.xkey.window != windows->image.id)
3631          break;
3632        /*
3633          Respond to a user key press.
3634        */
3635        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3636          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3637        switch ((int) key_symbol)
3638        {
3639          case XK_Escape:
3640          case XK_F20:
3641          {
3642            /*
3643              Prematurely exit.
3644            */
3645            state|=ExitState;
3646            break;
3647          }
3648          case XK_F1:
3649          case XK_Help:
3650          {
3651            XTextViewWidget(display,resource_info,windows,MagickFalse,
3652              "Help Viewer - Image Annotation",ImageColorEditHelp);
3653            break;
3654          }
3655          default:
3656          {
3657            (void) XBell(display,0);
3658            break;
3659          }
3660        }
3661        break;
3662      }
3663      case MotionNotify:
3664      {
3665        /*
3666          Map and unmap Info widget as cursor crosses its boundaries.
3667        */
3668        x=event.xmotion.x;
3669        y=event.xmotion.y;
3670        if (IfMagickTrue(windows->info.mapped) )
3671          {
3672            if ((x < (int) (windows->info.x+windows->info.width)) &&
3673                (y < (int) (windows->info.y+windows->info.height)))
3674              (void) XWithdrawWindow(display,windows->info.id,
3675                windows->info.screen);
3676          }
3677        else
3678          if ((x > (int) (windows->info.x+windows->info.width)) ||
3679              (y > (int) (windows->info.y+windows->info.height)))
3680            (void) XMapWindow(display,windows->info.id);
3681        break;
3682      }
3683      default:
3684        break;
3685    }
3686    if (event.xany.window == windows->magnify.id)
3687      {
3688        x=windows->magnify.x-windows->image.x;
3689        y=windows->magnify.y-windows->image.y;
3690      }
3691    x_offset=x;
3692    y_offset=y;
3693    if ((state & UpdateConfigurationState) != 0)
3694      {
3695        CacheView
3696          *image_view;
3697
3698        int
3699          x,
3700          y;
3701
3702        /*
3703          Pixel edit is relative to image configuration.
3704        */
3705        (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3706          MagickTrue);
3707        color=windows->pixel_info->pen_colors[pen_id];
3708        XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3709        width=(unsigned int) (*image)->columns;
3710        height=(unsigned int) (*image)->rows;
3711        x=0;
3712        y=0;
3713        if (windows->image.crop_geometry != (char *) NULL)
3714          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3715            &width,&height);
3716        x_offset=(int)
3717          (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3718        y_offset=(int)
3719          (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3720        if ((x_offset < 0) || (y_offset < 0))
3721          continue;
3722        if ((x_offset >= (int) (*image)->columns) ||
3723            (y_offset >= (int) (*image)->rows))
3724          continue;
3725        image_view=AcquireAuthenticCacheView(*image,exception);
3726        switch (method)
3727        {
3728          case PointMethod:
3729          default:
3730          {
3731            /*
3732              Update color information using point algorithm.
3733            */
3734            if (IfMagickFalse(SetImageStorageClass(*image,DirectClass,exception)) )
3735              return(MagickFalse);
3736            q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3737              (ssize_t) y_offset,1,1,exception);
3738            if (q == (Quantum *) NULL)
3739              break;
3740            SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3741            SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3742            SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3743            (void) SyncCacheViewAuthenticPixels(image_view,exception);
3744            break;
3745          }
3746          case ReplaceMethod:
3747          {
3748            PixelInfo
3749              pixel,
3750              target;
3751
3752            /*
3753              Update color information using replace algorithm.
3754            */
3755            (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
3756              x_offset,(ssize_t) y_offset,&target,exception);
3757            if ((*image)->storage_class == DirectClass)
3758              {
3759                for (y=0; y < (int) (*image)->rows; y++)
3760                {
3761                  q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3762                    (*image)->columns,1,exception);
3763                  if (q == (Quantum *) NULL)
3764                    break;
3765                  for (x=0; x < (int) (*image)->columns; x++)
3766                  {
3767                    GetPixelInfoPixel(*image,q,&pixel);
3768                    if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
3769                      {
3770                        SetPixelRed(*image,ScaleShortToQuantum(
3771                          color.red),q);
3772                        SetPixelGreen(*image,ScaleShortToQuantum(
3773                          color.green),q);
3774                        SetPixelBlue(*image,ScaleShortToQuantum(
3775                          color.blue),q);
3776                      }
3777                    q+=GetPixelChannels(*image);
3778                  }
3779                  if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
3780                    break;
3781                }
3782              }
3783            else
3784              {
3785                for (i=0; i < (ssize_t) (*image)->colors; i++)
3786                  if (IsFuzzyEquivalencePixelInfo((*image)->colormap+i,&target))
3787                    {
3788                      (*image)->colormap[i].red=(double) ScaleShortToQuantum(
3789                        color.red);
3790                      (*image)->colormap[i].green=(double) ScaleShortToQuantum(
3791                        color.green);
3792                      (*image)->colormap[i].blue=(double) ScaleShortToQuantum(
3793                        color.blue);
3794                    }
3795                (void) SyncImage(*image,exception);
3796              }
3797            break;
3798          }
3799          case FloodfillMethod:
3800          case FillToBorderMethod:
3801          {
3802            DrawInfo
3803              *draw_info;
3804
3805            PixelInfo
3806              target;
3807
3808            /*
3809              Update color information using floodfill algorithm.
3810            */
3811            (void) GetOneVirtualPixelInfo(*image,
3812              GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
3813              y_offset,&target,exception);
3814            if (method == FillToBorderMethod)
3815              {
3816                target.red=(double)
3817                  ScaleShortToQuantum(border_color.red);
3818                target.green=(double)
3819                  ScaleShortToQuantum(border_color.green);
3820                target.blue=(double)
3821                  ScaleShortToQuantum(border_color.blue);
3822              }
3823            draw_info=CloneDrawInfo(resource_info->image_info,
3824              (DrawInfo *) NULL);
3825            (void) QueryColorCompliance(resource_info->pen_colors[pen_id],
3826              AllCompliance,&draw_info->fill,exception);
3827            (void) FloodfillPaintImage(*image,draw_info,&target,
3828              (ssize_t)x_offset,(ssize_t)y_offset,
3829              method != FloodfillMethod ? MagickTrue : MagickFalse,exception);
3830            draw_info=DestroyDrawInfo(draw_info);
3831            break;
3832          }
3833          case ResetMethod:
3834          {
3835            /*
3836              Update color information using reset algorithm.
3837            */
3838            if (IfMagickFalse(SetImageStorageClass(*image,DirectClass,exception)) )
3839              return(MagickFalse);
3840            for (y=0; y < (int) (*image)->rows; y++)
3841            {
3842              q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3843                (*image)->columns,1,exception);
3844              if (q == (Quantum *) NULL)
3845                break;
3846              for (x=0; x < (int) (*image)->columns; x++)
3847              {
3848                SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3849                SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3850                SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3851                q+=GetPixelChannels(*image);
3852              }
3853              if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
3854                break;
3855            }
3856            break;
3857          }
3858        }
3859        image_view=DestroyCacheView(image_view);
3860        state&=(~UpdateConfigurationState);
3861      }
3862  } while ((state & ExitState) == 0);
3863  (void) XSelectInput(display,windows->image.id,
3864    windows->image.attributes.event_mask);
3865  XSetCursorState(display,windows,MagickFalse);
3866  (void) XFreeCursor(display,cursor);
3867  return(MagickTrue);
3868}
3869
3870/*
3871%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3872%                                                                             %
3873%                                                                             %
3874%                                                                             %
3875+   X C o m p o s i t e I m a g e                                             %
3876%                                                                             %
3877%                                                                             %
3878%                                                                             %
3879%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3880%
3881%  XCompositeImage() requests an image name from the user, reads the image and
3882%  composites it with the X window image at a location the user chooses with
3883%  the pointer.
3884%
3885%  The format of the XCompositeImage method is:
3886%
3887%      MagickBooleanType XCompositeImage(Display *display,
3888%        XResourceInfo *resource_info,XWindows *windows,Image *image,
3889%        ExceptionInfo *exception)
3890%
3891%  A description of each parameter follows:
3892%
3893%    o display: Specifies a connection to an X server;  returned from
3894%      XOpenDisplay.
3895%
3896%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3897%
3898%    o windows: Specifies a pointer to a XWindows structure.
3899%
3900%    o image: the image; returned from ReadImage.
3901%
3902%    o exception: return any errors or warnings in this structure.
3903%
3904*/
3905static MagickBooleanType XCompositeImage(Display *display,
3906  XResourceInfo *resource_info,XWindows *windows,Image *image,
3907  ExceptionInfo *exception)
3908{
3909  static char
3910    displacement_geometry[MagickPathExtent] = "30x30",
3911    filename[MagickPathExtent] = "\0";
3912
3913  static const char
3914    *CompositeMenu[] =
3915    {
3916      "Operators",
3917      "Dissolve",
3918      "Displace",
3919      "Help",
3920      "Dismiss",
3921      (char *) NULL
3922    };
3923
3924  static CompositeOperator
3925    compose = CopyCompositeOp;
3926
3927  static const ModeType
3928    CompositeCommands[] =
3929    {
3930      CompositeOperatorsCommand,
3931      CompositeDissolveCommand,
3932      CompositeDisplaceCommand,
3933      CompositeHelpCommand,
3934      CompositeDismissCommand
3935    };
3936
3937  char
3938    text[MagickPathExtent];
3939
3940  Cursor
3941    cursor;
3942
3943  Image
3944    *composite_image;
3945
3946  int
3947    entry,
3948    id,
3949    x,
3950    y;
3951
3952  double
3953    blend,
3954    scale_factor;
3955
3956  RectangleInfo
3957    highlight_info,
3958    composite_info;
3959
3960  unsigned int
3961    height,
3962    width;
3963
3964  size_t
3965    state;
3966
3967  XEvent
3968    event;
3969
3970  /*
3971    Request image file name from user.
3972  */
3973  XFileBrowserWidget(display,windows,"Composite",filename);
3974  if (*filename == '\0')
3975    return(MagickTrue);
3976  /*
3977    Read image.
3978  */
3979  XSetCursorState(display,windows,MagickTrue);
3980  XCheckRefreshWindows(display,windows);
3981  (void) CopyMagickString(resource_info->image_info->filename,filename,
3982    MagickPathExtent);
3983  composite_image=ReadImage(resource_info->image_info,exception);
3984  CatchException(exception);
3985  XSetCursorState(display,windows,MagickFalse);
3986  if (composite_image == (Image *) NULL)
3987    return(MagickFalse);
3988  /*
3989    Map Command widget.
3990  */
3991  (void) CloneString(&windows->command.name,"Composite");
3992  windows->command.data=1;
3993  (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
3994  (void) XMapRaised(display,windows->command.id);
3995  XClientMessage(display,windows->image.id,windows->im_protocols,
3996    windows->im_update_widget,CurrentTime);
3997  /*
3998    Track pointer until button 1 is pressed.
3999  */
4000  XQueryPosition(display,windows->image.id,&x,&y);
4001  (void) XSelectInput(display,windows->image.id,
4002    windows->image.attributes.event_mask | PointerMotionMask);
4003  composite_info.x=(ssize_t) windows->image.x+x;
4004  composite_info.y=(ssize_t) windows->image.y+y;
4005  composite_info.width=0;
4006  composite_info.height=0;
4007  cursor=XCreateFontCursor(display,XC_ul_angle);
4008  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4009  blend=0.0;
4010  state=DefaultState;
4011  do
4012  {
4013    if (IfMagickTrue(windows->info.mapped) )
4014      {
4015        /*
4016          Display pointer position.
4017        */
4018        (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
4019          (long) composite_info.x,(long) composite_info.y);
4020        XInfoWidget(display,windows,text);
4021      }
4022    highlight_info=composite_info;
4023    highlight_info.x=composite_info.x-windows->image.x;
4024    highlight_info.y=composite_info.y-windows->image.y;
4025    XHighlightRectangle(display,windows->image.id,
4026      windows->image.highlight_context,&highlight_info);
4027    /*
4028      Wait for next event.
4029    */
4030    XScreenEvent(display,windows,&event,exception);
4031    XHighlightRectangle(display,windows->image.id,
4032      windows->image.highlight_context,&highlight_info);
4033    if (event.xany.window == windows->command.id)
4034      {
4035        /*
4036          Select a command from the Command widget.
4037        */
4038        id=XCommandWidget(display,windows,CompositeMenu,&event);
4039        if (id < 0)
4040          continue;
4041        switch (CompositeCommands[id])
4042        {
4043          case CompositeOperatorsCommand:
4044          {
4045            char
4046              command[MagickPathExtent],
4047              **operators;
4048
4049            /*
4050              Select a command from the pop-up menu.
4051            */
4052            operators=GetCommandOptions(MagickComposeOptions);
4053            if (operators == (char **) NULL)
4054              break;
4055            entry=XMenuWidget(display,windows,CompositeMenu[id],
4056              (const char **) operators,command);
4057            if (entry >= 0)
4058              compose=(CompositeOperator) ParseCommandOption(
4059                MagickComposeOptions,MagickFalse,operators[entry]);
4060            operators=DestroyStringList(operators);
4061            break;
4062          }
4063          case CompositeDissolveCommand:
4064          {
4065            static char
4066              factor[MagickPathExtent] = "20.0";
4067
4068            /*
4069              Dissolve the two images a given percent.
4070            */
4071            (void) XSetFunction(display,windows->image.highlight_context,
4072              GXcopy);
4073            (void) XDialogWidget(display,windows,"Dissolve",
4074              "Enter the blend factor (0.0 - 99.9%):",factor);
4075            (void) XSetFunction(display,windows->image.highlight_context,
4076              GXinvert);
4077            if (*factor == '\0')
4078              break;
4079            blend=StringToDouble(factor,(char **) NULL);
4080            compose=DissolveCompositeOp;
4081            break;
4082          }
4083          case CompositeDisplaceCommand:
4084          {
4085            /*
4086              Get horizontal and vertical scale displacement geometry.
4087            */
4088            (void) XSetFunction(display,windows->image.highlight_context,
4089              GXcopy);
4090            (void) XDialogWidget(display,windows,"Displace",
4091              "Enter the horizontal and vertical scale:",displacement_geometry);
4092            (void) XSetFunction(display,windows->image.highlight_context,
4093              GXinvert);
4094            if (*displacement_geometry == '\0')
4095              break;
4096            compose=DisplaceCompositeOp;
4097            break;
4098          }
4099          case CompositeHelpCommand:
4100          {
4101            (void) XSetFunction(display,windows->image.highlight_context,
4102              GXcopy);
4103            XTextViewWidget(display,resource_info,windows,MagickFalse,
4104              "Help Viewer - Image Composite",ImageCompositeHelp);
4105            (void) XSetFunction(display,windows->image.highlight_context,
4106              GXinvert);
4107            break;
4108          }
4109          case CompositeDismissCommand:
4110          {
4111            /*
4112              Prematurely exit.
4113            */
4114            state|=EscapeState;
4115            state|=ExitState;
4116            break;
4117          }
4118          default:
4119            break;
4120        }
4121        continue;
4122      }
4123    switch (event.type)
4124    {
4125      case ButtonPress:
4126      {
4127        if (IfMagickTrue(image->debug) )
4128          (void) LogMagickEvent(X11Event,GetMagickModule(),
4129            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4130            event.xbutton.button,event.xbutton.x,event.xbutton.y);
4131        if (event.xbutton.button != Button1)
4132          break;
4133        if (event.xbutton.window != windows->image.id)
4134          break;
4135        /*
4136          Change cursor.
4137        */
4138        composite_info.width=composite_image->columns;
4139        composite_info.height=composite_image->rows;
4140        (void) XCheckDefineCursor(display,windows->image.id,cursor);
4141        composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4142        composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4143        break;
4144      }
4145      case ButtonRelease:
4146      {
4147        if (IfMagickTrue(image->debug) )
4148          (void) LogMagickEvent(X11Event,GetMagickModule(),
4149            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4150            event.xbutton.button,event.xbutton.x,event.xbutton.y);
4151        if (event.xbutton.button != Button1)
4152          break;
4153        if (event.xbutton.window != windows->image.id)
4154          break;
4155        if ((composite_info.width != 0) && (composite_info.height != 0))
4156          {
4157            /*
4158              User has selected the location of the composite image.
4159            */
4160            composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4161            composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4162            state|=ExitState;
4163          }
4164        break;
4165      }
4166      case Expose:
4167        break;
4168      case KeyPress:
4169      {
4170        char
4171          command[MagickPathExtent];
4172
4173        KeySym
4174          key_symbol;
4175
4176        int
4177          length;
4178
4179        if (event.xkey.window != windows->image.id)
4180          break;
4181        /*
4182          Respond to a user key press.
4183        */
4184        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4185          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4186        *(command+length)='\0';
4187        if (IfMagickTrue(image->debug) )
4188          (void) LogMagickEvent(X11Event,GetMagickModule(),
4189            "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4190        switch ((int) key_symbol)
4191        {
4192          case XK_Escape:
4193          case XK_F20:
4194          {
4195            /*
4196              Prematurely exit.
4197            */
4198            composite_image=DestroyImage(composite_image);
4199            state|=EscapeState;
4200            state|=ExitState;
4201            break;
4202          }
4203          case XK_F1:
4204          case XK_Help:
4205          {
4206            (void) XSetFunction(display,windows->image.highlight_context,
4207              GXcopy);
4208            XTextViewWidget(display,resource_info,windows,MagickFalse,
4209              "Help Viewer - Image Composite",ImageCompositeHelp);
4210            (void) XSetFunction(display,windows->image.highlight_context,
4211              GXinvert);
4212            break;
4213          }
4214          default:
4215          {
4216            (void) XBell(display,0);
4217            break;
4218          }
4219        }
4220        break;
4221      }
4222      case MotionNotify:
4223      {
4224        /*
4225          Map and unmap Info widget as text cursor crosses its boundaries.
4226        */
4227        x=event.xmotion.x;
4228        y=event.xmotion.y;
4229        if (IfMagickTrue(windows->info.mapped) )
4230          {
4231            if ((x < (int) (windows->info.x+windows->info.width)) &&
4232                (y < (int) (windows->info.y+windows->info.height)))
4233              (void) XWithdrawWindow(display,windows->info.id,
4234                windows->info.screen);
4235          }
4236        else
4237          if ((x > (int) (windows->info.x+windows->info.width)) ||
4238              (y > (int) (windows->info.y+windows->info.height)))
4239            (void) XMapWindow(display,windows->info.id);
4240        composite_info.x=(ssize_t) windows->image.x+x;
4241        composite_info.y=(ssize_t) windows->image.y+y;
4242        break;
4243      }
4244      default:
4245      {
4246        if (IfMagickTrue(image->debug) )
4247          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4248            event.type);
4249        break;
4250      }
4251    }
4252  } while ((state & ExitState) == 0);
4253  (void) XSelectInput(display,windows->image.id,
4254    windows->image.attributes.event_mask);
4255  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4256  XSetCursorState(display,windows,MagickFalse);
4257  (void) XFreeCursor(display,cursor);
4258  if ((state & EscapeState) != 0)
4259    return(MagickTrue);
4260  /*
4261    Image compositing is relative to image configuration.
4262  */
4263  XSetCursorState(display,windows,MagickTrue);
4264  XCheckRefreshWindows(display,windows);
4265  width=(unsigned int) image->columns;
4266  height=(unsigned int) image->rows;
4267  x=0;
4268  y=0;
4269  if (windows->image.crop_geometry != (char *) NULL)
4270    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4271  scale_factor=(double) width/windows->image.ximage->width;
4272  composite_info.x+=x;
4273  composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4274  composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4275  scale_factor=(double) height/windows->image.ximage->height;
4276  composite_info.y+=y;
4277  composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4278  composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4279  if ((composite_info.width != composite_image->columns) ||
4280      (composite_info.height != composite_image->rows))
4281    {
4282      Image
4283        *resize_image;
4284
4285      /*
4286        Scale composite image.
4287      */
4288      resize_image=ResizeImage(composite_image,composite_info.width,
4289        composite_info.height,composite_image->filter,exception);
4290      composite_image=DestroyImage(composite_image);
4291      if (resize_image == (Image *) NULL)
4292        {
4293          XSetCursorState(display,windows,MagickFalse);
4294          return(MagickFalse);
4295        }
4296      composite_image=resize_image;
4297    }
4298  if (compose == DisplaceCompositeOp)
4299    (void) SetImageArtifact(composite_image,"compose:args",
4300      displacement_geometry);
4301  if (blend != 0.0)
4302    {
4303      CacheView
4304        *image_view;
4305
4306      int
4307        y;
4308
4309      Quantum
4310        opacity;
4311
4312      register int
4313        x;
4314
4315      register Quantum
4316        *q;
4317
4318      /*
4319        Create mattes for blending.
4320      */
4321      (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,exception);
4322      opacity=(Quantum) (ScaleQuantumToChar(QuantumRange)-
4323        ((ssize_t) ScaleQuantumToChar(QuantumRange)*blend)/100);
4324      if (IfMagickFalse(SetImageStorageClass(image,DirectClass,exception)) )
4325        return(MagickFalse);
4326      image->alpha_trait=BlendPixelTrait;
4327      image_view=AcquireAuthenticCacheView(image,exception);
4328      for (y=0; y < (int) image->rows; y++)
4329      {
4330        q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4331          exception);
4332        if (q == (Quantum *) NULL)
4333          break;
4334        for (x=0; x < (int) image->columns; x++)
4335        {
4336          SetPixelAlpha(image,opacity,q);
4337          q+=GetPixelChannels(image);
4338        }
4339        if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
4340          break;
4341      }
4342      image_view=DestroyCacheView(image_view);
4343    }
4344  /*
4345    Composite image with X Image window.
4346  */
4347  (void) CompositeImage(image,composite_image,compose,MagickTrue,
4348    composite_info.x,composite_info.y,exception);
4349  composite_image=DestroyImage(composite_image);
4350  XSetCursorState(display,windows,MagickFalse);
4351  /*
4352    Update image configuration.
4353  */
4354  XConfigureImageColormap(display,resource_info,windows,image,exception);
4355  (void) XConfigureImage(display,resource_info,windows,image,exception);
4356  return(MagickTrue);
4357}
4358
4359/*
4360%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4361%                                                                             %
4362%                                                                             %
4363%                                                                             %
4364+   X C o n f i g u r e I m a g e                                             %
4365%                                                                             %
4366%                                                                             %
4367%                                                                             %
4368%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4369%
4370%  XConfigureImage() creates a new X image.  It also notifies the window
4371%  manager of the new image size and configures the transient widows.
4372%
4373%  The format of the XConfigureImage method is:
4374%
4375%      MagickBooleanType XConfigureImage(Display *display,
4376%        XResourceInfo *resource_info,XWindows *windows,Image *image,
4377%        ExceptionInfo *exception)
4378%
4379%  A description of each parameter follows:
4380%
4381%    o display: Specifies a connection to an X server; returned from
4382%      XOpenDisplay.
4383%
4384%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4385%
4386%    o windows: Specifies a pointer to a XWindows structure.
4387%
4388%    o image: the image.
4389%
4390%    o exception: return any errors or warnings in this structure.
4391%
4392%    o exception: return any errors or warnings in this structure.
4393%
4394*/
4395static MagickBooleanType XConfigureImage(Display *display,
4396  XResourceInfo *resource_info,XWindows *windows,Image *image,
4397  ExceptionInfo *exception)
4398{
4399  char
4400    geometry[MagickPathExtent];
4401
4402  MagickStatusType
4403    status;
4404
4405  size_t
4406    mask,
4407    height,
4408    width;
4409
4410  ssize_t
4411    x,
4412    y;
4413
4414  XSizeHints
4415    *size_hints;
4416
4417  XWindowChanges
4418    window_changes;
4419
4420  /*
4421    Dismiss if window dimensions are zero.
4422  */
4423  width=(unsigned int) windows->image.window_changes.width;
4424  height=(unsigned int) windows->image.window_changes.height;
4425  if (IfMagickTrue(image->debug) )
4426    (void) LogMagickEvent(X11Event,GetMagickModule(),
4427      "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4428      windows->image.ximage->height,(double) width,(double) height);
4429  if ((width*height) == 0)
4430    return(MagickTrue);
4431  x=0;
4432  y=0;
4433  /*
4434    Resize image to fit Image window dimensions.
4435  */
4436  XSetCursorState(display,windows,MagickTrue);
4437  (void) XFlush(display);
4438  if (((int) width != windows->image.ximage->width) ||
4439      ((int) height != windows->image.ximage->height))
4440    image->taint=MagickTrue;
4441  windows->magnify.x=(int)
4442    width*windows->magnify.x/windows->image.ximage->width;
4443  windows->magnify.y=(int)
4444    height*windows->magnify.y/windows->image.ximage->height;
4445  windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4446  windows->image.y=(int)
4447    (height*windows->image.y/windows->image.ximage->height);
4448  status=XMakeImage(display,resource_info,&windows->image,image,
4449    (unsigned int) width,(unsigned int) height,exception);
4450  if (IfMagickFalse(status) )
4451    XNoticeWidget(display,windows,"Unable to configure X image:",
4452      windows->image.name);
4453  /*
4454    Notify window manager of the new configuration.
4455  */
4456  if (resource_info->image_geometry != (char *) NULL)
4457    (void) FormatLocaleString(geometry,MagickPathExtent,"%s>!",
4458      resource_info->image_geometry);
4459  else
4460    (void) FormatLocaleString(geometry,MagickPathExtent,"%ux%u+0+0>!",
4461      XDisplayWidth(display,windows->image.screen),
4462      XDisplayHeight(display,windows->image.screen));
4463  (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4464  window_changes.width=(int) width;
4465  if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4466    window_changes.width=XDisplayWidth(display,windows->image.screen);
4467  window_changes.height=(int) height;
4468  if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4469    window_changes.height=XDisplayHeight(display,windows->image.screen);
4470  mask=(size_t) (CWWidth | CWHeight);
4471  if (resource_info->backdrop)
4472    {
4473      mask|=CWX | CWY;
4474      window_changes.x=(int)
4475        ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4476      window_changes.y=(int)
4477        ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4478    }
4479  (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4480    (unsigned int) mask,&window_changes);
4481  (void) XClearWindow(display,windows->image.id);
4482  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4483  /*
4484    Update Magnify window configuration.
4485  */
4486  if (IfMagickTrue(windows->magnify.mapped) )
4487    XMakeMagnifyImage(display,windows,exception);
4488  windows->pan.crop_geometry=windows->image.crop_geometry;
4489  XBestIconSize(display,&windows->pan,image);
4490  while (((windows->pan.width << 1) < MaxIconSize) &&
4491         ((windows->pan.height << 1) < MaxIconSize))
4492  {
4493    windows->pan.width<<=1;
4494    windows->pan.height<<=1;
4495  }
4496  if (windows->pan.geometry != (char *) NULL)
4497    (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4498      &windows->pan.width,&windows->pan.height);
4499  window_changes.width=(int) windows->pan.width;
4500  window_changes.height=(int) windows->pan.height;
4501  size_hints=XAllocSizeHints();
4502  if (size_hints != (XSizeHints *) NULL)
4503    {
4504      /*
4505        Set new size hints.
4506      */
4507      size_hints->flags=PSize | PMinSize | PMaxSize;
4508      size_hints->width=window_changes.width;
4509      size_hints->height=window_changes.height;
4510      size_hints->min_width=size_hints->width;
4511      size_hints->min_height=size_hints->height;
4512      size_hints->max_width=size_hints->width;
4513      size_hints->max_height=size_hints->height;
4514      (void) XSetNormalHints(display,windows->pan.id,size_hints);
4515      (void) XFree((void *) size_hints);
4516    }
4517  (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4518    (unsigned int) (CWWidth | CWHeight),&window_changes);
4519  /*
4520    Update icon window configuration.
4521  */
4522  windows->icon.crop_geometry=windows->image.crop_geometry;
4523  XBestIconSize(display,&windows->icon,image);
4524  window_changes.width=(int) windows->icon.width;
4525  window_changes.height=(int) windows->icon.height;
4526  (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4527    (unsigned int) (CWWidth | CWHeight),&window_changes);
4528  XSetCursorState(display,windows,MagickFalse);
4529  return(status != 0 ? MagickTrue : MagickFalse);
4530}
4531
4532/*
4533%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4534%                                                                             %
4535%                                                                             %
4536%                                                                             %
4537+   X C r o p I m a g e                                                       %
4538%                                                                             %
4539%                                                                             %
4540%                                                                             %
4541%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4542%
4543%  XCropImage() allows the user to select a region of the image and crop, copy,
4544%  or cut it.  For copy or cut, the image can subsequently be composited onto
4545%  the image with XPasteImage.
4546%
4547%  The format of the XCropImage method is:
4548%
4549%      MagickBooleanType XCropImage(Display *display,
4550%        XResourceInfo *resource_info,XWindows *windows,Image *image,
4551%        const ClipboardMode mode,ExceptionInfo *exception)
4552%
4553%  A description of each parameter follows:
4554%
4555%    o display: Specifies a connection to an X server; returned from
4556%      XOpenDisplay.
4557%
4558%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4559%
4560%    o windows: Specifies a pointer to a XWindows structure.
4561%
4562%    o image: the image; returned from ReadImage.
4563%
4564%    o mode: This unsigned value specified whether the image should be
4565%      cropped, copied, or cut.
4566%
4567%    o exception: return any errors or warnings in this structure.
4568%
4569*/
4570static MagickBooleanType XCropImage(Display *display,
4571  XResourceInfo *resource_info,XWindows *windows,Image *image,
4572  const ClipboardMode mode,ExceptionInfo *exception)
4573{
4574  static const char
4575    *CropModeMenu[] =
4576    {
4577      "Help",
4578      "Dismiss",
4579      (char *) NULL
4580    },
4581    *RectifyModeMenu[] =
4582    {
4583      "Crop",
4584      "Help",
4585      "Dismiss",
4586      (char *) NULL
4587    };
4588
4589  static const ModeType
4590    CropCommands[] =
4591    {
4592      CropHelpCommand,
4593      CropDismissCommand
4594    },
4595    RectifyCommands[] =
4596    {
4597      RectifyCopyCommand,
4598      RectifyHelpCommand,
4599      RectifyDismissCommand
4600    };
4601
4602  CacheView
4603    *image_view;
4604
4605  char
4606    command[MagickPathExtent],
4607    text[MagickPathExtent];
4608
4609  Cursor
4610    cursor;
4611
4612  int
4613    id,
4614    x,
4615    y;
4616
4617  KeySym
4618    key_symbol;
4619
4620  Image
4621    *crop_image;
4622
4623  double
4624    scale_factor;
4625
4626  RectangleInfo
4627    crop_info,
4628    highlight_info;
4629
4630  register Quantum
4631    *q;
4632
4633  unsigned int
4634    height,
4635    width;
4636
4637  size_t
4638    state;
4639
4640  XEvent
4641    event;
4642
4643  /*
4644    Map Command widget.
4645  */
4646  switch (mode)
4647  {
4648    case CopyMode:
4649    {
4650      (void) CloneString(&windows->command.name,"Copy");
4651      break;
4652    }
4653    case CropMode:
4654    {
4655      (void) CloneString(&windows->command.name,"Crop");
4656      break;
4657    }
4658    case CutMode:
4659    {
4660      (void) CloneString(&windows->command.name,"Cut");
4661      break;
4662    }
4663  }
4664  RectifyModeMenu[0]=windows->command.name;
4665  windows->command.data=0;
4666  (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4667  (void) XMapRaised(display,windows->command.id);
4668  XClientMessage(display,windows->image.id,windows->im_protocols,
4669    windows->im_update_widget,CurrentTime);
4670  /*
4671    Track pointer until button 1 is pressed.
4672  */
4673  XQueryPosition(display,windows->image.id,&x,&y);
4674  (void) XSelectInput(display,windows->image.id,
4675    windows->image.attributes.event_mask | PointerMotionMask);
4676  crop_info.x=(ssize_t) windows->image.x+x;
4677  crop_info.y=(ssize_t) windows->image.y+y;
4678  crop_info.width=0;
4679  crop_info.height=0;
4680  cursor=XCreateFontCursor(display,XC_fleur);
4681  state=DefaultState;
4682  do
4683  {
4684    if (IfMagickTrue(windows->info.mapped) )
4685      {
4686        /*
4687          Display pointer position.
4688        */
4689        (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
4690          (long) crop_info.x,(long) crop_info.y);
4691        XInfoWidget(display,windows,text);
4692      }
4693    /*
4694      Wait for next event.
4695    */
4696    XScreenEvent(display,windows,&event,exception);
4697    if (event.xany.window == windows->command.id)
4698      {
4699        /*
4700          Select a command from the Command widget.
4701        */
4702        id=XCommandWidget(display,windows,CropModeMenu,&event);
4703        if (id < 0)
4704          continue;
4705        switch (CropCommands[id])
4706        {
4707          case CropHelpCommand:
4708          {
4709            switch (mode)
4710            {
4711              case CopyMode:
4712              {
4713                XTextViewWidget(display,resource_info,windows,MagickFalse,
4714                  "Help Viewer - Image Copy",ImageCopyHelp);
4715                break;
4716              }
4717              case CropMode:
4718              {
4719                XTextViewWidget(display,resource_info,windows,MagickFalse,
4720                  "Help Viewer - Image Crop",ImageCropHelp);
4721                break;
4722              }
4723              case CutMode:
4724              {
4725                XTextViewWidget(display,resource_info,windows,MagickFalse,
4726                  "Help Viewer - Image Cut",ImageCutHelp);
4727                break;
4728              }
4729            }
4730            break;
4731          }
4732          case CropDismissCommand:
4733          {
4734            /*
4735              Prematurely exit.
4736            */
4737            state|=EscapeState;
4738            state|=ExitState;
4739            break;
4740          }
4741          default:
4742            break;
4743        }
4744        continue;
4745      }
4746    switch (event.type)
4747    {
4748      case ButtonPress:
4749      {
4750        if (event.xbutton.button != Button1)
4751          break;
4752        if (event.xbutton.window != windows->image.id)
4753          break;
4754        /*
4755          Note first corner of cropping rectangle-- exit loop.
4756        */
4757        (void) XCheckDefineCursor(display,windows->image.id,cursor);
4758        crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4759        crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4760        state|=ExitState;
4761        break;
4762      }
4763      case ButtonRelease:
4764        break;
4765      case Expose:
4766        break;
4767      case KeyPress:
4768      {
4769        if (event.xkey.window != windows->image.id)
4770          break;
4771        /*
4772          Respond to a user key press.
4773        */
4774        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4775          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4776        switch ((int) key_symbol)
4777        {
4778          case XK_Escape:
4779          case XK_F20:
4780          {
4781            /*
4782              Prematurely exit.
4783            */
4784            state|=EscapeState;
4785            state|=ExitState;
4786            break;
4787          }
4788          case XK_F1:
4789          case XK_Help:
4790          {
4791            switch (mode)
4792            {
4793              case CopyMode:
4794              {
4795                XTextViewWidget(display,resource_info,windows,MagickFalse,
4796                  "Help Viewer - Image Copy",ImageCopyHelp);
4797                break;
4798              }
4799              case CropMode:
4800              {
4801                XTextViewWidget(display,resource_info,windows,MagickFalse,
4802                  "Help Viewer - Image Crop",ImageCropHelp);
4803                break;
4804              }
4805              case CutMode:
4806              {
4807                XTextViewWidget(display,resource_info,windows,MagickFalse,
4808                  "Help Viewer - Image Cut",ImageCutHelp);
4809                break;
4810              }
4811            }
4812            break;
4813          }
4814          default:
4815          {
4816            (void) XBell(display,0);
4817            break;
4818          }
4819        }
4820        break;
4821      }
4822      case MotionNotify:
4823      {
4824        if (event.xmotion.window != windows->image.id)
4825          break;
4826        /*
4827          Map and unmap Info widget as text cursor crosses its boundaries.
4828        */
4829        x=event.xmotion.x;
4830        y=event.xmotion.y;
4831        if (IfMagickTrue(windows->info.mapped) )
4832          {
4833            if ((x < (int) (windows->info.x+windows->info.width)) &&
4834                (y < (int) (windows->info.y+windows->info.height)))
4835              (void) XWithdrawWindow(display,windows->info.id,
4836                windows->info.screen);
4837          }
4838        else
4839          if ((x > (int) (windows->info.x+windows->info.width)) ||
4840              (y > (int) (windows->info.y+windows->info.height)))
4841            (void) XMapWindow(display,windows->info.id);
4842        crop_info.x=(ssize_t) windows->image.x+x;
4843        crop_info.y=(ssize_t) windows->image.y+y;
4844        break;
4845      }
4846      default:
4847        break;
4848    }
4849  } while ((state & ExitState) == 0);
4850  (void) XSelectInput(display,windows->image.id,
4851    windows->image.attributes.event_mask);
4852  if ((state & EscapeState) != 0)
4853    {
4854      /*
4855        User want to exit without cropping.
4856      */
4857      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4858      (void) XFreeCursor(display,cursor);
4859      return(MagickTrue);
4860    }
4861  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4862  do
4863  {
4864    /*
4865      Size rectangle as pointer moves until the mouse button is released.
4866    */
4867    x=(int) crop_info.x;
4868    y=(int) crop_info.y;
4869    crop_info.width=0;
4870    crop_info.height=0;
4871    state=DefaultState;
4872    do
4873    {
4874      highlight_info=crop_info;
4875      highlight_info.x=crop_info.x-windows->image.x;
4876      highlight_info.y=crop_info.y-windows->image.y;
4877      if ((highlight_info.width > 3) && (highlight_info.height > 3))
4878        {
4879          /*
4880            Display info and draw cropping rectangle.
4881          */
4882          if (IfMagickFalse(windows->info.mapped) )
4883            (void) XMapWindow(display,windows->info.id);
4884          (void) FormatLocaleString(text,MagickPathExtent,
4885            " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4886            crop_info.height,(double) crop_info.x,(double) crop_info.y);
4887          XInfoWidget(display,windows,text);
4888          XHighlightRectangle(display,windows->image.id,
4889            windows->image.highlight_context,&highlight_info);
4890        }
4891      else
4892        if (IfMagickTrue(windows->info.mapped) )
4893          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4894      /*
4895        Wait for next event.
4896      */
4897      XScreenEvent(display,windows,&event,exception);
4898      if ((highlight_info.width > 3) && (highlight_info.height > 3))
4899        XHighlightRectangle(display,windows->image.id,
4900          windows->image.highlight_context,&highlight_info);
4901      switch (event.type)
4902      {
4903        case ButtonPress:
4904        {
4905          crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4906          crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4907          break;
4908        }
4909        case ButtonRelease:
4910        {
4911          /*
4912            User has committed to cropping rectangle.
4913          */
4914          crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4915          crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4916          XSetCursorState(display,windows,MagickFalse);
4917          state|=ExitState;
4918          windows->command.data=0;
4919          (void) XCommandWidget(display,windows,RectifyModeMenu,
4920            (XEvent *) NULL);
4921          break;
4922        }
4923        case Expose:
4924          break;
4925        case MotionNotify:
4926        {
4927          crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4928          crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4929        }
4930        default:
4931          break;
4932      }
4933      if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4934          ((state & ExitState) != 0))
4935        {
4936          /*
4937            Check boundary conditions.
4938          */
4939          if (crop_info.x < 0)
4940            crop_info.x=0;
4941          else
4942            if (crop_info.x > (ssize_t) windows->image.ximage->width)
4943              crop_info.x=(ssize_t) windows->image.ximage->width;
4944          if ((int) crop_info.x < x)
4945            crop_info.width=(unsigned int) (x-crop_info.x);
4946          else
4947            {
4948              crop_info.width=(unsigned int) (crop_info.x-x);
4949              crop_info.x=(ssize_t) x;
4950            }
4951          if (crop_info.y < 0)
4952            crop_info.y=0;
4953          else
4954            if (crop_info.y > (ssize_t) windows->image.ximage->height)
4955              crop_info.y=(ssize_t) windows->image.ximage->height;
4956          if ((int) crop_info.y < y)
4957            crop_info.height=(unsigned int) (y-crop_info.y);
4958          else
4959            {
4960              crop_info.height=(unsigned int) (crop_info.y-y);
4961              crop_info.y=(ssize_t) y;
4962            }
4963        }
4964    } while ((state & ExitState) == 0);
4965    /*
4966      Wait for user to grab a corner of the rectangle or press return.
4967    */
4968    state=DefaultState;
4969    (void) XMapWindow(display,windows->info.id);
4970    do
4971    {
4972      if (IfMagickTrue(windows->info.mapped) )
4973        {
4974          /*
4975            Display pointer position.
4976          */
4977          (void) FormatLocaleString(text,MagickPathExtent,
4978            " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4979            crop_info.height,(double) crop_info.x,(double) crop_info.y);
4980          XInfoWidget(display,windows,text);
4981        }
4982      highlight_info=crop_info;
4983      highlight_info.x=crop_info.x-windows->image.x;
4984      highlight_info.y=crop_info.y-windows->image.y;
4985      if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4986        {
4987          state|=EscapeState;
4988          state|=ExitState;
4989          break;
4990        }
4991      XHighlightRectangle(display,windows->image.id,
4992        windows->image.highlight_context,&highlight_info);
4993      XScreenEvent(display,windows,&event,exception);
4994      if (event.xany.window == windows->command.id)
4995        {
4996          /*
4997            Select a command from the Command widget.
4998          */
4999          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5000          id=XCommandWidget(display,windows,RectifyModeMenu,&event);
5001          (void) XSetFunction(display,windows->image.highlight_context,
5002            GXinvert);
5003          XHighlightRectangle(display,windows->image.id,
5004            windows->image.highlight_context,&highlight_info);
5005          if (id >= 0)
5006            switch (RectifyCommands[id])
5007            {
5008              case RectifyCopyCommand:
5009              {
5010                state|=ExitState;
5011                break;
5012              }
5013              case RectifyHelpCommand:
5014              {
5015                (void) XSetFunction(display,windows->image.highlight_context,
5016                  GXcopy);
5017                switch (mode)
5018                {
5019                  case CopyMode:
5020                  {
5021                    XTextViewWidget(display,resource_info,windows,MagickFalse,
5022                      "Help Viewer - Image Copy",ImageCopyHelp);
5023                    break;
5024                  }
5025                  case CropMode:
5026                  {
5027                    XTextViewWidget(display,resource_info,windows,MagickFalse,
5028                      "Help Viewer - Image Crop",ImageCropHelp);
5029                    break;
5030                  }
5031                  case CutMode:
5032                  {
5033                    XTextViewWidget(display,resource_info,windows,MagickFalse,
5034                      "Help Viewer - Image Cut",ImageCutHelp);
5035                    break;
5036                  }
5037                }
5038                (void) XSetFunction(display,windows->image.highlight_context,
5039                  GXinvert);
5040                break;
5041              }
5042              case RectifyDismissCommand:
5043              {
5044                /*
5045                  Prematurely exit.
5046                */
5047                state|=EscapeState;
5048                state|=ExitState;
5049                break;
5050              }
5051              default:
5052                break;
5053            }
5054          continue;
5055        }
5056      XHighlightRectangle(display,windows->image.id,
5057        windows->image.highlight_context,&highlight_info);
5058      switch (event.type)
5059      {
5060        case ButtonPress:
5061        {
5062          if (event.xbutton.button != Button1)
5063            break;
5064          if (event.xbutton.window != windows->image.id)
5065            break;
5066          x=windows->image.x+event.xbutton.x;
5067          y=windows->image.y+event.xbutton.y;
5068          if ((x < (int) (crop_info.x+RoiDelta)) &&
5069              (x > (int) (crop_info.x-RoiDelta)) &&
5070              (y < (int) (crop_info.y+RoiDelta)) &&
5071              (y > (int) (crop_info.y-RoiDelta)))
5072            {
5073              crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5074              crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5075              state|=UpdateConfigurationState;
5076              break;
5077            }
5078          if ((x < (int) (crop_info.x+RoiDelta)) &&
5079              (x > (int) (crop_info.x-RoiDelta)) &&
5080              (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5081              (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5082            {
5083              crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5084              state|=UpdateConfigurationState;
5085              break;
5086            }
5087          if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5088              (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5089              (y < (int) (crop_info.y+RoiDelta)) &&
5090              (y > (int) (crop_info.y-RoiDelta)))
5091            {
5092              crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5093              state|=UpdateConfigurationState;
5094              break;
5095            }
5096          if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5097              (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5098              (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5099              (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5100            {
5101              state|=UpdateConfigurationState;
5102              break;
5103            }
5104        }
5105        case ButtonRelease:
5106        {
5107          if (event.xbutton.window == windows->pan.id)
5108            if ((highlight_info.x != crop_info.x-windows->image.x) ||
5109                (highlight_info.y != crop_info.y-windows->image.y))
5110              XHighlightRectangle(display,windows->image.id,
5111                windows->image.highlight_context,&highlight_info);
5112          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5113            event.xbutton.time);
5114          break;
5115        }
5116        case Expose:
5117        {
5118          if (event.xexpose.window == windows->image.id)
5119            if (event.xexpose.count == 0)
5120              {
5121                event.xexpose.x=(int) highlight_info.x;
5122                event.xexpose.y=(int) highlight_info.y;
5123                event.xexpose.width=(int) highlight_info.width;
5124                event.xexpose.height=(int) highlight_info.height;
5125                XRefreshWindow(display,&windows->image,&event);
5126              }
5127          if (event.xexpose.window == windows->info.id)
5128            if (event.xexpose.count == 0)
5129              XInfoWidget(display,windows,text);
5130          break;
5131        }
5132        case KeyPress:
5133        {
5134          if (event.xkey.window != windows->image.id)
5135            break;
5136          /*
5137            Respond to a user key press.
5138          */
5139          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5140            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5141          switch ((int) key_symbol)
5142          {
5143            case XK_Escape:
5144            case XK_F20:
5145              state|=EscapeState;
5146            case XK_Return:
5147            {
5148              state|=ExitState;
5149              break;
5150            }
5151            case XK_Home:
5152            case XK_KP_Home:
5153            {
5154              crop_info.x=(ssize_t) (windows->image.width/2L-crop_info.width/
5155                2L);
5156              crop_info.y=(ssize_t) (windows->image.height/2L-crop_info.height/
5157                2L);
5158              break;
5159            }
5160            case XK_Left:
5161            case XK_KP_Left:
5162            {
5163              crop_info.x--;
5164              break;
5165            }
5166            case XK_Up:
5167            case XK_KP_Up:
5168            case XK_Next:
5169            {
5170              crop_info.y--;
5171              break;
5172            }
5173            case XK_Right:
5174            case XK_KP_Right:
5175            {
5176              crop_info.x++;
5177              break;
5178            }
5179            case XK_Prior:
5180            case XK_Down:
5181            case XK_KP_Down:
5182            {
5183              crop_info.y++;
5184              break;
5185            }
5186            case XK_F1:
5187            case XK_Help:
5188            {
5189              (void) XSetFunction(display,windows->image.highlight_context,
5190                GXcopy);
5191              switch (mode)
5192              {
5193                case CopyMode:
5194                {
5195                  XTextViewWidget(display,resource_info,windows,MagickFalse,
5196                    "Help Viewer - Image Copy",ImageCopyHelp);
5197                  break;
5198                }
5199                case CropMode:
5200                {
5201                  XTextViewWidget(display,resource_info,windows,MagickFalse,
5202                    "Help Viewer - Image Cropg",ImageCropHelp);
5203                  break;
5204                }
5205                case CutMode:
5206                {
5207                  XTextViewWidget(display,resource_info,windows,MagickFalse,
5208                    "Help Viewer - Image Cutg",ImageCutHelp);
5209                  break;
5210                }
5211              }
5212              (void) XSetFunction(display,windows->image.highlight_context,
5213                GXinvert);
5214              break;
5215            }
5216            default:
5217            {
5218              (void) XBell(display,0);
5219              break;
5220            }
5221          }
5222          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5223            event.xkey.time);
5224          break;
5225        }
5226        case KeyRelease:
5227          break;
5228        case MotionNotify:
5229        {
5230          if (event.xmotion.window != windows->image.id)
5231            break;
5232          /*
5233            Map and unmap Info widget as text cursor crosses its boundaries.
5234          */
5235          x=event.xmotion.x;
5236          y=event.xmotion.y;
5237          if (IfMagickTrue(windows->info.mapped) )
5238            {
5239              if ((x < (int) (windows->info.x+windows->info.width)) &&
5240                  (y < (int) (windows->info.y+windows->info.height)))
5241                (void) XWithdrawWindow(display,windows->info.id,
5242                  windows->info.screen);
5243            }
5244          else
5245            if ((x > (int) (windows->info.x+windows->info.width)) ||
5246                (y > (int) (windows->info.y+windows->info.height)))
5247              (void) XMapWindow(display,windows->info.id);
5248          crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5249          crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5250          break;
5251        }
5252        case SelectionRequest:
5253        {
5254          XSelectionEvent
5255            notify;
5256
5257          XSelectionRequestEvent
5258            *request;
5259
5260          /*
5261            Set primary selection.
5262          */
5263          (void) FormatLocaleString(text,MagickPathExtent,
5264            "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5265            crop_info.height,(double) crop_info.x,(double) crop_info.y);
5266          request=(&(event.xselectionrequest));
5267          (void) XChangeProperty(request->display,request->requestor,
5268            request->property,request->target,8,PropModeReplace,
5269            (unsigned char *) text,(int) strlen(text));
5270          notify.type=SelectionNotify;
5271          notify.display=request->display;
5272          notify.requestor=request->requestor;
5273          notify.selection=request->selection;
5274          notify.target=request->target;
5275          notify.time=request->time;
5276          if (request->property == None)
5277            notify.property=request->target;
5278          else
5279            notify.property=request->property;
5280          (void) XSendEvent(request->display,request->requestor,False,0,
5281            (XEvent *) &notify);
5282        }
5283        default:
5284          break;
5285      }
5286      if ((state & UpdateConfigurationState) != 0)
5287        {
5288          (void) XPutBackEvent(display,&event);
5289          (void) XCheckDefineCursor(display,windows->image.id,cursor);
5290          break;
5291        }
5292    } while ((state & ExitState) == 0);
5293  } while ((state & ExitState) == 0);
5294  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5295  XSetCursorState(display,windows,MagickFalse);
5296  if ((state & EscapeState) != 0)
5297    return(MagickTrue);
5298  if (mode == CropMode)
5299    if (((int) crop_info.width != windows->image.ximage->width) ||
5300        ((int) crop_info.height != windows->image.ximage->height))
5301      {
5302        /*
5303          Reconfigure Image window as defined by cropping rectangle.
5304        */
5305        XSetCropGeometry(display,windows,&crop_info,image);
5306        windows->image.window_changes.width=(int) crop_info.width;
5307        windows->image.window_changes.height=(int) crop_info.height;
5308        (void) XConfigureImage(display,resource_info,windows,image,exception);
5309        return(MagickTrue);
5310      }
5311  /*
5312    Copy image before applying image transforms.
5313  */
5314  XSetCursorState(display,windows,MagickTrue);
5315  XCheckRefreshWindows(display,windows);
5316  width=(unsigned int) image->columns;
5317  height=(unsigned int) image->rows;
5318  x=0;
5319  y=0;
5320  if (windows->image.crop_geometry != (char *) NULL)
5321    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5322  scale_factor=(double) width/windows->image.ximage->width;
5323  crop_info.x+=x;
5324  crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5325  crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5326  scale_factor=(double) height/windows->image.ximage->height;
5327  crop_info.y+=y;
5328  crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5329  crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5330  crop_image=CropImage(image,&crop_info,exception);
5331  XSetCursorState(display,windows,MagickFalse);
5332  if (crop_image == (Image *) NULL)
5333    return(MagickFalse);
5334  if (resource_info->copy_image != (Image *) NULL)
5335    resource_info->copy_image=DestroyImage(resource_info->copy_image);
5336  resource_info->copy_image=crop_image;
5337  if (mode == CopyMode)
5338    {
5339      (void) XConfigureImage(display,resource_info,windows,image,exception);
5340      return(MagickTrue);
5341    }
5342  /*
5343    Cut image.
5344  */
5345  if (IfMagickFalse(SetImageStorageClass(image,DirectClass,exception)) )
5346    return(MagickFalse);
5347  image->alpha_trait=BlendPixelTrait;
5348  image_view=AcquireAuthenticCacheView(image,exception);
5349  for (y=0; y < (int) crop_info.height; y++)
5350  {
5351    q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5352      crop_info.width,1,exception);
5353    if (q == (Quantum *) NULL)
5354      break;
5355    for (x=0; x < (int) crop_info.width; x++)
5356    {
5357      SetPixelAlpha(image,TransparentAlpha,q);
5358      q+=GetPixelChannels(image);
5359    }
5360    if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
5361      break;
5362  }
5363  image_view=DestroyCacheView(image_view);
5364  /*
5365    Update image configuration.
5366  */
5367  XConfigureImageColormap(display,resource_info,windows,image,exception);
5368  (void) XConfigureImage(display,resource_info,windows,image,exception);
5369  return(MagickTrue);
5370}
5371
5372/*
5373%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5374%                                                                             %
5375%                                                                             %
5376%                                                                             %
5377+   X D r a w I m a g e                                                       %
5378%                                                                             %
5379%                                                                             %
5380%                                                                             %
5381%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5382%
5383%  XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5384%  the image.
5385%
5386%  The format of the XDrawEditImage method is:
5387%
5388%      MagickBooleanType XDrawEditImage(Display *display,
5389%        XResourceInfo *resource_info,XWindows *windows,Image **image,
5390%        ExceptionInfo *exception)
5391%
5392%  A description of each parameter follows:
5393%
5394%    o display: Specifies a connection to an X server; returned from
5395%      XOpenDisplay.
5396%
5397%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5398%
5399%    o windows: Specifies a pointer to a XWindows structure.
5400%
5401%    o image: the image.
5402%
5403%    o exception: return any errors or warnings in this structure.
5404%
5405*/
5406static MagickBooleanType XDrawEditImage(Display *display,
5407  XResourceInfo *resource_info,XWindows *windows,Image **image,
5408  ExceptionInfo *exception)
5409{
5410  static const char
5411    *DrawMenu[] =
5412    {
5413      "Element",
5414      "Color",
5415      "Stipple",
5416      "Width",
5417      "Undo",
5418      "Help",
5419      "Dismiss",
5420      (char *) NULL
5421    };
5422
5423  static ElementType
5424    element = PointElement;
5425
5426  static const ModeType
5427    DrawCommands[] =
5428    {
5429      DrawElementCommand,
5430      DrawColorCommand,
5431      DrawStippleCommand,
5432      DrawWidthCommand,
5433      DrawUndoCommand,
5434      DrawHelpCommand,
5435      DrawDismissCommand
5436    };
5437
5438  static Pixmap
5439    stipple = (Pixmap) NULL;
5440
5441  static unsigned int
5442    pen_id = 0,
5443    line_width = 1;
5444
5445  char
5446    command[MagickPathExtent],
5447    text[MagickPathExtent];
5448
5449  Cursor
5450    cursor;
5451
5452  int
5453    entry,
5454    id,
5455    number_coordinates,
5456    x,
5457    y;
5458
5459  double
5460    degrees;
5461
5462  MagickStatusType
5463    status;
5464
5465  RectangleInfo
5466    rectangle_info;
5467
5468  register int
5469    i;
5470
5471  unsigned int
5472    distance,
5473    height,
5474    max_coordinates,
5475    width;
5476
5477  size_t
5478    state;
5479
5480  Window
5481    root_window;
5482
5483  XDrawInfo
5484    draw_info;
5485
5486  XEvent
5487    event;
5488
5489  XPoint
5490    *coordinate_info;
5491
5492  XSegment
5493    line_info;
5494
5495  /*
5496    Allocate polygon info.
5497  */
5498  max_coordinates=2048;
5499  coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5500    sizeof(*coordinate_info));
5501  if (coordinate_info == (XPoint *) NULL)
5502    {
5503      (void) ThrowMagickException(exception,GetMagickModule(),
5504        ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5505      return(MagickFalse);
5506    }
5507  /*
5508    Map Command widget.
5509  */
5510  (void) CloneString(&windows->command.name,"Draw");
5511  windows->command.data=4;
5512  (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5513  (void) XMapRaised(display,windows->command.id);
5514  XClientMessage(display,windows->image.id,windows->im_protocols,
5515    windows->im_update_widget,CurrentTime);
5516  /*
5517    Wait for first button press.
5518  */
5519  root_window=XRootWindow(display,XDefaultScreen(display));
5520  draw_info.stencil=OpaqueStencil;
5521  status=MagickTrue;
5522  cursor=XCreateFontCursor(display,XC_tcross);
5523  for ( ; ; )
5524  {
5525    XQueryPosition(display,windows->image.id,&x,&y);
5526    (void) XSelectInput(display,windows->image.id,
5527      windows->image.attributes.event_mask | PointerMotionMask);
5528    (void) XCheckDefineCursor(display,windows->image.id,cursor);
5529    state=DefaultState;
5530    do
5531    {
5532      if (IfMagickTrue(windows->info.mapped) )
5533        {
5534          /*
5535            Display pointer position.
5536          */
5537          (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
5538            x+windows->image.x,y+windows->image.y);
5539          XInfoWidget(display,windows,text);
5540        }
5541      /*
5542        Wait for next event.
5543      */
5544      XScreenEvent(display,windows,&event,exception);
5545      if (event.xany.window == windows->command.id)
5546        {
5547          /*
5548            Select a command from the Command widget.
5549          */
5550          id=XCommandWidget(display,windows,DrawMenu,&event);
5551          if (id < 0)
5552            continue;
5553          switch (DrawCommands[id])
5554          {
5555            case DrawElementCommand:
5556            {
5557              static const char
5558                *Elements[] =
5559                {
5560                  "point",
5561                  "line",
5562                  "rectangle",
5563                  "fill rectangle",
5564                  "circle",
5565                  "fill circle",
5566                  "ellipse",
5567                  "fill ellipse",
5568                  "polygon",
5569                  "fill polygon",
5570                  (char *) NULL,
5571                };
5572
5573              /*
5574                Select a command from the pop-up menu.
5575              */
5576              element=(ElementType) (XMenuWidget(display,windows,
5577                DrawMenu[id],Elements,command)+1);
5578              break;
5579            }
5580            case DrawColorCommand:
5581            {
5582              const char
5583                *ColorMenu[MaxNumberPens+1];
5584
5585              int
5586                pen_number;
5587
5588              MagickBooleanType
5589                transparent;
5590
5591              XColor
5592                color;
5593
5594              /*
5595                Initialize menu selections.
5596              */
5597              for (i=0; i < (int) (MaxNumberPens-2); i++)
5598                ColorMenu[i]=resource_info->pen_colors[i];
5599              ColorMenu[MaxNumberPens-2]="transparent";
5600              ColorMenu[MaxNumberPens-1]="Browser...";
5601              ColorMenu[MaxNumberPens]=(char *) NULL;
5602              /*
5603                Select a pen color from the pop-up menu.
5604              */
5605              pen_number=XMenuWidget(display,windows,DrawMenu[id],
5606                (const char **) ColorMenu,command);
5607              if (pen_number < 0)
5608                break;
5609              transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5610                MagickFalse;
5611              if (IfMagickTrue(transparent) )
5612                {
5613                  draw_info.stencil=TransparentStencil;
5614                  break;
5615                }
5616              if (pen_number == (MaxNumberPens-1))
5617                {
5618                  static char
5619                    color_name[MagickPathExtent] = "gray";
5620
5621                  /*
5622                    Select a pen color from a dialog.
5623                  */
5624                  resource_info->pen_colors[pen_number]=color_name;
5625                  XColorBrowserWidget(display,windows,"Select",color_name);
5626                  if (*color_name == '\0')
5627                    break;
5628                }
5629              /*
5630                Set pen color.
5631              */
5632              (void) XParseColor(display,windows->map_info->colormap,
5633                resource_info->pen_colors[pen_number],&color);
5634              XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5635                (unsigned int) MaxColors,&color);
5636              windows->pixel_info->pen_colors[pen_number]=color;
5637              pen_id=(unsigned int) pen_number;
5638              draw_info.stencil=OpaqueStencil;
5639              break;
5640            }
5641            case DrawStippleCommand:
5642            {
5643              Image
5644                *stipple_image;
5645
5646              ImageInfo
5647                *image_info;
5648
5649              int
5650                status;
5651
5652              static char
5653                filename[MagickPathExtent] = "\0";
5654
5655              static const char
5656                *StipplesMenu[] =
5657                {
5658                  "Brick",
5659                  "Diagonal",
5660                  "Scales",
5661                  "Vertical",
5662                  "Wavy",
5663                  "Translucent",
5664                  "Opaque",
5665                  (char *) NULL,
5666                  (char *) NULL,
5667                };
5668
5669              /*
5670                Select a command from the pop-up menu.
5671              */
5672              StipplesMenu[7]="Open...";
5673              entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5674                command);
5675              if (entry < 0)
5676                break;
5677              if (stipple != (Pixmap) NULL)
5678                (void) XFreePixmap(display,stipple);
5679              stipple=(Pixmap) NULL;
5680              if (entry != 7)
5681                {
5682                  switch (entry)
5683                  {
5684                    case 0:
5685                    {
5686                      stipple=XCreateBitmapFromData(display,root_window,
5687                        (char *) BricksBitmap,BricksWidth,BricksHeight);
5688                      break;
5689                    }
5690                    case 1:
5691                    {
5692                      stipple=XCreateBitmapFromData(display,root_window,
5693                        (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5694                      break;
5695                    }
5696                    case 2:
5697                    {
5698                      stipple=XCreateBitmapFromData(display,root_window,
5699                        (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5700                      break;
5701                    }
5702                    case 3:
5703                    {
5704                      stipple=XCreateBitmapFromData(display,root_window,
5705                        (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5706                      break;
5707                    }
5708                    case 4:
5709                    {
5710                      stipple=XCreateBitmapFromData(display,root_window,
5711                        (char *) WavyBitmap,WavyWidth,WavyHeight);
5712                      break;
5713                    }
5714                    case 5:
5715                    {
5716                      stipple=XCreateBitmapFromData(display,root_window,
5717                        (char *) HighlightBitmap,HighlightWidth,
5718                        HighlightHeight);
5719                      break;
5720                    }
5721                    case 6:
5722                    default:
5723                    {
5724                      stipple=XCreateBitmapFromData(display,root_window,
5725                        (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5726                      break;
5727                    }
5728                  }
5729                  break;
5730                }
5731              XFileBrowserWidget(display,windows,"Stipple",filename);
5732              if (*filename == '\0')
5733                break;
5734              /*
5735                Read image.
5736              */
5737              XSetCursorState(display,windows,MagickTrue);
5738              XCheckRefreshWindows(display,windows);
5739              image_info=AcquireImageInfo();
5740              (void) CopyMagickString(image_info->filename,filename,
5741                MagickPathExtent);
5742              stipple_image=ReadImage(image_info,exception);
5743              CatchException(exception);
5744              XSetCursorState(display,windows,MagickFalse);
5745              if (stipple_image == (Image *) NULL)
5746                break;
5747              (void) AcquireUniqueFileResource(filename);
5748              (void) FormatLocaleString(stipple_image->filename,MagickPathExtent,
5749                "xbm:%s",filename);
5750              (void) WriteImage(image_info,stipple_image,exception);
5751              stipple_image=DestroyImage(stipple_image);
5752              image_info=DestroyImageInfo(image_info);
5753              status=XReadBitmapFile(display,root_window,filename,&width,
5754                &height,&stipple,&x,&y);
5755              (void) RelinquishUniqueFileResource(filename);
5756              if ((status != BitmapSuccess) != 0)
5757                XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5758                  filename);
5759              break;
5760            }
5761            case DrawWidthCommand:
5762            {
5763              static char
5764                width[MagickPathExtent] = "0";
5765
5766              static const char
5767                *WidthsMenu[] =
5768                {
5769                  "1",
5770                  "2",
5771                  "4",
5772                  "8",
5773                  "16",
5774                  "Dialog...",
5775                  (char *) NULL,
5776                };
5777
5778              /*
5779                Select a command from the pop-up menu.
5780              */
5781              entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5782                command);
5783              if (entry < 0)
5784                break;
5785              if (entry != 5)
5786                {
5787                  line_width=(unsigned int) StringToUnsignedLong(
5788                    WidthsMenu[entry]);
5789                  break;
5790                }
5791              (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5792                width);
5793              if (*width == '\0')
5794                break;
5795              line_width=(unsigned int) StringToUnsignedLong(width);
5796              break;
5797            }
5798            case DrawUndoCommand:
5799            {
5800              (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5801                image,exception);
5802              break;
5803            }
5804            case DrawHelpCommand:
5805            {
5806              XTextViewWidget(display,resource_info,windows,MagickFalse,
5807                "Help Viewer - Image Rotation",ImageDrawHelp);
5808              (void) XCheckDefineCursor(display,windows->image.id,cursor);
5809              break;
5810            }
5811            case DrawDismissCommand:
5812            {
5813              /*
5814                Prematurely exit.
5815              */
5816              state|=EscapeState;
5817              state|=ExitState;
5818              break;
5819            }
5820            default:
5821              break;
5822          }
5823          (void) XCheckDefineCursor(display,windows->image.id,cursor);
5824          continue;
5825        }
5826      switch (event.type)
5827      {
5828        case ButtonPress:
5829        {
5830          if (event.xbutton.button != Button1)
5831            break;
5832          if (event.xbutton.window != windows->image.id)
5833            break;
5834          /*
5835            exit loop.
5836          */
5837          x=event.xbutton.x;
5838          y=event.xbutton.y;
5839          state|=ExitState;
5840          break;
5841        }
5842        case ButtonRelease:
5843          break;
5844        case Expose:
5845          break;
5846        case KeyPress:
5847        {
5848          KeySym
5849            key_symbol;
5850
5851          if (event.xkey.window != windows->image.id)
5852            break;
5853          /*
5854            Respond to a user key press.
5855          */
5856          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5857            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5858          switch ((int) key_symbol)
5859          {
5860            case XK_Escape:
5861            case XK_F20:
5862            {
5863              /*
5864                Prematurely exit.
5865              */
5866              state|=EscapeState;
5867              state|=ExitState;
5868              break;
5869            }
5870            case XK_F1:
5871            case XK_Help:
5872            {
5873              XTextViewWidget(display,resource_info,windows,MagickFalse,
5874                "Help Viewer - Image Rotation",ImageDrawHelp);
5875              break;
5876            }
5877            default:
5878            {
5879              (void) XBell(display,0);
5880              break;
5881            }
5882          }
5883          break;
5884        }
5885        case MotionNotify:
5886        {
5887          /*
5888            Map and unmap Info widget as text cursor crosses its boundaries.
5889          */
5890          x=event.xmotion.x;
5891          y=event.xmotion.y;
5892          if (IfMagickTrue(windows->info.mapped) )
5893            {
5894              if ((x < (int) (windows->info.x+windows->info.width)) &&
5895                  (y < (int) (windows->info.y+windows->info.height)))
5896                (void) XWithdrawWindow(display,windows->info.id,
5897                  windows->info.screen);
5898            }
5899          else
5900            if ((x > (int) (windows->info.x+windows->info.width)) ||
5901                (y > (int) (windows->info.y+windows->info.height)))
5902              (void) XMapWindow(display,windows->info.id);
5903          break;
5904        }
5905      }
5906    } while ((state & ExitState) == 0);
5907    (void) XSelectInput(display,windows->image.id,
5908      windows->image.attributes.event_mask);
5909    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5910    if ((state & EscapeState) != 0)
5911      break;
5912    /*
5913      Draw element as pointer moves until the button is released.
5914    */
5915    distance=0;
5916    degrees=0.0;
5917    line_info.x1=x;
5918    line_info.y1=y;
5919    line_info.x2=x;
5920    line_info.y2=y;
5921    rectangle_info.x=(ssize_t) x;
5922    rectangle_info.y=(ssize_t) y;
5923    rectangle_info.width=0;
5924    rectangle_info.height=0;
5925    number_coordinates=1;
5926    coordinate_info->x=x;
5927    coordinate_info->y=y;
5928    (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5929    state=DefaultState;
5930    do
5931    {
5932      switch (element)
5933      {
5934        case PointElement:
5935        default:
5936        {
5937          if (number_coordinates > 1)
5938            {
5939              (void) XDrawLines(display,windows->image.id,
5940                windows->image.highlight_context,coordinate_info,
5941                number_coordinates,CoordModeOrigin);
5942              (void) FormatLocaleString(text,MagickPathExtent," %+d%+d",
5943                coordinate_info[number_coordinates-1].x,
5944                coordinate_info[number_coordinates-1].y);
5945              XInfoWidget(display,windows,text);
5946            }
5947          break;
5948        }
5949        case LineElement:
5950        {
5951          if (distance > 9)
5952            {
5953              /*
5954                Display angle of the line.
5955              */
5956              degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5957                line_info.y1),(double) (line_info.x2-line_info.x1)));
5958              (void) FormatLocaleString(text,MagickPathExtent," %g",
5959                (double) degrees);
5960              XInfoWidget(display,windows,text);
5961              XHighlightLine(display,windows->image.id,
5962                windows->image.highlight_context,&line_info);
5963            }
5964          else
5965            if (IfMagickTrue(windows->info.mapped) )
5966              (void) XWithdrawWindow(display,windows->info.id,
5967                windows->info.screen);
5968          break;
5969        }
5970        case RectangleElement:
5971        case FillRectangleElement:
5972        {
5973          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5974            {
5975              /*
5976                Display info and draw drawing rectangle.
5977              */
5978              (void) FormatLocaleString(text,MagickPathExtent,
5979                " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5980                (double) rectangle_info.height,(double) rectangle_info.x,
5981                (double) rectangle_info.y);
5982              XInfoWidget(display,windows,text);
5983              XHighlightRectangle(display,windows->image.id,
5984                windows->image.highlight_context,&rectangle_info);
5985            }
5986          else
5987            if (IfMagickTrue(windows->info.mapped) )
5988              (void) XWithdrawWindow(display,windows->info.id,
5989                windows->info.screen);
5990          break;
5991        }
5992        case CircleElement:
5993        case FillCircleElement:
5994        case EllipseElement:
5995        case FillEllipseElement:
5996        {
5997          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5998            {
5999              /*
6000                Display info and draw drawing rectangle.
6001              */
6002              (void) FormatLocaleString(text,MagickPathExtent,
6003                " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
6004                (double) rectangle_info.height,(double) rectangle_info.x,
6005                (double) rectangle_info.y);
6006              XInfoWidget(display,windows,text);
6007              XHighlightEllipse(display,windows->image.id,
6008                windows->image.highlight_context,&rectangle_info);
6009            }
6010          else
6011            if (IfMagickTrue(windows->info.mapped) )
6012              (void) XWithdrawWindow(display,windows->info.id,
6013                windows->info.screen);
6014          break;
6015        }
6016        case PolygonElement:
6017        case FillPolygonElement:
6018        {
6019          if (number_coordinates > 1)
6020            (void) XDrawLines(display,windows->image.id,
6021              windows->image.highlight_context,coordinate_info,
6022              number_coordinates,CoordModeOrigin);
6023          if (distance > 9)
6024            {
6025              /*
6026                Display angle of the line.
6027              */
6028              degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
6029                line_info.y1),(double) (line_info.x2-line_info.x1)));
6030              (void) FormatLocaleString(text,MagickPathExtent," %g",
6031                (double) degrees);
6032              XInfoWidget(display,windows,text);
6033              XHighlightLine(display,windows->image.id,
6034                windows->image.highlight_context,&line_info);
6035            }
6036          else
6037            if (IfMagickTrue(windows->info.mapped) )
6038              (void) XWithdrawWindow(display,windows->info.id,
6039                windows->info.screen);
6040          break;
6041        }
6042      }
6043      /*
6044        Wait for next event.
6045      */
6046      XScreenEvent(display,windows,&event,exception);
6047      switch (element)
6048      {
6049        case PointElement:
6050        default:
6051        {
6052          if (number_coordinates > 1)
6053            (void) XDrawLines(display,windows->image.id,
6054              windows->image.highlight_context,coordinate_info,
6055              number_coordinates,CoordModeOrigin);
6056          break;
6057        }
6058        case LineElement:
6059        {
6060          if (distance > 9)
6061            XHighlightLine(display,windows->image.id,
6062              windows->image.highlight_context,&line_info);
6063          break;
6064        }
6065        case RectangleElement:
6066        case FillRectangleElement:
6067        {
6068          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6069            XHighlightRectangle(display,windows->image.id,
6070              windows->image.highlight_context,&rectangle_info);
6071          break;
6072        }
6073        case CircleElement:
6074        case FillCircleElement:
6075        case EllipseElement:
6076        case FillEllipseElement:
6077        {
6078          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6079            XHighlightEllipse(display,windows->image.id,
6080              windows->image.highlight_context,&rectangle_info);
6081          break;
6082        }
6083        case PolygonElement:
6084        case FillPolygonElement:
6085        {
6086          if (number_coordinates > 1)
6087            (void) XDrawLines(display,windows->image.id,
6088              windows->image.highlight_context,coordinate_info,
6089              number_coordinates,CoordModeOrigin);
6090          if (distance > 9)
6091            XHighlightLine(display,windows->image.id,
6092              windows->image.highlight_context,&line_info);
6093          break;
6094        }
6095      }
6096      switch (event.type)
6097      {
6098        case ButtonPress:
6099          break;
6100        case ButtonRelease:
6101        {
6102          /*
6103            User has committed to element.
6104          */
6105          line_info.x2=event.xbutton.x;
6106          line_info.y2=event.xbutton.y;
6107          rectangle_info.x=(ssize_t) event.xbutton.x;
6108          rectangle_info.y=(ssize_t) event.xbutton.y;
6109          coordinate_info[number_coordinates].x=event.xbutton.x;
6110          coordinate_info[number_coordinates].y=event.xbutton.y;
6111          if (((element != PolygonElement) &&
6112               (element != FillPolygonElement)) || (distance <= 9))
6113            {
6114              state|=ExitState;
6115              break;
6116            }
6117          number_coordinates++;
6118          if (number_coordinates < (int) max_coordinates)
6119            {
6120              line_info.x1=event.xbutton.x;
6121              line_info.y1=event.xbutton.y;
6122              break;
6123            }
6124          max_coordinates<<=1;
6125          coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6126            max_coordinates,sizeof(*coordinate_info));
6127          if (coordinate_info == (XPoint *) NULL)
6128            (void) ThrowMagickException(exception,GetMagickModule(),
6129              ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6130          break;
6131        }
6132        case Expose:
6133          break;
6134        case MotionNotify:
6135        {
6136          if (event.xmotion.window != windows->image.id)
6137            break;
6138          if (element != PointElement)
6139            {
6140              line_info.x2=event.xmotion.x;
6141              line_info.y2=event.xmotion.y;
6142              rectangle_info.x=(ssize_t) event.xmotion.x;
6143              rectangle_info.y=(ssize_t) event.xmotion.y;
6144              break;
6145            }
6146          coordinate_info[number_coordinates].x=event.xbutton.x;
6147          coordinate_info[number_coordinates].y=event.xbutton.y;
6148          number_coordinates++;
6149          if (number_coordinates < (int) max_coordinates)
6150            break;
6151          max_coordinates<<=1;
6152          coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6153            max_coordinates,sizeof(*coordinate_info));
6154          if (coordinate_info == (XPoint *) NULL)
6155            (void) ThrowMagickException(exception,GetMagickModule(),
6156              ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6157          break;
6158        }
6159        default:
6160          break;
6161      }
6162      /*
6163        Check boundary conditions.
6164      */
6165      if (line_info.x2 < 0)
6166        line_info.x2=0;
6167      else
6168        if (line_info.x2 > (int) windows->image.width)
6169          line_info.x2=(short) windows->image.width;
6170      if (line_info.y2 < 0)
6171        line_info.y2=0;
6172      else
6173        if (line_info.y2 > (int) windows->image.height)
6174          line_info.y2=(short) windows->image.height;
6175      distance=(unsigned int)
6176        (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6177         ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6178      if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6179          ((state & ExitState) != 0))
6180        {
6181          if (rectangle_info.x < 0)
6182            rectangle_info.x=0;
6183          else
6184            if (rectangle_info.x > (ssize_t) windows->image.width)
6185              rectangle_info.x=(ssize_t) windows->image.width;
6186          if ((int) rectangle_info.x < x)
6187            rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6188          else
6189            {
6190              rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6191              rectangle_info.x=(ssize_t) x;
6192            }
6193          if (rectangle_info.y < 0)
6194            rectangle_info.y=0;
6195          else
6196            if (rectangle_info.y > (ssize_t) windows->image.height)
6197              rectangle_info.y=(ssize_t) windows->image.height;
6198          if ((int) rectangle_info.y < y)
6199            rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6200          else
6201            {
6202              rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6203              rectangle_info.y=(ssize_t) y;
6204            }
6205        }
6206    } while ((state & ExitState) == 0);
6207    (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6208    if ((element == PointElement) || (element == PolygonElement) ||
6209        (element == FillPolygonElement))
6210      {
6211        /*
6212          Determine polygon bounding box.
6213        */
6214        rectangle_info.x=(ssize_t) coordinate_info->x;
6215        rectangle_info.y=(ssize_t) coordinate_info->y;
6216        x=coordinate_info->x;
6217        y=coordinate_info->y;
6218        for (i=1; i < number_coordinates; i++)
6219        {
6220          if (coordinate_info[i].x > x)
6221            x=coordinate_info[i].x;
6222          if (coordinate_info[i].y > y)
6223            y=coordinate_info[i].y;
6224          if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6225            rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6226          if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6227            rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6228        }
6229        rectangle_info.width=(size_t) (x-rectangle_info.x);
6230        rectangle_info.height=(size_t) (y-rectangle_info.y);
6231        for (i=0; i < number_coordinates; i++)
6232        {
6233          coordinate_info[i].x-=rectangle_info.x;
6234          coordinate_info[i].y-=rectangle_info.y;
6235        }
6236      }
6237    else
6238      if (distance <= 9)
6239        continue;
6240      else
6241        if ((element == RectangleElement) ||
6242            (element == CircleElement) || (element == EllipseElement))
6243          {
6244            rectangle_info.width--;
6245            rectangle_info.height--;
6246          }
6247    /*
6248      Drawing is relative to image configuration.
6249    */
6250    draw_info.x=(int) rectangle_info.x;
6251    draw_info.y=(int) rectangle_info.y;
6252    (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6253      image,exception);
6254    width=(unsigned int) (*image)->columns;
6255    height=(unsigned int) (*image)->rows;
6256    x=0;
6257    y=0;
6258    if (windows->image.crop_geometry != (char *) NULL)
6259      (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6260    draw_info.x+=windows->image.x-(line_width/2);
6261    if (draw_info.x < 0)
6262      draw_info.x=0;
6263    draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6264    draw_info.y+=windows->image.y-(line_width/2);
6265    if (draw_info.y < 0)
6266      draw_info.y=0;
6267    draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6268    draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6269    if (draw_info.width > (unsigned int) (*image)->columns)
6270      draw_info.width=(unsigned int) (*image)->columns;
6271    draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6272    if (draw_info.height > (unsigned int) (*image)->rows)
6273      draw_info.height=(unsigned int) (*image)->rows;
6274    (void) FormatLocaleString(draw_info.geometry,MagickPathExtent,"%ux%u%+d%+d",
6275      width*draw_info.width/windows->image.ximage->width,
6276      height*draw_info.height/windows->image.ximage->height,
6277      draw_info.x+x,draw_info.y+y);
6278    /*
6279      Initialize drawing attributes.
6280    */
6281    draw_info.degrees=0.0;
6282    draw_info.element=element;
6283    draw_info.stipple=stipple;
6284    draw_info.line_width=line_width;
6285    draw_info.line_info=line_info;
6286    if (line_info.x1 > (int) (line_width/2))
6287      draw_info.line_info.x1=(short) line_width/2;
6288    if (line_info.y1 > (int) (line_width/2))
6289      draw_info.line_info.y1=(short) line_width/2;
6290    draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6291    draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6292    if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6293      {
6294        draw_info.line_info.x2=(-draw_info.line_info.x2);
6295        draw_info.line_info.y2=(-draw_info.line_info.y2);
6296      }
6297    if (draw_info.line_info.x2 < 0)
6298      {
6299        draw_info.line_info.x2=(-draw_info.line_info.x2);
6300        Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6301      }
6302    if (draw_info.line_info.y2 < 0)
6303      {
6304        draw_info.line_info.y2=(-draw_info.line_info.y2);
6305        Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6306      }
6307    draw_info.rectangle_info=rectangle_info;
6308    if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6309      draw_info.rectangle_info.x=(ssize_t) line_width/2;
6310    if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6311      draw_info.rectangle_info.y=(ssize_t) line_width/2;
6312    draw_info.number_coordinates=(unsigned int) number_coordinates;
6313    draw_info.coordinate_info=coordinate_info;
6314    windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6315    /*
6316      Draw element on image.
6317    */
6318    XSetCursorState(display,windows,MagickTrue);
6319    XCheckRefreshWindows(display,windows);
6320    status=XDrawImage(display,windows->pixel_info,&draw_info,*image,exception);
6321    XSetCursorState(display,windows,MagickFalse);
6322    /*
6323      Update image colormap and return to image drawing.
6324    */
6325    XConfigureImageColormap(display,resource_info,windows,*image,exception);
6326    (void) XConfigureImage(display,resource_info,windows,*image,exception);
6327  }
6328  XSetCursorState(display,windows,MagickFalse);
6329  coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6330  return(status != 0 ? MagickTrue : MagickFalse);
6331}
6332
6333/*
6334%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6335%                                                                             %
6336%                                                                             %
6337%                                                                             %
6338+   X D r a w P a n R e c t a n g l e                                         %
6339%                                                                             %
6340%                                                                             %
6341%                                                                             %
6342%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6343%
6344%  XDrawPanRectangle() draws a rectangle in the pan window.  The pan window
6345%  displays a zoom image and the rectangle shows which portion of the image is
6346%  displayed in the Image window.
6347%
6348%  The format of the XDrawPanRectangle method is:
6349%
6350%      XDrawPanRectangle(Display *display,XWindows *windows)
6351%
6352%  A description of each parameter follows:
6353%
6354%    o display: Specifies a connection to an X server;  returned from
6355%      XOpenDisplay.
6356%
6357%    o windows: Specifies a pointer to a XWindows structure.
6358%
6359*/
6360static void XDrawPanRectangle(Display *display,XWindows *windows)
6361{
6362  double
6363    scale_factor;
6364
6365  RectangleInfo
6366    highlight_info;
6367
6368  /*
6369    Determine dimensions of the panning rectangle.
6370  */
6371  scale_factor=(double) windows->pan.width/windows->image.ximage->width;
6372  highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6373  highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6374  scale_factor=(double)
6375    windows->pan.height/windows->image.ximage->height;
6376  highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6377  highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6378  /*
6379    Display the panning rectangle.
6380  */
6381  (void) XClearWindow(display,windows->pan.id);
6382  XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6383    &highlight_info);
6384}
6385
6386/*
6387%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6388%                                                                             %
6389%                                                                             %
6390%                                                                             %
6391+   X I m a g e C a c h e                                                     %
6392%                                                                             %
6393%                                                                             %
6394%                                                                             %
6395%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6396%
6397%  XImageCache() handles the creation, manipulation, and destruction of the
6398%  image cache (undo and redo buffers).
6399%
6400%  The format of the XImageCache method is:
6401%
6402%      void XImageCache(Display *display,XResourceInfo *resource_info,
6403%        XWindows *windows,const CommandType command,Image **image,
6404%        ExceptionInfo *exception)
6405%
6406%  A description of each parameter follows:
6407%
6408%    o display: Specifies a connection to an X server; returned from
6409%      XOpenDisplay.
6410%
6411%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6412%
6413%    o windows: Specifies a pointer to a XWindows structure.
6414%
6415%    o command: Specifies a command to perform.
6416%
6417%    o image: the image;  XImageCache may transform the image and return a new
6418%      image pointer.
6419%
6420%    o exception: return any errors or warnings in this structure.
6421%
6422*/
6423static void XImageCache(Display *display,XResourceInfo *resource_info,
6424  XWindows *windows,const CommandType command,Image **image,
6425  ExceptionInfo *exception)
6426{
6427  Image
6428    *cache_image;
6429
6430  static Image
6431    *redo_image = (Image *) NULL,
6432    *undo_image = (Image *) NULL;
6433
6434  switch (command)
6435  {
6436    case FreeBuffersCommand:
6437    {
6438      /*
6439        Free memory from the undo and redo cache.
6440      */
6441      while (undo_image != (Image *) NULL)
6442      {
6443        cache_image=undo_image;
6444        undo_image=GetPreviousImageInList(undo_image);
6445        cache_image->list=DestroyImage(cache_image->list);
6446        cache_image=DestroyImage(cache_image);
6447      }
6448      undo_image=NewImageList();
6449      if (redo_image != (Image *) NULL)
6450        redo_image=DestroyImage(redo_image);
6451      redo_image=NewImageList();
6452      return;
6453    }
6454    case UndoCommand:
6455    {
6456      char
6457        image_geometry[MagickPathExtent];
6458
6459      /*
6460        Undo the last image transformation.
6461      */
6462      if (undo_image == (Image *) NULL)
6463        {
6464          (void) XBell(display,0);
6465          return;
6466        }
6467      cache_image=undo_image;
6468      undo_image=GetPreviousImageInList(undo_image);
6469      windows->image.window_changes.width=(int) cache_image->columns;
6470      windows->image.window_changes.height=(int) cache_image->rows;
6471      (void) FormatLocaleString(image_geometry,MagickPathExtent,"%dx%d!",
6472        windows->image.ximage->width,windows->image.ximage->height);
6473      (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
6474        exception);
6475      if (windows->image.crop_geometry != (char *) NULL)
6476        windows->image.crop_geometry=(char *) RelinquishMagickMemory(
6477          windows->image.crop_geometry);
6478      windows->image.crop_geometry=cache_image->geometry;
6479      if (redo_image != (Image *) NULL)
6480        redo_image=DestroyImage(redo_image);
6481      redo_image=(*image);
6482      *image=cache_image->list;
6483      cache_image=DestroyImage(cache_image);
6484      if (IfMagickTrue(windows->image.orphan) )
6485        return;
6486      XConfigureImageColormap(display,resource_info,windows,*image,exception);
6487      (void) XConfigureImage(display,resource_info,windows,*image,exception);
6488      return;
6489    }
6490    case CutCommand:
6491    case PasteCommand:
6492    case ApplyCommand:
6493    case HalfSizeCommand:
6494    case OriginalSizeCommand:
6495    case DoubleSizeCommand:
6496    case ResizeCommand:
6497    case TrimCommand:
6498    case CropCommand:
6499    case ChopCommand:
6500    case FlipCommand:
6501    case FlopCommand:
6502    case RotateRightCommand:
6503    case RotateLeftCommand:
6504    case RotateCommand:
6505    case ShearCommand:
6506    case RollCommand:
6507    case NegateCommand:
6508    case ContrastStretchCommand:
6509    case SigmoidalContrastCommand:
6510    case NormalizeCommand:
6511    case EqualizeCommand:
6512    case HueCommand:
6513    case SaturationCommand:
6514    case BrightnessCommand:
6515    case GammaCommand:
6516    case SpiffCommand:
6517    case DullCommand:
6518    case GrayscaleCommand:
6519    case MapCommand:
6520    case QuantizeCommand:
6521    case DespeckleCommand:
6522    case EmbossCommand:
6523    case ReduceNoiseCommand:
6524    case AddNoiseCommand:
6525    case SharpenCommand:
6526    case BlurCommand:
6527    case ThresholdCommand:
6528    case EdgeDetectCommand:
6529    case SpreadCommand:
6530    case ShadeCommand:
6531    case RaiseCommand:
6532    case SegmentCommand:
6533    case SolarizeCommand:
6534    case SepiaToneCommand:
6535    case SwirlCommand:
6536    case ImplodeCommand:
6537    case VignetteCommand:
6538    case WaveCommand:
6539    case OilPaintCommand:
6540    case CharcoalDrawCommand:
6541    case AnnotateCommand:
6542    case AddBorderCommand:
6543    case AddFrameCommand:
6544    case CompositeCommand:
6545    case CommentCommand:
6546    case LaunchCommand:
6547    case RegionofInterestCommand:
6548    case SaveToUndoBufferCommand:
6549    case RedoCommand:
6550    {
6551      Image
6552        *previous_image;
6553
6554      ssize_t
6555        bytes;
6556
6557      bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelInfo));
6558      if (undo_image != (Image *) NULL)
6559        {
6560          /*
6561            Ensure the undo cache has enough memory available.
6562          */
6563          previous_image=undo_image;
6564          while (previous_image != (Image *) NULL)
6565          {
6566            bytes+=previous_image->list->columns*previous_image->list->rows*
6567              sizeof(PixelInfo);
6568            if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6569              {
6570                previous_image=GetPreviousImageInList(previous_image);
6571                continue;
6572              }
6573            bytes-=previous_image->list->columns*previous_image->list->rows*
6574              sizeof(PixelInfo);
6575            if (previous_image == undo_image)
6576              undo_image=NewImageList();
6577            else
6578              previous_image->next->previous=NewImageList();
6579            break;
6580          }
6581          while (previous_image != (Image *) NULL)
6582          {
6583            /*
6584              Delete any excess memory from undo cache.
6585            */
6586            cache_image=previous_image;
6587            previous_image=GetPreviousImageInList(previous_image);
6588            cache_image->list=DestroyImage(cache_image->list);
6589            cache_image=DestroyImage(cache_image);
6590          }
6591        }
6592      if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6593        break;
6594      /*
6595        Save image before transformations are applied.
6596      */
6597      cache_image=AcquireImage((ImageInfo *) NULL,exception);
6598      if (cache_image == (Image *) NULL)
6599        break;
6600      XSetCursorState(display,windows,MagickTrue);
6601      XCheckRefreshWindows(display,windows);
6602      cache_image->list=CloneImage(*image,0,0,MagickTrue,exception);
6603      XSetCursorState(display,windows,MagickFalse);
6604      if (cache_image->list == (Image *) NULL)
6605        {
6606          cache_image=DestroyImage(cache_image);
6607          break;
6608        }
6609      cache_image->columns=(size_t) windows->image.ximage->width;
6610      cache_image->rows=(size_t) windows->image.ximage->height;
6611      cache_image->geometry=windows->image.crop_geometry;
6612      if (windows->image.crop_geometry != (char *) NULL)
6613        {
6614          cache_image->geometry=AcquireString((char *) NULL);
6615          (void) CopyMagickString(cache_image->geometry,
6616            windows->image.crop_geometry,MagickPathExtent);
6617        }
6618      if (undo_image == (Image *) NULL)
6619        {
6620          undo_image=cache_image;
6621          break;
6622        }
6623      undo_image->next=cache_image;
6624      undo_image->next->previous=undo_image;
6625      undo_image=undo_image->next;
6626      break;
6627    }
6628    default:
6629      break;
6630  }
6631  if (command == RedoCommand)
6632    {
6633      /*
6634        Redo the last image transformation.
6635      */
6636      if (redo_image == (Image *) NULL)
6637        {
6638          (void) XBell(display,0);
6639          return;
6640        }
6641      windows->image.window_changes.width=(int) redo_image->columns;
6642      windows->image.window_changes.height=(int) redo_image->rows;
6643      if (windows->image.crop_geometry != (char *) NULL)
6644        windows->image.crop_geometry=(char *)
6645          RelinquishMagickMemory(windows->image.crop_geometry);
6646      windows->image.crop_geometry=redo_image->geometry;
6647      *image=DestroyImage(*image);
6648      *image=redo_image;
6649      redo_image=NewImageList();
6650      if (IfMagickTrue(windows->image.orphan) )
6651        return;
6652      XConfigureImageColormap(display,resource_info,windows,*image,exception);
6653      (void) XConfigureImage(display,resource_info,windows,*image,exception);
6654      return;
6655    }
6656  if (command != InfoCommand)
6657    return;
6658  /*
6659    Display image info.
6660  */
6661  XSetCursorState(display,windows,MagickTrue);
6662  XCheckRefreshWindows(display,windows);
6663  XDisplayImageInfo(display,resource_info,windows,undo_image,*image,exception);
6664  XSetCursorState(display,windows,MagickFalse);
6665}
6666
6667/*
6668%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6669%                                                                             %
6670%                                                                             %
6671%                                                                             %
6672+   X I m a g e W i n d o w C o m m a n d                                     %
6673%                                                                             %
6674%                                                                             %
6675%                                                                             %
6676%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6677%
6678%  XImageWindowCommand() makes a transform to the image or Image window as
6679%  specified by a user menu button or keyboard command.
6680%
6681%  The format of the XImageWindowCommand method is:
6682%
6683%      CommandType XImageWindowCommand(Display *display,
6684%        XResourceInfo *resource_info,XWindows *windows,
6685%        const MagickStatusType state,KeySym key_symbol,Image **image,
6686%        ExceptionInfo *exception)
6687%
6688%  A description of each parameter follows:
6689%
6690%    o nexus:  Method XImageWindowCommand returns an image when the
6691%      user chooses 'Open Image' from the command menu.  Otherwise a null
6692%      image is returned.
6693%
6694%    o display: Specifies a connection to an X server; returned from
6695%      XOpenDisplay.
6696%
6697%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6698%
6699%    o windows: Specifies a pointer to a XWindows structure.
6700%
6701%    o state: key mask.
6702%
6703%    o key_symbol: Specifies a command to perform.
6704%
6705%    o image: the image;  XImageWIndowCommand may transform the image and
6706%      return a new image pointer.
6707%
6708%    o exception: return any errors or warnings in this structure.
6709%
6710*/
6711static CommandType XImageWindowCommand(Display *display,
6712  XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6713  KeySym key_symbol,Image **image,ExceptionInfo *exception)
6714{
6715  static char
6716    delta[MagickPathExtent] = "";
6717
6718  static const char
6719    Digits[] = "01234567890";
6720
6721  static KeySym
6722    last_symbol = XK_0;
6723
6724  if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6725    {
6726      if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6727        {
6728          *delta='\0';
6729          resource_info->quantum=1;
6730        }
6731      last_symbol=key_symbol;
6732      delta[strlen(delta)+1]='\0';
6733      delta[strlen(delta)]=Digits[key_symbol-XK_0];
6734      resource_info->quantum=StringToLong(delta);
6735      return(NullCommand);
6736    }
6737  last_symbol=key_symbol;
6738  if (resource_info->immutable)
6739    {
6740      /*
6741        Virtual image window has a restricted command set.
6742      */
6743      switch (key_symbol)
6744      {
6745        case XK_question:
6746          return(InfoCommand);
6747        case XK_p:
6748        case XK_Print:
6749          return(PrintCommand);
6750        case XK_space:
6751          return(NextCommand);
6752        case XK_q:
6753        case XK_Escape:
6754          return(QuitCommand);
6755        default:
6756          break;
6757      }
6758      return(NullCommand);
6759    }
6760  switch ((int) key_symbol)
6761  {
6762    case XK_o:
6763    {
6764      if ((state & ControlMask) == 0)
6765        break;
6766      return(OpenCommand);
6767    }
6768    case XK_space:
6769      return(NextCommand);
6770    case XK_BackSpace:
6771      return(FormerCommand);
6772    case XK_s:
6773    {
6774      if ((state & Mod1Mask) != 0)
6775        return(SwirlCommand);
6776      if ((state & ControlMask) == 0)
6777        return(ShearCommand);
6778      return(SaveCommand);
6779    }
6780    case XK_p:
6781    case XK_Print:
6782    {
6783      if ((state & Mod1Mask) != 0)
6784        return(OilPaintCommand);
6785      if ((state & Mod4Mask) != 0)
6786        return(ColorCommand);
6787      if ((state & ControlMask) == 0)
6788        return(NullCommand);
6789      return(PrintCommand);
6790    }
6791    case XK_d:
6792    {
6793      if ((state & Mod4Mask) != 0)
6794        return(DrawCommand);
6795      if ((state & ControlMask) == 0)
6796        return(NullCommand);
6797      return(DeleteCommand);
6798    }
6799    case XK_Select:
6800    {
6801      if ((state & ControlMask) == 0)
6802        return(NullCommand);
6803      return(SelectCommand);
6804    }
6805    case XK_n:
6806    {
6807      if ((state & ControlMask) == 0)
6808        return(NullCommand);
6809      return(NewCommand);
6810    }
6811    case XK_q:
6812    case XK_Escape:
6813      return(QuitCommand);
6814    case XK_z:
6815    case XK_Undo:
6816    {
6817      if ((state & ControlMask) == 0)
6818        return(NullCommand);
6819      return(UndoCommand);
6820    }
6821    case XK_r:
6822    case XK_Redo:
6823    {
6824      if ((state & ControlMask) == 0)
6825        return(RollCommand);
6826      return(RedoCommand);
6827    }
6828    case XK_x:
6829    {
6830      if ((state & ControlMask) == 0)
6831        return(NullCommand);
6832      return(CutCommand);
6833    }
6834    case XK_c:
6835    {
6836      if ((state & Mod1Mask) != 0)
6837        return(CharcoalDrawCommand);
6838      if ((state & ControlMask) == 0)
6839        return(CropCommand);
6840      return(CopyCommand);
6841    }
6842    case XK_v:
6843    case XK_Insert:
6844    {
6845      if ((state & Mod4Mask) != 0)
6846        return(CompositeCommand);
6847      if ((state & ControlMask) == 0)
6848        return(FlipCommand);
6849      return(PasteCommand);
6850    }
6851    case XK_less:
6852      return(HalfSizeCommand);
6853    case XK_minus:
6854      return(OriginalSizeCommand);
6855    case XK_greater:
6856      return(DoubleSizeCommand);
6857    case XK_percent:
6858      return(ResizeCommand);
6859    case XK_at:
6860      return(RefreshCommand);
6861    case XK_bracketleft:
6862      return(ChopCommand);
6863    case XK_h:
6864      return(FlopCommand);
6865    case XK_slash:
6866      return(RotateRightCommand);
6867    case XK_backslash:
6868      return(RotateLeftCommand);
6869    case XK_asterisk:
6870      return(RotateCommand);
6871    case XK_t:
6872      return(TrimCommand);
6873    case XK_H:
6874      return(HueCommand);
6875    case XK_S:
6876      return(SaturationCommand);
6877    case XK_L:
6878      return(BrightnessCommand);
6879    case XK_G:
6880      return(GammaCommand);
6881    case XK_C:
6882      return(SpiffCommand);
6883    case XK_Z:
6884      return(DullCommand);
6885    case XK_N:
6886      return(NormalizeCommand);
6887    case XK_equal:
6888      return(EqualizeCommand);
6889    case XK_asciitilde:
6890      return(NegateCommand);
6891    case XK_period:
6892      return(GrayscaleCommand);
6893    case XK_numbersign:
6894      return(QuantizeCommand);
6895    case XK_F2:
6896      return(DespeckleCommand);
6897    case XK_F3:
6898      return(EmbossCommand);
6899    case XK_F4:
6900      return(ReduceNoiseCommand);
6901    case XK_F5:
6902      return(AddNoiseCommand);
6903    case XK_F6:
6904      return(SharpenCommand);
6905    case XK_F7:
6906      return(BlurCommand);
6907    case XK_F8:
6908      return(ThresholdCommand);
6909    case XK_F9:
6910      return(EdgeDetectCommand);
6911    case XK_F10:
6912      return(SpreadCommand);
6913    case XK_F11:
6914      return(ShadeCommand);
6915    case XK_F12:
6916      return(RaiseCommand);
6917    case XK_F13:
6918      return(SegmentCommand);
6919    case XK_i:
6920    {
6921      if ((state & Mod1Mask) == 0)
6922        return(NullCommand);
6923      return(ImplodeCommand);
6924    }
6925    case XK_w:
6926    {
6927      if ((state & Mod1Mask) == 0)
6928        return(NullCommand);
6929      return(WaveCommand);
6930    }
6931    case XK_m:
6932    {
6933      if ((state & Mod4Mask) == 0)
6934        return(NullCommand);
6935      return(MatteCommand);
6936    }
6937    case XK_b:
6938    {
6939      if ((state & Mod4Mask) == 0)
6940        return(NullCommand);
6941      return(AddBorderCommand);
6942    }
6943    case XK_f:
6944    {
6945      if ((state & Mod4Mask) == 0)
6946        return(NullCommand);
6947      return(AddFrameCommand);
6948    }
6949    case XK_exclam:
6950    {
6951      if ((state & Mod4Mask) == 0)
6952        return(NullCommand);
6953      return(CommentCommand);
6954    }
6955    case XK_a:
6956    {
6957      if ((state & Mod1Mask) != 0)
6958        return(ApplyCommand);
6959      if ((state & Mod4Mask) != 0)
6960        return(AnnotateCommand);
6961      if ((state & ControlMask) == 0)
6962        return(NullCommand);
6963      return(RegionofInterestCommand);
6964    }
6965    case XK_question:
6966      return(InfoCommand);
6967    case XK_plus:
6968      return(ZoomCommand);
6969    case XK_P:
6970    {
6971      if ((state & ShiftMask) == 0)
6972        return(NullCommand);
6973      return(ShowPreviewCommand);
6974    }
6975    case XK_Execute:
6976      return(LaunchCommand);
6977    case XK_F1:
6978      return(HelpCommand);
6979    case XK_Find:
6980      return(BrowseDocumentationCommand);
6981    case XK_Menu:
6982    {
6983      (void) XMapRaised(display,windows->command.id);
6984      return(NullCommand);
6985    }
6986    case XK_Next:
6987    case XK_Prior:
6988    case XK_Home:
6989    case XK_KP_Home:
6990    {
6991      XTranslateImage(display,windows,*image,key_symbol);
6992      return(NullCommand);
6993    }
6994    case XK_Up:
6995    case XK_KP_Up:
6996    case XK_Down:
6997    case XK_KP_Down:
6998    case XK_Left:
6999    case XK_KP_Left:
7000    case XK_Right:
7001    case XK_KP_Right:
7002    {
7003      if ((state & Mod1Mask) != 0)
7004        {
7005          RectangleInfo
7006            crop_info;
7007
7008          /*
7009            Trim one pixel from edge of image.
7010          */
7011          crop_info.x=0;
7012          crop_info.y=0;
7013          crop_info.width=(size_t) windows->image.ximage->width;
7014          crop_info.height=(size_t) windows->image.ximage->height;
7015          if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
7016            {
7017              if (resource_info->quantum >= (int) crop_info.height)
7018                resource_info->quantum=(int) crop_info.height-1;
7019              crop_info.height-=resource_info->quantum;
7020            }
7021          if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
7022            {
7023              if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
7024                resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
7025              crop_info.y+=resource_info->quantum;
7026              crop_info.height-=resource_info->quantum;
7027            }
7028          if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
7029            {
7030              if (resource_info->quantum >= (int) crop_info.width)
7031                resource_info->quantum=(int) crop_info.width-1;
7032              crop_info.width-=resource_info->quantum;
7033            }
7034          if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
7035            {
7036              if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
7037                resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
7038              crop_info.x+=resource_info->quantum;
7039              crop_info.width-=resource_info->quantum;
7040            }
7041          if ((int) (windows->image.x+windows->image.width) >
7042              (int) crop_info.width)
7043            windows->image.x=(int) (crop_info.width-windows->image.width);
7044          if ((int) (windows->image.y+windows->image.height) >
7045              (int) crop_info.height)
7046            windows->image.y=(int) (crop_info.height-windows->image.height);
7047          XSetCropGeometry(display,windows,&crop_info,*image);
7048          windows->image.window_changes.width=(int) crop_info.width;
7049          windows->image.window_changes.height=(int) crop_info.height;
7050          (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
7051          (void) XConfigureImage(display,resource_info,windows,*image,
7052            exception);
7053          return(NullCommand);
7054        }
7055      XTranslateImage(display,windows,*image,key_symbol);
7056      return(NullCommand);
7057    }
7058    default:
7059      return(NullCommand);
7060  }
7061  return(NullCommand);
7062}
7063
7064/*
7065%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7066%                                                                             %
7067%                                                                             %
7068%                                                                             %
7069+   X M a g i c k C o m m a n d                                               %
7070%                                                                             %
7071%                                                                             %
7072%                                                                             %
7073%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7074%
7075%  XMagickCommand() makes a transform to the image or Image window as
7076%  specified by a user menu button or keyboard command.
7077%
7078%  The format of the XMagickCommand method is:
7079%
7080%      Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7081%        XWindows *windows,const CommandType command,Image **image,
7082%        ExceptionInfo *exception)
7083%
7084%  A description of each parameter follows:
7085%
7086%    o display: Specifies a connection to an X server; returned from
7087%      XOpenDisplay.
7088%
7089%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7090%
7091%    o windows: Specifies a pointer to a XWindows structure.
7092%
7093%    o command: Specifies a command to perform.
7094%
7095%    o image: the image;  XMagickCommand may transform the image and return a
7096%      new image pointer.
7097%
7098%    o exception: return any errors or warnings in this structure.
7099%
7100*/
7101static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7102  XWindows *windows,const CommandType command,Image **image,
7103  ExceptionInfo *exception)
7104{
7105  char
7106    filename[MagickPathExtent],
7107    geometry[MagickPathExtent],
7108    modulate_factors[MagickPathExtent];
7109
7110  GeometryInfo
7111    geometry_info;
7112
7113  Image
7114    *nexus;
7115
7116  ImageInfo
7117    *image_info;
7118
7119  int
7120    x,
7121    y;
7122
7123  MagickStatusType
7124    flags,
7125    status;
7126
7127  QuantizeInfo
7128    quantize_info;
7129
7130  RectangleInfo
7131    page_geometry;
7132
7133  register int
7134    i;
7135
7136  static char
7137    color[MagickPathExtent] = "gray";
7138
7139  unsigned int
7140    height,
7141    width;
7142
7143  /*
7144    Process user command.
7145  */
7146  XCheckRefreshWindows(display,windows);
7147  XImageCache(display,resource_info,windows,command,image,exception);
7148  nexus=NewImageList();
7149  windows->image.window_changes.width=windows->image.ximage->width;
7150  windows->image.window_changes.height=windows->image.ximage->height;
7151  image_info=CloneImageInfo(resource_info->image_info);
7152  SetGeometryInfo(&geometry_info);
7153  GetQuantizeInfo(&quantize_info);
7154  switch (command)
7155  {
7156    case OpenCommand:
7157    {
7158      /*
7159        Load image.
7160      */
7161      nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7162      break;
7163    }
7164    case NextCommand:
7165    {
7166      /*
7167        Display next image.
7168      */
7169      for (i=0; i < resource_info->quantum; i++)
7170        XClientMessage(display,windows->image.id,windows->im_protocols,
7171          windows->im_next_image,CurrentTime);
7172      break;
7173    }
7174    case FormerCommand:
7175    {
7176      /*
7177        Display former image.
7178      */
7179      for (i=0; i < resource_info->quantum; i++)
7180        XClientMessage(display,windows->image.id,windows->im_protocols,
7181          windows->im_former_image,CurrentTime);
7182      break;
7183    }
7184    case SelectCommand:
7185    {
7186      int
7187        status;
7188
7189      /*
7190        Select image.
7191      */
7192      if (*resource_info->home_directory == '\0')
7193        (void) CopyMagickString(resource_info->home_directory,".",
7194          MagickPathExtent);
7195      status=chdir(resource_info->home_directory);
7196      if (status == -1)
7197        (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
7198          "UnableToOpenFile","%s",resource_info->home_directory);
7199      nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7200      break;
7201    }
7202    case SaveCommand:
7203    {
7204      /*
7205        Save image.
7206      */
7207      status=XSaveImage(display,resource_info,windows,*image,exception);
7208      if (IfMagickFalse(status) )
7209        {
7210          char
7211            message[MagickPathExtent];
7212
7213          (void) FormatLocaleString(message,MagickPathExtent,"%s:%s",
7214            exception->reason != (char *) NULL ? exception->reason : "",
7215            exception->description != (char *) NULL ? exception->description :
7216            "");
7217          XNoticeWidget(display,windows,"Unable to save file:",message);
7218          break;
7219        }
7220      break;
7221    }
7222    case PrintCommand:
7223    {
7224      /*
7225        Print image.
7226      */
7227      status=XPrintImage(display,resource_info,windows,*image,exception);
7228      if (IfMagickFalse(status) )
7229        {
7230          char
7231            message[MagickPathExtent];
7232
7233          (void) FormatLocaleString(message,MagickPathExtent,"%s:%s",
7234            exception->reason != (char *) NULL ? exception->reason : "",
7235            exception->description != (char *) NULL ? exception->description :
7236            "");
7237          XNoticeWidget(display,windows,"Unable to print file:",message);
7238          break;
7239        }
7240      break;
7241    }
7242    case DeleteCommand:
7243    {
7244      static char
7245        filename[MagickPathExtent] = "\0";
7246
7247      /*
7248        Delete image file.
7249      */
7250      XFileBrowserWidget(display,windows,"Delete",filename);
7251      if (*filename == '\0')
7252        break;
7253      status=ShredFile(filename);
7254      if (IfMagickTrue(status) )
7255        XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7256      break;
7257    }
7258    case NewCommand:
7259    {
7260      int
7261        status;
7262
7263      static char
7264        color[MagickPathExtent] = "gray",
7265        geometry[MagickPathExtent] = "640x480";
7266
7267      static const char
7268        *format = "gradient";
7269
7270      /*
7271        Query user for canvas geometry.
7272      */
7273      status=XDialogWidget(display,windows,"New","Enter image geometry:",
7274        geometry);
7275      if (*geometry == '\0')
7276        break;
7277      if (status == 0)
7278        format="xc";
7279      XColorBrowserWidget(display,windows,"Select",color);
7280      if (*color == '\0')
7281        break;
7282      /*
7283        Create canvas.
7284      */
7285      (void) FormatLocaleString(image_info->filename,MagickPathExtent,
7286        "%s:%s",format,color);
7287      (void) CloneString(&image_info->size,geometry);
7288      nexus=ReadImage(image_info,exception);
7289      CatchException(exception);
7290      XClientMessage(display,windows->image.id,windows->im_protocols,
7291        windows->im_next_image,CurrentTime);
7292      break;
7293    }
7294    case VisualDirectoryCommand:
7295    {
7296      /*
7297        Visual Image directory.
7298      */
7299      nexus=XVisualDirectoryImage(display,resource_info,windows,exception);
7300      break;
7301    }
7302    case QuitCommand:
7303    {
7304      /*
7305        exit program.
7306      */
7307      if (IfMagickFalse(resource_info->confirm_exit) )
7308        XClientMessage(display,windows->image.id,windows->im_protocols,
7309          windows->im_exit,CurrentTime);
7310      else
7311        {
7312          int
7313            status;
7314
7315          /*
7316            Confirm program exit.
7317          */
7318          status=XConfirmWidget(display,windows,"Do you really want to exit",
7319            resource_info->client_name);
7320          if (status > 0)
7321            XClientMessage(display,windows->image.id,windows->im_protocols,
7322              windows->im_exit,CurrentTime);
7323        }
7324      break;
7325    }
7326    case CutCommand:
7327    {
7328      /*
7329        Cut image.
7330      */
7331      (void) XCropImage(display,resource_info,windows,*image,CutMode,exception);
7332      break;
7333    }
7334    case CopyCommand:
7335    {
7336      /*
7337        Copy image.
7338      */
7339      (void) XCropImage(display,resource_info,windows,*image,CopyMode,
7340        exception);
7341      break;
7342    }
7343    case PasteCommand:
7344    {
7345      /*
7346        Paste image.
7347      */
7348      status=XPasteImage(display,resource_info,windows,*image,exception);
7349      if (IfMagickFalse(status) )
7350        {
7351          XNoticeWidget(display,windows,"Unable to paste X image",
7352            (*image)->filename);
7353          break;
7354        }
7355      break;
7356    }
7357    case HalfSizeCommand:
7358    {
7359      /*
7360        Half image size.
7361      */
7362      windows->image.window_changes.width=windows->image.ximage->width/2;
7363      windows->image.window_changes.height=windows->image.ximage->height/2;
7364      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7365      break;
7366    }
7367    case OriginalSizeCommand:
7368    {
7369      /*
7370        Original image size.
7371      */
7372      windows->image.window_changes.width=(int) (*image)->columns;
7373      windows->image.window_changes.height=(int) (*image)->rows;
7374      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7375      break;
7376    }
7377    case DoubleSizeCommand:
7378    {
7379      /*
7380        Double the image size.
7381      */
7382      windows->image.window_changes.width=windows->image.ximage->width << 1;
7383      windows->image.window_changes.height=windows->image.ximage->height << 1;
7384      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7385      break;
7386    }
7387    case ResizeCommand:
7388    {
7389      int
7390        status;
7391
7392      size_t
7393        height,
7394        width;
7395
7396      ssize_t
7397        x,
7398        y;
7399
7400      /*
7401        Resize image.
7402      */
7403      width=(size_t) windows->image.ximage->width;
7404      height=(size_t) windows->image.ximage->height;
7405      x=0;
7406      y=0;
7407      (void) FormatLocaleString(geometry,MagickPathExtent,"%.20gx%.20g+0+0",
7408        (double) width,(double) height);
7409      status=XDialogWidget(display,windows,"Resize",
7410        "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7411      if (*geometry == '\0')
7412        break;
7413      if (status == 0)
7414        (void) ConcatenateMagickString(geometry,"!",MagickPathExtent);
7415      (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7416      windows->image.window_changes.width=(int) width;
7417      windows->image.window_changes.height=(int) height;
7418      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7419      break;
7420    }
7421    case ApplyCommand:
7422    {
7423      char
7424        image_geometry[MagickPathExtent];
7425
7426      if ((windows->image.crop_geometry == (char *) NULL) &&
7427          ((int) (*image)->columns == windows->image.ximage->width) &&
7428          ((int) (*image)->rows == windows->image.ximage->height))
7429        break;
7430      /*
7431        Apply size transforms to image.
7432      */
7433      XSetCursorState(display,windows,MagickTrue);
7434      XCheckRefreshWindows(display,windows);
7435      /*
7436        Crop and/or scale displayed image.
7437      */
7438      (void) FormatLocaleString(image_geometry,MagickPathExtent,"%dx%d!",
7439        windows->image.ximage->width,windows->image.ximage->height);
7440      (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
7441        exception);
7442      if (windows->image.crop_geometry != (char *) NULL)
7443        windows->image.crop_geometry=(char *) RelinquishMagickMemory(
7444          windows->image.crop_geometry);
7445      windows->image.x=0;
7446      windows->image.y=0;
7447      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7448      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7449      break;
7450    }
7451    case RefreshCommand:
7452    {
7453      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7454      break;
7455    }
7456    case RestoreCommand:
7457    {
7458      /*
7459        Restore Image window to its original size.
7460      */
7461      if ((windows->image.width == (unsigned int) (*image)->columns) &&
7462          (windows->image.height == (unsigned int) (*image)->rows) &&
7463          (windows->image.crop_geometry == (char *) NULL))
7464        {
7465          (void) XBell(display,0);
7466          break;
7467        }
7468      windows->image.window_changes.width=(int) (*image)->columns;
7469      windows->image.window_changes.height=(int) (*image)->rows;
7470      if (windows->image.crop_geometry != (char *) NULL)
7471        {
7472          windows->image.crop_geometry=(char *)
7473            RelinquishMagickMemory(windows->image.crop_geometry);
7474          windows->image.crop_geometry=(char *) NULL;
7475          windows->image.x=0;
7476          windows->image.y=0;
7477        }
7478      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7479      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7480      break;
7481    }
7482    case CropCommand:
7483    {
7484      /*
7485        Crop image.
7486      */
7487      (void) XCropImage(display,resource_info,windows,*image,CropMode,
7488        exception);
7489      break;
7490    }
7491    case ChopCommand:
7492    {
7493      /*
7494        Chop image.
7495      */
7496      status=XChopImage(display,resource_info,windows,image,exception);
7497      if (IfMagickFalse(status) )
7498        {
7499          XNoticeWidget(display,windows,"Unable to cut X image",
7500            (*image)->filename);
7501          break;
7502        }
7503      break;
7504    }
7505    case FlopCommand:
7506    {
7507      Image
7508        *flop_image;
7509
7510      /*
7511        Flop image scanlines.
7512      */
7513      XSetCursorState(display,windows,MagickTrue);
7514      XCheckRefreshWindows(display,windows);
7515      flop_image=FlopImage(*image,exception);
7516      if (flop_image != (Image *) NULL)
7517        {
7518          *image=DestroyImage(*image);
7519          *image=flop_image;
7520        }
7521      CatchException(exception);
7522      XSetCursorState(display,windows,MagickFalse);
7523      if (windows->image.crop_geometry != (char *) NULL)
7524        {
7525          /*
7526            Flop crop geometry.
7527          */
7528          width=(unsigned int) (*image)->columns;
7529          height=(unsigned int) (*image)->rows;
7530          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7531            &width,&height);
7532          (void) FormatLocaleString(windows->image.crop_geometry,MagickPathExtent,
7533            "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7534        }
7535      if (IfMagickTrue(windows->image.orphan) )
7536        break;
7537      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7538      break;
7539    }
7540    case FlipCommand:
7541    {
7542      Image
7543        *flip_image;
7544
7545      /*
7546        Flip image scanlines.
7547      */
7548      XSetCursorState(display,windows,MagickTrue);
7549      XCheckRefreshWindows(display,windows);
7550      flip_image=FlipImage(*image,exception);
7551      if (flip_image != (Image *) NULL)
7552        {
7553          *image=DestroyImage(*image);
7554          *image=flip_image;
7555        }
7556      CatchException(exception);
7557      XSetCursorState(display,windows,MagickFalse);
7558      if (windows->image.crop_geometry != (char *) NULL)
7559        {
7560          /*
7561            Flip crop geometry.
7562          */
7563          width=(unsigned int) (*image)->columns;
7564          height=(unsigned int) (*image)->rows;
7565          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7566            &width,&height);
7567          (void) FormatLocaleString(windows->image.crop_geometry,MagickPathExtent,
7568            "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7569        }
7570      if (IfMagickTrue(windows->image.orphan) )
7571        break;
7572      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7573      break;
7574    }
7575    case RotateRightCommand:
7576    {
7577      /*
7578        Rotate image 90 degrees clockwise.
7579      */
7580      status=XRotateImage(display,resource_info,windows,90.0,image,exception);
7581      if (IfMagickFalse(status) )
7582        {
7583          XNoticeWidget(display,windows,"Unable to rotate X image",
7584            (*image)->filename);
7585          break;
7586        }
7587      break;
7588    }
7589    case RotateLeftCommand:
7590    {
7591      /*
7592        Rotate image 90 degrees counter-clockwise.
7593      */
7594      status=XRotateImage(display,resource_info,windows,-90.0,image,exception);
7595      if (IfMagickFalse(status) )
7596        {
7597          XNoticeWidget(display,windows,"Unable to rotate X image",
7598            (*image)->filename);
7599          break;
7600        }
7601      break;
7602    }
7603    case RotateCommand:
7604    {
7605      /*
7606        Rotate image.
7607      */
7608      status=XRotateImage(display,resource_info,windows,0.0,image,exception);
7609      if (IfMagickFalse(status) )
7610        {
7611          XNoticeWidget(display,windows,"Unable to rotate X image",
7612            (*image)->filename);
7613          break;
7614        }
7615      break;
7616    }
7617    case ShearCommand:
7618    {
7619      Image
7620        *shear_image;
7621
7622      static char
7623        geometry[MagickPathExtent] = "45.0x45.0";
7624
7625      /*
7626        Query user for shear color and geometry.
7627      */
7628      XColorBrowserWidget(display,windows,"Select",color);
7629      if (*color == '\0')
7630        break;
7631      (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7632        geometry);
7633      if (*geometry == '\0')
7634        break;
7635      /*
7636        Shear image.
7637      */
7638      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7639        exception);
7640      XSetCursorState(display,windows,MagickTrue);
7641      XCheckRefreshWindows(display,windows);
7642      (void) QueryColorCompliance(color,AllCompliance,
7643        &(*image)->background_color,exception);
7644      flags=ParseGeometry(geometry,&geometry_info);
7645      if ((flags & SigmaValue) == 0)
7646        geometry_info.sigma=geometry_info.rho;
7647      shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7648        exception);
7649      if (shear_image != (Image *) NULL)
7650        {
7651          *image=DestroyImage(*image);
7652          *image=shear_image;
7653        }
7654      CatchException(exception);
7655      XSetCursorState(display,windows,MagickFalse);
7656      if (IfMagickTrue(windows->image.orphan) )
7657        break;
7658      windows->image.window_changes.width=(int) (*image)->columns;
7659      windows->image.window_changes.height=(int) (*image)->rows;
7660      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7661      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7662      break;
7663    }
7664    case RollCommand:
7665    {
7666      Image
7667        *roll_image;
7668
7669      static char
7670        geometry[MagickPathExtent] = "+2+2";
7671
7672      /*
7673        Query user for the roll geometry.
7674      */
7675      (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7676        geometry);
7677      if (*geometry == '\0')
7678        break;
7679      /*
7680        Roll image.
7681      */
7682      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7683        exception);
7684      XSetCursorState(display,windows,MagickTrue);
7685      XCheckRefreshWindows(display,windows);
7686      (void) ParsePageGeometry(*image,geometry,&page_geometry,
7687        exception);
7688      roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7689        exception);
7690      if (roll_image != (Image *) NULL)
7691        {
7692          *image=DestroyImage(*image);
7693          *image=roll_image;
7694        }
7695      CatchException(exception);
7696      XSetCursorState(display,windows,MagickFalse);
7697      if (IfMagickTrue(windows->image.orphan) )
7698        break;
7699      windows->image.window_changes.width=(int) (*image)->columns;
7700      windows->image.window_changes.height=(int) (*image)->rows;
7701      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7702      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7703      break;
7704    }
7705    case TrimCommand:
7706    {
7707      static char
7708        fuzz[MagickPathExtent];
7709
7710      /*
7711        Query user for the fuzz factor.
7712      */
7713      (void) FormatLocaleString(fuzz,MagickPathExtent,"%g%%",100.0*
7714        (*image)->fuzz/(QuantumRange+1.0));
7715      (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7716      if (*fuzz == '\0')
7717        break;
7718      (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+1.0);
7719      /*
7720        Trim image.
7721      */
7722      status=XTrimImage(display,resource_info,windows,*image,exception);
7723      if (IfMagickFalse(status) )
7724        {
7725          XNoticeWidget(display,windows,"Unable to trim X image",
7726            (*image)->filename);
7727          break;
7728        }
7729      break;
7730    }
7731    case HueCommand:
7732    {
7733      static char
7734        hue_percent[MagickPathExtent] = "110";
7735
7736      /*
7737        Query user for percent hue change.
7738      */
7739      (void) XDialogWidget(display,windows,"Apply",
7740        "Enter percent change in image hue (0-200):",hue_percent);
7741      if (*hue_percent == '\0')
7742        break;
7743      /*
7744        Vary the image hue.
7745      */
7746      XSetCursorState(display,windows,MagickTrue);
7747      XCheckRefreshWindows(display,windows);
7748      (void) CopyMagickString(modulate_factors,"100.0/100.0/",MagickPathExtent);
7749      (void) ConcatenateMagickString(modulate_factors,hue_percent,
7750        MagickPathExtent);
7751      (void) ModulateImage(*image,modulate_factors,exception);
7752      XSetCursorState(display,windows,MagickFalse);
7753      if (IfMagickTrue(windows->image.orphan) )
7754        break;
7755      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7756      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7757      break;
7758    }
7759    case SaturationCommand:
7760    {
7761      static char
7762        saturation_percent[MagickPathExtent] = "110";
7763
7764      /*
7765        Query user for percent saturation change.
7766      */
7767      (void) XDialogWidget(display,windows,"Apply",
7768        "Enter percent change in color saturation (0-200):",saturation_percent);
7769      if (*saturation_percent == '\0')
7770        break;
7771      /*
7772        Vary color saturation.
7773      */
7774      XSetCursorState(display,windows,MagickTrue);
7775      XCheckRefreshWindows(display,windows);
7776      (void) CopyMagickString(modulate_factors,"100.0/",MagickPathExtent);
7777      (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7778        MagickPathExtent);
7779      (void) ModulateImage(*image,modulate_factors,exception);
7780      XSetCursorState(display,windows,MagickFalse);
7781      if (IfMagickTrue(windows->image.orphan) )
7782        break;
7783      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7784      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7785      break;
7786    }
7787    case BrightnessCommand:
7788    {
7789      static char
7790        brightness_percent[MagickPathExtent] = "110";
7791
7792      /*
7793        Query user for percent brightness change.
7794      */
7795      (void) XDialogWidget(display,windows,"Apply",
7796        "Enter percent change in color brightness (0-200):",brightness_percent);
7797      if (*brightness_percent == '\0')
7798        break;
7799      /*
7800        Vary the color brightness.
7801      */
7802      XSetCursorState(display,windows,MagickTrue);
7803      XCheckRefreshWindows(display,windows);
7804      (void) CopyMagickString(modulate_factors,brightness_percent,
7805        MagickPathExtent);
7806      (void) ModulateImage(*image,modulate_factors,exception);
7807      XSetCursorState(display,windows,MagickFalse);
7808      if (IfMagickTrue(windows->image.orphan) )
7809        break;
7810      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7811      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7812      break;
7813    }
7814    case GammaCommand:
7815    {
7816      static char
7817        factor[MagickPathExtent] = "1.6";
7818
7819      /*
7820        Query user for gamma value.
7821      */
7822      (void) XDialogWidget(display,windows,"Gamma",
7823        "Enter gamma value (e.g. 1.2):",factor);
7824      if (*factor == '\0')
7825        break;
7826      /*
7827        Gamma correct image.
7828      */
7829      XSetCursorState(display,windows,MagickTrue);
7830      XCheckRefreshWindows(display,windows);
7831      (void) GammaImage(*image,strtod(factor,(char **) NULL),exception);
7832      XSetCursorState(display,windows,MagickFalse);
7833      if (IfMagickTrue(windows->image.orphan) )
7834        break;
7835      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7836      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7837      break;
7838    }
7839    case SpiffCommand:
7840    {
7841      /*
7842        Sharpen the image contrast.
7843      */
7844      XSetCursorState(display,windows,MagickTrue);
7845      XCheckRefreshWindows(display,windows);
7846      (void) ContrastImage(*image,MagickTrue,exception);
7847      XSetCursorState(display,windows,MagickFalse);
7848      if (IfMagickTrue(windows->image.orphan) )
7849        break;
7850      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7851      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7852      break;
7853    }
7854    case DullCommand:
7855    {
7856      /*
7857        Dull the image contrast.
7858      */
7859      XSetCursorState(display,windows,MagickTrue);
7860      XCheckRefreshWindows(display,windows);
7861      (void) ContrastImage(*image,MagickFalse,exception);
7862      XSetCursorState(display,windows,MagickFalse);
7863      if (IfMagickTrue(windows->image.orphan) )
7864        break;
7865      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7866      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7867      break;
7868    }
7869    case ContrastStretchCommand:
7870    {
7871      double
7872        black_point,
7873        white_point;
7874
7875      static char
7876        levels[MagickPathExtent] = "1%";
7877
7878      /*
7879        Query user for gamma value.
7880      */
7881      (void) XDialogWidget(display,windows,"Contrast Stretch",
7882        "Enter black and white points:",levels);
7883      if (*levels == '\0')
7884        break;
7885      /*
7886        Contrast stretch image.
7887      */
7888      XSetCursorState(display,windows,MagickTrue);
7889      XCheckRefreshWindows(display,windows);
7890      flags=ParseGeometry(levels,&geometry_info);
7891      black_point=geometry_info.rho;
7892      white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7893      if ((flags & PercentValue) != 0)
7894        {
7895          black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7896          white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7897        }
7898      white_point=(double) (*image)->columns*(*image)->rows-white_point;
7899      (void) ContrastStretchImage(*image,black_point,white_point,
7900        exception);
7901      XSetCursorState(display,windows,MagickFalse);
7902      if (IfMagickTrue(windows->image.orphan) )
7903        break;
7904      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7905      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7906      break;
7907    }
7908    case SigmoidalContrastCommand:
7909    {
7910      GeometryInfo
7911        geometry_info;
7912
7913      MagickStatusType
7914        flags;
7915
7916      static char
7917        levels[MagickPathExtent] = "3x50%";
7918
7919      /*
7920        Query user for gamma value.
7921      */
7922      (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7923        "Enter contrast and midpoint:",levels);
7924      if (*levels == '\0')
7925        break;
7926      /*
7927        Contrast stretch image.
7928      */
7929      XSetCursorState(display,windows,MagickTrue);
7930      XCheckRefreshWindows(display,windows);
7931      flags=ParseGeometry(levels,&geometry_info);
7932      if ((flags & SigmaValue) == 0)
7933        geometry_info.sigma=1.0*QuantumRange/2.0;
7934      if ((flags & PercentValue) != 0)
7935        geometry_info.sigma=1.0*QuantumRange*geometry_info.sigma/100.0;
7936      (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
7937        geometry_info.sigma,exception);
7938      XSetCursorState(display,windows,MagickFalse);
7939      if (IfMagickTrue(windows->image.orphan) )
7940        break;
7941      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7942      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7943      break;
7944    }
7945    case NormalizeCommand:
7946    {
7947      /*
7948        Perform histogram normalization on the image.
7949      */
7950      XSetCursorState(display,windows,MagickTrue);
7951      XCheckRefreshWindows(display,windows);
7952      (void) NormalizeImage(*image,exception);
7953      XSetCursorState(display,windows,MagickFalse);
7954      if (IfMagickTrue(windows->image.orphan) )
7955        break;
7956      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7957      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7958      break;
7959    }
7960    case EqualizeCommand:
7961    {
7962      /*
7963        Perform histogram equalization on the image.
7964      */
7965      XSetCursorState(display,windows,MagickTrue);
7966      XCheckRefreshWindows(display,windows);
7967      (void) EqualizeImage(*image,exception);
7968      XSetCursorState(display,windows,MagickFalse);
7969      if (IfMagickTrue(windows->image.orphan) )
7970        break;
7971      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7972      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7973      break;
7974    }
7975    case NegateCommand:
7976    {
7977      /*
7978        Negate colors in image.
7979      */
7980      XSetCursorState(display,windows,MagickTrue);
7981      XCheckRefreshWindows(display,windows);
7982      (void) NegateImage(*image,MagickFalse,exception);
7983      XSetCursorState(display,windows,MagickFalse);
7984      if (IfMagickTrue(windows->image.orphan) )
7985        break;
7986      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7987      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7988      break;
7989    }
7990    case GrayscaleCommand:
7991    {
7992      /*
7993        Convert image to grayscale.
7994      */
7995      XSetCursorState(display,windows,MagickTrue);
7996      XCheckRefreshWindows(display,windows);
7997      (void) SetImageType(*image,(*image)->alpha_trait == UndefinedPixelTrait ?
7998        GrayscaleType : GrayscaleAlphaType,exception);
7999      XSetCursorState(display,windows,MagickFalse);
8000      if (IfMagickTrue(windows->image.orphan) )
8001        break;
8002      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8003      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8004      break;
8005    }
8006    case MapCommand:
8007    {
8008      Image
8009        *affinity_image;
8010
8011      static char
8012        filename[MagickPathExtent] = "\0";
8013
8014      /*
8015        Request image file name from user.
8016      */
8017      XFileBrowserWidget(display,windows,"Map",filename);
8018      if (*filename == '\0')
8019        break;
8020      /*
8021        Map image.
8022      */
8023      XSetCursorState(display,windows,MagickTrue);
8024      XCheckRefreshWindows(display,windows);
8025      (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
8026      affinity_image=ReadImage(image_info,exception);
8027      if (affinity_image != (Image *) NULL)
8028        {
8029          (void) RemapImage(&quantize_info,*image,affinity_image,exception);
8030          affinity_image=DestroyImage(affinity_image);
8031        }
8032      CatchException(exception);
8033      XSetCursorState(display,windows,MagickFalse);
8034      if (IfMagickTrue(windows->image.orphan) )
8035        break;
8036      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8037      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8038      break;
8039    }
8040    case QuantizeCommand:
8041    {
8042      int
8043        status;
8044
8045      static char
8046        colors[MagickPathExtent] = "256";
8047
8048      /*
8049        Query user for maximum number of colors.
8050      */
8051      status=XDialogWidget(display,windows,"Quantize",
8052        "Maximum number of colors:",colors);
8053      if (*colors == '\0')
8054        break;
8055      /*
8056        Color reduce the image.
8057      */
8058      XSetCursorState(display,windows,MagickTrue);
8059      XCheckRefreshWindows(display,windows);
8060      quantize_info.number_colors=StringToUnsignedLong(colors);
8061      quantize_info.dither_method=status != 0 ? RiemersmaDitherMethod :
8062        NoDitherMethod;
8063      (void) QuantizeImage(&quantize_info,*image,exception);
8064      XSetCursorState(display,windows,MagickFalse);
8065      if (IfMagickTrue(windows->image.orphan) )
8066        break;
8067      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8068      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8069      break;
8070    }
8071    case DespeckleCommand:
8072    {
8073      Image
8074        *despeckle_image;
8075
8076      /*
8077        Despeckle image.
8078      */
8079      XSetCursorState(display,windows,MagickTrue);
8080      XCheckRefreshWindows(display,windows);
8081      despeckle_image=DespeckleImage(*image,exception);
8082      if (despeckle_image != (Image *) NULL)
8083        {
8084          *image=DestroyImage(*image);
8085          *image=despeckle_image;
8086        }
8087      CatchException(exception);
8088      XSetCursorState(display,windows,MagickFalse);
8089      if (IfMagickTrue(windows->image.orphan) )
8090        break;
8091      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8092      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8093      break;
8094    }
8095    case EmbossCommand:
8096    {
8097      Image
8098        *emboss_image;
8099
8100      static char
8101        radius[MagickPathExtent] = "0.0x1.0";
8102
8103      /*
8104        Query user for emboss radius.
8105      */
8106      (void) XDialogWidget(display,windows,"Emboss",
8107        "Enter the emboss radius and standard deviation:",radius);
8108      if (*radius == '\0')
8109        break;
8110      /*
8111        Reduce noise in the image.
8112      */
8113      XSetCursorState(display,windows,MagickTrue);
8114      XCheckRefreshWindows(display,windows);
8115      flags=ParseGeometry(radius,&geometry_info);
8116      if ((flags & SigmaValue) == 0)
8117        geometry_info.sigma=1.0;
8118      emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8119        exception);
8120      if (emboss_image != (Image *) NULL)
8121        {
8122          *image=DestroyImage(*image);
8123          *image=emboss_image;
8124        }
8125      CatchException(exception);
8126      XSetCursorState(display,windows,MagickFalse);
8127      if (IfMagickTrue(windows->image.orphan) )
8128        break;
8129      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8130      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8131      break;
8132    }
8133    case ReduceNoiseCommand:
8134    {
8135      Image
8136        *noise_image;
8137
8138      static char
8139        radius[MagickPathExtent] = "0";
8140
8141      /*
8142        Query user for noise radius.
8143      */
8144      (void) XDialogWidget(display,windows,"Reduce Noise",
8145        "Enter the noise radius:",radius);
8146      if (*radius == '\0')
8147        break;
8148      /*
8149        Reduce noise in the image.
8150      */
8151      XSetCursorState(display,windows,MagickTrue);
8152      XCheckRefreshWindows(display,windows);
8153      flags=ParseGeometry(radius,&geometry_info);
8154      noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8155        geometry_info.rho,(size_t) geometry_info.rho,exception);
8156      if (noise_image != (Image *) NULL)
8157        {
8158          *image=DestroyImage(*image);
8159          *image=noise_image;
8160        }
8161      CatchException(exception);
8162      XSetCursorState(display,windows,MagickFalse);
8163      if (IfMagickTrue(windows->image.orphan) )
8164        break;
8165      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8166      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8167      break;
8168    }
8169    case AddNoiseCommand:
8170    {
8171      char
8172        **noises;
8173
8174      Image
8175        *noise_image;
8176
8177      static char
8178        noise_type[MagickPathExtent] = "Gaussian";
8179
8180      /*
8181        Add noise to the image.
8182      */
8183      noises=GetCommandOptions(MagickNoiseOptions);
8184      if (noises == (char **) NULL)
8185        break;
8186      XListBrowserWidget(display,windows,&windows->widget,
8187        (const char **) noises,"Add Noise",
8188        "Select a type of noise to add to your image:",noise_type);
8189      noises=DestroyStringList(noises);
8190      if (*noise_type == '\0')
8191        break;
8192      XSetCursorState(display,windows,MagickTrue);
8193      XCheckRefreshWindows(display,windows);
8194      noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8195        MagickNoiseOptions,MagickFalse,noise_type),1.0,exception);
8196      if (noise_image != (Image *) NULL)
8197        {
8198          *image=DestroyImage(*image);
8199          *image=noise_image;
8200        }
8201      CatchException(exception);
8202      XSetCursorState(display,windows,MagickFalse);
8203      if (IfMagickTrue(windows->image.orphan) )
8204        break;
8205      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8206      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8207      break;
8208    }
8209    case SharpenCommand:
8210    {
8211      Image
8212        *sharp_image;
8213
8214      static char
8215        radius[MagickPathExtent] = "0.0x1.0";
8216
8217      /*
8218        Query user for sharpen radius.
8219      */
8220      (void) XDialogWidget(display,windows,"Sharpen",
8221        "Enter the sharpen radius and standard deviation:",radius);
8222      if (*radius == '\0')
8223        break;
8224      /*
8225        Sharpen image scanlines.
8226      */
8227      XSetCursorState(display,windows,MagickTrue);
8228      XCheckRefreshWindows(display,windows);
8229      flags=ParseGeometry(radius,&geometry_info);
8230      sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8231        exception);
8232      if (sharp_image != (Image *) NULL)
8233        {
8234          *image=DestroyImage(*image);
8235          *image=sharp_image;
8236        }
8237      CatchException(exception);
8238      XSetCursorState(display,windows,MagickFalse);
8239      if (IfMagickTrue(windows->image.orphan) )
8240        break;
8241      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8242      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8243      break;
8244    }
8245    case BlurCommand:
8246    {
8247      Image
8248        *blur_image;
8249
8250      static char
8251        radius[MagickPathExtent] = "0.0x1.0";
8252
8253      /*
8254        Query user for blur radius.
8255      */
8256      (void) XDialogWidget(display,windows,"Blur",
8257        "Enter the blur radius and standard deviation:",radius);
8258      if (*radius == '\0')
8259        break;
8260      /*
8261        Blur an image.
8262      */
8263      XSetCursorState(display,windows,MagickTrue);
8264      XCheckRefreshWindows(display,windows);
8265      flags=ParseGeometry(radius,&geometry_info);
8266      blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8267        exception);
8268      if (blur_image != (Image *) NULL)
8269        {
8270          *image=DestroyImage(*image);
8271          *image=blur_image;
8272        }
8273      CatchException(exception);
8274      XSetCursorState(display,windows,MagickFalse);
8275      if (IfMagickTrue(windows->image.orphan) )
8276        break;
8277      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8278      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8279      break;
8280    }
8281    case ThresholdCommand:
8282    {
8283      double
8284        threshold;
8285
8286      static char
8287        factor[MagickPathExtent] = "128";
8288
8289      /*
8290        Query user for threshold value.
8291      */
8292      (void) XDialogWidget(display,windows,"Threshold",
8293        "Enter threshold value:",factor);
8294      if (*factor == '\0')
8295        break;
8296      /*
8297        Gamma correct image.
8298      */
8299      XSetCursorState(display,windows,MagickTrue);
8300      XCheckRefreshWindows(display,windows);
8301      threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8302      (void) BilevelImage(*image,threshold,exception);
8303      XSetCursorState(display,windows,MagickFalse);
8304      if (IfMagickTrue(windows->image.orphan) )
8305        break;
8306      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8307      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8308      break;
8309    }
8310    case EdgeDetectCommand:
8311    {
8312      Image
8313        *edge_image;
8314
8315      static char
8316        radius[MagickPathExtent] = "0";
8317
8318      /*
8319        Query user for edge factor.
8320      */
8321      (void) XDialogWidget(display,windows,"Detect Edges",
8322        "Enter the edge detect radius:",radius);
8323      if (*radius == '\0')
8324        break;
8325      /*
8326        Detect edge in image.
8327      */
8328      XSetCursorState(display,windows,MagickTrue);
8329      XCheckRefreshWindows(display,windows);
8330      flags=ParseGeometry(radius,&geometry_info);
8331      edge_image=EdgeImage(*image,geometry_info.rho,exception);
8332      if (edge_image != (Image *) NULL)
8333        {
8334          *image=DestroyImage(*image);
8335          *image=edge_image;
8336        }
8337      CatchException(exception);
8338      XSetCursorState(display,windows,MagickFalse);
8339      if (IfMagickTrue(windows->image.orphan) )
8340        break;
8341      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8342      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8343      break;
8344    }
8345    case SpreadCommand:
8346    {
8347      Image
8348        *spread_image;
8349
8350      static char
8351        amount[MagickPathExtent] = "2";
8352
8353      /*
8354        Query user for spread amount.
8355      */
8356      (void) XDialogWidget(display,windows,"Spread",
8357        "Enter the displacement amount:",amount);
8358      if (*amount == '\0')
8359        break;
8360      /*
8361        Displace image pixels by a random amount.
8362      */
8363      XSetCursorState(display,windows,MagickTrue);
8364      XCheckRefreshWindows(display,windows);
8365      flags=ParseGeometry(amount,&geometry_info);
8366      spread_image=EdgeImage(*image,geometry_info.rho,exception);
8367      if (spread_image != (Image *) NULL)
8368        {
8369          *image=DestroyImage(*image);
8370          *image=spread_image;
8371        }
8372      CatchException(exception);
8373      XSetCursorState(display,windows,MagickFalse);
8374      if (IfMagickTrue(windows->image.orphan) )
8375        break;
8376      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8377      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8378      break;
8379    }
8380    case ShadeCommand:
8381    {
8382      Image
8383        *shade_image;
8384
8385      int
8386        status;
8387
8388      static char
8389        geometry[MagickPathExtent] = "30x30";
8390
8391      /*
8392        Query user for the shade geometry.
8393      */
8394      status=XDialogWidget(display,windows,"Shade",
8395        "Enter the azimuth and elevation of the light source:",geometry);
8396      if (*geometry == '\0')
8397        break;
8398      /*
8399        Shade image pixels.
8400      */
8401      XSetCursorState(display,windows,MagickTrue);
8402      XCheckRefreshWindows(display,windows);
8403      flags=ParseGeometry(geometry,&geometry_info);
8404      if ((flags & SigmaValue) == 0)
8405        geometry_info.sigma=1.0;
8406      shade_image=ShadeImage(*image,status != 0 ? MagickTrue : MagickFalse,
8407        geometry_info.rho,geometry_info.sigma,exception);
8408      if (shade_image != (Image *) NULL)
8409        {
8410          *image=DestroyImage(*image);
8411          *image=shade_image;
8412        }
8413      CatchException(exception);
8414      XSetCursorState(display,windows,MagickFalse);
8415      if (IfMagickTrue(windows->image.orphan) )
8416        break;
8417      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8418      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8419      break;
8420    }
8421    case RaiseCommand:
8422    {
8423      static char
8424        bevel_width[MagickPathExtent] = "10";
8425
8426      /*
8427        Query user for bevel width.
8428      */
8429      (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8430      if (*bevel_width == '\0')
8431        break;
8432      /*
8433        Raise an image.
8434      */
8435      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8436        exception);
8437      XSetCursorState(display,windows,MagickTrue);
8438      XCheckRefreshWindows(display,windows);
8439      (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8440        exception);
8441      (void) RaiseImage(*image,&page_geometry,MagickTrue,exception);
8442      XSetCursorState(display,windows,MagickFalse);
8443      if (IfMagickTrue(windows->image.orphan) )
8444        break;
8445      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8446      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8447      break;
8448    }
8449    case SegmentCommand:
8450    {
8451      static char
8452        threshold[MagickPathExtent] = "1.0x1.5";
8453
8454      /*
8455        Query user for smoothing threshold.
8456      */
8457      (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8458        threshold);
8459      if (*threshold == '\0')
8460        break;
8461      /*
8462        Segment an image.
8463      */
8464      XSetCursorState(display,windows,MagickTrue);
8465      XCheckRefreshWindows(display,windows);
8466      flags=ParseGeometry(threshold,&geometry_info);
8467      if ((flags & SigmaValue) == 0)
8468        geometry_info.sigma=1.0;
8469      (void) SegmentImage(*image,sRGBColorspace,MagickFalse,geometry_info.rho,
8470        geometry_info.sigma,exception);
8471      XSetCursorState(display,windows,MagickFalse);
8472      if (IfMagickTrue(windows->image.orphan) )
8473        break;
8474      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8475      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8476      break;
8477    }
8478    case SepiaToneCommand:
8479    {
8480      double
8481        threshold;
8482
8483      Image
8484        *sepia_image;
8485
8486      static char
8487        factor[MagickPathExtent] = "80%";
8488
8489      /*
8490        Query user for sepia-tone factor.
8491      */
8492      (void) XDialogWidget(display,windows,"Sepia Tone",
8493        "Enter the sepia tone factor (0 - 99.9%):",factor);
8494      if (*factor == '\0')
8495        break;
8496      /*
8497        Sepia tone image pixels.
8498      */
8499      XSetCursorState(display,windows,MagickTrue);
8500      XCheckRefreshWindows(display,windows);
8501      threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8502      sepia_image=SepiaToneImage(*image,threshold,exception);
8503      if (sepia_image != (Image *) NULL)
8504        {
8505          *image=DestroyImage(*image);
8506          *image=sepia_image;
8507        }
8508      CatchException(exception);
8509      XSetCursorState(display,windows,MagickFalse);
8510      if (IfMagickTrue(windows->image.orphan) )
8511        break;
8512      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8513      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8514      break;
8515    }
8516    case SolarizeCommand:
8517    {
8518      double
8519        threshold;
8520
8521      static char
8522        factor[MagickPathExtent] = "60%";
8523
8524      /*
8525        Query user for solarize factor.
8526      */
8527      (void) XDialogWidget(display,windows,"Solarize",
8528        "Enter the solarize factor (0 - 99.9%):",factor);
8529      if (*factor == '\0')
8530        break;
8531      /*
8532        Solarize image pixels.
8533      */
8534      XSetCursorState(display,windows,MagickTrue);
8535      XCheckRefreshWindows(display,windows);
8536      threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8537      (void) SolarizeImage(*image,threshold,exception);
8538      XSetCursorState(display,windows,MagickFalse);
8539      if (IfMagickTrue(windows->image.orphan) )
8540        break;
8541      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8542      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8543      break;
8544    }
8545    case SwirlCommand:
8546    {
8547      Image
8548        *swirl_image;
8549
8550      static char
8551        degrees[MagickPathExtent] = "60";
8552
8553      /*
8554        Query user for swirl angle.
8555      */
8556      (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8557        degrees);
8558      if (*degrees == '\0')
8559        break;
8560      /*
8561        Swirl image pixels about the center.
8562      */
8563      XSetCursorState(display,windows,MagickTrue);
8564      XCheckRefreshWindows(display,windows);
8565      flags=ParseGeometry(degrees,&geometry_info);
8566      swirl_image=SwirlImage(*image,geometry_info.rho,(*image)->interpolate,
8567        exception);
8568      if (swirl_image != (Image *) NULL)
8569        {
8570          *image=DestroyImage(*image);
8571          *image=swirl_image;
8572        }
8573      CatchException(exception);
8574      XSetCursorState(display,windows,MagickFalse);
8575      if (IfMagickTrue(windows->image.orphan) )
8576        break;
8577      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8578      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8579      break;
8580    }
8581    case ImplodeCommand:
8582    {
8583      Image
8584        *implode_image;
8585
8586      static char
8587        factor[MagickPathExtent] = "0.3";
8588
8589      /*
8590        Query user for implode factor.
8591      */
8592      (void) XDialogWidget(display,windows,"Implode",
8593        "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8594      if (*factor == '\0')
8595        break;
8596      /*
8597        Implode image pixels about the center.
8598      */
8599      XSetCursorState(display,windows,MagickTrue);
8600      XCheckRefreshWindows(display,windows);
8601      flags=ParseGeometry(factor,&geometry_info);
8602      implode_image=ImplodeImage(*image,geometry_info.rho,(*image)->interpolate,
8603        exception);
8604      if (implode_image != (Image *) NULL)
8605        {
8606          *image=DestroyImage(*image);
8607          *image=implode_image;
8608        }
8609      CatchException(exception);
8610      XSetCursorState(display,windows,MagickFalse);
8611      if (IfMagickTrue(windows->image.orphan) )
8612        break;
8613      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8614      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8615      break;
8616    }
8617    case VignetteCommand:
8618    {
8619      Image
8620        *vignette_image;
8621
8622      static char
8623        geometry[MagickPathExtent] = "0x20";
8624
8625      /*
8626        Query user for the vignette geometry.
8627      */
8628      (void) XDialogWidget(display,windows,"Vignette",
8629        "Enter the radius, sigma, and x and y offsets:",geometry);
8630      if (*geometry == '\0')
8631        break;
8632      /*
8633        Soften the edges of the image in vignette style
8634      */
8635      XSetCursorState(display,windows,MagickTrue);
8636      XCheckRefreshWindows(display,windows);
8637      flags=ParseGeometry(geometry,&geometry_info);
8638      if ((flags & SigmaValue) == 0)
8639        geometry_info.sigma=1.0;
8640      if ((flags & XiValue) == 0)
8641        geometry_info.xi=0.1*(*image)->columns;
8642      if ((flags & PsiValue) == 0)
8643        geometry_info.psi=0.1*(*image)->rows;
8644      vignette_image=VignetteImage(*image,geometry_info.rho,0.0,(ssize_t)
8645        ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-0.5),
8646        exception);
8647      if (vignette_image != (Image *) NULL)
8648        {
8649          *image=DestroyImage(*image);
8650          *image=vignette_image;
8651        }
8652      CatchException(exception);
8653      XSetCursorState(display,windows,MagickFalse);
8654      if (IfMagickTrue(windows->image.orphan) )
8655        break;
8656      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8657      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8658      break;
8659    }
8660    case WaveCommand:
8661    {
8662      Image
8663        *wave_image;
8664
8665      static char
8666        geometry[MagickPathExtent] = "25x150";
8667
8668      /*
8669        Query user for the wave geometry.
8670      */
8671      (void) XDialogWidget(display,windows,"Wave",
8672        "Enter the amplitude and length of the wave:",geometry);
8673      if (*geometry == '\0')
8674        break;
8675      /*
8676        Alter an image along a sine wave.
8677      */
8678      XSetCursorState(display,windows,MagickTrue);
8679      XCheckRefreshWindows(display,windows);
8680      flags=ParseGeometry(geometry,&geometry_info);
8681      if ((flags & SigmaValue) == 0)
8682        geometry_info.sigma=1.0;
8683      wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8684        (*image)->interpolate,exception);
8685      if (wave_image != (Image *) NULL)
8686        {
8687          *image=DestroyImage(*image);
8688          *image=wave_image;
8689        }
8690      CatchException(exception);
8691      XSetCursorState(display,windows,MagickFalse);
8692      if (IfMagickTrue(windows->image.orphan) )
8693        break;
8694      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8695      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8696      break;
8697    }
8698    case OilPaintCommand:
8699    {
8700      Image
8701        *paint_image;
8702
8703      static char
8704        radius[MagickPathExtent] = "0";
8705
8706      /*
8707        Query user for circular neighborhood radius.
8708      */
8709      (void) XDialogWidget(display,windows,"Oil Paint",
8710        "Enter the mask radius:",radius);
8711      if (*radius == '\0')
8712        break;
8713      /*
8714        OilPaint image scanlines.
8715      */
8716      XSetCursorState(display,windows,MagickTrue);
8717      XCheckRefreshWindows(display,windows);
8718      flags=ParseGeometry(radius,&geometry_info);
8719      paint_image=OilPaintImage(*image,geometry_info.rho,geometry_info.sigma,
8720        exception);
8721      if (paint_image != (Image *) NULL)
8722        {
8723          *image=DestroyImage(*image);
8724          *image=paint_image;
8725        }
8726      CatchException(exception);
8727      XSetCursorState(display,windows,MagickFalse);
8728      if (IfMagickTrue(windows->image.orphan) )
8729        break;
8730      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8731      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8732      break;
8733    }
8734    case CharcoalDrawCommand:
8735    {
8736      Image
8737        *charcoal_image;
8738
8739      static char
8740        radius[MagickPathExtent] = "0x1";
8741
8742      /*
8743        Query user for charcoal radius.
8744      */
8745      (void) XDialogWidget(display,windows,"Charcoal Draw",
8746        "Enter the charcoal radius and sigma:",radius);
8747      if (*radius == '\0')
8748        break;
8749      /*
8750        Charcoal the image.
8751      */
8752      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8753        exception);
8754      XSetCursorState(display,windows,MagickTrue);
8755      XCheckRefreshWindows(display,windows);
8756      flags=ParseGeometry(radius,&geometry_info);
8757      if ((flags & SigmaValue) == 0)
8758        geometry_info.sigma=geometry_info.rho;
8759      charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8760        exception);
8761      if (charcoal_image != (Image *) NULL)
8762        {
8763          *image=DestroyImage(*image);
8764          *image=charcoal_image;
8765        }
8766      CatchException(exception);
8767      XSetCursorState(display,windows,MagickFalse);
8768      if (IfMagickTrue(windows->image.orphan) )
8769        break;
8770      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8771      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8772      break;
8773    }
8774    case AnnotateCommand:
8775    {
8776      /*
8777        Annotate the image with text.
8778      */
8779      status=XAnnotateEditImage(display,resource_info,windows,*image,exception);
8780      if (IfMagickFalse(status) )
8781        {
8782          XNoticeWidget(display,windows,"Unable to annotate X image",
8783            (*image)->filename);
8784          break;
8785        }
8786      break;
8787    }
8788    case DrawCommand:
8789    {
8790      /*
8791        Draw image.
8792      */
8793      status=XDrawEditImage(display,resource_info,windows,image,exception);
8794      if (IfMagickFalse(status) )
8795        {
8796          XNoticeWidget(display,windows,"Unable to draw on the X image",
8797            (*image)->filename);
8798          break;
8799        }
8800      break;
8801    }
8802    case ColorCommand:
8803    {
8804      /*
8805        Color edit.
8806      */
8807      status=XColorEditImage(display,resource_info,windows,image,exception);
8808      if (IfMagickFalse(status) )
8809        {
8810          XNoticeWidget(display,windows,"Unable to pixel edit X image",
8811            (*image)->filename);
8812          break;
8813        }
8814      break;
8815    }
8816    case MatteCommand:
8817    {
8818      /*
8819        Matte edit.
8820      */
8821      status=XMatteEditImage(display,resource_info,windows,image,exception);
8822      if (IfMagickFalse(status) )
8823        {
8824          XNoticeWidget(display,windows,"Unable to matte edit X image",
8825            (*image)->filename);
8826          break;
8827        }
8828      break;
8829    }
8830    case CompositeCommand:
8831    {
8832      /*
8833        Composite image.
8834      */
8835      status=XCompositeImage(display,resource_info,windows,*image,
8836        exception);
8837      if (IfMagickFalse(status) )
8838        {
8839          XNoticeWidget(display,windows,"Unable to composite X image",
8840            (*image)->filename);
8841          break;
8842        }
8843      break;
8844    }
8845    case AddBorderCommand:
8846    {
8847      Image
8848        *border_image;
8849
8850      static char
8851        geometry[MagickPathExtent] = "6x6";
8852
8853      /*
8854        Query user for border color and geometry.
8855      */
8856      XColorBrowserWidget(display,windows,"Select",color);
8857      if (*color == '\0')
8858        break;
8859      (void) XDialogWidget(display,windows,"Add Border",
8860        "Enter border geometry:",geometry);
8861      if (*geometry == '\0')
8862        break;
8863      /*
8864        Add a border to the image.
8865      */
8866      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8867        exception);
8868      XSetCursorState(display,windows,MagickTrue);
8869      XCheckRefreshWindows(display,windows);
8870      (void) QueryColorCompliance(color,AllCompliance,&(*image)->border_color,
8871        exception);
8872      (void) ParsePageGeometry(*image,geometry,&page_geometry,
8873        exception);
8874      border_image=BorderImage(*image,&page_geometry,(*image)->compose,
8875        exception);
8876      if (border_image != (Image *) NULL)
8877        {
8878          *image=DestroyImage(*image);
8879          *image=border_image;
8880        }
8881      CatchException(exception);
8882      XSetCursorState(display,windows,MagickFalse);
8883      if (IfMagickTrue(windows->image.orphan) )
8884        break;
8885      windows->image.window_changes.width=(int) (*image)->columns;
8886      windows->image.window_changes.height=(int) (*image)->rows;
8887      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8888      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8889      break;
8890    }
8891    case AddFrameCommand:
8892    {
8893      FrameInfo
8894        frame_info;
8895
8896      Image
8897        *frame_image;
8898
8899      static char
8900        geometry[MagickPathExtent] = "6x6";
8901
8902      /*
8903        Query user for frame color and geometry.
8904      */
8905      XColorBrowserWidget(display,windows,"Select",color);
8906      if (*color == '\0')
8907        break;
8908      (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8909        geometry);
8910      if (*geometry == '\0')
8911        break;
8912      /*
8913        Surround image with an ornamental border.
8914      */
8915      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8916        exception);
8917      XSetCursorState(display,windows,MagickTrue);
8918      XCheckRefreshWindows(display,windows);
8919      (void) QueryColorCompliance(color,AllCompliance,&(*image)->matte_color,
8920        exception);
8921      (void) ParsePageGeometry(*image,geometry,&page_geometry,
8922        exception);
8923      frame_info.width=page_geometry.width;
8924      frame_info.height=page_geometry.height;
8925      frame_info.outer_bevel=page_geometry.x;
8926      frame_info.inner_bevel=page_geometry.y;
8927      frame_info.x=(ssize_t) frame_info.width;
8928      frame_info.y=(ssize_t) frame_info.height;
8929      frame_info.width=(*image)->columns+2*frame_info.width;
8930      frame_info.height=(*image)->rows+2*frame_info.height;
8931      frame_image=FrameImage(*image,&frame_info,(*image)->compose,exception);
8932      if (frame_image != (Image *) NULL)
8933        {
8934          *image=DestroyImage(*image);
8935          *image=frame_image;
8936        }
8937      CatchException(exception);
8938      XSetCursorState(display,windows,MagickFalse);
8939      if (IfMagickTrue(windows->image.orphan) )
8940        break;
8941      windows->image.window_changes.width=(int) (*image)->columns;
8942      windows->image.window_changes.height=(int) (*image)->rows;
8943      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8944      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8945      break;
8946    }
8947    case CommentCommand:
8948    {
8949      const char
8950        *value;
8951
8952      FILE
8953        *file;
8954
8955      int
8956        unique_file;
8957
8958      /*
8959        Edit image comment.
8960      */
8961      unique_file=AcquireUniqueFileResource(image_info->filename);
8962      if (unique_file == -1)
8963        XNoticeWidget(display,windows,"Unable to edit image comment",
8964          image_info->filename);
8965      value=GetImageProperty(*image,"comment",exception);
8966      if (value == (char *) NULL)
8967        unique_file=close(unique_file)-1;
8968      else
8969        {
8970          register const char
8971            *p;
8972
8973          file=fdopen(unique_file,"w");
8974          if (file == (FILE *) NULL)
8975            {
8976              XNoticeWidget(display,windows,"Unable to edit image comment",
8977                image_info->filename);
8978              break;
8979            }
8980          for (p=value; *p != '\0'; p++)
8981            (void) fputc((int) *p,file);
8982          (void) fputc('\n',file);
8983          (void) fclose(file);
8984        }
8985      XSetCursorState(display,windows,MagickTrue);
8986      XCheckRefreshWindows(display,windows);
8987      status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
8988        exception);
8989      if (IfMagickFalse(status) )
8990        XNoticeWidget(display,windows,"Unable to edit image comment",
8991          (char *) NULL);
8992      else
8993        {
8994          char
8995            *comment;
8996
8997          comment=FileToString(image_info->filename,~0UL,exception);
8998          if (comment != (char *) NULL)
8999            {
9000              (void) SetImageProperty(*image,"comment",comment,exception);
9001              (*image)->taint=MagickTrue;
9002            }
9003        }
9004      (void) RelinquishUniqueFileResource(image_info->filename);
9005      XSetCursorState(display,windows,MagickFalse);
9006      break;
9007    }
9008    case LaunchCommand:
9009    {
9010      /*
9011        Launch program.
9012      */
9013      XSetCursorState(display,windows,MagickTrue);
9014      XCheckRefreshWindows(display,windows);
9015      (void) AcquireUniqueFilename(filename);
9016      (void) FormatLocaleString((*image)->filename,MagickPathExtent,"launch:%s",
9017        filename);
9018      status=WriteImage(image_info,*image,exception);
9019      if (IfMagickFalse(status) )
9020        XNoticeWidget(display,windows,"Unable to launch image editor",
9021          (char *) NULL);
9022      else
9023        {
9024          nexus=ReadImage(resource_info->image_info,exception);
9025          CatchException(exception);
9026          XClientMessage(display,windows->image.id,windows->im_protocols,
9027            windows->im_next_image,CurrentTime);
9028        }
9029      (void) RelinquishUniqueFileResource(filename);
9030      XSetCursorState(display,windows,MagickFalse);
9031      break;
9032    }
9033    case RegionofInterestCommand:
9034    {
9035      /*
9036        Apply an image processing technique to a region of interest.
9037      */
9038      (void) XROIImage(display,resource_info,windows,image,exception);
9039      break;
9040    }
9041    case InfoCommand:
9042      break;
9043    case ZoomCommand:
9044    {
9045      /*
9046        Zoom image.
9047      */
9048      if (IfMagickTrue(windows->magnify.mapped) )
9049        (void) XRaiseWindow(display,windows->magnify.id);
9050      else
9051        {
9052          /*
9053            Make magnify image.
9054          */
9055          XSetCursorState(display,windows,MagickTrue);
9056          (void) XMapRaised(display,windows->magnify.id);
9057          XSetCursorState(display,windows,MagickFalse);
9058        }
9059      break;
9060    }
9061    case ShowPreviewCommand:
9062    {
9063      char
9064        **previews;
9065
9066      Image
9067        *preview_image;
9068
9069      static char
9070        preview_type[MagickPathExtent] = "Gamma";
9071
9072      /*
9073        Select preview type from menu.
9074      */
9075      previews=GetCommandOptions(MagickPreviewOptions);
9076      if (previews == (char **) NULL)
9077        break;
9078      XListBrowserWidget(display,windows,&windows->widget,
9079        (const char **) previews,"Preview",
9080        "Select an enhancement, effect, or F/X:",preview_type);
9081      previews=DestroyStringList(previews);
9082      if (*preview_type == '\0')
9083        break;
9084      /*
9085        Show image preview.
9086      */
9087      XSetCursorState(display,windows,MagickTrue);
9088      XCheckRefreshWindows(display,windows);
9089      image_info->preview_type=(PreviewType)
9090        ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
9091      image_info->group=(ssize_t) windows->image.id;
9092      (void) DeleteImageProperty(*image,"label");
9093      (void) SetImageProperty(*image,"label","Preview",exception);
9094      (void) AcquireUniqueFilename(filename);
9095      (void) FormatLocaleString((*image)->filename,MagickPathExtent,"preview:%s",
9096        filename);
9097      status=WriteImage(image_info,*image,exception);
9098      (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
9099      preview_image=ReadImage(image_info,exception);
9100      (void) RelinquishUniqueFileResource(filename);
9101      if (preview_image == (Image *) NULL)
9102        break;
9103      (void) FormatLocaleString(preview_image->filename,MagickPathExtent,"show:%s",
9104        filename);
9105      status=WriteImage(image_info,preview_image,exception);
9106      preview_image=DestroyImage(preview_image);
9107      if (IfMagickFalse(status) )
9108        XNoticeWidget(display,windows,"Unable to show image preview",
9109          (*image)->filename);
9110      XDelay(display,1500);
9111      XSetCursorState(display,windows,MagickFalse);
9112      break;
9113    }
9114    case ShowHistogramCommand:
9115    {
9116      Image
9117        *histogram_image;
9118
9119      /*
9120        Show image histogram.
9121      */
9122      XSetCursorState(display,windows,MagickTrue);
9123      XCheckRefreshWindows(display,windows);
9124      image_info->group=(ssize_t) windows->image.id;
9125      (void) DeleteImageProperty(*image,"label");
9126      (void) SetImageProperty(*image,"label","Histogram",exception);
9127      (void) AcquireUniqueFilename(filename);
9128      (void) FormatLocaleString((*image)->filename,MagickPathExtent,"histogram:%s",
9129        filename);
9130      status=WriteImage(image_info,*image,exception);
9131      (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
9132      histogram_image=ReadImage(image_info,exception);
9133      (void) RelinquishUniqueFileResource(filename);
9134      if (histogram_image == (Image *) NULL)
9135        break;
9136      (void) FormatLocaleString(histogram_image->filename,MagickPathExtent,
9137        "show:%s",filename);
9138      status=WriteImage(image_info,histogram_image,exception);
9139      histogram_image=DestroyImage(histogram_image);
9140      if (IfMagickFalse(status) )
9141        XNoticeWidget(display,windows,"Unable to show histogram",
9142          (*image)->filename);
9143      XDelay(display,1500);
9144      XSetCursorState(display,windows,MagickFalse);
9145      break;
9146    }
9147    case ShowMatteCommand:
9148    {
9149      Image
9150        *matte_image;
9151
9152      if ((*image)->alpha_trait == UndefinedPixelTrait)
9153        {
9154          XNoticeWidget(display,windows,
9155            "Image does not have any matte information",(*image)->filename);
9156          break;
9157        }
9158      /*
9159        Show image matte.
9160      */
9161      XSetCursorState(display,windows,MagickTrue);
9162      XCheckRefreshWindows(display,windows);
9163      image_info->group=(ssize_t) windows->image.id;
9164      (void) DeleteImageProperty(*image,"label");
9165      (void) SetImageProperty(*image,"label","Matte",exception);
9166      (void) AcquireUniqueFilename(filename);
9167      (void) FormatLocaleString((*image)->filename,MagickPathExtent,"matte:%s",
9168        filename);
9169      status=WriteImage(image_info,*image,exception);
9170      (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
9171      matte_image=ReadImage(image_info,exception);
9172      (void) RelinquishUniqueFileResource(filename);
9173      if (matte_image == (Image *) NULL)
9174        break;
9175      (void) FormatLocaleString(matte_image->filename,MagickPathExtent,"show:%s",
9176        filename);
9177      status=WriteImage(image_info,matte_image,exception);
9178      matte_image=DestroyImage(matte_image);
9179      if (IfMagickFalse(status) )
9180        XNoticeWidget(display,windows,"Unable to show matte",
9181          (*image)->filename);
9182      XDelay(display,1500);
9183      XSetCursorState(display,windows,MagickFalse);
9184      break;
9185    }
9186    case BackgroundCommand:
9187    {
9188      /*
9189        Background image.
9190      */
9191      status=XBackgroundImage(display,resource_info,windows,image,exception);
9192      if (IfMagickFalse(status) )
9193        break;
9194      nexus=CloneImage(*image,0,0,MagickTrue,exception);
9195      if (nexus != (Image *) NULL)
9196        XClientMessage(display,windows->image.id,windows->im_protocols,
9197          windows->im_next_image,CurrentTime);
9198      break;
9199    }
9200    case SlideShowCommand:
9201    {
9202      static char
9203        delay[MagickPathExtent] = "5";
9204
9205      /*
9206        Display next image after pausing.
9207      */
9208      (void) XDialogWidget(display,windows,"Slide Show",
9209        "Pause how many 1/100ths of a second between images:",delay);
9210      if (*delay == '\0')
9211        break;
9212      resource_info->delay=StringToUnsignedLong(delay);
9213      XClientMessage(display,windows->image.id,windows->im_protocols,
9214        windows->im_next_image,CurrentTime);
9215      break;
9216    }
9217    case PreferencesCommand:
9218    {
9219      /*
9220        Set user preferences.
9221      */
9222      status=XPreferencesWidget(display,resource_info,windows);
9223      if (IfMagickFalse(status) )
9224        break;
9225      nexus=CloneImage(*image,0,0,MagickTrue,exception);
9226      if (nexus != (Image *) NULL)
9227        XClientMessage(display,windows->image.id,windows->im_protocols,
9228          windows->im_next_image,CurrentTime);
9229      break;
9230    }
9231    case HelpCommand:
9232    {
9233      /*
9234        User requested help.
9235      */
9236      XTextViewWidget(display,resource_info,windows,MagickFalse,
9237        "Help Viewer - Display",DisplayHelp);
9238      break;
9239    }
9240    case BrowseDocumentationCommand:
9241    {
9242      Atom
9243        mozilla_atom;
9244
9245      Window
9246        mozilla_window,
9247        root_window;
9248
9249      /*
9250        Browse the ImageMagick documentation.
9251      */
9252      root_window=XRootWindow(display,XDefaultScreen(display));
9253      mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9254      mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9255      if (mozilla_window != (Window) NULL)
9256        {
9257          char
9258            command[MagickPathExtent],
9259            *url;
9260
9261          /*
9262            Display documentation using Netscape remote control.
9263          */
9264          url=GetMagickHomeURL();
9265          (void) FormatLocaleString(command,MagickPathExtent,
9266            "openurl(%s,new-tab)",url);
9267          url=DestroyString(url);
9268          mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9269          (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9270            8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9271          XSetCursorState(display,windows,MagickFalse);
9272          break;
9273        }
9274      XSetCursorState(display,windows,MagickTrue);
9275      XCheckRefreshWindows(display,windows);
9276      status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9277        exception);
9278      if (IfMagickFalse(status) )
9279        XNoticeWidget(display,windows,"Unable to browse documentation",
9280          (char *) NULL);
9281      XDelay(display,1500);
9282      XSetCursorState(display,windows,MagickFalse);
9283      break;
9284    }
9285    case VersionCommand:
9286    {
9287      XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9288        GetMagickCopyright());
9289      break;
9290    }
9291    case SaveToUndoBufferCommand:
9292      break;
9293    default:
9294    {
9295      (void) XBell(display,0);
9296      break;
9297    }
9298  }
9299  image_info=DestroyImageInfo(image_info);
9300  return(nexus);
9301}
9302
9303/*
9304%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9305%                                                                             %
9306%                                                                             %
9307%                                                                             %
9308+   X M a g n i f y I m a g e                                                 %
9309%                                                                             %
9310%                                                                             %
9311%                                                                             %
9312%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9313%
9314%  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9315%  The magnified portion is displayed in a separate window.
9316%
9317%  The format of the XMagnifyImage method is:
9318%
9319%      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9320%        ExceptionInfo *exception)
9321%
9322%  A description of each parameter follows:
9323%
9324%    o display: Specifies a connection to an X server;  returned from
9325%      XOpenDisplay.
9326%
9327%    o windows: Specifies a pointer to a XWindows structure.
9328%
9329%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
9330%      the entire image is refreshed.
9331%
9332%    o exception: return any errors or warnings in this structure.
9333%
9334*/
9335static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9336  ExceptionInfo *exception)
9337{
9338  char
9339    text[MagickPathExtent];
9340
9341  register int
9342    x,
9343    y;
9344
9345  size_t
9346    state;
9347
9348  /*
9349    Update magnified image until the mouse button is released.
9350  */
9351  (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9352  state=DefaultState;
9353  x=event->xbutton.x;
9354  y=event->xbutton.y;
9355  windows->magnify.x=(int) windows->image.x+x;
9356  windows->magnify.y=(int) windows->image.y+y;
9357  do
9358  {
9359    /*
9360      Map and unmap Info widget as text cursor crosses its boundaries.
9361    */
9362    if (IfMagickTrue(windows->info.mapped) )
9363      {
9364        if ((x < (int) (windows->info.x+windows->info.width)) &&
9365            (y < (int) (windows->info.y+windows->info.height)))
9366          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9367      }
9368    else
9369      if ((x > (int) (windows->info.x+windows->info.width)) ||
9370          (y > (int) (windows->info.y+windows->info.height)))
9371        (void) XMapWindow(display,windows->info.id);
9372    if (IfMagickTrue(windows->info.mapped) )
9373      {
9374        /*
9375          Display pointer position.
9376        */
9377        (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
9378          windows->magnify.x,windows->magnify.y);
9379        XInfoWidget(display,windows,text);
9380      }
9381    /*
9382      Wait for next event.
9383    */
9384    XScreenEvent(display,windows,event,exception);
9385    switch (event->type)
9386    {
9387      case ButtonPress:
9388        break;
9389      case ButtonRelease:
9390      {
9391        /*
9392          User has finished magnifying image.
9393        */
9394        x=event->xbutton.x;
9395        y=event->xbutton.y;
9396        state|=ExitState;
9397        break;
9398      }
9399      case Expose:
9400        break;
9401      case MotionNotify:
9402      {
9403        x=event->xmotion.x;
9404        y=event->xmotion.y;
9405        break;
9406      }
9407      default:
9408        break;
9409    }
9410    /*
9411      Check boundary conditions.
9412    */
9413    if (x < 0)
9414      x=0;
9415    else
9416      if (x >= (int) windows->image.width)
9417        x=(int) windows->image.width-1;
9418    if (y < 0)
9419      y=0;
9420    else
9421     if (y >= (int) windows->image.height)
9422       y=(int) windows->image.height-1;
9423  } while ((state & ExitState) == 0);
9424  /*
9425    Display magnified image.
9426  */
9427  XSetCursorState(display,windows,MagickFalse);
9428}
9429
9430/*
9431%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9432%                                                                             %
9433%                                                                             %
9434%                                                                             %
9435+   X M a g n i f y W i n d o w C o m m a n d                                 %
9436%                                                                             %
9437%                                                                             %
9438%                                                                             %
9439%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9440%
9441%  XMagnifyWindowCommand() moves the image within an Magnify window by one
9442%  pixel as specified by the key symbol.
9443%
9444%  The format of the XMagnifyWindowCommand method is:
9445%
9446%      void XMagnifyWindowCommand(Display *display,XWindows *windows,
9447%        const MagickStatusType state,const KeySym key_symbol,
9448%        ExceptionInfo *exception)
9449%
9450%  A description of each parameter follows:
9451%
9452%    o display: Specifies a connection to an X server; returned from
9453%      XOpenDisplay.
9454%
9455%    o windows: Specifies a pointer to a XWindows structure.
9456%
9457%    o state: key mask.
9458%
9459%    o key_symbol: Specifies a KeySym which indicates which side of the image
9460%      to trim.
9461%
9462%    o exception: return any errors or warnings in this structure.
9463%
9464*/
9465static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9466  const MagickStatusType state,const KeySym key_symbol,ExceptionInfo *exception)
9467{
9468  unsigned int
9469    quantum;
9470
9471  /*
9472    User specified a magnify factor or position.
9473  */
9474  quantum=1;
9475  if ((state & Mod1Mask) != 0)
9476    quantum=10;
9477  switch ((int) key_symbol)
9478  {
9479    case QuitCommand:
9480    {
9481      (void) XWithdrawWindow(display,windows->magnify.id,
9482        windows->magnify.screen);
9483      break;
9484    }
9485    case XK_Home:
9486    case XK_KP_Home:
9487    {
9488      windows->magnify.x=(int) windows->image.width/2;
9489      windows->magnify.y=(int) windows->image.height/2;
9490      break;
9491    }
9492    case XK_Left:
9493    case XK_KP_Left:
9494    {
9495      if (windows->magnify.x > 0)
9496        windows->magnify.x-=quantum;
9497      break;
9498    }
9499    case XK_Up:
9500    case XK_KP_Up:
9501    {
9502      if (windows->magnify.y > 0)
9503        windows->magnify.y-=quantum;
9504      break;
9505    }
9506    case XK_Right:
9507    case XK_KP_Right:
9508    {
9509      if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9510        windows->magnify.x+=quantum;
9511      break;
9512    }
9513    case XK_Down:
9514    case XK_KP_Down:
9515    {
9516      if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9517        windows->magnify.y+=quantum;
9518      break;
9519    }
9520    case XK_0:
9521    case XK_1:
9522    case XK_2:
9523    case XK_3:
9524    case XK_4:
9525    case XK_5:
9526    case XK_6:
9527    case XK_7:
9528    case XK_8:
9529    case XK_9:
9530    {
9531      windows->magnify.data=(key_symbol-XK_0);
9532      break;
9533    }
9534    case XK_KP_0:
9535    case XK_KP_1:
9536    case XK_KP_2:
9537    case XK_KP_3:
9538    case XK_KP_4:
9539    case XK_KP_5:
9540    case XK_KP_6:
9541    case XK_KP_7:
9542    case XK_KP_8:
9543    case XK_KP_9:
9544    {
9545      windows->magnify.data=(key_symbol-XK_KP_0);
9546      break;
9547    }
9548    default:
9549      break;
9550  }
9551  XMakeMagnifyImage(display,windows,exception);
9552}
9553
9554/*
9555%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9556%                                                                             %
9557%                                                                             %
9558%                                                                             %
9559+   X M a k e P a n I m a g e                                                 %
9560%                                                                             %
9561%                                                                             %
9562%                                                                             %
9563%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9564%
9565%  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9566%  icon window.
9567%
9568%  The format of the XMakePanImage method is:
9569%
9570%        void XMakePanImage(Display *display,XResourceInfo *resource_info,
9571%          XWindows *windows,Image *image,ExceptionInfo *exception)
9572%
9573%  A description of each parameter follows:
9574%
9575%    o display: Specifies a connection to an X server;  returned from
9576%      XOpenDisplay.
9577%
9578%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9579%
9580%    o windows: Specifies a pointer to a XWindows structure.
9581%
9582%    o image: the image.
9583%
9584%    o exception: return any errors or warnings in this structure.
9585%
9586*/
9587static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9588  XWindows *windows,Image *image,ExceptionInfo *exception)
9589{
9590  MagickStatusType
9591    status;
9592
9593  /*
9594    Create and display image for panning icon.
9595  */
9596  XSetCursorState(display,windows,MagickTrue);
9597  XCheckRefreshWindows(display,windows);
9598  windows->pan.x=(int) windows->image.x;
9599  windows->pan.y=(int) windows->image.y;
9600  status=XMakeImage(display,resource_info,&windows->pan,image,
9601    windows->pan.width,windows->pan.height,exception);
9602  if (IfMagickFalse(status) )
9603    ThrowXWindowException(ResourceLimitError,
9604     "MemoryAllocationFailed",image->filename);
9605  (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9606    windows->pan.pixmap);
9607  (void) XClearWindow(display,windows->pan.id);
9608  XDrawPanRectangle(display,windows);
9609  XSetCursorState(display,windows,MagickFalse);
9610}
9611
9612/*
9613%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9614%                                                                             %
9615%                                                                             %
9616%                                                                             %
9617+   X M a t t a E d i t I m a g e                                             %
9618%                                                                             %
9619%                                                                             %
9620%                                                                             %
9621%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9622%
9623%  XMatteEditImage() allows the user to interactively change the Matte channel
9624%  of an image.  If the image is PseudoClass it is promoted to DirectClass
9625%  before the matte information is stored.
9626%
9627%  The format of the XMatteEditImage method is:
9628%
9629%      MagickBooleanType XMatteEditImage(Display *display,
9630%        XResourceInfo *resource_info,XWindows *windows,Image **image,
9631%        ExceptionInfo *exception)
9632%
9633%  A description of each parameter follows:
9634%
9635%    o display: Specifies a connection to an X server;  returned from
9636%      XOpenDisplay.
9637%
9638%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9639%
9640%    o windows: Specifies a pointer to a XWindows structure.
9641%
9642%    o image: the image; returned from ReadImage.
9643%
9644%    o exception: return any errors or warnings in this structure.
9645%
9646*/
9647static MagickBooleanType XMatteEditImage(Display *display,
9648  XResourceInfo *resource_info,XWindows *windows,Image **image,
9649  ExceptionInfo *exception)
9650{
9651  static char
9652    matte[MagickPathExtent] = "0";
9653
9654  static const char
9655    *MatteEditMenu[] =
9656    {
9657      "Method",
9658      "Border Color",
9659      "Fuzz",
9660      "Matte Value",
9661      "Undo",
9662      "Help",
9663      "Dismiss",
9664      (char *) NULL
9665    };
9666
9667  static const ModeType
9668    MatteEditCommands[] =
9669    {
9670      MatteEditMethod,
9671      MatteEditBorderCommand,
9672      MatteEditFuzzCommand,
9673      MatteEditValueCommand,
9674      MatteEditUndoCommand,
9675      MatteEditHelpCommand,
9676      MatteEditDismissCommand
9677    };
9678
9679  static PaintMethod
9680    method = PointMethod;
9681
9682  static XColor
9683    border_color = { 0, 0, 0, 0, 0, 0 };
9684
9685  char
9686    command[MagickPathExtent],
9687    text[MagickPathExtent];
9688
9689  Cursor
9690    cursor;
9691
9692  int
9693    entry,
9694    id,
9695    x,
9696    x_offset,
9697    y,
9698    y_offset;
9699
9700  register int
9701    i;
9702
9703  register Quantum
9704    *q;
9705
9706  unsigned int
9707    height,
9708    width;
9709
9710  size_t
9711    state;
9712
9713  XEvent
9714    event;
9715
9716  /*
9717    Map Command widget.
9718  */
9719  (void) CloneString(&windows->command.name,"Matte Edit");
9720  windows->command.data=4;
9721  (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9722  (void) XMapRaised(display,windows->command.id);
9723  XClientMessage(display,windows->image.id,windows->im_protocols,
9724    windows->im_update_widget,CurrentTime);
9725  /*
9726    Make cursor.
9727  */
9728  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9729    resource_info->background_color,resource_info->foreground_color);
9730  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9731  /*
9732    Track pointer until button 1 is pressed.
9733  */
9734  XQueryPosition(display,windows->image.id,&x,&y);
9735  (void) XSelectInput(display,windows->image.id,
9736    windows->image.attributes.event_mask | PointerMotionMask);
9737  state=DefaultState;
9738  do
9739  {
9740    if (IfMagickTrue(windows->info.mapped) )
9741      {
9742        /*
9743          Display pointer position.
9744        */
9745        (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
9746          x+windows->image.x,y+windows->image.y);
9747        XInfoWidget(display,windows,text);
9748      }
9749    /*
9750      Wait for next event.
9751    */
9752    XScreenEvent(display,windows,&event,exception);
9753    if (event.xany.window == windows->command.id)
9754      {
9755        /*
9756          Select a command from the Command widget.
9757        */
9758        id=XCommandWidget(display,windows,MatteEditMenu,&event);
9759        if (id < 0)
9760          {
9761            (void) XCheckDefineCursor(display,windows->image.id,cursor);
9762            continue;
9763          }
9764        switch (MatteEditCommands[id])
9765        {
9766          case MatteEditMethod:
9767          {
9768            char
9769              **methods;
9770
9771            /*
9772              Select a method from the pop-up menu.
9773            */
9774            methods=GetCommandOptions(MagickMethodOptions);
9775            if (methods == (char **) NULL)
9776              break;
9777            entry=XMenuWidget(display,windows,MatteEditMenu[id],
9778              (const char **) methods,command);
9779            if (entry >= 0)
9780              method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9781                MagickFalse,methods[entry]);
9782            methods=DestroyStringList(methods);
9783            break;
9784          }
9785          case MatteEditBorderCommand:
9786          {
9787            const char
9788              *ColorMenu[MaxNumberPens];
9789
9790            int
9791              pen_number;
9792
9793            /*
9794              Initialize menu selections.
9795            */
9796            for (i=0; i < (int) (MaxNumberPens-2); i++)
9797              ColorMenu[i]=resource_info->pen_colors[i];
9798            ColorMenu[MaxNumberPens-2]="Browser...";
9799            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9800            /*
9801              Select a pen color from the pop-up menu.
9802            */
9803            pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9804              (const char **) ColorMenu,command);
9805            if (pen_number < 0)
9806              break;
9807            if (pen_number == (MaxNumberPens-2))
9808              {
9809                static char
9810                  color_name[MagickPathExtent] = "gray";
9811
9812                /*
9813                  Select a pen color from a dialog.
9814                */
9815                resource_info->pen_colors[pen_number]=color_name;
9816                XColorBrowserWidget(display,windows,"Select",color_name);
9817                if (*color_name == '\0')
9818                  break;
9819              }
9820            /*
9821              Set border color.
9822            */
9823            (void) XParseColor(display,windows->map_info->colormap,
9824              resource_info->pen_colors[pen_number],&border_color);
9825            break;
9826          }
9827          case MatteEditFuzzCommand:
9828          {
9829            static char
9830              fuzz[MagickPathExtent];
9831
9832            static const char
9833              *FuzzMenu[] =
9834              {
9835                "0%",
9836                "2%",
9837                "5%",
9838                "10%",
9839                "15%",
9840                "Dialog...",
9841                (char *) NULL,
9842              };
9843
9844            /*
9845              Select a command from the pop-up menu.
9846            */
9847            entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9848              command);
9849            if (entry < 0)
9850              break;
9851            if (entry != 5)
9852              {
9853                (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
9854                  QuantumRange+1.0);
9855                break;
9856              }
9857            (void) CopyMagickString(fuzz,"20%",MagickPathExtent);
9858            (void) XDialogWidget(display,windows,"Ok",
9859              "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9860            if (*fuzz == '\0')
9861              break;
9862            (void) ConcatenateMagickString(fuzz,"%",MagickPathExtent);
9863            (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
9864              1.0);
9865            break;
9866          }
9867          case MatteEditValueCommand:
9868          {
9869            static char
9870              message[MagickPathExtent];
9871
9872            static const char
9873              *MatteMenu[] =
9874              {
9875                "Opaque",
9876                "Transparent",
9877                "Dialog...",
9878                (char *) NULL,
9879              };
9880
9881            /*
9882              Select a command from the pop-up menu.
9883            */
9884            entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9885              command);
9886            if (entry < 0)
9887              break;
9888            if (entry != 2)
9889              {
9890                (void) FormatLocaleString(matte,MagickPathExtent,QuantumFormat,
9891                  OpaqueAlpha);
9892                if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9893                  (void) FormatLocaleString(matte,MagickPathExtent,QuantumFormat,
9894                    (Quantum) TransparentAlpha);
9895                break;
9896              }
9897            (void) FormatLocaleString(message,MagickPathExtent,
9898              "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9899              QuantumRange);
9900            (void) XDialogWidget(display,windows,"Matte",message,matte);
9901            if (*matte == '\0')
9902              break;
9903            break;
9904          }
9905          case MatteEditUndoCommand:
9906          {
9907            (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9908              image,exception);
9909            break;
9910          }
9911          case MatteEditHelpCommand:
9912          {
9913            XTextViewWidget(display,resource_info,windows,MagickFalse,
9914              "Help Viewer - Matte Edit",ImageMatteEditHelp);
9915            break;
9916          }
9917          case MatteEditDismissCommand:
9918          {
9919            /*
9920              Prematurely exit.
9921            */
9922            state|=EscapeState;
9923            state|=ExitState;
9924            break;
9925          }
9926          default:
9927            break;
9928        }
9929        (void) XCheckDefineCursor(display,windows->image.id,cursor);
9930        continue;
9931      }
9932    switch (event.type)
9933    {
9934      case ButtonPress:
9935      {
9936        if (event.xbutton.button != Button1)
9937          break;
9938        if ((event.xbutton.window != windows->image.id) &&
9939            (event.xbutton.window != windows->magnify.id))
9940          break;
9941        /*
9942          Update matte data.
9943        */
9944        x=event.xbutton.x;
9945        y=event.xbutton.y;
9946        (void) XMagickCommand(display,resource_info,windows,
9947          SaveToUndoBufferCommand,image,exception);
9948        state|=UpdateConfigurationState;
9949        break;
9950      }
9951      case ButtonRelease:
9952      {
9953        if (event.xbutton.button != Button1)
9954          break;
9955        if ((event.xbutton.window != windows->image.id) &&
9956            (event.xbutton.window != windows->magnify.id))
9957          break;
9958        /*
9959          Update colormap information.
9960        */
9961        x=event.xbutton.x;
9962        y=event.xbutton.y;
9963        XConfigureImageColormap(display,resource_info,windows,*image,exception);
9964        (void) XConfigureImage(display,resource_info,windows,*image,exception);
9965        XInfoWidget(display,windows,text);
9966        (void) XCheckDefineCursor(display,windows->image.id,cursor);
9967        state&=(~UpdateConfigurationState);
9968        break;
9969      }
9970      case Expose:
9971        break;
9972      case KeyPress:
9973      {
9974        char
9975          command[MagickPathExtent];
9976
9977        KeySym
9978          key_symbol;
9979
9980        if (event.xkey.window == windows->magnify.id)
9981          {
9982            Window
9983              window;
9984
9985            window=windows->magnify.id;
9986            while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9987          }
9988        if (event.xkey.window != windows->image.id)
9989          break;
9990        /*
9991          Respond to a user key press.
9992        */
9993        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
9994          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
9995        switch ((int) key_symbol)
9996        {
9997          case XK_Escape:
9998          case XK_F20:
9999          {
10000            /*
10001              Prematurely exit.
10002            */
10003            state|=ExitState;
10004            break;
10005          }
10006          case XK_F1:
10007          case XK_Help:
10008          {
10009            XTextViewWidget(display,resource_info,windows,MagickFalse,
10010              "Help Viewer - Matte Edit",ImageMatteEditHelp);
10011            break;
10012          }
10013          default:
10014          {
10015            (void) XBell(display,0);
10016            break;
10017          }
10018        }
10019        break;
10020      }
10021      case MotionNotify:
10022      {
10023        /*
10024          Map and unmap Info widget as cursor crosses its boundaries.
10025        */
10026        x=event.xmotion.x;
10027        y=event.xmotion.y;
10028        if (IfMagickTrue(windows->info.mapped) )
10029          {
10030            if ((x < (int) (windows->info.x+windows->info.width)) &&
10031                (y < (int) (windows->info.y+windows->info.height)))
10032              (void) XWithdrawWindow(display,windows->info.id,
10033                windows->info.screen);
10034          }
10035        else
10036          if ((x > (int) (windows->info.x+windows->info.width)) ||
10037              (y > (int) (windows->info.y+windows->info.height)))
10038            (void) XMapWindow(display,windows->info.id);
10039        break;
10040      }
10041      default:
10042        break;
10043    }
10044    if (event.xany.window == windows->magnify.id)
10045      {
10046        x=windows->magnify.x-windows->image.x;
10047        y=windows->magnify.y-windows->image.y;
10048      }
10049    x_offset=x;
10050    y_offset=y;
10051    if ((state & UpdateConfigurationState) != 0)
10052      {
10053        CacheView
10054          *image_view;
10055
10056        int
10057          x,
10058          y;
10059
10060        /*
10061          Matte edit is relative to image configuration.
10062        */
10063        (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
10064          MagickTrue);
10065        XPutPixel(windows->image.ximage,x_offset,y_offset,
10066          windows->pixel_info->background_color.pixel);
10067        width=(unsigned int) (*image)->columns;
10068        height=(unsigned int) (*image)->rows;
10069        x=0;
10070        y=0;
10071        if (windows->image.crop_geometry != (char *) NULL)
10072          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
10073            &height);
10074        x_offset=(int) (width*(windows->image.x+x_offset)/
10075          windows->image.ximage->width+x);
10076        y_offset=(int) (height*(windows->image.y+y_offset)/
10077          windows->image.ximage->height+y);
10078        if ((x_offset < 0) || (y_offset < 0))
10079          continue;
10080        if ((x_offset >= (int) (*image)->columns) ||
10081            (y_offset >= (int) (*image)->rows))
10082          continue;
10083        if (IfMagickFalse(SetImageStorageClass(*image,DirectClass,exception)) )
10084          return(MagickFalse);
10085        if ((*image)->alpha_trait == UndefinedPixelTrait)
10086          (void) SetImageAlphaChannel(*image,OpaqueAlphaChannel,exception);
10087        image_view=AcquireAuthenticCacheView(*image,exception);
10088        switch (method)
10089        {
10090          case PointMethod:
10091          default:
10092          {
10093            /*
10094              Update matte information using point algorithm.
10095            */
10096            q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10097              (ssize_t) y_offset,1,1,exception);
10098            if (q == (Quantum *) NULL)
10099              break;
10100            SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10101            (void) SyncCacheViewAuthenticPixels(image_view,exception);
10102            break;
10103          }
10104          case ReplaceMethod:
10105          {
10106            PixelInfo
10107              pixel,
10108              target;
10109
10110            /*
10111              Update matte information using replace algorithm.
10112            */
10113            (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
10114              x_offset,(ssize_t) y_offset,&target,exception);
10115            for (y=0; y < (int) (*image)->rows; y++)
10116            {
10117              q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10118                (*image)->columns,1,exception);
10119              if (q == (Quantum *) NULL)
10120                break;
10121              for (x=0; x < (int) (*image)->columns; x++)
10122              {
10123                GetPixelInfoPixel(*image,q,&pixel);
10124                if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
10125                  SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10126                q+=GetPixelChannels(*image);
10127              }
10128              if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
10129                break;
10130            }
10131            break;
10132          }
10133          case FloodfillMethod:
10134          case FillToBorderMethod:
10135          {
10136            ChannelType
10137              channel_mask;
10138
10139            DrawInfo
10140              *draw_info;
10141
10142            PixelInfo
10143              target;
10144
10145            /*
10146              Update matte information using floodfill algorithm.
10147            */
10148            (void) GetOneVirtualPixelInfo(*image,
10149              GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
10150              y_offset,&target,exception);
10151            if (method == FillToBorderMethod)
10152              {
10153                target.red=(double) ScaleShortToQuantum(
10154                  border_color.red);
10155                target.green=(double) ScaleShortToQuantum(
10156                  border_color.green);
10157                target.blue=(double) ScaleShortToQuantum(
10158                  border_color.blue);
10159              }
10160            draw_info=CloneDrawInfo(resource_info->image_info,
10161              (DrawInfo *) NULL);
10162            draw_info->fill.alpha=(double) ClampToQuantum(
10163              StringToDouble(matte,(char **) NULL));
10164            channel_mask=SetImageChannelMask(*image,AlphaChannel);
10165            (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10166              x_offset,(ssize_t) y_offset,
10167              method != FloodfillMethod ? MagickTrue : MagickFalse,exception);
10168            (void) SetPixelChannelMask(*image,channel_mask);
10169            draw_info=DestroyDrawInfo(draw_info);
10170            break;
10171          }
10172          case ResetMethod:
10173          {
10174            /*
10175              Update matte information using reset algorithm.
10176            */
10177            if (IfMagickFalse(SetImageStorageClass(*image,DirectClass,exception)) )
10178              return(MagickFalse);
10179            for (y=0; y < (int) (*image)->rows; y++)
10180            {
10181              q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10182                (*image)->columns,1,exception);
10183              if (q == (Quantum *) NULL)
10184                break;
10185              for (x=0; x < (int) (*image)->columns; x++)
10186              {
10187                SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10188                q+=GetPixelChannels(*image);
10189              }
10190              if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
10191                break;
10192            }
10193            if (StringToLong(matte) == (long) OpaqueAlpha)
10194              (*image)->alpha_trait=UndefinedPixelTrait;
10195            break;
10196          }
10197        }
10198        image_view=DestroyCacheView(image_view);
10199        state&=(~UpdateConfigurationState);
10200      }
10201  } while ((state & ExitState) == 0);
10202  (void) XSelectInput(display,windows->image.id,
10203    windows->image.attributes.event_mask);
10204  XSetCursorState(display,windows,MagickFalse);
10205  (void) XFreeCursor(display,cursor);
10206  return(MagickTrue);
10207}
10208
10209/*
10210%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10211%                                                                             %
10212%                                                                             %
10213%                                                                             %
10214+   X O p e n I m a g e                                                       %
10215%                                                                             %
10216%                                                                             %
10217%                                                                             %
10218%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10219%
10220%  XOpenImage() loads an image from a file.
10221%
10222%  The format of the XOpenImage method is:
10223%
10224%     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10225%       XWindows *windows,const unsigned int command)
10226%
10227%  A description of each parameter follows:
10228%
10229%    o display: Specifies a connection to an X server; returned from
10230%      XOpenDisplay.
10231%
10232%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10233%
10234%    o windows: Specifies a pointer to a XWindows structure.
10235%
10236%    o command: A value other than zero indicates that the file is selected
10237%      from the command line argument list.
10238%
10239*/
10240static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10241  XWindows *windows,const MagickBooleanType command)
10242{
10243  const MagickInfo
10244    *magick_info;
10245
10246  ExceptionInfo
10247    *exception;
10248
10249  Image
10250    *nexus;
10251
10252  ImageInfo
10253    *image_info;
10254
10255  static char
10256    filename[MagickPathExtent] = "\0";
10257
10258  /*
10259    Request file name from user.
10260  */
10261  if (IfMagickFalse(command) )
10262    XFileBrowserWidget(display,windows,"Open",filename);
10263  else
10264    {
10265      char
10266        **filelist,
10267        **files;
10268
10269      int
10270        count,
10271        status;
10272
10273      register int
10274        i,
10275        j;
10276
10277      /*
10278        Select next image from the command line.
10279      */
10280      status=XGetCommand(display,windows->image.id,&files,&count);
10281      if (status == 0)
10282        {
10283          ThrowXWindowException(XServerError,"UnableToGetProperty","...");
10284          return((Image *) NULL);
10285        }
10286      filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10287      if (filelist == (char **) NULL)
10288        {
10289          ThrowXWindowException(ResourceLimitError,
10290            "MemoryAllocationFailed","...");
10291          (void) XFreeStringList(files);
10292          return((Image *) NULL);
10293        }
10294      j=0;
10295      for (i=1; i < count; i++)
10296        if (*files[i] != '-')
10297          filelist[j++]=files[i];
10298      filelist[j]=(char *) NULL;
10299      XListBrowserWidget(display,windows,&windows->widget,
10300        (const char **) filelist,"Load","Select Image to Load:",filename);
10301      filelist=(char **) RelinquishMagickMemory(filelist);
10302      (void) XFreeStringList(files);
10303    }
10304  if (*filename == '\0')
10305    return((Image *) NULL);
10306  image_info=CloneImageInfo(resource_info->image_info);
10307  (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10308    (void *) NULL);
10309  (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
10310  exception=AcquireExceptionInfo();
10311  (void) SetImageInfo(image_info,0,exception);
10312  if (LocaleCompare(image_info->magick,"X") == 0)
10313    {
10314      char
10315        seconds[MagickPathExtent];
10316
10317      /*
10318        User may want to delay the X server screen grab.
10319      */
10320      (void) CopyMagickString(seconds,"0",MagickPathExtent);
10321      (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10322        seconds);
10323      if (*seconds == '\0')
10324        return((Image *) NULL);
10325      XDelay(display,(size_t) (1000*StringToLong(seconds)));
10326    }
10327  magick_info=GetMagickInfo(image_info->magick,exception);
10328  if ((magick_info != (const MagickInfo *) NULL) &&
10329      GetMagickRawSupport(magick_info) == MagickTrue)
10330    {
10331      char
10332        geometry[MagickPathExtent];
10333
10334      /*
10335        Request image size from the user.
10336      */
10337      (void) CopyMagickString(geometry,"512x512",MagickPathExtent);
10338      if (image_info->size != (char *) NULL)
10339        (void) CopyMagickString(geometry,image_info->size,MagickPathExtent);
10340      (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10341        geometry);
10342      (void) CloneString(&image_info->size,geometry);
10343    }
10344  /*
10345    Load the image.
10346  */
10347  XSetCursorState(display,windows,MagickTrue);
10348  XCheckRefreshWindows(display,windows);
10349  (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
10350  nexus=ReadImage(image_info,exception);
10351  CatchException(exception);
10352  XSetCursorState(display,windows,MagickFalse);
10353  if (nexus != (Image *) NULL)
10354    XClientMessage(display,windows->image.id,windows->im_protocols,
10355      windows->im_next_image,CurrentTime);
10356  else
10357    {
10358      char
10359        *text,
10360        **textlist;
10361
10362      /*
10363        Unknown image format.
10364      */
10365      text=FileToString(filename,~0UL,exception);
10366      if (text == (char *) NULL)
10367        return((Image *) NULL);
10368      textlist=StringToList(text);
10369      if (textlist != (char **) NULL)
10370        {
10371          char
10372            title[MagickPathExtent];
10373
10374          register int
10375            i;
10376
10377          (void) FormatLocaleString(title,MagickPathExtent,
10378            "Unknown format: %s",filename);
10379          XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10380            (const char **) textlist);
10381          for (i=0; textlist[i] != (char *) NULL; i++)
10382            textlist[i]=DestroyString(textlist[i]);
10383          textlist=(char **) RelinquishMagickMemory(textlist);
10384        }
10385      text=DestroyString(text);
10386    }
10387  exception=DestroyExceptionInfo(exception);
10388  image_info=DestroyImageInfo(image_info);
10389  return(nexus);
10390}
10391
10392/*
10393%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10394%                                                                             %
10395%                                                                             %
10396%                                                                             %
10397+   X P a n I m a g e                                                         %
10398%                                                                             %
10399%                                                                             %
10400%                                                                             %
10401%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10402%
10403%  XPanImage() pans the image until the mouse button is released.
10404%
10405%  The format of the XPanImage method is:
10406%
10407%      void XPanImage(Display *display,XWindows *windows,XEvent *event,
10408%        ExceptionInfo *exception)
10409%
10410%  A description of each parameter follows:
10411%
10412%    o display: Specifies a connection to an X server;  returned from
10413%      XOpenDisplay.
10414%
10415%    o windows: Specifies a pointer to a XWindows structure.
10416%
10417%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10418%      the entire image is refreshed.
10419%
10420%    o exception: return any errors or warnings in this structure.
10421%
10422*/
10423static void XPanImage(Display *display,XWindows *windows,XEvent *event,
10424  ExceptionInfo *exception)
10425{
10426  char
10427    text[MagickPathExtent];
10428
10429  Cursor
10430    cursor;
10431
10432  double
10433    x_factor,
10434    y_factor;
10435
10436  RectangleInfo
10437    pan_info;
10438
10439  size_t
10440    state;
10441
10442  /*
10443    Define cursor.
10444  */
10445  if ((windows->image.ximage->width > (int) windows->image.width) &&
10446      (windows->image.ximage->height > (int) windows->image.height))
10447    cursor=XCreateFontCursor(display,XC_fleur);
10448  else
10449    if (windows->image.ximage->width > (int) windows->image.width)
10450      cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10451    else
10452      if (windows->image.ximage->height > (int) windows->image.height)
10453        cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10454      else
10455        cursor=XCreateFontCursor(display,XC_arrow);
10456  (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10457  /*
10458    Pan image as pointer moves until the mouse button is released.
10459  */
10460  x_factor=(double) windows->image.ximage->width/windows->pan.width;
10461  y_factor=(double) windows->image.ximage->height/windows->pan.height;
10462  pan_info.width=windows->pan.width*windows->image.width/
10463    windows->image.ximage->width;
10464  pan_info.height=windows->pan.height*windows->image.height/
10465    windows->image.ximage->height;
10466  pan_info.x=0;
10467  pan_info.y=0;
10468  state=UpdateConfigurationState;
10469  do
10470  {
10471    switch (event->type)
10472    {
10473      case ButtonPress:
10474      {
10475        /*
10476          User choose an initial pan location.
10477        */
10478        pan_info.x=(ssize_t) event->xbutton.x;
10479        pan_info.y=(ssize_t) event->xbutton.y;
10480        state|=UpdateConfigurationState;
10481        break;
10482      }
10483      case ButtonRelease:
10484      {
10485        /*
10486          User has finished panning the image.
10487        */
10488        pan_info.x=(ssize_t) event->xbutton.x;
10489        pan_info.y=(ssize_t) event->xbutton.y;
10490        state|=UpdateConfigurationState | ExitState;
10491        break;
10492      }
10493      case MotionNotify:
10494      {
10495        pan_info.x=(ssize_t) event->xmotion.x;
10496        pan_info.y=(ssize_t) event->xmotion.y;
10497        state|=UpdateConfigurationState;
10498      }
10499      default:
10500        break;
10501    }
10502    if ((state & UpdateConfigurationState) != 0)
10503      {
10504        /*
10505          Check boundary conditions.
10506        */
10507        if (pan_info.x < (ssize_t) (pan_info.width/2))
10508          pan_info.x=0;
10509        else
10510          pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10511        if (pan_info.x < 0)
10512          pan_info.x=0;
10513        else
10514          if ((int) (pan_info.x+windows->image.width) >
10515              windows->image.ximage->width)
10516            pan_info.x=(ssize_t)
10517              (windows->image.ximage->width-windows->image.width);
10518        if (pan_info.y < (ssize_t) (pan_info.height/2))
10519          pan_info.y=0;
10520        else
10521          pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10522        if (pan_info.y < 0)
10523          pan_info.y=0;
10524        else
10525          if ((int) (pan_info.y+windows->image.height) >
10526              windows->image.ximage->height)
10527            pan_info.y=(ssize_t)
10528              (windows->image.ximage->height-windows->image.height);
10529        if ((windows->image.x != (int) pan_info.x) ||
10530            (windows->image.y != (int) pan_info.y))
10531          {
10532            /*
10533              Display image pan offset.
10534            */
10535            windows->image.x=(int) pan_info.x;
10536            windows->image.y=(int) pan_info.y;
10537            (void) FormatLocaleString(text,MagickPathExtent," %ux%u%+d%+d ",
10538              windows->image.width,windows->image.height,windows->image.x,
10539              windows->image.y);
10540            XInfoWidget(display,windows,text);
10541            /*
10542              Refresh Image window.
10543            */
10544            XDrawPanRectangle(display,windows);
10545            XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10546          }
10547        state&=(~UpdateConfigurationState);
10548      }
10549    /*
10550      Wait for next event.
10551    */
10552    if ((state & ExitState) == 0)
10553      XScreenEvent(display,windows,event,exception);
10554  } while ((state & ExitState) == 0);
10555  /*
10556    Restore cursor.
10557  */
10558  (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10559  (void) XFreeCursor(display,cursor);
10560  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10561}
10562
10563/*
10564%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10565%                                                                             %
10566%                                                                             %
10567%                                                                             %
10568+   X P a s t e I m a g e                                                     %
10569%                                                                             %
10570%                                                                             %
10571%                                                                             %
10572%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10573%
10574%  XPasteImage() pastes an image previously saved with XCropImage in the X
10575%  window image at a location the user chooses with the pointer.
10576%
10577%  The format of the XPasteImage method is:
10578%
10579%      MagickBooleanType XPasteImage(Display *display,
10580%        XResourceInfo *resource_info,XWindows *windows,Image *image,
10581%        ExceptionInfo *exception)
10582%
10583%  A description of each parameter follows:
10584%
10585%    o display: Specifies a connection to an X server;  returned from
10586%      XOpenDisplay.
10587%
10588%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10589%
10590%    o windows: Specifies a pointer to a XWindows structure.
10591%
10592%    o image: the image; returned from ReadImage.
10593%
10594%    o exception: return any errors or warnings in this structure.
10595%
10596*/
10597static MagickBooleanType XPasteImage(Display *display,
10598  XResourceInfo *resource_info,XWindows *windows,Image *image,
10599  ExceptionInfo *exception)
10600{
10601  static const char
10602    *PasteMenu[] =
10603    {
10604      "Operator",
10605      "Help",
10606      "Dismiss",
10607      (char *) NULL
10608    };
10609
10610  static const ModeType
10611    PasteCommands[] =
10612    {
10613      PasteOperatorsCommand,
10614      PasteHelpCommand,
10615      PasteDismissCommand
10616    };
10617
10618  static CompositeOperator
10619    compose = CopyCompositeOp;
10620
10621  char
10622    text[MagickPathExtent];
10623
10624  Cursor
10625    cursor;
10626
10627  Image
10628    *paste_image;
10629
10630  int
10631    entry,
10632    id,
10633    x,
10634    y;
10635
10636  double
10637    scale_factor;
10638
10639  RectangleInfo
10640    highlight_info,
10641    paste_info;
10642
10643  unsigned int
10644    height,
10645    width;
10646
10647  size_t
10648    state;
10649
10650  XEvent
10651    event;
10652
10653  /*
10654    Copy image.
10655  */
10656  if (resource_info->copy_image == (Image *) NULL)
10657    return(MagickFalse);
10658  paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,exception);
10659  /*
10660    Map Command widget.
10661  */
10662  (void) CloneString(&windows->command.name,"Paste");
10663  windows->command.data=1;
10664  (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10665  (void) XMapRaised(display,windows->command.id);
10666  XClientMessage(display,windows->image.id,windows->im_protocols,
10667    windows->im_update_widget,CurrentTime);
10668  /*
10669    Track pointer until button 1 is pressed.
10670  */
10671  XSetCursorState(display,windows,MagickFalse);
10672  XQueryPosition(display,windows->image.id,&x,&y);
10673  (void) XSelectInput(display,windows->image.id,
10674    windows->image.attributes.event_mask | PointerMotionMask);
10675  paste_info.x=(ssize_t) windows->image.x+x;
10676  paste_info.y=(ssize_t) windows->image.y+y;
10677  paste_info.width=0;
10678  paste_info.height=0;
10679  cursor=XCreateFontCursor(display,XC_ul_angle);
10680  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10681  state=DefaultState;
10682  do
10683  {
10684    if (IfMagickTrue(windows->info.mapped) )
10685      {
10686        /*
10687          Display pointer position.
10688        */
10689        (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
10690          (long) paste_info.x,(long) paste_info.y);
10691        XInfoWidget(display,windows,text);
10692      }
10693    highlight_info=paste_info;
10694    highlight_info.x=paste_info.x-windows->image.x;
10695    highlight_info.y=paste_info.y-windows->image.y;
10696    XHighlightRectangle(display,windows->image.id,
10697      windows->image.highlight_context,&highlight_info);
10698    /*
10699      Wait for next event.
10700    */
10701    XScreenEvent(display,windows,&event,exception);
10702    XHighlightRectangle(display,windows->image.id,
10703      windows->image.highlight_context,&highlight_info);
10704    if (event.xany.window == windows->command.id)
10705      {
10706        /*
10707          Select a command from the Command widget.
10708        */
10709        id=XCommandWidget(display,windows,PasteMenu,&event);
10710        if (id < 0)
10711          continue;
10712        switch (PasteCommands[id])
10713        {
10714          case PasteOperatorsCommand:
10715          {
10716            char
10717              command[MagickPathExtent],
10718              **operators;
10719
10720            /*
10721              Select a command from the pop-up menu.
10722            */
10723            operators=GetCommandOptions(MagickComposeOptions);
10724            if (operators == (char **) NULL)
10725              break;
10726            entry=XMenuWidget(display,windows,PasteMenu[id],
10727              (const char **) operators,command);
10728            if (entry >= 0)
10729              compose=(CompositeOperator) ParseCommandOption(
10730                MagickComposeOptions,MagickFalse,operators[entry]);
10731            operators=DestroyStringList(operators);
10732            break;
10733          }
10734          case PasteHelpCommand:
10735          {
10736            XTextViewWidget(display,resource_info,windows,MagickFalse,
10737              "Help Viewer - Image Composite",ImagePasteHelp);
10738            break;
10739          }
10740          case PasteDismissCommand:
10741          {
10742            /*
10743              Prematurely exit.
10744            */
10745            state|=EscapeState;
10746            state|=ExitState;
10747            break;
10748          }
10749          default:
10750            break;
10751        }
10752        continue;
10753      }
10754    switch (event.type)
10755    {
10756      case ButtonPress:
10757      {
10758        if (IfMagickTrue(image->debug) )
10759          (void) LogMagickEvent(X11Event,GetMagickModule(),
10760            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10761            event.xbutton.button,event.xbutton.x,event.xbutton.y);
10762        if (event.xbutton.button != Button1)
10763          break;
10764        if (event.xbutton.window != windows->image.id)
10765          break;
10766        /*
10767          Paste rectangle is relative to image configuration.
10768        */
10769        width=(unsigned int) image->columns;
10770        height=(unsigned int) image->rows;
10771        x=0;
10772        y=0;
10773        if (windows->image.crop_geometry != (char *) NULL)
10774          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10775            &width,&height);
10776        scale_factor=(double) windows->image.ximage->width/width;
10777        paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10778        scale_factor=(double) windows->image.ximage->height/height;
10779        paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10780        (void) XCheckDefineCursor(display,windows->image.id,cursor);
10781        paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10782        paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10783        break;
10784      }
10785      case ButtonRelease:
10786      {
10787        if (IfMagickTrue(image->debug) )
10788          (void) LogMagickEvent(X11Event,GetMagickModule(),
10789            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10790            event.xbutton.button,event.xbutton.x,event.xbutton.y);
10791        if (event.xbutton.button != Button1)
10792          break;
10793        if (event.xbutton.window != windows->image.id)
10794          break;
10795        if ((paste_info.width != 0) && (paste_info.height != 0))
10796          {
10797            /*
10798              User has selected the location of the paste image.
10799            */
10800            paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10801            paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10802            state|=ExitState;
10803          }
10804        break;
10805      }
10806      case Expose:
10807        break;
10808      case KeyPress:
10809      {
10810        char
10811          command[MagickPathExtent];
10812
10813        KeySym
10814          key_symbol;
10815
10816        int
10817          length;
10818
10819        if (event.xkey.window != windows->image.id)
10820          break;
10821        /*
10822          Respond to a user key press.
10823        */
10824        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10825          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10826        *(command+length)='\0';
10827        if (IfMagickTrue(image->debug) )
10828          (void) LogMagickEvent(X11Event,GetMagickModule(),
10829            "Key press: 0x%lx (%s)",(long) key_symbol,command);
10830        switch ((int) key_symbol)
10831        {
10832          case XK_Escape:
10833          case XK_F20:
10834          {
10835            /*
10836              Prematurely exit.
10837            */
10838            paste_image=DestroyImage(paste_image);
10839            state|=EscapeState;
10840            state|=ExitState;
10841            break;
10842          }
10843          case XK_F1:
10844          case XK_Help:
10845          {
10846            (void) XSetFunction(display,windows->image.highlight_context,
10847              GXcopy);
10848            XTextViewWidget(display,resource_info,windows,MagickFalse,
10849              "Help Viewer - Image Composite",ImagePasteHelp);
10850            (void) XSetFunction(display,windows->image.highlight_context,
10851              GXinvert);
10852            break;
10853          }
10854          default:
10855          {
10856            (void) XBell(display,0);
10857            break;
10858          }
10859        }
10860        break;
10861      }
10862      case MotionNotify:
10863      {
10864        /*
10865          Map and unmap Info widget as text cursor crosses its boundaries.
10866        */
10867        x=event.xmotion.x;
10868        y=event.xmotion.y;
10869        if (IfMagickTrue(windows->info.mapped) )
10870          {
10871            if ((x < (int) (windows->info.x+windows->info.width)) &&
10872                (y < (int) (windows->info.y+windows->info.height)))
10873              (void) XWithdrawWindow(display,windows->info.id,
10874                windows->info.screen);
10875          }
10876        else
10877          if ((x > (int) (windows->info.x+windows->info.width)) ||
10878              (y > (int) (windows->info.y+windows->info.height)))
10879            (void) XMapWindow(display,windows->info.id);
10880        paste_info.x=(ssize_t) windows->image.x+x;
10881        paste_info.y=(ssize_t) windows->image.y+y;
10882        break;
10883      }
10884      default:
10885      {
10886        if (IfMagickTrue(image->debug) )
10887          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10888            event.type);
10889        break;
10890      }
10891    }
10892  } while ((state & ExitState) == 0);
10893  (void) XSelectInput(display,windows->image.id,
10894    windows->image.attributes.event_mask);
10895  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10896  XSetCursorState(display,windows,MagickFalse);
10897  (void) XFreeCursor(display,cursor);
10898  if ((state & EscapeState) != 0)
10899    return(MagickTrue);
10900  /*
10901    Image pasting is relative to image configuration.
10902  */
10903  XSetCursorState(display,windows,MagickTrue);
10904  XCheckRefreshWindows(display,windows);
10905  width=(unsigned int) image->columns;
10906  height=(unsigned int) image->rows;
10907  x=0;
10908  y=0;
10909  if (windows->image.crop_geometry != (char *) NULL)
10910    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10911  scale_factor=(double) width/windows->image.ximage->width;
10912  paste_info.x+=x;
10913  paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10914  paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10915  scale_factor=(double) height/windows->image.ximage->height;
10916  paste_info.y+=y;
10917  paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10918  paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10919  /*
10920    Paste image with X Image window.
10921  */
10922  (void) CompositeImage(image,paste_image,compose,MagickTrue,paste_info.x,
10923    paste_info.y,exception);
10924  paste_image=DestroyImage(paste_image);
10925  XSetCursorState(display,windows,MagickFalse);
10926  /*
10927    Update image colormap.
10928  */
10929  XConfigureImageColormap(display,resource_info,windows,image,exception);
10930  (void) XConfigureImage(display,resource_info,windows,image,exception);
10931  return(MagickTrue);
10932}
10933
10934/*
10935%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10936%                                                                             %
10937%                                                                             %
10938%                                                                             %
10939+   X P r i n t I m a g e                                                     %
10940%                                                                             %
10941%                                                                             %
10942%                                                                             %
10943%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10944%
10945%  XPrintImage() prints an image to a Postscript printer.
10946%
10947%  The format of the XPrintImage method is:
10948%
10949%      MagickBooleanType XPrintImage(Display *display,
10950%        XResourceInfo *resource_info,XWindows *windows,Image *image,
10951%        ExceptionInfo *exception)
10952%
10953%  A description of each parameter follows:
10954%
10955%    o display: Specifies a connection to an X server; returned from
10956%      XOpenDisplay.
10957%
10958%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10959%
10960%    o windows: Specifies a pointer to a XWindows structure.
10961%
10962%    o image: the image.
10963%
10964%    o exception: return any errors or warnings in this structure.
10965%
10966*/
10967static MagickBooleanType XPrintImage(Display *display,
10968  XResourceInfo *resource_info,XWindows *windows,Image *image,
10969  ExceptionInfo *exception)
10970{
10971  char
10972    filename[MagickPathExtent],
10973    geometry[MagickPathExtent];
10974
10975  Image
10976    *print_image;
10977
10978  ImageInfo
10979    *image_info;
10980
10981  MagickStatusType
10982    status;
10983
10984  /*
10985    Request Postscript page geometry from user.
10986  */
10987  image_info=CloneImageInfo(resource_info->image_info);
10988  (void) FormatLocaleString(geometry,MagickPathExtent,"Letter");
10989  if (image_info->page != (char *) NULL)
10990    (void) CopyMagickString(geometry,image_info->page,MagickPathExtent);
10991  XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
10992    "Select Postscript Page Geometry:",geometry);
10993  if (*geometry == '\0')
10994    return(MagickTrue);
10995  image_info->page=GetPageGeometry(geometry);
10996  /*
10997    Apply image transforms.
10998  */
10999  XSetCursorState(display,windows,MagickTrue);
11000  XCheckRefreshWindows(display,windows);
11001  print_image=CloneImage(image,0,0,MagickTrue,exception);
11002  if (print_image == (Image *) NULL)
11003    return(MagickFalse);
11004  (void) FormatLocaleString(geometry,MagickPathExtent,"%dx%d!",
11005    windows->image.ximage->width,windows->image.ximage->height);
11006  (void) TransformImage(&print_image,windows->image.crop_geometry,geometry,
11007    exception);
11008  /*
11009    Print image.
11010  */
11011  (void) AcquireUniqueFilename(filename);
11012  (void) FormatLocaleString(print_image->filename,MagickPathExtent,"print:%s",
11013    filename);
11014  status=WriteImage(image_info,print_image,exception);
11015  (void) RelinquishUniqueFileResource(filename);
11016  print_image=DestroyImage(print_image);
11017  image_info=DestroyImageInfo(image_info);
11018  XSetCursorState(display,windows,MagickFalse);
11019  return(status != 0 ? MagickTrue : MagickFalse);
11020}
11021
11022/*
11023%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11024%                                                                             %
11025%                                                                             %
11026%                                                                             %
11027+   X R O I I m a g e                                                         %
11028%                                                                             %
11029%                                                                             %
11030%                                                                             %
11031%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11032%
11033%  XROIImage() applies an image processing technique to a region of interest.
11034%
11035%  The format of the XROIImage method is:
11036%
11037%      MagickBooleanType XROIImage(Display *display,
11038%        XResourceInfo *resource_info,XWindows *windows,Image **image,
11039%        ExceptionInfo *exception)
11040%
11041%  A description of each parameter follows:
11042%
11043%    o display: Specifies a connection to an X server; returned from
11044%      XOpenDisplay.
11045%
11046%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11047%
11048%    o windows: Specifies a pointer to a XWindows structure.
11049%
11050%    o image: the image; returned from ReadImage.
11051%
11052%    o exception: return any errors or warnings in this structure.
11053%
11054*/
11055static MagickBooleanType XROIImage(Display *display,
11056  XResourceInfo *resource_info,XWindows *windows,Image **image,
11057  ExceptionInfo *exception)
11058{
11059#define ApplyMenus  7
11060
11061  static const char
11062    *ROIMenu[] =
11063    {
11064      "Help",
11065      "Dismiss",
11066      (char *) NULL
11067    },
11068    *ApplyMenu[] =
11069    {
11070      "File",
11071      "Edit",
11072      "Transform",
11073      "Enhance",
11074      "Effects",
11075      "F/X",
11076      "Miscellany",
11077      "Help",
11078      "Dismiss",
11079      (char *) NULL
11080    },
11081    *FileMenu[] =
11082    {
11083      "Save...",
11084      "Print...",
11085      (char *) NULL
11086    },
11087    *EditMenu[] =
11088    {
11089      "Undo",
11090      "Redo",
11091      (char *) NULL
11092    },
11093    *TransformMenu[] =
11094    {
11095      "Flop",
11096      "Flip",
11097      "Rotate Right",
11098      "Rotate Left",
11099      (char *) NULL
11100    },
11101    *EnhanceMenu[] =
11102    {
11103      "Hue...",
11104      "Saturation...",
11105      "Brightness...",
11106      "Gamma...",
11107      "Spiff",
11108      "Dull",
11109      "Contrast Stretch...",
11110      "Sigmoidal Contrast...",
11111      "Normalize",
11112      "Equalize",
11113      "Negate",
11114      "Grayscale",
11115      "Map...",
11116      "Quantize...",
11117      (char *) NULL
11118    },
11119    *EffectsMenu[] =
11120    {
11121      "Despeckle",
11122      "Emboss",
11123      "Reduce Noise",
11124      "Add Noise",
11125      "Sharpen...",
11126      "Blur...",
11127      "Threshold...",
11128      "Edge Detect...",
11129      "Spread...",
11130      "Shade...",
11131      "Raise...",
11132      "Segment...",
11133      (char *) NULL
11134    },
11135    *FXMenu[] =
11136    {
11137      "Solarize...",
11138      "Sepia Tone...",
11139      "Swirl...",
11140      "Implode...",
11141      "Vignette...",
11142      "Wave...",
11143      "Oil Paint...",
11144      "Charcoal Draw...",
11145      (char *) NULL
11146    },
11147    *MiscellanyMenu[] =
11148    {
11149      "Image Info",
11150      "Zoom Image",
11151      "Show Preview...",
11152      "Show Histogram",
11153      "Show Matte",
11154      (char *) NULL
11155    };
11156
11157  static const char
11158    **Menus[ApplyMenus] =
11159    {
11160      FileMenu,
11161      EditMenu,
11162      TransformMenu,
11163      EnhanceMenu,
11164      EffectsMenu,
11165      FXMenu,
11166      MiscellanyMenu
11167    };
11168
11169  static const CommandType
11170    ApplyCommands[] =
11171    {
11172      NullCommand,
11173      NullCommand,
11174      NullCommand,
11175      NullCommand,
11176      NullCommand,
11177      NullCommand,
11178      NullCommand,
11179      HelpCommand,
11180      QuitCommand
11181    },
11182    FileCommands[] =
11183    {
11184      SaveCommand,
11185      PrintCommand
11186    },
11187    EditCommands[] =
11188    {
11189      UndoCommand,
11190      RedoCommand
11191    },
11192    TransformCommands[] =
11193    {
11194      FlopCommand,
11195      FlipCommand,
11196      RotateRightCommand,
11197      RotateLeftCommand
11198    },
11199    EnhanceCommands[] =
11200    {
11201      HueCommand,
11202      SaturationCommand,
11203      BrightnessCommand,
11204      GammaCommand,
11205      SpiffCommand,
11206      DullCommand,
11207      ContrastStretchCommand,
11208      SigmoidalContrastCommand,
11209      NormalizeCommand,
11210      EqualizeCommand,
11211      NegateCommand,
11212      GrayscaleCommand,
11213      MapCommand,
11214      QuantizeCommand
11215    },
11216    EffectsCommands[] =
11217    {
11218      DespeckleCommand,
11219      EmbossCommand,
11220      ReduceNoiseCommand,
11221      AddNoiseCommand,
11222      SharpenCommand,
11223      BlurCommand,
11224      EdgeDetectCommand,
11225      SpreadCommand,
11226      ShadeCommand,
11227      RaiseCommand,
11228      SegmentCommand
11229    },
11230    FXCommands[] =
11231    {
11232      SolarizeCommand,
11233      SepiaToneCommand,
11234      SwirlCommand,
11235      ImplodeCommand,
11236      VignetteCommand,
11237      WaveCommand,
11238      OilPaintCommand,
11239      CharcoalDrawCommand
11240    },
11241    MiscellanyCommands[] =
11242    {
11243      InfoCommand,
11244      ZoomCommand,
11245      ShowPreviewCommand,
11246      ShowHistogramCommand,
11247      ShowMatteCommand
11248    },
11249    ROICommands[] =
11250    {
11251      ROIHelpCommand,
11252      ROIDismissCommand
11253    };
11254
11255  static const CommandType
11256    *Commands[ApplyMenus] =
11257    {
11258      FileCommands,
11259      EditCommands,
11260      TransformCommands,
11261      EnhanceCommands,
11262      EffectsCommands,
11263      FXCommands,
11264      MiscellanyCommands
11265    };
11266
11267  char
11268    command[MagickPathExtent],
11269    text[MagickPathExtent];
11270
11271  CommandType
11272    command_type;
11273
11274  Cursor
11275    cursor;
11276
11277  Image
11278    *roi_image;
11279
11280  int
11281    entry,
11282    id,
11283    x,
11284    y;
11285
11286  double
11287    scale_factor;
11288
11289  MagickProgressMonitor
11290    progress_monitor;
11291
11292  RectangleInfo
11293    crop_info,
11294    highlight_info,
11295    roi_info;
11296
11297  unsigned int
11298    height,
11299    width;
11300
11301  size_t
11302    state;
11303
11304  XEvent
11305    event;
11306
11307  /*
11308    Map Command widget.
11309  */
11310  (void) CloneString(&windows->command.name,"ROI");
11311  windows->command.data=0;
11312  (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11313  (void) XMapRaised(display,windows->command.id);
11314  XClientMessage(display,windows->image.id,windows->im_protocols,
11315    windows->im_update_widget,CurrentTime);
11316  /*
11317    Track pointer until button 1 is pressed.
11318  */
11319  XQueryPosition(display,windows->image.id,&x,&y);
11320  (void) XSelectInput(display,windows->image.id,
11321    windows->image.attributes.event_mask | PointerMotionMask);
11322  roi_info.x=(ssize_t) windows->image.x+x;
11323  roi_info.y=(ssize_t) windows->image.y+y;
11324  roi_info.width=0;
11325  roi_info.height=0;
11326  cursor=XCreateFontCursor(display,XC_fleur);
11327  state=DefaultState;
11328  do
11329  {
11330    if (IfMagickTrue(windows->info.mapped) )
11331      {
11332        /*
11333          Display pointer position.
11334        */
11335        (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
11336          (long) roi_info.x,(long) roi_info.y);
11337        XInfoWidget(display,windows,text);
11338      }
11339    /*
11340      Wait for next event.
11341    */
11342    XScreenEvent(display,windows,&event,exception);
11343    if (event.xany.window == windows->command.id)
11344      {
11345        /*
11346          Select a command from the Command widget.
11347        */
11348        id=XCommandWidget(display,windows,ROIMenu,&event);
11349        if (id < 0)
11350          continue;
11351        switch (ROICommands[id])
11352        {
11353          case ROIHelpCommand:
11354          {
11355            XTextViewWidget(display,resource_info,windows,MagickFalse,
11356              "Help Viewer - Region of Interest",ImageROIHelp);
11357            break;
11358          }
11359          case ROIDismissCommand:
11360          {
11361            /*
11362              Prematurely exit.
11363            */
11364            state|=EscapeState;
11365            state|=ExitState;
11366            break;
11367          }
11368          default:
11369            break;
11370        }
11371        continue;
11372      }
11373    switch (event.type)
11374    {
11375      case ButtonPress:
11376      {
11377        if (event.xbutton.button != Button1)
11378          break;
11379        if (event.xbutton.window != windows->image.id)
11380          break;
11381        /*
11382          Note first corner of region of interest rectangle-- exit loop.
11383        */
11384        (void) XCheckDefineCursor(display,windows->image.id,cursor);
11385        roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11386        roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11387        state|=ExitState;
11388        break;
11389      }
11390      case ButtonRelease:
11391        break;
11392      case Expose:
11393        break;
11394      case KeyPress:
11395      {
11396        KeySym
11397          key_symbol;
11398
11399        if (event.xkey.window != windows->image.id)
11400          break;
11401        /*
11402          Respond to a user key press.
11403        */
11404        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11405          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11406        switch ((int) key_symbol)
11407        {
11408          case XK_Escape:
11409          case XK_F20:
11410          {
11411            /*
11412              Prematurely exit.
11413            */
11414            state|=EscapeState;
11415            state|=ExitState;
11416            break;
11417          }
11418          case XK_F1:
11419          case XK_Help:
11420          {
11421            XTextViewWidget(display,resource_info,windows,MagickFalse,
11422              "Help Viewer - Region of Interest",ImageROIHelp);
11423            break;
11424          }
11425          default:
11426          {
11427            (void) XBell(display,0);
11428            break;
11429          }
11430        }
11431        break;
11432      }
11433      case MotionNotify:
11434      {
11435        /*
11436          Map and unmap Info widget as text cursor crosses its boundaries.
11437        */
11438        x=event.xmotion.x;
11439        y=event.xmotion.y;
11440        if (IfMagickTrue(windows->info.mapped) )
11441          {
11442            if ((x < (int) (windows->info.x+windows->info.width)) &&
11443                (y < (int) (windows->info.y+windows->info.height)))
11444              (void) XWithdrawWindow(display,windows->info.id,
11445                windows->info.screen);
11446          }
11447        else
11448          if ((x > (int) (windows->info.x+windows->info.width)) ||
11449              (y > (int) (windows->info.y+windows->info.height)))
11450            (void) XMapWindow(display,windows->info.id);
11451        roi_info.x=(ssize_t) windows->image.x+x;
11452        roi_info.y=(ssize_t) windows->image.y+y;
11453        break;
11454      }
11455      default:
11456        break;
11457    }
11458  } while ((state & ExitState) == 0);
11459  (void) XSelectInput(display,windows->image.id,
11460    windows->image.attributes.event_mask);
11461  if ((state & EscapeState) != 0)
11462    {
11463      /*
11464        User want to exit without region of interest.
11465      */
11466      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11467      (void) XFreeCursor(display,cursor);
11468      return(MagickTrue);
11469    }
11470  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11471  do
11472  {
11473    /*
11474      Size rectangle as pointer moves until the mouse button is released.
11475    */
11476    x=(int) roi_info.x;
11477    y=(int) roi_info.y;
11478    roi_info.width=0;
11479    roi_info.height=0;
11480    state=DefaultState;
11481    do
11482    {
11483      highlight_info=roi_info;
11484      highlight_info.x=roi_info.x-windows->image.x;
11485      highlight_info.y=roi_info.y-windows->image.y;
11486      if ((highlight_info.width > 3) && (highlight_info.height > 3))
11487        {
11488          /*
11489            Display info and draw region of interest rectangle.
11490          */
11491          if (IfMagickFalse(windows->info.mapped) )
11492            (void) XMapWindow(display,windows->info.id);
11493          (void) FormatLocaleString(text,MagickPathExtent,
11494            " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11495            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11496          XInfoWidget(display,windows,text);
11497          XHighlightRectangle(display,windows->image.id,
11498            windows->image.highlight_context,&highlight_info);
11499        }
11500      else
11501        if (IfMagickTrue(windows->info.mapped) )
11502          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11503      /*
11504        Wait for next event.
11505      */
11506      XScreenEvent(display,windows,&event,exception);
11507      if ((highlight_info.width > 3) && (highlight_info.height > 3))
11508        XHighlightRectangle(display,windows->image.id,
11509          windows->image.highlight_context,&highlight_info);
11510      switch (event.type)
11511      {
11512        case ButtonPress:
11513        {
11514          roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11515          roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11516          break;
11517        }
11518        case ButtonRelease:
11519        {
11520          /*
11521            User has committed to region of interest rectangle.
11522          */
11523          roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11524          roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11525          XSetCursorState(display,windows,MagickFalse);
11526          state|=ExitState;
11527          if (LocaleCompare(windows->command.name,"Apply") == 0)
11528            break;
11529          (void) CloneString(&windows->command.name,"Apply");
11530          windows->command.data=ApplyMenus;
11531          (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11532          break;
11533        }
11534        case Expose:
11535          break;
11536        case MotionNotify:
11537        {
11538          roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11539          roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11540        }
11541        default:
11542          break;
11543      }
11544      if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11545          ((state & ExitState) != 0))
11546        {
11547          /*
11548            Check boundary conditions.
11549          */
11550          if (roi_info.x < 0)
11551            roi_info.x=0;
11552          else
11553            if (roi_info.x > (ssize_t) windows->image.ximage->width)
11554              roi_info.x=(ssize_t) windows->image.ximage->width;
11555          if ((int) roi_info.x < x)
11556            roi_info.width=(unsigned int) (x-roi_info.x);
11557          else
11558            {
11559              roi_info.width=(unsigned int) (roi_info.x-x);
11560              roi_info.x=(ssize_t) x;
11561            }
11562          if (roi_info.y < 0)
11563            roi_info.y=0;
11564          else
11565            if (roi_info.y > (ssize_t) windows->image.ximage->height)
11566              roi_info.y=(ssize_t) windows->image.ximage->height;
11567          if ((int) roi_info.y < y)
11568            roi_info.height=(unsigned int) (y-roi_info.y);
11569          else
11570            {
11571              roi_info.height=(unsigned int) (roi_info.y-y);
11572              roi_info.y=(ssize_t) y;
11573            }
11574        }
11575    } while ((state & ExitState) == 0);
11576    /*
11577      Wait for user to grab a corner of the rectangle or press return.
11578    */
11579    state=DefaultState;
11580    command_type=NullCommand;
11581    crop_info.x=0;
11582    crop_info.y=0;
11583    (void) XMapWindow(display,windows->info.id);
11584    do
11585    {
11586      if (IfMagickTrue(windows->info.mapped) )
11587        {
11588          /*
11589            Display pointer position.
11590          */
11591          (void) FormatLocaleString(text,MagickPathExtent,
11592            " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11593            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11594          XInfoWidget(display,windows,text);
11595        }
11596      highlight_info=roi_info;
11597      highlight_info.x=roi_info.x-windows->image.x;
11598      highlight_info.y=roi_info.y-windows->image.y;
11599      if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11600        {
11601          state|=EscapeState;
11602          state|=ExitState;
11603          break;
11604        }
11605      if ((state & UpdateRegionState) != 0)
11606        {
11607          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11608          switch (command_type)
11609          {
11610            case UndoCommand:
11611            case RedoCommand:
11612            {
11613              (void) XMagickCommand(display,resource_info,windows,command_type,
11614                image,exception);
11615              break;
11616            }
11617            default:
11618            {
11619              /*
11620                Region of interest is relative to image configuration.
11621              */
11622              progress_monitor=SetImageProgressMonitor(*image,
11623                (MagickProgressMonitor) NULL,(*image)->client_data);
11624              crop_info=roi_info;
11625              width=(unsigned int) (*image)->columns;
11626              height=(unsigned int) (*image)->rows;
11627              x=0;
11628              y=0;
11629              if (windows->image.crop_geometry != (char *) NULL)
11630                (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11631                  &width,&height);
11632              scale_factor=(double) width/windows->image.ximage->width;
11633              crop_info.x+=x;
11634              crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11635              crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11636              scale_factor=(double)
11637                height/windows->image.ximage->height;
11638              crop_info.y+=y;
11639              crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11640              crop_info.height=(unsigned int)
11641                (scale_factor*crop_info.height+0.5);
11642              roi_image=CropImage(*image,&crop_info,exception);
11643              (void) SetImageProgressMonitor(*image,progress_monitor,
11644                (*image)->client_data);
11645              if (roi_image == (Image *) NULL)
11646                continue;
11647              /*
11648                Apply image processing technique to the region of interest.
11649              */
11650              windows->image.orphan=MagickTrue;
11651              (void) XMagickCommand(display,resource_info,windows,command_type,
11652                &roi_image,exception);
11653              progress_monitor=SetImageProgressMonitor(*image,
11654                (MagickProgressMonitor) NULL,(*image)->client_data);
11655              (void) XMagickCommand(display,resource_info,windows,
11656                SaveToUndoBufferCommand,image,exception);
11657              windows->image.orphan=MagickFalse;
11658              (void) CompositeImage(*image,roi_image,CopyCompositeOp,
11659                MagickTrue,crop_info.x,crop_info.y,exception);
11660              roi_image=DestroyImage(roi_image);
11661              (void) SetImageProgressMonitor(*image,progress_monitor,
11662                (*image)->client_data);
11663              break;
11664            }
11665          }
11666          if (command_type != InfoCommand)
11667            {
11668              XConfigureImageColormap(display,resource_info,windows,*image,
11669                exception);
11670              (void) XConfigureImage(display,resource_info,windows,*image,
11671                exception);
11672            }
11673          XCheckRefreshWindows(display,windows);
11674          XInfoWidget(display,windows,text);
11675          (void) XSetFunction(display,windows->image.highlight_context,
11676            GXinvert);
11677          state&=(~UpdateRegionState);
11678        }
11679      XHighlightRectangle(display,windows->image.id,
11680        windows->image.highlight_context,&highlight_info);
11681      XScreenEvent(display,windows,&event,exception);
11682      if (event.xany.window == windows->command.id)
11683        {
11684          /*
11685            Select a command from the Command widget.
11686          */
11687          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11688          command_type=NullCommand;
11689          id=XCommandWidget(display,windows,ApplyMenu,&event);
11690          if (id >= 0)
11691            {
11692              (void) CopyMagickString(command,ApplyMenu[id],MagickPathExtent);
11693              command_type=ApplyCommands[id];
11694              if (id < ApplyMenus)
11695                {
11696                  /*
11697                    Select a command from a pop-up menu.
11698                  */
11699                  entry=XMenuWidget(display,windows,ApplyMenu[id],
11700                    (const char **) Menus[id],command);
11701                  if (entry >= 0)
11702                    {
11703                      (void) CopyMagickString(command,Menus[id][entry],
11704                        MagickPathExtent);
11705                      command_type=Commands[id][entry];
11706                    }
11707                }
11708            }
11709          (void) XSetFunction(display,windows->image.highlight_context,
11710            GXinvert);
11711          XHighlightRectangle(display,windows->image.id,
11712            windows->image.highlight_context,&highlight_info);
11713          if (command_type == HelpCommand)
11714            {
11715              (void) XSetFunction(display,windows->image.highlight_context,
11716                GXcopy);
11717              XTextViewWidget(display,resource_info,windows,MagickFalse,
11718                "Help Viewer - Region of Interest",ImageROIHelp);
11719              (void) XSetFunction(display,windows->image.highlight_context,
11720                GXinvert);
11721              continue;
11722            }
11723          if (command_type == QuitCommand)
11724            {
11725              /*
11726                exit.
11727              */
11728              state|=EscapeState;
11729              state|=ExitState;
11730              continue;
11731            }
11732          if (command_type != NullCommand)
11733            state|=UpdateRegionState;
11734          continue;
11735        }
11736      XHighlightRectangle(display,windows->image.id,
11737        windows->image.highlight_context,&highlight_info);
11738      switch (event.type)
11739      {
11740        case ButtonPress:
11741        {
11742          x=windows->image.x;
11743          y=windows->image.y;
11744          if (event.xbutton.button != Button1)
11745            break;
11746          if (event.xbutton.window != windows->image.id)
11747            break;
11748          x=windows->image.x+event.xbutton.x;
11749          y=windows->image.y+event.xbutton.y;
11750          if ((x < (int) (roi_info.x+RoiDelta)) &&
11751              (x > (int) (roi_info.x-RoiDelta)) &&
11752              (y < (int) (roi_info.y+RoiDelta)) &&
11753              (y > (int) (roi_info.y-RoiDelta)))
11754            {
11755              roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11756              roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11757              state|=UpdateConfigurationState;
11758              break;
11759            }
11760          if ((x < (int) (roi_info.x+RoiDelta)) &&
11761              (x > (int) (roi_info.x-RoiDelta)) &&
11762              (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11763              (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11764            {
11765              roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11766              state|=UpdateConfigurationState;
11767              break;
11768            }
11769          if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11770              (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11771              (y < (int) (roi_info.y+RoiDelta)) &&
11772              (y > (int) (roi_info.y-RoiDelta)))
11773            {
11774              roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11775              state|=UpdateConfigurationState;
11776              break;
11777            }
11778          if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11779              (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11780              (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11781              (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11782            {
11783              state|=UpdateConfigurationState;
11784              break;
11785            }
11786        }
11787        case ButtonRelease:
11788        {
11789          if (event.xbutton.window == windows->pan.id)
11790            if ((highlight_info.x != crop_info.x-windows->image.x) ||
11791                (highlight_info.y != crop_info.y-windows->image.y))
11792              XHighlightRectangle(display,windows->image.id,
11793                windows->image.highlight_context,&highlight_info);
11794          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11795            event.xbutton.time);
11796          break;
11797        }
11798        case Expose:
11799        {
11800          if (event.xexpose.window == windows->image.id)
11801            if (event.xexpose.count == 0)
11802              {
11803                event.xexpose.x=(int) highlight_info.x;
11804                event.xexpose.y=(int) highlight_info.y;
11805                event.xexpose.width=(int) highlight_info.width;
11806                event.xexpose.height=(int) highlight_info.height;
11807                XRefreshWindow(display,&windows->image,&event);
11808              }
11809          if (event.xexpose.window == windows->info.id)
11810            if (event.xexpose.count == 0)
11811              XInfoWidget(display,windows,text);
11812          break;
11813        }
11814        case KeyPress:
11815        {
11816          KeySym
11817            key_symbol;
11818
11819          if (event.xkey.window != windows->image.id)
11820            break;
11821          /*
11822            Respond to a user key press.
11823          */
11824          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11825            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11826          switch ((int) key_symbol)
11827          {
11828            case XK_Shift_L:
11829            case XK_Shift_R:
11830              break;
11831            case XK_Escape:
11832            case XK_F20:
11833              state|=EscapeState;
11834            case XK_Return:
11835            {
11836              state|=ExitState;
11837              break;
11838            }
11839            case XK_Home:
11840            case XK_KP_Home:
11841            {
11842              roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11843              roi_info.y=(ssize_t) (windows->image.height/2L-
11844                roi_info.height/2L);
11845              break;
11846            }
11847            case XK_Left:
11848            case XK_KP_Left:
11849            {
11850              roi_info.x--;
11851              break;
11852            }
11853            case XK_Up:
11854            case XK_KP_Up:
11855            case XK_Next:
11856            {
11857              roi_info.y--;
11858              break;
11859            }
11860            case XK_Right:
11861            case XK_KP_Right:
11862            {
11863              roi_info.x++;
11864              break;
11865            }
11866            case XK_Prior:
11867            case XK_Down:
11868            case XK_KP_Down:
11869            {
11870              roi_info.y++;
11871              break;
11872            }
11873            case XK_F1:
11874            case XK_Help:
11875            {
11876              (void) XSetFunction(display,windows->image.highlight_context,
11877                GXcopy);
11878              XTextViewWidget(display,resource_info,windows,MagickFalse,
11879                "Help Viewer - Region of Interest",ImageROIHelp);
11880              (void) XSetFunction(display,windows->image.highlight_context,
11881                GXinvert);
11882              break;
11883            }
11884            default:
11885            {
11886              command_type=XImageWindowCommand(display,resource_info,windows,
11887                event.xkey.state,key_symbol,image,exception);
11888              if (command_type != NullCommand)
11889                state|=UpdateRegionState;
11890              break;
11891            }
11892          }
11893          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11894            event.xkey.time);
11895          break;
11896        }
11897        case KeyRelease:
11898          break;
11899        case MotionNotify:
11900        {
11901          if (event.xbutton.window != windows->image.id)
11902            break;
11903          /*
11904            Map and unmap Info widget as text cursor crosses its boundaries.
11905          */
11906          x=event.xmotion.x;
11907          y=event.xmotion.y;
11908          if (IfMagickTrue(windows->info.mapped) )
11909            {
11910              if ((x < (int) (windows->info.x+windows->info.width)) &&
11911                  (y < (int) (windows->info.y+windows->info.height)))
11912                (void) XWithdrawWindow(display,windows->info.id,
11913                  windows->info.screen);
11914            }
11915          else
11916            if ((x > (int) (windows->info.x+windows->info.width)) ||
11917                (y > (int) (windows->info.y+windows->info.height)))
11918              (void) XMapWindow(display,windows->info.id);
11919          roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11920          roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11921          break;
11922        }
11923        case SelectionRequest:
11924        {
11925          XSelectionEvent
11926            notify;
11927
11928          XSelectionRequestEvent
11929            *request;
11930
11931          /*
11932            Set primary selection.
11933          */
11934          (void) FormatLocaleString(text,MagickPathExtent,
11935            "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11936            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11937          request=(&(event.xselectionrequest));
11938          (void) XChangeProperty(request->display,request->requestor,
11939            request->property,request->target,8,PropModeReplace,
11940            (unsigned char *) text,(int) strlen(text));
11941          notify.type=SelectionNotify;
11942          notify.display=request->display;
11943          notify.requestor=request->requestor;
11944          notify.selection=request->selection;
11945          notify.target=request->target;
11946          notify.time=request->time;
11947          if (request->property == None)
11948            notify.property=request->target;
11949          else
11950            notify.property=request->property;
11951          (void) XSendEvent(request->display,request->requestor,False,0,
11952            (XEvent *) &notify);
11953        }
11954        default:
11955          break;
11956      }
11957      if ((state & UpdateConfigurationState) != 0)
11958        {
11959          (void) XPutBackEvent(display,&event);
11960          (void) XCheckDefineCursor(display,windows->image.id,cursor);
11961          break;
11962        }
11963    } while ((state & ExitState) == 0);
11964  } while ((state & ExitState) == 0);
11965  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11966  XSetCursorState(display,windows,MagickFalse);
11967  if ((state & EscapeState) != 0)
11968    return(MagickTrue);
11969  return(MagickTrue);
11970}
11971
11972/*
11973%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11974%                                                                             %
11975%                                                                             %
11976%                                                                             %
11977+   X R o t a t e I m a g e                                                   %
11978%                                                                             %
11979%                                                                             %
11980%                                                                             %
11981%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11982%
11983%  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
11984%  rotation angle is computed from the slope of a line drawn by the user.
11985%
11986%  The format of the XRotateImage method is:
11987%
11988%      MagickBooleanType XRotateImage(Display *display,
11989%        XResourceInfo *resource_info,XWindows *windows,double degrees,
11990%        Image **image,ExceptionInfo *exception)
11991%
11992%  A description of each parameter follows:
11993%
11994%    o display: Specifies a connection to an X server; returned from
11995%      XOpenDisplay.
11996%
11997%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11998%
11999%    o windows: Specifies a pointer to a XWindows structure.
12000%
12001%    o degrees: Specifies the number of degrees to rotate the image.
12002%
12003%    o image: the image.
12004%
12005%    o exception: return any errors or warnings in this structure.
12006%
12007*/
12008static MagickBooleanType XRotateImage(Display *display,
12009  XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
12010  ExceptionInfo *exception)
12011{
12012  static const char
12013    *RotateMenu[] =
12014    {
12015      "Pixel Color",
12016      "Direction",
12017      "Help",
12018      "Dismiss",
12019      (char *) NULL
12020    };
12021
12022  static ModeType
12023    direction = HorizontalRotateCommand;
12024
12025  static const ModeType
12026    DirectionCommands[] =
12027    {
12028      HorizontalRotateCommand,
12029      VerticalRotateCommand
12030    },
12031    RotateCommands[] =
12032    {
12033      RotateColorCommand,
12034      RotateDirectionCommand,
12035      RotateHelpCommand,
12036      RotateDismissCommand
12037    };
12038
12039  static unsigned int
12040    pen_id = 0;
12041
12042  char
12043    command[MagickPathExtent],
12044    text[MagickPathExtent];
12045
12046  Image
12047    *rotate_image;
12048
12049  int
12050    id,
12051    x,
12052    y;
12053
12054  double
12055    normalized_degrees;
12056
12057  register int
12058    i;
12059
12060  unsigned int
12061    height,
12062    rotations,
12063    width;
12064
12065  if (degrees == 0.0)
12066    {
12067      unsigned int
12068        distance;
12069
12070      size_t
12071        state;
12072
12073      XEvent
12074        event;
12075
12076      XSegment
12077        rotate_info;
12078
12079      /*
12080        Map Command widget.
12081      */
12082      (void) CloneString(&windows->command.name,"Rotate");
12083      windows->command.data=2;
12084      (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12085      (void) XMapRaised(display,windows->command.id);
12086      XClientMessage(display,windows->image.id,windows->im_protocols,
12087        windows->im_update_widget,CurrentTime);
12088      /*
12089        Wait for first button press.
12090      */
12091      (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12092      XQueryPosition(display,windows->image.id,&x,&y);
12093      rotate_info.x1=x;
12094      rotate_info.y1=y;
12095      rotate_info.x2=x;
12096      rotate_info.y2=y;
12097      state=DefaultState;
12098      do
12099      {
12100        XHighlightLine(display,windows->image.id,
12101          windows->image.highlight_context,&rotate_info);
12102        /*
12103          Wait for next event.
12104        */
12105        XScreenEvent(display,windows,&event,exception);
12106        XHighlightLine(display,windows->image.id,
12107          windows->image.highlight_context,&rotate_info);
12108        if (event.xany.window == windows->command.id)
12109          {
12110            /*
12111              Select a command from the Command widget.
12112            */
12113            id=XCommandWidget(display,windows,RotateMenu,&event);
12114            if (id < 0)
12115              continue;
12116            (void) XSetFunction(display,windows->image.highlight_context,
12117              GXcopy);
12118            switch (RotateCommands[id])
12119            {
12120              case RotateColorCommand:
12121              {
12122                const char
12123                  *ColorMenu[MaxNumberPens];
12124
12125                int
12126                  pen_number;
12127
12128                XColor
12129                  color;
12130
12131                /*
12132                  Initialize menu selections.
12133                */
12134                for (i=0; i < (int) (MaxNumberPens-2); i++)
12135                  ColorMenu[i]=resource_info->pen_colors[i];
12136                ColorMenu[MaxNumberPens-2]="Browser...";
12137                ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12138                /*
12139                  Select a pen color from the pop-up menu.
12140                */
12141                pen_number=XMenuWidget(display,windows,RotateMenu[id],
12142                  (const char **) ColorMenu,command);
12143                if (pen_number < 0)
12144                  break;
12145                if (pen_number == (MaxNumberPens-2))
12146                  {
12147                    static char
12148                      color_name[MagickPathExtent] = "gray";
12149
12150                    /*
12151                      Select a pen color from a dialog.
12152                    */
12153                    resource_info->pen_colors[pen_number]=color_name;
12154                    XColorBrowserWidget(display,windows,"Select",color_name);
12155                    if (*color_name == '\0')
12156                      break;
12157                  }
12158                /*
12159                  Set pen color.
12160                */
12161                (void) XParseColor(display,windows->map_info->colormap,
12162                  resource_info->pen_colors[pen_number],&color);
12163                XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12164                  (unsigned int) MaxColors,&color);
12165                windows->pixel_info->pen_colors[pen_number]=color;
12166                pen_id=(unsigned int) pen_number;
12167                break;
12168              }
12169              case RotateDirectionCommand:
12170              {
12171                static const char
12172                  *Directions[] =
12173                  {
12174                    "horizontal",
12175                    "vertical",
12176                    (char *) NULL,
12177                  };
12178
12179                /*
12180                  Select a command from the pop-up menu.
12181                */
12182                id=XMenuWidget(display,windows,RotateMenu[id],
12183                  Directions,command);
12184                if (id >= 0)
12185                  direction=DirectionCommands[id];
12186                break;
12187              }
12188              case RotateHelpCommand:
12189              {
12190                XTextViewWidget(display,resource_info,windows,MagickFalse,
12191                  "Help Viewer - Image Rotation",ImageRotateHelp);
12192                break;
12193              }
12194              case RotateDismissCommand:
12195              {
12196                /*
12197                  Prematurely exit.
12198                */
12199                state|=EscapeState;
12200                state|=ExitState;
12201                break;
12202              }
12203              default:
12204                break;
12205            }
12206            (void) XSetFunction(display,windows->image.highlight_context,
12207              GXinvert);
12208            continue;
12209          }
12210        switch (event.type)
12211        {
12212          case ButtonPress:
12213          {
12214            if (event.xbutton.button != Button1)
12215              break;
12216            if (event.xbutton.window != windows->image.id)
12217              break;
12218            /*
12219              exit loop.
12220            */
12221            (void) XSetFunction(display,windows->image.highlight_context,
12222              GXcopy);
12223            rotate_info.x1=event.xbutton.x;
12224            rotate_info.y1=event.xbutton.y;
12225            state|=ExitState;
12226            break;
12227          }
12228          case ButtonRelease:
12229            break;
12230          case Expose:
12231            break;
12232          case KeyPress:
12233          {
12234            char
12235              command[MagickPathExtent];
12236
12237            KeySym
12238              key_symbol;
12239
12240            if (event.xkey.window != windows->image.id)
12241              break;
12242            /*
12243              Respond to a user key press.
12244            */
12245            (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12246              sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12247            switch ((int) key_symbol)
12248            {
12249              case XK_Escape:
12250              case XK_F20:
12251              {
12252                /*
12253                  Prematurely exit.
12254                */
12255                state|=EscapeState;
12256                state|=ExitState;
12257                break;
12258              }
12259              case XK_F1:
12260              case XK_Help:
12261              {
12262                (void) XSetFunction(display,windows->image.highlight_context,
12263                  GXcopy);
12264                XTextViewWidget(display,resource_info,windows,MagickFalse,
12265                  "Help Viewer - Image Rotation",ImageRotateHelp);
12266                (void) XSetFunction(display,windows->image.highlight_context,
12267                  GXinvert);
12268                break;
12269              }
12270              default:
12271              {
12272                (void) XBell(display,0);
12273                break;
12274              }
12275            }
12276            break;
12277          }
12278          case MotionNotify:
12279          {
12280            rotate_info.x1=event.xmotion.x;
12281            rotate_info.y1=event.xmotion.y;
12282          }
12283        }
12284        rotate_info.x2=rotate_info.x1;
12285        rotate_info.y2=rotate_info.y1;
12286        if (direction == HorizontalRotateCommand)
12287          rotate_info.x2+=32;
12288        else
12289          rotate_info.y2-=32;
12290      } while ((state & ExitState) == 0);
12291      (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12292      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12293      if ((state & EscapeState) != 0)
12294        return(MagickTrue);
12295      /*
12296        Draw line as pointer moves until the mouse button is released.
12297      */
12298      distance=0;
12299      (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12300      state=DefaultState;
12301      do
12302      {
12303        if (distance > 9)
12304          {
12305            /*
12306              Display info and draw rotation line.
12307            */
12308            if (IfMagickFalse(windows->info.mapped) )
12309              (void) XMapWindow(display,windows->info.id);
12310            (void) FormatLocaleString(text,MagickPathExtent," %g",
12311              direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12312            XInfoWidget(display,windows,text);
12313            XHighlightLine(display,windows->image.id,
12314              windows->image.highlight_context,&rotate_info);
12315          }
12316        else
12317          if (IfMagickTrue(windows->info.mapped) )
12318            (void) XWithdrawWindow(display,windows->info.id,
12319              windows->info.screen);
12320        /*
12321          Wait for next event.
12322        */
12323        XScreenEvent(display,windows,&event,exception);
12324        if (distance > 9)
12325          XHighlightLine(display,windows->image.id,
12326            windows->image.highlight_context,&rotate_info);
12327        switch (event.type)
12328        {
12329          case ButtonPress:
12330            break;
12331          case ButtonRelease:
12332          {
12333            /*
12334              User has committed to rotation line.
12335            */
12336            rotate_info.x2=event.xbutton.x;
12337            rotate_info.y2=event.xbutton.y;
12338            state|=ExitState;
12339            break;
12340          }
12341          case Expose:
12342            break;
12343          case MotionNotify:
12344          {
12345            rotate_info.x2=event.xmotion.x;
12346            rotate_info.y2=event.xmotion.y;
12347          }
12348          default:
12349            break;
12350        }
12351        /*
12352          Check boundary conditions.
12353        */
12354        if (rotate_info.x2 < 0)
12355          rotate_info.x2=0;
12356        else
12357          if (rotate_info.x2 > (int) windows->image.width)
12358            rotate_info.x2=(short) windows->image.width;
12359        if (rotate_info.y2 < 0)
12360          rotate_info.y2=0;
12361        else
12362          if (rotate_info.y2 > (int) windows->image.height)
12363            rotate_info.y2=(short) windows->image.height;
12364        /*
12365          Compute rotation angle from the slope of the line.
12366        */
12367        degrees=0.0;
12368        distance=(unsigned int)
12369          ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12370          ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12371        if (distance > 9)
12372          degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12373            rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12374      } while ((state & ExitState) == 0);
12375      (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12376      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12377      if (distance <= 9)
12378        return(MagickTrue);
12379    }
12380  if (direction == VerticalRotateCommand)
12381    degrees-=90.0;
12382  if (degrees == 0.0)
12383    return(MagickTrue);
12384  /*
12385    Rotate image.
12386  */
12387  normalized_degrees=degrees;
12388  while (normalized_degrees < -45.0)
12389    normalized_degrees+=360.0;
12390  for (rotations=0; normalized_degrees > 45.0; rotations++)
12391    normalized_degrees-=90.0;
12392  if (normalized_degrees != 0.0)
12393    (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12394      exception);
12395  XSetCursorState(display,windows,MagickTrue);
12396  XCheckRefreshWindows(display,windows);
12397  (*image)->background_color.red=(double) ScaleShortToQuantum(
12398    windows->pixel_info->pen_colors[pen_id].red);
12399  (*image)->background_color.green=(double) ScaleShortToQuantum(
12400    windows->pixel_info->pen_colors[pen_id].green);
12401  (*image)->background_color.blue=(double) ScaleShortToQuantum(
12402    windows->pixel_info->pen_colors[pen_id].blue);
12403  rotate_image=RotateImage(*image,degrees,exception);
12404  XSetCursorState(display,windows,MagickFalse);
12405  if (rotate_image == (Image *) NULL)
12406    return(MagickFalse);
12407  *image=DestroyImage(*image);
12408  *image=rotate_image;
12409  if (windows->image.crop_geometry != (char *) NULL)
12410    {
12411      /*
12412        Rotate crop geometry.
12413      */
12414      width=(unsigned int) (*image)->columns;
12415      height=(unsigned int) (*image)->rows;
12416      (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12417      switch (rotations % 4)
12418      {
12419        default:
12420        case 0:
12421          break;
12422        case 1:
12423        {
12424          /*
12425            Rotate 90 degrees.
12426          */
12427          (void) FormatLocaleString(windows->image.crop_geometry,MagickPathExtent,
12428            "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12429            (int) height-y,x);
12430          break;
12431        }
12432        case 2:
12433        {
12434          /*
12435            Rotate 180 degrees.
12436          */
12437          (void) FormatLocaleString(windows->image.crop_geometry,MagickPathExtent,
12438            "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12439          break;
12440        }
12441        case 3:
12442        {
12443          /*
12444            Rotate 270 degrees.
12445          */
12446          (void) FormatLocaleString(windows->image.crop_geometry,MagickPathExtent,
12447            "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12448          break;
12449        }
12450      }
12451    }
12452  if (IfMagickTrue(windows->image.orphan) )
12453    return(MagickTrue);
12454  if (normalized_degrees != 0.0)
12455    {
12456      /*
12457        Update image colormap.
12458      */
12459      windows->image.window_changes.width=(int) (*image)->columns;
12460      windows->image.window_changes.height=(int) (*image)->rows;
12461      if (windows->image.crop_geometry != (char *) NULL)
12462        {
12463          /*
12464            Obtain dimensions of image from crop geometry.
12465          */
12466          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12467            &width,&height);
12468          windows->image.window_changes.width=(int) width;
12469          windows->image.window_changes.height=(int) height;
12470        }
12471      XConfigureImageColormap(display,resource_info,windows,*image,exception);
12472    }
12473  else
12474    if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12475      {
12476        windows->image.window_changes.width=windows->image.ximage->height;
12477        windows->image.window_changes.height=windows->image.ximage->width;
12478      }
12479  /*
12480    Update image configuration.
12481  */
12482  (void) XConfigureImage(display,resource_info,windows,*image,exception);
12483  return(MagickTrue);
12484}
12485
12486/*
12487%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12488%                                                                             %
12489%                                                                             %
12490%                                                                             %
12491+   X S a v e I m a g e                                                       %
12492%                                                                             %
12493%                                                                             %
12494%                                                                             %
12495%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12496%
12497%  XSaveImage() saves an image to a file.
12498%
12499%  The format of the XSaveImage method is:
12500%
12501%      MagickBooleanType XSaveImage(Display *display,
12502%        XResourceInfo *resource_info,XWindows *windows,Image *image,
12503%        ExceptionInfo *exception)
12504%
12505%  A description of each parameter follows:
12506%
12507%    o display: Specifies a connection to an X server; returned from
12508%      XOpenDisplay.
12509%
12510%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12511%
12512%    o windows: Specifies a pointer to a XWindows structure.
12513%
12514%    o image: the image.
12515%
12516%    o exception: return any errors or warnings in this structure.
12517%
12518*/
12519static MagickBooleanType XSaveImage(Display *display,
12520  XResourceInfo *resource_info,XWindows *windows,Image *image,
12521  ExceptionInfo *exception)
12522{
12523  char
12524    filename[MagickPathExtent],
12525    geometry[MagickPathExtent];
12526
12527  Image
12528    *save_image;
12529
12530  ImageInfo
12531    *image_info;
12532
12533  MagickStatusType
12534    status;
12535
12536  /*
12537    Request file name from user.
12538  */
12539  if (resource_info->write_filename != (char *) NULL)
12540    (void) CopyMagickString(filename,resource_info->write_filename,
12541      MagickPathExtent);
12542  else
12543    {
12544      char
12545        path[MagickPathExtent];
12546
12547      int
12548        status;
12549
12550      GetPathComponent(image->filename,HeadPath,path);
12551      GetPathComponent(image->filename,TailPath,filename);
12552      if (*path != '\0')
12553        {
12554          status=chdir(path);
12555          if (status == -1)
12556            (void) ThrowMagickException(exception,GetMagickModule(),
12557              FileOpenError,"UnableToOpenFile","%s",path);
12558        }
12559    }
12560  XFileBrowserWidget(display,windows,"Save",filename);
12561  if (*filename == '\0')
12562    return(MagickTrue);
12563  if (IfMagickTrue(IsPathAccessible(filename)) )
12564    {
12565      int
12566        status;
12567
12568      /*
12569        File exists-- seek user's permission before overwriting.
12570      */
12571      status=XConfirmWidget(display,windows,"Overwrite",filename);
12572      if (status <= 0)
12573        return(MagickTrue);
12574    }
12575  image_info=CloneImageInfo(resource_info->image_info);
12576  (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
12577  (void) SetImageInfo(image_info,1,exception);
12578  if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12579      (LocaleCompare(image_info->magick,"JPG") == 0))
12580    {
12581      char
12582        quality[MagickPathExtent];
12583
12584      int
12585        status;
12586
12587      /*
12588        Request JPEG quality from user.
12589      */
12590      (void) FormatLocaleString(quality,MagickPathExtent,"%.20g",(double)
12591        image->quality);
12592      status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12593        quality);
12594      if (*quality == '\0')
12595        return(MagickTrue);
12596      image->quality=StringToUnsignedLong(quality);
12597      image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12598    }
12599  if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12600      (LocaleCompare(image_info->magick,"PDF") == 0) ||
12601      (LocaleCompare(image_info->magick,"PS") == 0) ||
12602      (LocaleCompare(image_info->magick,"PS2") == 0))
12603    {
12604      char
12605        geometry[MagickPathExtent];
12606
12607      /*
12608        Request page geometry from user.
12609      */
12610      (void) CopyMagickString(geometry,PSPageGeometry,MagickPathExtent);
12611      if (LocaleCompare(image_info->magick,"PDF") == 0)
12612        (void) CopyMagickString(geometry,PSPageGeometry,MagickPathExtent);
12613      if (image_info->page != (char *) NULL)
12614        (void) CopyMagickString(geometry,image_info->page,MagickPathExtent);
12615      XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12616        "Select page geometry:",geometry);
12617      if (*geometry != '\0')
12618        image_info->page=GetPageGeometry(geometry);
12619    }
12620  /*
12621    Apply image transforms.
12622  */
12623  XSetCursorState(display,windows,MagickTrue);
12624  XCheckRefreshWindows(display,windows);
12625  save_image=CloneImage(image,0,0,MagickTrue,exception);
12626  if (save_image == (Image *) NULL)
12627    return(MagickFalse);
12628  (void) FormatLocaleString(geometry,MagickPathExtent,"%dx%d!",
12629    windows->image.ximage->width,windows->image.ximage->height);
12630  (void) TransformImage(&save_image,windows->image.crop_geometry,geometry,
12631    exception);
12632  /*
12633    Write image.
12634  */
12635  (void) CopyMagickString(save_image->filename,filename,MagickPathExtent);
12636  status=WriteImage(image_info,save_image,exception);
12637  if (IfMagickTrue(status) )
12638    image->taint=MagickFalse;
12639  save_image=DestroyImage(save_image);
12640  image_info=DestroyImageInfo(image_info);
12641  XSetCursorState(display,windows,MagickFalse);
12642  return(status != 0 ? MagickTrue : MagickFalse);
12643}
12644
12645/*
12646%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12647%                                                                             %
12648%                                                                             %
12649%                                                                             %
12650+   X S c r e e n E v e n t                                                   %
12651%                                                                             %
12652%                                                                             %
12653%                                                                             %
12654%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12655%
12656%  XScreenEvent() handles global events associated with the Pan and Magnify
12657%  windows.
12658%
12659%  The format of the XScreenEvent function is:
12660%
12661%      void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12662%        ExceptionInfo *exception)
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%    o exception: return any errors or warnings in this structure.
12674%
12675*/
12676
12677#if defined(__cplusplus) || defined(c_plusplus)
12678extern "C" {
12679#endif
12680
12681static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12682{
12683  register XWindows
12684    *windows;
12685
12686  windows=(XWindows *) data;
12687  if ((event->type == ClientMessage) &&
12688      (event->xclient.window == windows->image.id))
12689    return(MagickFalse);
12690  return(MagickTrue);
12691}
12692
12693#if defined(__cplusplus) || defined(c_plusplus)
12694}
12695#endif
12696
12697static void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12698  ExceptionInfo *exception)
12699{
12700  register int
12701    x,
12702    y;
12703
12704  (void) XIfEvent(display,event,XPredicate,(char *) windows);
12705  if (event->xany.window == windows->command.id)
12706    return;
12707  switch (event->type)
12708  {
12709    case ButtonPress:
12710    case ButtonRelease:
12711    {
12712      if ((event->xbutton.button == Button3) &&
12713          (event->xbutton.state & Mod1Mask))
12714        {
12715          /*
12716            Convert Alt-Button3 to Button2.
12717          */
12718          event->xbutton.button=Button2;
12719          event->xbutton.state&=(~Mod1Mask);
12720        }
12721      if (event->xbutton.window == windows->backdrop.id)
12722        {
12723          (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12724            event->xbutton.time);
12725          break;
12726        }
12727      if (event->xbutton.window == windows->pan.id)
12728        {
12729          XPanImage(display,windows,event,exception);
12730          break;
12731        }
12732      if (event->xbutton.window == windows->image.id)
12733        if (event->xbutton.button == Button2)
12734          {
12735            /*
12736              Update magnified image.
12737            */
12738            x=event->xbutton.x;
12739            y=event->xbutton.y;
12740            if (x < 0)
12741              x=0;
12742            else
12743              if (x >= (int) windows->image.width)
12744                x=(int) (windows->image.width-1);
12745            windows->magnify.x=(int) windows->image.x+x;
12746            if (y < 0)
12747              y=0;
12748            else
12749             if (y >= (int) windows->image.height)
12750               y=(int) (windows->image.height-1);
12751            windows->magnify.y=windows->image.y+y;
12752            if (IfMagickFalse(windows->magnify.mapped) )
12753              (void) XMapRaised(display,windows->magnify.id);
12754            XMakeMagnifyImage(display,windows,exception);
12755            if (event->type == ButtonRelease)
12756              (void) XWithdrawWindow(display,windows->info.id,
12757                windows->info.screen);
12758            break;
12759          }
12760      break;
12761    }
12762    case ClientMessage:
12763    {
12764      /*
12765        If client window delete message, exit.
12766      */
12767      if (event->xclient.message_type != windows->wm_protocols)
12768        break;
12769      if (*event->xclient.data.l != (long) windows->wm_delete_window)
12770        break;
12771      if (event->xclient.window == windows->magnify.id)
12772        {
12773          (void) XWithdrawWindow(display,windows->magnify.id,
12774            windows->magnify.screen);
12775          break;
12776        }
12777      break;
12778    }
12779    case ConfigureNotify:
12780    {
12781      if (event->xconfigure.window == windows->magnify.id)
12782        {
12783          unsigned int
12784            magnify;
12785
12786          /*
12787            Magnify window has a new configuration.
12788          */
12789          windows->magnify.width=(unsigned int) event->xconfigure.width;
12790          windows->magnify.height=(unsigned int) event->xconfigure.height;
12791          if (IfMagickFalse(windows->magnify.mapped) )
12792            break;
12793          magnify=1;
12794          while ((int) magnify <= event->xconfigure.width)
12795            magnify<<=1;
12796          while ((int) magnify <= event->xconfigure.height)
12797            magnify<<=1;
12798          magnify>>=1;
12799          if (((int) magnify != event->xconfigure.width) ||
12800              ((int) magnify != event->xconfigure.height))
12801            {
12802              XWindowChanges
12803                window_changes;
12804
12805              window_changes.width=(int) magnify;
12806              window_changes.height=(int) magnify;
12807              (void) XReconfigureWMWindow(display,windows->magnify.id,
12808                windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12809                &window_changes);
12810              break;
12811            }
12812          XMakeMagnifyImage(display,windows,exception);
12813          break;
12814        }
12815      break;
12816    }
12817    case Expose:
12818    {
12819      if (event->xexpose.window == windows->image.id)
12820        {
12821          XRefreshWindow(display,&windows->image,event);
12822          break;
12823        }
12824      if (event->xexpose.window == windows->pan.id)
12825        if (event->xexpose.count == 0)
12826          {
12827            XDrawPanRectangle(display,windows);
12828            break;
12829          }
12830      if (event->xexpose.window == windows->magnify.id)
12831        if (event->xexpose.count == 0)
12832          {
12833            XMakeMagnifyImage(display,windows,exception);
12834            break;
12835          }
12836      break;
12837    }
12838    case KeyPress:
12839    {
12840      char
12841        command[MagickPathExtent];
12842
12843      KeySym
12844        key_symbol;
12845
12846      if (event->xkey.window != windows->magnify.id)
12847        break;
12848      /*
12849        Respond to a user key press.
12850      */
12851      (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12852        sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12853      XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol,
12854        exception);
12855      break;
12856    }
12857    case MapNotify:
12858    {
12859      if (event->xmap.window == windows->magnify.id)
12860        {
12861          windows->magnify.mapped=MagickTrue;
12862          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12863          break;
12864        }
12865      if (event->xmap.window == windows->info.id)
12866        {
12867          windows->info.mapped=MagickTrue;
12868          break;
12869        }
12870      break;
12871    }
12872    case MotionNotify:
12873    {
12874      while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12875      if (event->xmotion.window == windows->image.id)
12876        if (IfMagickTrue(windows->magnify.mapped) )
12877          {
12878            /*
12879              Update magnified image.
12880            */
12881            x=event->xmotion.x;
12882            y=event->xmotion.y;
12883            if (x < 0)
12884              x=0;
12885            else
12886              if (x >= (int) windows->image.width)
12887                x=(int) (windows->image.width-1);
12888            windows->magnify.x=(int) windows->image.x+x;
12889            if (y < 0)
12890              y=0;
12891            else
12892             if (y >= (int) windows->image.height)
12893               y=(int) (windows->image.height-1);
12894            windows->magnify.y=windows->image.y+y;
12895            XMakeMagnifyImage(display,windows,exception);
12896          }
12897      break;
12898    }
12899    case UnmapNotify:
12900    {
12901      if (event->xunmap.window == windows->magnify.id)
12902        {
12903          windows->magnify.mapped=MagickFalse;
12904          break;
12905        }
12906      if (event->xunmap.window == windows->info.id)
12907        {
12908          windows->info.mapped=MagickFalse;
12909          break;
12910        }
12911      break;
12912    }
12913    default:
12914      break;
12915  }
12916}
12917
12918/*
12919%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12920%                                                                             %
12921%                                                                             %
12922%                                                                             %
12923+   X S e t C r o p G e o m e t r y                                           %
12924%                                                                             %
12925%                                                                             %
12926%                                                                             %
12927%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12928%
12929%  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12930%  and translates it to a cropping geometry relative to the image.
12931%
12932%  The format of the XSetCropGeometry method is:
12933%
12934%      void XSetCropGeometry(Display *display,XWindows *windows,
12935%        RectangleInfo *crop_info,Image *image)
12936%
12937%  A description of each parameter follows:
12938%
12939%    o display: Specifies a connection to an X server; returned from
12940%      XOpenDisplay.
12941%
12942%    o windows: Specifies a pointer to a XWindows structure.
12943%
12944%    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12945%      Image window to crop.
12946%
12947%    o image: the image.
12948%
12949*/
12950static void XSetCropGeometry(Display *display,XWindows *windows,
12951  RectangleInfo *crop_info,Image *image)
12952{
12953  char
12954    text[MagickPathExtent];
12955
12956  int
12957    x,
12958    y;
12959
12960  double
12961    scale_factor;
12962
12963  unsigned int
12964    height,
12965    width;
12966
12967  if (IfMagickTrue(windows->info.mapped) )
12968    {
12969      /*
12970        Display info on cropping rectangle.
12971      */
12972      (void) FormatLocaleString(text,MagickPathExtent," %.20gx%.20g%+.20g%+.20g",
12973        (double) crop_info->width,(double) crop_info->height,(double)
12974        crop_info->x,(double) crop_info->y);
12975      XInfoWidget(display,windows,text);
12976    }
12977  /*
12978    Cropping geometry is relative to any previous crop geometry.
12979  */
12980  x=0;
12981  y=0;
12982  width=(unsigned int) image->columns;
12983  height=(unsigned int) image->rows;
12984  if (windows->image.crop_geometry != (char *) NULL)
12985    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12986  else
12987    windows->image.crop_geometry=AcquireString((char *) NULL);
12988  /*
12989    Define the crop geometry string from the cropping rectangle.
12990  */
12991  scale_factor=(double) width/windows->image.ximage->width;
12992  if (crop_info->x > 0)
12993    x+=(int) (scale_factor*crop_info->x+0.5);
12994  width=(unsigned int) (scale_factor*crop_info->width+0.5);
12995  if (width == 0)
12996    width=1;
12997  scale_factor=(double) height/windows->image.ximage->height;
12998  if (crop_info->y > 0)
12999    y+=(int) (scale_factor*crop_info->y+0.5);
13000  height=(unsigned int) (scale_factor*crop_info->height+0.5);
13001  if (height == 0)
13002    height=1;
13003  (void) FormatLocaleString(windows->image.crop_geometry,MagickPathExtent,
13004    "%ux%u%+d%+d",width,height,x,y);
13005}
13006
13007/*
13008%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13009%                                                                             %
13010%                                                                             %
13011%                                                                             %
13012+   X T i l e I m a g e                                                       %
13013%                                                                             %
13014%                                                                             %
13015%                                                                             %
13016%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13017%
13018%  XTileImage() loads or deletes a selected tile from a visual image directory.
13019%  The load or delete command is chosen from a menu.
13020%
13021%  The format of the XTileImage method is:
13022%
13023%      Image *XTileImage(Display *display,XResourceInfo *resource_info,
13024%        XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13025%
13026%  A description of each parameter follows:
13027%
13028%    o tile_image:  XTileImage reads or deletes the tile image
13029%      and returns it.  A null image is returned if an error occurs.
13030%
13031%    o display: Specifies a connection to an X server;  returned from
13032%      XOpenDisplay.
13033%
13034%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13035%
13036%    o windows: Specifies a pointer to a XWindows structure.
13037%
13038%    o image: the image; returned from ReadImage.
13039%
13040%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
13041%      the entire image is refreshed.
13042%
13043%    o exception: return any errors or warnings in this structure.
13044%
13045*/
13046static Image *XTileImage(Display *display,XResourceInfo *resource_info,
13047  XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13048{
13049  static const char
13050    *VerbMenu[] =
13051    {
13052      "Load",
13053      "Next",
13054      "Former",
13055      "Delete",
13056      "Update",
13057      (char *) NULL,
13058    };
13059
13060  static const ModeType
13061    TileCommands[] =
13062    {
13063      TileLoadCommand,
13064      TileNextCommand,
13065      TileFormerCommand,
13066      TileDeleteCommand,
13067      TileUpdateCommand
13068    };
13069
13070  char
13071    command[MagickPathExtent],
13072    filename[MagickPathExtent];
13073
13074  Image
13075    *tile_image;
13076
13077  int
13078    id,
13079    status,
13080    tile,
13081    x,
13082    y;
13083
13084  double
13085    scale_factor;
13086
13087  register char
13088    *p,
13089    *q;
13090
13091  register int
13092    i;
13093
13094  unsigned int
13095    height,
13096    width;
13097
13098  /*
13099    Tile image is relative to montage image configuration.
13100  */
13101  x=0;
13102  y=0;
13103  width=(unsigned int) image->columns;
13104  height=(unsigned int) image->rows;
13105  if (windows->image.crop_geometry != (char *) NULL)
13106    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13107  scale_factor=(double) width/windows->image.ximage->width;
13108  event->xbutton.x+=windows->image.x;
13109  event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13110  scale_factor=(double) height/windows->image.ximage->height;
13111  event->xbutton.y+=windows->image.y;
13112  event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13113  /*
13114    Determine size and location of each tile in the visual image directory.
13115  */
13116  width=(unsigned int) image->columns;
13117  height=(unsigned int) image->rows;
13118  x=0;
13119  y=0;
13120  (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13121  tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13122    (event->xbutton.x-x)/width;
13123  if (tile < 0)
13124    {
13125      /*
13126        Button press is outside any tile.
13127      */
13128      (void) XBell(display,0);
13129      return((Image *) NULL);
13130    }
13131  /*
13132    Determine file name from the tile directory.
13133  */
13134  p=image->directory;
13135  for (i=tile; (i != 0) && (*p != '\0'); )
13136  {
13137    if (*p == '\n')
13138      i--;
13139    p++;
13140  }
13141  if (*p == '\0')
13142    {
13143      /*
13144        Button press is outside any tile.
13145      */
13146      (void) XBell(display,0);
13147      return((Image *) NULL);
13148    }
13149  /*
13150    Select a command from the pop-up menu.
13151  */
13152  id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13153  if (id < 0)
13154    return((Image *) NULL);
13155  q=p;
13156  while ((*q != '\n') && (*q != '\0'))
13157    q++;
13158  (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13159  /*
13160    Perform command for the selected tile.
13161  */
13162  XSetCursorState(display,windows,MagickTrue);
13163  XCheckRefreshWindows(display,windows);
13164  tile_image=NewImageList();
13165  switch (TileCommands[id])
13166  {
13167    case TileLoadCommand:
13168    {
13169      /*
13170        Load tile image.
13171      */
13172      XCheckRefreshWindows(display,windows);
13173      (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13174        MagickPathExtent);
13175      (void) CopyMagickString(resource_info->image_info->filename,filename,
13176        MagickPathExtent);
13177      tile_image=ReadImage(resource_info->image_info,exception);
13178      CatchException(exception);
13179      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13180      break;
13181    }
13182    case TileNextCommand:
13183    {
13184      /*
13185        Display next image.
13186      */
13187      XClientMessage(display,windows->image.id,windows->im_protocols,
13188        windows->im_next_image,CurrentTime);
13189      break;
13190    }
13191    case TileFormerCommand:
13192    {
13193      /*
13194        Display former image.
13195      */
13196      XClientMessage(display,windows->image.id,windows->im_protocols,
13197        windows->im_former_image,CurrentTime);
13198      break;
13199    }
13200    case TileDeleteCommand:
13201    {
13202      /*
13203        Delete tile image.
13204      */
13205      if (IfMagickFalse(IsPathAccessible(filename)) )
13206        {
13207          XNoticeWidget(display,windows,"Image file does not exist:",filename);
13208          break;
13209        }
13210      status=XConfirmWidget(display,windows,"Really delete tile",filename);
13211      if (status <= 0)
13212        break;
13213      status=ShredFile(filename);
13214      if (IfMagickTrue(status) )
13215        {
13216          XNoticeWidget(display,windows,"Unable to delete image file:",
13217            filename);
13218          break;
13219        }
13220    }
13221    case TileUpdateCommand:
13222    {
13223      int
13224        x_offset,
13225        y_offset;
13226
13227      PixelInfo
13228        pixel;
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 (IfMagickTrue(IsPathAccessible(filename)) )
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=AcquireAuthenticCacheView(image,exception);
13262        (void) GetOneCacheViewVirtualPixelInfo(image_view,0,0,&pixel,exception);
13263        for (i=0; i < (int) height; i++)
13264        {
13265          s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13266            y_offset+i,width,1,exception);
13267          if (s == (Quantum *) NULL)
13268            break;
13269          for (j=0; j < (int) width; j++)
13270          {
13271            SetPixelViaPixelInfo(image,&pixel,s);
13272            s+=GetPixelChannels(image);
13273          }
13274          if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
13275            break;
13276        }
13277        image_view=DestroyCacheView(image_view);
13278        tile++;
13279      }
13280      windows->image.window_changes.width=(int) image->columns;
13281      windows->image.window_changes.height=(int) image->rows;
13282      XConfigureImageColormap(display,resource_info,windows,image,exception);
13283      (void) XConfigureImage(display,resource_info,windows,image,exception);
13284      break;
13285    }
13286    default:
13287      break;
13288  }
13289  XSetCursorState(display,windows,MagickFalse);
13290  return(tile_image);
13291}
13292
13293/*
13294%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13295%                                                                             %
13296%                                                                             %
13297%                                                                             %
13298+   X T r a n s l a t e I m a g e                                             %
13299%                                                                             %
13300%                                                                             %
13301%                                                                             %
13302%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13303%
13304%  XTranslateImage() translates the image within an Image window by one pixel
13305%  as specified by the key symbol.  If the image has a montage string the
13306%  translation is respect to the width and height contained within the string.
13307%
13308%  The format of the XTranslateImage method is:
13309%
13310%      void XTranslateImage(Display *display,XWindows *windows,
13311%        Image *image,const KeySym key_symbol)
13312%
13313%  A description of each parameter follows:
13314%
13315%    o display: Specifies a connection to an X server; returned from
13316%      XOpenDisplay.
13317%
13318%    o windows: Specifies a pointer to a XWindows structure.
13319%
13320%    o image: the image.
13321%
13322%    o key_symbol: Specifies a KeySym which indicates which side of the image
13323%      to trim.
13324%
13325*/
13326static void XTranslateImage(Display *display,XWindows *windows,
13327  Image *image,const KeySym key_symbol)
13328{
13329  char
13330    text[MagickPathExtent];
13331
13332  int
13333    x,
13334    y;
13335
13336  unsigned int
13337    x_offset,
13338    y_offset;
13339
13340  /*
13341    User specified a pan position offset.
13342  */
13343  x_offset=windows->image.width;
13344  y_offset=windows->image.height;
13345  if (image->montage != (char *) NULL)
13346    (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13347  switch ((int) key_symbol)
13348  {
13349    case XK_Home:
13350    case XK_KP_Home:
13351    {
13352      windows->image.x=(int) windows->image.width/2;
13353      windows->image.y=(int) windows->image.height/2;
13354      break;
13355    }
13356    case XK_Left:
13357    case XK_KP_Left:
13358    {
13359      windows->image.x-=x_offset;
13360      break;
13361    }
13362    case XK_Next:
13363    case XK_Up:
13364    case XK_KP_Up:
13365    {
13366      windows->image.y-=y_offset;
13367      break;
13368    }
13369    case XK_Right:
13370    case XK_KP_Right:
13371    {
13372      windows->image.x+=x_offset;
13373      break;
13374    }
13375    case XK_Prior:
13376    case XK_Down:
13377    case XK_KP_Down:
13378    {
13379      windows->image.y+=y_offset;
13380      break;
13381    }
13382    default:
13383      return;
13384  }
13385  /*
13386    Check boundary conditions.
13387  */
13388  if (windows->image.x < 0)
13389    windows->image.x=0;
13390  else
13391    if ((int) (windows->image.x+windows->image.width) >
13392        windows->image.ximage->width)
13393      windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13394  if (windows->image.y < 0)
13395    windows->image.y=0;
13396  else
13397    if ((int) (windows->image.y+windows->image.height) >
13398        windows->image.ximage->height)
13399      windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13400  /*
13401    Refresh Image window.
13402  */
13403  (void) FormatLocaleString(text,MagickPathExtent," %ux%u%+d%+d ",
13404    windows->image.width,windows->image.height,windows->image.x,
13405    windows->image.y);
13406  XInfoWidget(display,windows,text);
13407  XCheckRefreshWindows(display,windows);
13408  XDrawPanRectangle(display,windows);
13409  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13410  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13411}
13412
13413/*
13414%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13415%                                                                             %
13416%                                                                             %
13417%                                                                             %
13418+   X T r i m I m a g e                                                       %
13419%                                                                             %
13420%                                                                             %
13421%                                                                             %
13422%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13423%
13424%  XTrimImage() trims the edges from the Image window.
13425%
13426%  The format of the XTrimImage method is:
13427%
13428%      MagickBooleanType XTrimImage(Display *display,
13429%        XResourceInfo *resource_info,XWindows *windows,Image *image,
13430%        ExceptionInfo *exception)
13431%
13432%  A description of each parameter follows:
13433%
13434%    o display: Specifies a connection to an X server; returned from
13435%      XOpenDisplay.
13436%
13437%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13438%
13439%    o windows: Specifies a pointer to a XWindows structure.
13440%
13441%    o image: the image.
13442%
13443%    o exception: return any errors or warnings in this structure.
13444%
13445*/
13446static MagickBooleanType XTrimImage(Display *display,
13447  XResourceInfo *resource_info,XWindows *windows,Image *image,
13448  ExceptionInfo *exception)
13449{
13450  RectangleInfo
13451    trim_info;
13452
13453  register int
13454    x,
13455    y;
13456
13457  size_t
13458    background,
13459    pixel;
13460
13461  /*
13462    Trim edges from image.
13463  */
13464  XSetCursorState(display,windows,MagickTrue);
13465  XCheckRefreshWindows(display,windows);
13466  /*
13467    Crop the left edge.
13468  */
13469  background=XGetPixel(windows->image.ximage,0,0);
13470  trim_info.width=(size_t) windows->image.ximage->width;
13471  for (x=0; x < windows->image.ximage->width; x++)
13472  {
13473    for (y=0; y < windows->image.ximage->height; y++)
13474    {
13475      pixel=XGetPixel(windows->image.ximage,x,y);
13476      if (pixel != background)
13477        break;
13478    }
13479    if (y < windows->image.ximage->height)
13480      break;
13481  }
13482  trim_info.x=(ssize_t) x;
13483  if (trim_info.x == (ssize_t) windows->image.ximage->width)
13484    {
13485      XSetCursorState(display,windows,MagickFalse);
13486      return(MagickFalse);
13487    }
13488  /*
13489    Crop the right edge.
13490  */
13491  background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13492  for (x=windows->image.ximage->width-1; x != 0; x--)
13493  {
13494    for (y=0; y < windows->image.ximage->height; y++)
13495    {
13496      pixel=XGetPixel(windows->image.ximage,x,y);
13497      if (pixel != background)
13498        break;
13499    }
13500    if (y < windows->image.ximage->height)
13501      break;
13502  }
13503  trim_info.width=(size_t) (x-trim_info.x+1);
13504  /*
13505    Crop the top edge.
13506  */
13507  background=XGetPixel(windows->image.ximage,0,0);
13508  trim_info.height=(size_t) windows->image.ximage->height;
13509  for (y=0; y < windows->image.ximage->height; y++)
13510  {
13511    for (x=0; x < windows->image.ximage->width; x++)
13512    {
13513      pixel=XGetPixel(windows->image.ximage,x,y);
13514      if (pixel != background)
13515        break;
13516    }
13517    if (x < windows->image.ximage->width)
13518      break;
13519  }
13520  trim_info.y=(ssize_t) y;
13521  /*
13522    Crop the bottom edge.
13523  */
13524  background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13525  for (y=windows->image.ximage->height-1; y != 0; y--)
13526  {
13527    for (x=0; x < windows->image.ximage->width; x++)
13528    {
13529      pixel=XGetPixel(windows->image.ximage,x,y);
13530      if (pixel != background)
13531        break;
13532    }
13533    if (x < windows->image.ximage->width)
13534      break;
13535  }
13536  trim_info.height=(size_t) y-trim_info.y+1;
13537  if (((unsigned int) trim_info.width != windows->image.width) ||
13538      ((unsigned int) trim_info.height != windows->image.height))
13539    {
13540      /*
13541        Reconfigure Image window as defined by the trimming rectangle.
13542      */
13543      XSetCropGeometry(display,windows,&trim_info,image);
13544      windows->image.window_changes.width=(int) trim_info.width;
13545      windows->image.window_changes.height=(int) trim_info.height;
13546      (void) XConfigureImage(display,resource_info,windows,image,exception);
13547    }
13548  XSetCursorState(display,windows,MagickFalse);
13549  return(MagickTrue);
13550}
13551
13552/*
13553%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13554%                                                                             %
13555%                                                                             %
13556%                                                                             %
13557+   X V i s u a l D i r e c t o r y I m a g e                                 %
13558%                                                                             %
13559%                                                                             %
13560%                                                                             %
13561%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13562%
13563%  XVisualDirectoryImage() creates a Visual Image Directory.
13564%
13565%  The format of the XVisualDirectoryImage method is:
13566%
13567%      Image *XVisualDirectoryImage(Display *display,
13568%        XResourceInfo *resource_info,XWindows *windows,
13569%        ExceptionInfo *exception)
13570%
13571%  A description of each parameter follows:
13572%
13573%    o display: Specifies a connection to an X server; returned from
13574%      XOpenDisplay.
13575%
13576%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13577%
13578%    o windows: Specifies a pointer to a XWindows structure.
13579%
13580%    o exception: return any errors or warnings in this structure.
13581%
13582*/
13583static Image *XVisualDirectoryImage(Display *display,
13584  XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
13585{
13586#define TileImageTag  "Scale/Image"
13587#define XClientName  "montage"
13588
13589  char
13590    **filelist;
13591
13592  Image
13593    *images,
13594    *montage_image,
13595    *next_image,
13596    *thumbnail_image;
13597
13598  ImageInfo
13599    *read_info;
13600
13601  int
13602    number_files;
13603
13604  MagickBooleanType
13605    backdrop;
13606
13607  MagickStatusType
13608    status;
13609
13610  MontageInfo
13611    *montage_info;
13612
13613  RectangleInfo
13614    geometry;
13615
13616  register int
13617    i;
13618
13619  static char
13620    filename[MagickPathExtent] = "\0",
13621    filenames[MagickPathExtent] = "*";
13622
13623  XResourceInfo
13624    background_resources;
13625
13626  /*
13627    Request file name from user.
13628  */
13629  XFileBrowserWidget(display,windows,"Directory",filenames);
13630  if (*filenames == '\0')
13631    return((Image *) NULL);
13632  /*
13633    Expand the filenames.
13634  */
13635  filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13636  if (filelist == (char **) NULL)
13637    {
13638      ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13639        filenames);
13640      return((Image *) NULL);
13641    }
13642  number_files=1;
13643  filelist[0]=filenames;
13644  status=ExpandFilenames(&number_files,&filelist);
13645  if (IfMagickFalse(status) || (number_files == 0))
13646    {
13647      if (number_files == 0)
13648        ThrowXWindowException(ImageError,"NoImagesWereFound",filenames)
13649      else
13650        ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13651          filenames);
13652      return((Image *) NULL);
13653    }
13654  /*
13655    Set image background resources.
13656  */
13657  background_resources=(*resource_info);
13658  background_resources.window_id=AcquireString("");
13659  (void) FormatLocaleString(background_resources.window_id,MagickPathExtent,
13660    "0x%lx",windows->image.id);
13661  background_resources.backdrop=MagickTrue;
13662  /*
13663    Read each image and convert them to a tile.
13664  */
13665  backdrop=((windows->visual_info->klass == TrueColor) ||
13666    (windows->visual_info->klass == DirectColor)) ? MagickTrue : MagickFalse;
13667  read_info=CloneImageInfo(resource_info->image_info);
13668  (void) SetImageOption(read_info,"jpeg:size","120x120");
13669  (void) CloneString(&read_info->size,DefaultTileGeometry);
13670  (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13671    (void *) NULL);
13672  images=NewImageList();
13673  XSetCursorState(display,windows,MagickTrue);
13674  XCheckRefreshWindows(display,windows);
13675  for (i=0; i < (int) number_files; i++)
13676  {
13677    (void) CopyMagickString(read_info->filename,filelist[i],MagickPathExtent);
13678    filelist[i]=DestroyString(filelist[i]);
13679    *read_info->magick='\0';
13680    next_image=ReadImage(read_info,exception);
13681    CatchException(exception);
13682    if (next_image != (Image *) NULL)
13683      {
13684        (void) DeleteImageProperty(next_image,"label");
13685        (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13686          read_info,next_image,DefaultTileLabel,exception),exception);
13687        (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13688          exception);
13689        thumbnail_image=ThumbnailImage(next_image,geometry.width,
13690          geometry.height,exception);
13691        if (thumbnail_image != (Image *) NULL)
13692          {
13693            next_image=DestroyImage(next_image);
13694            next_image=thumbnail_image;
13695          }
13696        if (backdrop)
13697          {
13698            (void) XDisplayBackgroundImage(display,&background_resources,
13699              next_image,exception);
13700            XSetCursorState(display,windows,MagickTrue);
13701          }
13702        AppendImageToList(&images,next_image);
13703        if (images->progress_monitor != (MagickProgressMonitor) NULL)
13704          {
13705            MagickBooleanType
13706              proceed;
13707
13708            proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13709              (MagickSizeType) number_files);
13710            if (IfMagickFalse(proceed) )
13711              break;
13712          }
13713      }
13714  }
13715  filelist=(char **) RelinquishMagickMemory(filelist);
13716  if (images == (Image *) NULL)
13717    {
13718      read_info=DestroyImageInfo(read_info);
13719      XSetCursorState(display,windows,MagickFalse);
13720      ThrowXWindowException(ImageError,"NoImagesWereLoaded",filenames);
13721      return((Image *) NULL);
13722    }
13723  /*
13724    Create the Visual Image Directory.
13725  */
13726  montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13727  montage_info->pointsize=10;
13728  if (resource_info->font != (char *) NULL)
13729    (void) CloneString(&montage_info->font,resource_info->font);
13730  (void) CopyMagickString(montage_info->filename,filename,MagickPathExtent);
13731  montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13732    images),exception);
13733  images=DestroyImageList(images);
13734  montage_info=DestroyMontageInfo(montage_info);
13735  read_info=DestroyImageInfo(read_info);
13736  XSetCursorState(display,windows,MagickFalse);
13737  if (montage_image == (Image *) NULL)
13738    return(montage_image);
13739  XClientMessage(display,windows->image.id,windows->im_protocols,
13740    windows->im_next_image,CurrentTime);
13741  return(montage_image);
13742}
13743
13744/*
13745%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13746%                                                                             %
13747%                                                                             %
13748%                                                                             %
13749%   X D i s p l a y B a c k g r o u n d I m a g e                             %
13750%                                                                             %
13751%                                                                             %
13752%                                                                             %
13753%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13754%
13755%  XDisplayBackgroundImage() displays an image in the background of a window.
13756%
13757%  The format of the XDisplayBackgroundImage method is:
13758%
13759%      MagickBooleanType XDisplayBackgroundImage(Display *display,
13760%        XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13761%
13762%  A description of each parameter follows:
13763%
13764%    o display: Specifies a connection to an X server;  returned from
13765%      XOpenDisplay.
13766%
13767%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13768%
13769%    o image: the image.
13770%
13771%    o exception: return any errors or warnings in this structure.
13772%
13773*/
13774MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13775  XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13776{
13777  char
13778    geometry[MagickPathExtent],
13779    visual_type[MagickPathExtent];
13780
13781  int
13782    height,
13783    status,
13784    width;
13785
13786  RectangleInfo
13787    geometry_info;
13788
13789  static XPixelInfo
13790    pixel;
13791
13792  static XStandardColormap
13793    *map_info;
13794
13795  static XVisualInfo
13796    *visual_info = (XVisualInfo *) NULL;
13797
13798  static XWindowInfo
13799    window_info;
13800
13801  size_t
13802    delay;
13803
13804  Window
13805    root_window;
13806
13807  XGCValues
13808    context_values;
13809
13810  XResourceInfo
13811    resources;
13812
13813  XWindowAttributes
13814    window_attributes;
13815
13816  /*
13817    Determine target window.
13818  */
13819  assert(image != (Image *) NULL);
13820  assert(image->signature == MagickCoreSignature);
13821  if (IfMagickTrue(image->debug) )
13822    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13823  resources=(*resource_info);
13824  window_info.id=(Window) NULL;
13825  root_window=XRootWindow(display,XDefaultScreen(display));
13826  if (LocaleCompare(resources.window_id,"root") == 0)
13827    window_info.id=root_window;
13828  else
13829    {
13830      if (isdigit((int) ((unsigned char) *resources.window_id)) != 0)
13831        window_info.id=XWindowByID(display,root_window,
13832          (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13833      if (window_info.id == (Window) NULL)
13834        window_info.id=XWindowByName(display,root_window,resources.window_id);
13835    }
13836  if (window_info.id == (Window) NULL)
13837    {
13838      ThrowXWindowException(XServerError,"NoWindowWithSpecifiedIDExists",
13839        resources.window_id);
13840      return(MagickFalse);
13841    }
13842  /*
13843    Determine window visual id.
13844  */
13845  window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13846  window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13847  (void) CopyMagickString(visual_type,"default",MagickPathExtent);
13848  status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13849  if (status != 0)
13850    (void) FormatLocaleString(visual_type,MagickPathExtent,"0x%lx",
13851      XVisualIDFromVisual(window_attributes.visual));
13852  if (visual_info == (XVisualInfo *) NULL)
13853    {
13854      /*
13855        Allocate standard colormap.
13856      */
13857      map_info=XAllocStandardColormap();
13858      if (map_info == (XStandardColormap *) NULL)
13859        ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13860          image->filename);
13861      map_info->colormap=(Colormap) NULL;
13862      pixel.pixels=(unsigned long *) NULL;
13863      /*
13864        Initialize visual info.
13865      */
13866      resources.map_type=(char *) NULL;
13867      resources.visual_type=visual_type;
13868      visual_info=XBestVisualInfo(display,map_info,&resources);
13869      if (visual_info == (XVisualInfo *) NULL)
13870        ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13871          resources.visual_type);
13872      /*
13873        Initialize window info.
13874      */
13875      window_info.ximage=(XImage *) NULL;
13876      window_info.matte_image=(XImage *) NULL;
13877      window_info.pixmap=(Pixmap) NULL;
13878      window_info.matte_pixmap=(Pixmap) NULL;
13879    }
13880  /*
13881    Free previous root colors.
13882  */
13883  if (window_info.id == root_window)
13884    (void) XDestroyWindowColors(display,root_window);
13885  /*
13886    Initialize Standard Colormap.
13887  */
13888  resources.colormap=SharedColormap;
13889  XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel,
13890    exception);
13891  /*
13892    Graphic context superclass.
13893  */
13894  context_values.background=pixel.background_color.pixel;
13895  context_values.foreground=pixel.foreground_color.pixel;
13896  pixel.annotate_context=XCreateGC(display,window_info.id,
13897    (size_t) (GCBackground | GCForeground),&context_values);
13898  if (pixel.annotate_context == (GC) NULL)
13899    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13900      image->filename);
13901  /*
13902    Initialize Image window attributes.
13903  */
13904  window_info.name=AcquireString("\0");
13905  window_info.icon_name=AcquireString("\0");
13906  XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13907    &resources,&window_info);
13908  /*
13909    Create the X image.
13910  */
13911  window_info.width=(unsigned int) image->columns;
13912  window_info.height=(unsigned int) image->rows;
13913  if ((image->columns != window_info.width) ||
13914      (image->rows != window_info.height))
13915    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13916      image->filename);
13917  (void) FormatLocaleString(geometry,MagickPathExtent,"%ux%u+0+0>",
13918    window_attributes.width,window_attributes.height);
13919  geometry_info.width=window_info.width;
13920  geometry_info.height=window_info.height;
13921  geometry_info.x=(ssize_t) window_info.x;
13922  geometry_info.y=(ssize_t) window_info.y;
13923  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13924    &geometry_info.width,&geometry_info.height);
13925  window_info.width=(unsigned int) geometry_info.width;
13926  window_info.height=(unsigned int) geometry_info.height;
13927  window_info.x=(int) geometry_info.x;
13928  window_info.y=(int) geometry_info.y;
13929  status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13930    window_info.height,exception);
13931  if (IfMagickFalse(status) )
13932    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13933      image->filename);
13934  window_info.x=0;
13935  window_info.y=0;
13936  if (IfMagickTrue(image->debug) )
13937    {
13938      (void) LogMagickEvent(X11Event,GetMagickModule(),
13939        "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13940        (double) image->columns,(double) image->rows);
13941      if (image->colors != 0)
13942        (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13943          image->colors);
13944      (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13945    }
13946  /*
13947    Adjust image dimensions as specified by backdrop or geometry options.
13948  */
13949  width=(int) window_info.width;
13950  height=(int) window_info.height;
13951  if (IfMagickTrue(resources.backdrop) )
13952    {
13953      /*
13954        Center image on window.
13955      */
13956      window_info.x=(window_attributes.width/2)-
13957        (window_info.ximage->width/2);
13958      window_info.y=(window_attributes.height/2)-
13959        (window_info.ximage->height/2);
13960      width=window_attributes.width;
13961      height=window_attributes.height;
13962    }
13963  if ((resources.image_geometry != (char *) NULL) &&
13964      (*resources.image_geometry != '\0'))
13965    {
13966      char
13967        default_geometry[MagickPathExtent];
13968
13969      int
13970        flags,
13971        gravity;
13972
13973      XSizeHints
13974        *size_hints;
13975
13976      /*
13977        User specified geometry.
13978      */
13979      size_hints=XAllocSizeHints();
13980      if (size_hints == (XSizeHints *) NULL)
13981        ThrowXWindowFatalException(ResourceLimitFatalError,
13982          "MemoryAllocationFailed",image->filename);
13983      size_hints->flags=0L;
13984      (void) FormatLocaleString(default_geometry,MagickPathExtent,"%dx%d",
13985        width,height);
13986      flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
13987        default_geometry,window_info.border_width,size_hints,&window_info.x,
13988        &window_info.y,&width,&height,&gravity);
13989      if (flags & (XValue | YValue))
13990        {
13991          width=window_attributes.width;
13992          height=window_attributes.height;
13993        }
13994      (void) XFree((void *) size_hints);
13995    }
13996  /*
13997    Create the X pixmap.
13998  */
13999  window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
14000    (unsigned int) height,window_info.depth);
14001  if (window_info.pixmap == (Pixmap) NULL)
14002    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
14003      image->filename);
14004  /*
14005    Display pixmap on the window.
14006  */
14007  if (((unsigned int) width > window_info.width) ||
14008      ((unsigned int) height > window_info.height))
14009    (void) XFillRectangle(display,window_info.pixmap,
14010      window_info.annotate_context,0,0,(unsigned int) width,
14011      (unsigned int) height);
14012  (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
14013    window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
14014    window_info.width,(unsigned int) window_info.height);
14015  (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
14016  (void) XClearWindow(display,window_info.id);
14017  delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
14018  XDelay(display,delay == 0UL ? 10UL : delay);
14019  (void) XSync(display,MagickFalse);
14020  return(window_info.id == root_window ? MagickTrue : MagickFalse);
14021}
14022
14023/*
14024%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14025%                                                                             %
14026%                                                                             %
14027%                                                                             %
14028+   X D i s p l a y I m a g e                                                 %
14029%                                                                             %
14030%                                                                             %
14031%                                                                             %
14032%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14033%
14034%  XDisplayImage() displays an image via X11.  A new image is created and
14035%  returned if the user interactively transforms the displayed image.
14036%
14037%  The format of the XDisplayImage method is:
14038%
14039%      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14040%        char **argv,int argc,Image **image,size_t *state,
14041%        ExceptionInfo *exception)
14042%
14043%  A description of each parameter follows:
14044%
14045%    o nexus:  Method XDisplayImage returns an image when the
14046%      user chooses 'Open Image' from the command menu or picks a tile
14047%      from the image directory.  Otherwise a null image is returned.
14048%
14049%    o display: Specifies a connection to an X server;  returned from
14050%      XOpenDisplay.
14051%
14052%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14053%
14054%    o argv: Specifies the application's argument list.
14055%
14056%    o argc: Specifies the number of arguments.
14057%
14058%    o image: Specifies an address to an address of an Image structure;
14059%
14060%    o exception: return any errors or warnings in this structure.
14061%
14062*/
14063MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14064  char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
14065{
14066#define MagnifySize  256  /* must be a power of 2 */
14067#define MagickMenus  10
14068#define MagickTitle  "Commands"
14069
14070  static const char
14071    *CommandMenu[] =
14072    {
14073      "File",
14074      "Edit",
14075      "View",
14076      "Transform",
14077      "Enhance",
14078      "Effects",
14079      "F/X",
14080      "Image Edit",
14081      "Miscellany",
14082      "Help",
14083      (char *) NULL
14084    },
14085    *FileMenu[] =
14086    {
14087      "Open...",
14088      "Next",
14089      "Former",
14090      "Select...",
14091      "Save...",
14092      "Print...",
14093      "Delete...",
14094      "New...",
14095      "Visual Directory...",
14096      "Quit",
14097      (char *) NULL
14098    },
14099    *EditMenu[] =
14100    {
14101      "Undo",
14102      "Redo",
14103      "Cut",
14104      "Copy",
14105      "Paste",
14106      (char *) NULL
14107    },
14108    *ViewMenu[] =
14109    {
14110      "Half Size",
14111      "Original Size",
14112      "Double Size",
14113      "Resize...",
14114      "Apply",
14115      "Refresh",
14116      "Restore",
14117      (char *) NULL
14118    },
14119    *TransformMenu[] =
14120    {
14121      "Crop",
14122      "Chop",
14123      "Flop",
14124      "Flip",
14125      "Rotate Right",
14126      "Rotate Left",
14127      "Rotate...",
14128      "Shear...",
14129      "Roll...",
14130      "Trim Edges",
14131      (char *) NULL
14132    },
14133    *EnhanceMenu[] =
14134    {
14135      "Hue...",
14136      "Saturation...",
14137      "Brightness...",
14138      "Gamma...",
14139      "Spiff",
14140      "Dull",
14141      "Contrast Stretch...",
14142      "Sigmoidal Contrast...",
14143      "Normalize",
14144      "Equalize",
14145      "Negate",
14146      "Grayscale",
14147      "Map...",
14148      "Quantize...",
14149      (char *) NULL
14150    },
14151    *EffectsMenu[] =
14152    {
14153      "Despeckle",
14154      "Emboss",
14155      "Reduce Noise",
14156      "Add Noise...",
14157      "Sharpen...",
14158      "Blur...",
14159      "Threshold...",
14160      "Edge Detect...",
14161      "Spread...",
14162      "Shade...",
14163      "Raise...",
14164      "Segment...",
14165      (char *) NULL
14166    },
14167    *FXMenu[] =
14168    {
14169      "Solarize...",
14170      "Sepia Tone...",
14171      "Swirl...",
14172      "Implode...",
14173      "Vignette...",
14174      "Wave...",
14175      "Oil Paint...",
14176      "Charcoal Draw...",
14177      (char *) NULL
14178    },
14179    *ImageEditMenu[] =
14180    {
14181      "Annotate...",
14182      "Draw...",
14183      "Color...",
14184      "Matte...",
14185      "Composite...",
14186      "Add Border...",
14187      "Add Frame...",
14188      "Comment...",
14189      "Launch...",
14190      "Region of Interest...",
14191      (char *) NULL
14192    },
14193    *MiscellanyMenu[] =
14194    {
14195      "Image Info",
14196      "Zoom Image",
14197      "Show Preview...",
14198      "Show Histogram",
14199      "Show Matte",
14200      "Background...",
14201      "Slide Show...",
14202      "Preferences...",
14203      (char *) NULL
14204    },
14205    *HelpMenu[] =
14206    {
14207      "Overview",
14208      "Browse Documentation",
14209      "About Display",
14210      (char *) NULL
14211    },
14212    *ShortCutsMenu[] =
14213    {
14214      "Next",
14215      "Former",
14216      "Open...",
14217      "Save...",
14218      "Print...",
14219      "Undo",
14220      "Restore",
14221      "Image Info",
14222      "Quit",
14223      (char *) NULL
14224    },
14225    *VirtualMenu[] =
14226    {
14227      "Image Info",
14228      "Print",
14229      "Next",
14230      "Quit",
14231      (char *) NULL
14232    };
14233
14234  static const char
14235    **Menus[MagickMenus] =
14236    {
14237      FileMenu,
14238      EditMenu,
14239      ViewMenu,
14240      TransformMenu,
14241      EnhanceMenu,
14242      EffectsMenu,
14243      FXMenu,
14244      ImageEditMenu,
14245      MiscellanyMenu,
14246      HelpMenu
14247    };
14248
14249  static CommandType
14250    CommandMenus[] =
14251    {
14252      NullCommand,
14253      NullCommand,
14254      NullCommand,
14255      NullCommand,
14256      NullCommand,
14257      NullCommand,
14258      NullCommand,
14259      NullCommand,
14260      NullCommand,
14261      NullCommand,
14262    },
14263    FileCommands[] =
14264    {
14265      OpenCommand,
14266      NextCommand,
14267      FormerCommand,
14268      SelectCommand,
14269      SaveCommand,
14270      PrintCommand,
14271      DeleteCommand,
14272      NewCommand,
14273      VisualDirectoryCommand,
14274      QuitCommand
14275    },
14276    EditCommands[] =
14277    {
14278      UndoCommand,
14279      RedoCommand,
14280      CutCommand,
14281      CopyCommand,
14282      PasteCommand
14283    },
14284    ViewCommands[] =
14285    {
14286      HalfSizeCommand,
14287      OriginalSizeCommand,
14288      DoubleSizeCommand,
14289      ResizeCommand,
14290      ApplyCommand,
14291      RefreshCommand,
14292      RestoreCommand
14293    },
14294    TransformCommands[] =
14295    {
14296      CropCommand,
14297      ChopCommand,
14298      FlopCommand,
14299      FlipCommand,
14300      RotateRightCommand,
14301      RotateLeftCommand,
14302      RotateCommand,
14303      ShearCommand,
14304      RollCommand,
14305      TrimCommand
14306    },
14307    EnhanceCommands[] =
14308    {
14309      HueCommand,
14310      SaturationCommand,
14311      BrightnessCommand,
14312      GammaCommand,
14313      SpiffCommand,
14314      DullCommand,
14315      ContrastStretchCommand,
14316      SigmoidalContrastCommand,
14317      NormalizeCommand,
14318      EqualizeCommand,
14319      NegateCommand,
14320      GrayscaleCommand,
14321      MapCommand,
14322      QuantizeCommand
14323    },
14324    EffectsCommands[] =
14325    {
14326      DespeckleCommand,
14327      EmbossCommand,
14328      ReduceNoiseCommand,
14329      AddNoiseCommand,
14330      SharpenCommand,
14331      BlurCommand,
14332      ThresholdCommand,
14333      EdgeDetectCommand,
14334      SpreadCommand,
14335      ShadeCommand,
14336      RaiseCommand,
14337      SegmentCommand
14338    },
14339    FXCommands[] =
14340    {
14341      SolarizeCommand,
14342      SepiaToneCommand,
14343      SwirlCommand,
14344      ImplodeCommand,
14345      VignetteCommand,
14346      WaveCommand,
14347      OilPaintCommand,
14348      CharcoalDrawCommand
14349    },
14350    ImageEditCommands[] =
14351    {
14352      AnnotateCommand,
14353      DrawCommand,
14354      ColorCommand,
14355      MatteCommand,
14356      CompositeCommand,
14357      AddBorderCommand,
14358      AddFrameCommand,
14359      CommentCommand,
14360      LaunchCommand,
14361      RegionofInterestCommand
14362    },
14363    MiscellanyCommands[] =
14364    {
14365      InfoCommand,
14366      ZoomCommand,
14367      ShowPreviewCommand,
14368      ShowHistogramCommand,
14369      ShowMatteCommand,
14370      BackgroundCommand,
14371      SlideShowCommand,
14372      PreferencesCommand
14373    },
14374    HelpCommands[] =
14375    {
14376      HelpCommand,
14377      BrowseDocumentationCommand,
14378      VersionCommand
14379    },
14380    ShortCutsCommands[] =
14381    {
14382      NextCommand,
14383      FormerCommand,
14384      OpenCommand,
14385      SaveCommand,
14386      PrintCommand,
14387      UndoCommand,
14388      RestoreCommand,
14389      InfoCommand,
14390      QuitCommand
14391    },
14392    VirtualCommands[] =
14393    {
14394      InfoCommand,
14395      PrintCommand,
14396      NextCommand,
14397      QuitCommand
14398    };
14399
14400  static CommandType
14401    *Commands[MagickMenus] =
14402    {
14403      FileCommands,
14404      EditCommands,
14405      ViewCommands,
14406      TransformCommands,
14407      EnhanceCommands,
14408      EffectsCommands,
14409      FXCommands,
14410      ImageEditCommands,
14411      MiscellanyCommands,
14412      HelpCommands
14413    };
14414
14415  char
14416    command[MagickPathExtent],
14417    *directory,
14418    geometry[MagickPathExtent],
14419    resource_name[MagickPathExtent];
14420
14421  CommandType
14422    command_type;
14423
14424  Image
14425    *display_image,
14426    *nexus;
14427
14428  int
14429    entry,
14430    id;
14431
14432  KeySym
14433    key_symbol;
14434
14435  MagickStatusType
14436    context_mask,
14437    status;
14438
14439  RectangleInfo
14440    geometry_info;
14441
14442  register int
14443    i;
14444
14445  static char
14446    working_directory[MagickPathExtent];
14447
14448  static XPoint
14449    vid_info;
14450
14451  static XWindowInfo
14452    *magick_windows[MaxXWindows];
14453
14454  static unsigned int
14455    number_windows;
14456
14457  struct stat
14458    attributes;
14459
14460  time_t
14461    timer,
14462    timestamp,
14463    update_time;
14464
14465  unsigned int
14466    height,
14467    width;
14468
14469  size_t
14470    delay;
14471
14472  WarningHandler
14473    warning_handler;
14474
14475  Window
14476    root_window;
14477
14478  XClassHint
14479    *class_hints;
14480
14481  XEvent
14482    event;
14483
14484  XFontStruct
14485    *font_info;
14486
14487  XGCValues
14488    context_values;
14489
14490  XPixelInfo
14491    *icon_pixel,
14492    *pixel;
14493
14494  XResourceInfo
14495    *icon_resources;
14496
14497  XStandardColormap
14498    *icon_map,
14499    *map_info;
14500
14501  XVisualInfo
14502    *icon_visual,
14503    *visual_info;
14504
14505  XWindowChanges
14506    window_changes;
14507
14508  XWindows
14509    *windows;
14510
14511  XWMHints
14512    *manager_hints;
14513
14514  assert(image != (Image **) NULL);
14515  assert((*image)->signature == MagickCoreSignature);
14516  if (IfMagickTrue((*image)->debug) )
14517    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14518  display_image=(*image);
14519  warning_handler=(WarningHandler) NULL;
14520  windows=XSetWindows((XWindows *) ~0);
14521  if (windows != (XWindows *) NULL)
14522    {
14523      int
14524        status;
14525
14526      if (*working_directory == '\0')
14527        (void) CopyMagickString(working_directory,".",MagickPathExtent);
14528      status=chdir(working_directory);
14529      if (status == -1)
14530        (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14531          "UnableToOpenFile","%s",working_directory);
14532      warning_handler=resource_info->display_warnings ?
14533        SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14534      warning_handler=resource_info->display_warnings ?
14535        SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14536    }
14537  else
14538    {
14539      /*
14540        Allocate windows structure.
14541      */
14542      resource_info->colors=display_image->colors;
14543      windows=XSetWindows(XInitializeWindows(display,resource_info));
14544      if (windows == (XWindows *) NULL)
14545        ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14546          (*image)->filename);
14547      /*
14548        Initialize window id's.
14549      */
14550      number_windows=0;
14551      magick_windows[number_windows++]=(&windows->icon);
14552      magick_windows[number_windows++]=(&windows->backdrop);
14553      magick_windows[number_windows++]=(&windows->image);
14554      magick_windows[number_windows++]=(&windows->info);
14555      magick_windows[number_windows++]=(&windows->command);
14556      magick_windows[number_windows++]=(&windows->widget);
14557      magick_windows[number_windows++]=(&windows->popup);
14558      magick_windows[number_windows++]=(&windows->magnify);
14559      magick_windows[number_windows++]=(&windows->pan);
14560      for (i=0; i < (int) number_windows; i++)
14561        magick_windows[i]->id=(Window) NULL;
14562      vid_info.x=0;
14563      vid_info.y=0;
14564    }
14565  /*
14566    Initialize font info.
14567  */
14568  if (windows->font_info != (XFontStruct *) NULL)
14569    (void) XFreeFont(display,windows->font_info);
14570  windows->font_info=XBestFont(display,resource_info,MagickFalse);
14571  if (windows->font_info == (XFontStruct *) NULL)
14572    ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14573      resource_info->font);
14574  /*
14575    Initialize Standard Colormap.
14576  */
14577  map_info=windows->map_info;
14578  icon_map=windows->icon_map;
14579  visual_info=windows->visual_info;
14580  icon_visual=windows->icon_visual;
14581  pixel=windows->pixel_info;
14582  icon_pixel=windows->icon_pixel;
14583  font_info=windows->font_info;
14584  icon_resources=windows->icon_resources;
14585  class_hints=windows->class_hints;
14586  manager_hints=windows->manager_hints;
14587  root_window=XRootWindow(display,visual_info->screen);
14588  nexus=NewImageList();
14589  if (IfMagickTrue(display_image->debug) )
14590    {
14591      (void) LogMagickEvent(X11Event,GetMagickModule(),
14592        "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14593        (double) display_image->scene,(double) display_image->columns,
14594        (double) display_image->rows);
14595      if (display_image->colors != 0)
14596        (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14597          display_image->colors);
14598      (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14599        display_image->magick);
14600    }
14601  XMakeStandardColormap(display,visual_info,resource_info,display_image,
14602    map_info,pixel,exception);
14603  display_image->taint=MagickFalse;
14604  /*
14605    Initialize graphic context.
14606  */
14607  windows->context.id=(Window) NULL;
14608  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14609    resource_info,&windows->context);
14610  (void) CloneString(&class_hints->res_name,resource_info->client_name);
14611  (void) CloneString(&class_hints->res_class,resource_info->client_name);
14612  class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14613  manager_hints->flags=InputHint | StateHint;
14614  manager_hints->input=MagickFalse;
14615  manager_hints->initial_state=WithdrawnState;
14616  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14617    &windows->context);
14618  if (IfMagickTrue(display_image->debug) )
14619    (void) LogMagickEvent(X11Event,GetMagickModule(),
14620      "Window id: 0x%lx (context)",windows->context.id);
14621  context_values.background=pixel->background_color.pixel;
14622  context_values.font=font_info->fid;
14623  context_values.foreground=pixel->foreground_color.pixel;
14624  context_values.graphics_exposures=MagickFalse;
14625  context_mask=(MagickStatusType)
14626    (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14627  if (pixel->annotate_context != (GC) NULL)
14628    (void) XFreeGC(display,pixel->annotate_context);
14629  pixel->annotate_context=XCreateGC(display,windows->context.id,
14630    context_mask,&context_values);
14631  if (pixel->annotate_context == (GC) NULL)
14632    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14633      display_image->filename);
14634  context_values.background=pixel->depth_color.pixel;
14635  if (pixel->widget_context != (GC) NULL)
14636    (void) XFreeGC(display,pixel->widget_context);
14637  pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14638    &context_values);
14639  if (pixel->widget_context == (GC) NULL)
14640    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14641      display_image->filename);
14642  context_values.background=pixel->foreground_color.pixel;
14643  context_values.foreground=pixel->background_color.pixel;
14644  context_values.plane_mask=context_values.background ^
14645    context_values.foreground;
14646  if (pixel->highlight_context != (GC) NULL)
14647    (void) XFreeGC(display,pixel->highlight_context);
14648  pixel->highlight_context=XCreateGC(display,windows->context.id,
14649    (size_t) (context_mask | GCPlaneMask),&context_values);
14650  if (pixel->highlight_context == (GC) NULL)
14651    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14652      display_image->filename);
14653  (void) XDestroyWindow(display,windows->context.id);
14654  /*
14655    Initialize icon window.
14656  */
14657  XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14658    icon_resources,&windows->icon);
14659  windows->icon.geometry=resource_info->icon_geometry;
14660  XBestIconSize(display,&windows->icon,display_image);
14661  windows->icon.attributes.colormap=XDefaultColormap(display,
14662    icon_visual->screen);
14663  windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14664  manager_hints->flags=InputHint | StateHint;
14665  manager_hints->input=MagickFalse;
14666  manager_hints->initial_state=IconicState;
14667  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14668    &windows->icon);
14669  if (IfMagickTrue(display_image->debug) )
14670    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14671      windows->icon.id);
14672  /*
14673    Initialize graphic context for icon window.
14674  */
14675  if (icon_pixel->annotate_context != (GC) NULL)
14676    (void) XFreeGC(display,icon_pixel->annotate_context);
14677  context_values.background=icon_pixel->background_color.pixel;
14678  context_values.foreground=icon_pixel->foreground_color.pixel;
14679  icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14680    (size_t) (GCBackground | GCForeground),&context_values);
14681  if (icon_pixel->annotate_context == (GC) NULL)
14682    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14683      display_image->filename);
14684  windows->icon.annotate_context=icon_pixel->annotate_context;
14685  /*
14686    Initialize Image window.
14687  */
14688  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14689    &windows->image);
14690  windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14691  if (IfMagickFalse(resource_info->use_shared_memory) )
14692    windows->image.shared_memory=MagickFalse;
14693  if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14694    {
14695      char
14696        *title;
14697
14698      title=InterpretImageProperties(resource_info->image_info,display_image,
14699        resource_info->title,exception);
14700      (void) CopyMagickString(windows->image.name,title,MagickPathExtent);
14701      (void) CopyMagickString(windows->image.icon_name,title,MagickPathExtent);
14702      title=DestroyString(title);
14703    }
14704  else
14705    {
14706      char
14707        filename[MagickPathExtent];
14708
14709      /*
14710        Window name is the base of the filename.
14711      */
14712      GetPathComponent(display_image->magick_filename,TailPath,filename);
14713      if (display_image->scene == 0)
14714        (void) FormatLocaleString(windows->image.name,MagickPathExtent,
14715          "%s: %s",MagickPackageName,filename);
14716      else
14717        (void) FormatLocaleString(windows->image.name,MagickPathExtent,
14718          "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14719          (double) display_image->scene,(double) GetImageListLength(
14720          display_image));
14721      (void) CopyMagickString(windows->image.icon_name,filename,MagickPathExtent);
14722    }
14723  if (resource_info->immutable)
14724    windows->image.immutable=MagickTrue;
14725  windows->image.use_pixmap=resource_info->use_pixmap;
14726  windows->image.geometry=resource_info->image_geometry;
14727  (void) FormatLocaleString(geometry,MagickPathExtent,"%ux%u+0+0>!",
14728    XDisplayWidth(display,visual_info->screen),
14729    XDisplayHeight(display,visual_info->screen));
14730  geometry_info.width=display_image->columns;
14731  geometry_info.height=display_image->rows;
14732  geometry_info.x=0;
14733  geometry_info.y=0;
14734  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14735    &geometry_info.width,&geometry_info.height);
14736  windows->image.width=(unsigned int) geometry_info.width;
14737  windows->image.height=(unsigned int) geometry_info.height;
14738  windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14739    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14740    KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14741    PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14742  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14743    resource_info,&windows->backdrop);
14744  if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14745    {
14746      /*
14747        Initialize backdrop window.
14748      */
14749      windows->backdrop.x=0;
14750      windows->backdrop.y=0;
14751      (void) CloneString(&windows->backdrop.name,"Backdrop");
14752      windows->backdrop.flags=(size_t) (USSize | USPosition);
14753      windows->backdrop.width=(unsigned int)
14754        XDisplayWidth(display,visual_info->screen);
14755      windows->backdrop.height=(unsigned int)
14756        XDisplayHeight(display,visual_info->screen);
14757      windows->backdrop.border_width=0;
14758      windows->backdrop.immutable=MagickTrue;
14759      windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14760        ButtonReleaseMask;
14761      windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14762        StructureNotifyMask;
14763      manager_hints->flags=IconWindowHint | InputHint | StateHint;
14764      manager_hints->icon_window=windows->icon.id;
14765      manager_hints->input=MagickTrue;
14766      manager_hints->initial_state=resource_info->iconic ? IconicState :
14767        NormalState;
14768      XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14769        &windows->backdrop);
14770      if (IfMagickTrue(display_image->debug) )
14771        (void) LogMagickEvent(X11Event,GetMagickModule(),
14772          "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14773      (void) XMapWindow(display,windows->backdrop.id);
14774      (void) XClearWindow(display,windows->backdrop.id);
14775      if (windows->image.id != (Window) NULL)
14776        {
14777          (void) XDestroyWindow(display,windows->image.id);
14778          windows->image.id=(Window) NULL;
14779        }
14780      /*
14781        Position image in the center the backdrop.
14782      */
14783      windows->image.flags|=USPosition;
14784      windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14785        (windows->image.width/2);
14786      windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14787        (windows->image.height/2);
14788    }
14789  manager_hints->flags=IconWindowHint | InputHint | StateHint;
14790  manager_hints->icon_window=windows->icon.id;
14791  manager_hints->input=MagickTrue;
14792  manager_hints->initial_state=resource_info->iconic ? IconicState :
14793    NormalState;
14794  if (windows->group_leader.id != (Window) NULL)
14795    {
14796      /*
14797        Follow the leader.
14798      */
14799      manager_hints->flags|=WindowGroupHint;
14800      manager_hints->window_group=windows->group_leader.id;
14801      (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14802      if (IfMagickTrue(display_image->debug) )
14803        (void) LogMagickEvent(X11Event,GetMagickModule(),
14804          "Window id: 0x%lx (group leader)",windows->group_leader.id);
14805    }
14806  XMakeWindow(display,
14807    (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14808    argv,argc,class_hints,manager_hints,&windows->image);
14809  (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14810    XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14811  if (windows->group_leader.id != (Window) NULL)
14812    (void) XSetTransientForHint(display,windows->image.id,
14813      windows->group_leader.id);
14814  if (IfMagickTrue(display_image->debug) )
14815    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14816      windows->image.id);
14817  /*
14818    Initialize Info widget.
14819  */
14820  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14821    &windows->info);
14822  (void) CloneString(&windows->info.name,"Info");
14823  (void) CloneString(&windows->info.icon_name,"Info");
14824  windows->info.border_width=1;
14825  windows->info.x=2;
14826  windows->info.y=2;
14827  windows->info.flags|=PPosition;
14828  windows->info.attributes.win_gravity=UnmapGravity;
14829  windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14830    StructureNotifyMask;
14831  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14832  manager_hints->input=MagickFalse;
14833  manager_hints->initial_state=NormalState;
14834  manager_hints->window_group=windows->image.id;
14835  XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14836    &windows->info);
14837  windows->info.highlight_stipple=XCreateBitmapFromData(display,
14838    windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14839  windows->info.shadow_stipple=XCreateBitmapFromData(display,
14840    windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14841  (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14842  if (IfMagickTrue(windows->image.mapped) )
14843    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14844  if (IfMagickTrue(display_image->debug) )
14845    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14846      windows->info.id);
14847  /*
14848    Initialize Command widget.
14849  */
14850  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14851    resource_info,&windows->command);
14852  windows->command.data=MagickMenus;
14853  (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14854  (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.command",
14855    resource_info->client_name);
14856  windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14857    resource_name,"geometry",(char *) NULL);
14858  (void) CloneString(&windows->command.name,MagickTitle);
14859  windows->command.border_width=0;
14860  windows->command.flags|=PPosition;
14861  windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14862    ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14863    OwnerGrabButtonMask | StructureNotifyMask;
14864  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14865  manager_hints->input=MagickTrue;
14866  manager_hints->initial_state=NormalState;
14867  manager_hints->window_group=windows->image.id;
14868  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14869    &windows->command);
14870  windows->command.highlight_stipple=XCreateBitmapFromData(display,
14871    windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14872    HighlightHeight);
14873  windows->command.shadow_stipple=XCreateBitmapFromData(display,
14874    windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14875  (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14876  if (IfMagickTrue(windows->command.mapped) )
14877    (void) XMapRaised(display,windows->command.id);
14878  if (IfMagickTrue(display_image->debug) )
14879    (void) LogMagickEvent(X11Event,GetMagickModule(),
14880      "Window id: 0x%lx (command)",windows->command.id);
14881  /*
14882    Initialize Widget window.
14883  */
14884  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14885    resource_info,&windows->widget);
14886  (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.widget",
14887    resource_info->client_name);
14888  windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14889    resource_name,"geometry",(char *) NULL);
14890  windows->widget.border_width=0;
14891  windows->widget.flags|=PPosition;
14892  windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14893    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14894    KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14895    StructureNotifyMask;
14896  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14897  manager_hints->input=MagickTrue;
14898  manager_hints->initial_state=NormalState;
14899  manager_hints->window_group=windows->image.id;
14900  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14901    &windows->widget);
14902  windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14903    windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14904  windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14905    windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14906  (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14907  if (IfMagickTrue(display_image->debug) )
14908    (void) LogMagickEvent(X11Event,GetMagickModule(),
14909      "Window id: 0x%lx (widget)",windows->widget.id);
14910  /*
14911    Initialize popup window.
14912  */
14913  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14914    resource_info,&windows->popup);
14915  windows->popup.border_width=0;
14916  windows->popup.flags|=PPosition;
14917  windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14918    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14919    KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14920  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14921  manager_hints->input=MagickTrue;
14922  manager_hints->initial_state=NormalState;
14923  manager_hints->window_group=windows->image.id;
14924  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14925    &windows->popup);
14926  windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14927    windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14928  windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14929    windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14930  (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14931  if (IfMagickTrue(display_image->debug) )
14932    (void) LogMagickEvent(X11Event,GetMagickModule(),
14933      "Window id: 0x%lx (pop up)",windows->popup.id);
14934  /*
14935    Initialize Magnify window and cursor.
14936  */
14937  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14938    resource_info,&windows->magnify);
14939  if (IfMagickFalse(resource_info->use_shared_memory) )
14940    windows->magnify.shared_memory=MagickFalse;
14941  (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.magnify",
14942    resource_info->client_name);
14943  windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14944    resource_name,"geometry",(char *) NULL);
14945  (void) FormatLocaleString(windows->magnify.name,MagickPathExtent,"Magnify %uX",
14946    resource_info->magnify);
14947  if (windows->magnify.cursor != (Cursor) NULL)
14948    (void) XFreeCursor(display,windows->magnify.cursor);
14949  windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14950    map_info->colormap,resource_info->background_color,
14951    resource_info->foreground_color);
14952  if (windows->magnify.cursor == (Cursor) NULL)
14953    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14954      display_image->filename);
14955  windows->magnify.width=MagnifySize;
14956  windows->magnify.height=MagnifySize;
14957  windows->magnify.flags|=PPosition;
14958  windows->magnify.min_width=MagnifySize;
14959  windows->magnify.min_height=MagnifySize;
14960  windows->magnify.width_inc=MagnifySize;
14961  windows->magnify.height_inc=MagnifySize;
14962  windows->magnify.data=resource_info->magnify;
14963  windows->magnify.attributes.cursor=windows->magnify.cursor;
14964  windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14965    ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14966    StructureNotifyMask;
14967  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14968  manager_hints->input=MagickTrue;
14969  manager_hints->initial_state=NormalState;
14970  manager_hints->window_group=windows->image.id;
14971  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14972    &windows->magnify);
14973  if (IfMagickTrue(display_image->debug) )
14974    (void) LogMagickEvent(X11Event,GetMagickModule(),
14975      "Window id: 0x%lx (magnify)",windows->magnify.id);
14976  (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14977  /*
14978    Initialize panning window.
14979  */
14980  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14981    resource_info,&windows->pan);
14982  (void) CloneString(&windows->pan.name,"Pan Icon");
14983  windows->pan.width=windows->icon.width;
14984  windows->pan.height=windows->icon.height;
14985  (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.pan",
14986    resource_info->client_name);
14987  windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
14988    resource_name,"geometry",(char *) NULL);
14989  (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
14990    &windows->pan.width,&windows->pan.height);
14991  windows->pan.flags|=PPosition;
14992  windows->pan.immutable=MagickTrue;
14993  windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14994    ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
14995    StructureNotifyMask;
14996  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14997  manager_hints->input=MagickFalse;
14998  manager_hints->initial_state=NormalState;
14999  manager_hints->window_group=windows->image.id;
15000  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
15001    &windows->pan);
15002  if (IfMagickTrue(display_image->debug) )
15003    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
15004      windows->pan.id);
15005  (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
15006  if (IfMagickTrue(windows->info.mapped) )
15007    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15008  if (IfMagickFalse(windows->image.mapped) ||
15009      (windows->backdrop.id != (Window) NULL))
15010    (void) XMapWindow(display,windows->image.id);
15011  /*
15012    Set our progress monitor and warning handlers.
15013  */
15014  if (warning_handler == (WarningHandler) NULL)
15015    {
15016      warning_handler=resource_info->display_warnings ?
15017        SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
15018      warning_handler=resource_info->display_warnings ?
15019        SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
15020    }
15021  /*
15022    Initialize Image and Magnify X images.
15023  */
15024  windows->image.x=0;
15025  windows->image.y=0;
15026  windows->magnify.shape=MagickFalse;
15027  width=(unsigned int) display_image->columns;
15028  height=(unsigned int) display_image->rows;
15029  if ((display_image->columns != width) || (display_image->rows != height))
15030    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15031      display_image->filename);
15032  status=XMakeImage(display,resource_info,&windows->image,display_image,
15033    width,height,exception);
15034  if (IfMagickFalse(status) )
15035    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15036      display_image->filename);
15037  status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
15038    windows->magnify.width,windows->magnify.height,exception);
15039  if (IfMagickFalse(status))
15040    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15041      display_image->filename);
15042  if (IfMagickTrue(windows->magnify.mapped) )
15043    (void) XMapRaised(display,windows->magnify.id);
15044  if (IfMagickTrue(windows->pan.mapped) )
15045    (void) XMapRaised(display,windows->pan.id);
15046  windows->image.window_changes.width=(int) display_image->columns;
15047  windows->image.window_changes.height=(int) display_image->rows;
15048  (void) XConfigureImage(display,resource_info,windows,display_image,exception);
15049  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15050  (void) XSync(display,MagickFalse);
15051  /*
15052    Respond to events.
15053  */
15054  delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
15055  timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15056  update_time=0;
15057  if (IfMagickTrue(resource_info->update) )
15058    {
15059      MagickBooleanType
15060        status;
15061
15062      /*
15063        Determine when file data was last modified.
15064      */
15065      status=GetPathAttributes(display_image->filename,&attributes);
15066      if (IfMagickTrue(status) )
15067        update_time=attributes.st_mtime;
15068    }
15069  *state&=(~FormerImageState);
15070  *state&=(~MontageImageState);
15071  *state&=(~NextImageState);
15072  do
15073  {
15074    /*
15075      Handle a window event.
15076    */
15077    if (IfMagickTrue(windows->image.mapped) )
15078      if ((display_image->delay != 0) || (resource_info->update != 0))
15079        {
15080          if (timer < time((time_t *) NULL))
15081            {
15082              if (IfMagickFalse(resource_info->update) )
15083                *state|=NextImageState | ExitState;
15084              else
15085                {
15086                  MagickBooleanType
15087                    status;
15088
15089                  /*
15090                    Determine if image file was modified.
15091                  */
15092                  status=GetPathAttributes(display_image->filename,&attributes);
15093                  if (IfMagickTrue(status) )
15094                    if (update_time != attributes.st_mtime)
15095                      {
15096                        /*
15097                          Redisplay image.
15098                        */
15099                        (void) FormatLocaleString(
15100                          resource_info->image_info->filename,MagickPathExtent,
15101                          "%s:%s",display_image->magick,
15102                          display_image->filename);
15103                        nexus=ReadImage(resource_info->image_info,exception);
15104                        if (nexus != (Image *) NULL)
15105                          *state|=NextImageState | ExitState;
15106                      }
15107                  delay=display_image->delay/MagickMax(
15108                    display_image->ticks_per_second,1L);
15109                  timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15110                }
15111            }
15112          if (XEventsQueued(display,QueuedAfterFlush) == 0)
15113            {
15114              /*
15115                Do not block if delay > 0.
15116              */
15117              XDelay(display,SuspendTime << 2);
15118              continue;
15119            }
15120        }
15121    timestamp=time((time_t *) NULL);
15122    (void) XNextEvent(display,&event);
15123    if ((windows->image.stasis == MagickFalse) ||
15124        (windows->magnify.stasis == MagickFalse))
15125      {
15126        if ((time((time_t *) NULL)-timestamp) > 0)
15127          {
15128            windows->image.stasis=MagickTrue;
15129            windows->magnify.stasis=MagickTrue;
15130          }
15131      }
15132    if (event.xany.window == windows->command.id)
15133      {
15134        /*
15135          Select a command from the Command widget.
15136        */
15137        id=XCommandWidget(display,windows,CommandMenu,&event);
15138        if (id < 0)
15139          continue;
15140        (void) CopyMagickString(command,CommandMenu[id],MagickPathExtent);
15141        command_type=CommandMenus[id];
15142        if (id < MagickMenus)
15143          {
15144            /*
15145              Select a command from a pop-up menu.
15146            */
15147            entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15148              command);
15149            if (entry < 0)
15150              continue;
15151            (void) CopyMagickString(command,Menus[id][entry],MagickPathExtent);
15152            command_type=Commands[id][entry];
15153          }
15154        if (command_type != NullCommand)
15155          nexus=XMagickCommand(display,resource_info,windows,command_type,
15156            &display_image,exception);
15157        continue;
15158      }
15159    switch (event.type)
15160    {
15161      case ButtonPress:
15162      {
15163        if (IfMagickTrue(display_image->debug) )
15164          (void) LogMagickEvent(X11Event,GetMagickModule(),
15165            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15166            event.xbutton.button,event.xbutton.x,event.xbutton.y);
15167        if ((event.xbutton.button == Button3) &&
15168            (event.xbutton.state & Mod1Mask))
15169          {
15170            /*
15171              Convert Alt-Button3 to Button2.
15172            */
15173            event.xbutton.button=Button2;
15174            event.xbutton.state&=(~Mod1Mask);
15175          }
15176        if (event.xbutton.window == windows->backdrop.id)
15177          {
15178            (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15179              event.xbutton.time);
15180            break;
15181          }
15182        if (event.xbutton.window == windows->image.id)
15183          {
15184            switch (event.xbutton.button)
15185            {
15186              case Button1:
15187              {
15188                if (resource_info->immutable)
15189                  {
15190                    /*
15191                      Select a command from the Virtual menu.
15192                    */
15193                    entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15194                      command);
15195                    if (entry >= 0)
15196                      nexus=XMagickCommand(display,resource_info,windows,
15197                        VirtualCommands[entry],&display_image,exception);
15198                    break;
15199                  }
15200                /*
15201                  Map/unmap Command widget.
15202                */
15203                if (IfMagickTrue(windows->command.mapped) )
15204                  (void) XWithdrawWindow(display,windows->command.id,
15205                    windows->command.screen);
15206                else
15207                  {
15208                    (void) XCommandWidget(display,windows,CommandMenu,
15209                      (XEvent *) NULL);
15210                    (void) XMapRaised(display,windows->command.id);
15211                  }
15212                break;
15213              }
15214              case Button2:
15215              {
15216                /*
15217                  User pressed the image magnify button.
15218                */
15219                (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15220                  &display_image,exception);
15221                XMagnifyImage(display,windows,&event,exception);
15222                break;
15223              }
15224              case Button3:
15225              {
15226                if (resource_info->immutable)
15227                  {
15228                    /*
15229                      Select a command from the Virtual menu.
15230                    */
15231                    entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15232                      command);
15233                    if (entry >= 0)
15234                      nexus=XMagickCommand(display,resource_info,windows,
15235                        VirtualCommands[entry],&display_image,exception);
15236                    break;
15237                  }
15238                if (display_image->montage != (char *) NULL)
15239                  {
15240                    /*
15241                      Open or delete a tile from a visual image directory.
15242                    */
15243                    nexus=XTileImage(display,resource_info,windows,
15244                      display_image,&event,exception);
15245                    if (nexus != (Image *) NULL)
15246                      *state|=MontageImageState | NextImageState | ExitState;
15247                    vid_info.x=(short int) windows->image.x;
15248                    vid_info.y=(short int) windows->image.y;
15249                    break;
15250                  }
15251                /*
15252                  Select a command from the Short Cuts menu.
15253                */
15254                entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15255                  command);
15256                if (entry >= 0)
15257                  nexus=XMagickCommand(display,resource_info,windows,
15258                    ShortCutsCommands[entry],&display_image,exception);
15259                break;
15260              }
15261              case Button4:
15262              {
15263                /*
15264                  Wheel up.
15265                */
15266                XTranslateImage(display,windows,*image,XK_Up);
15267                break;
15268              }
15269              case Button5:
15270              {
15271                /*
15272                  Wheel down.
15273                */
15274                XTranslateImage(display,windows,*image,XK_Down);
15275                break;
15276              }
15277              default:
15278                break;
15279            }
15280            break;
15281          }
15282        if (event.xbutton.window == windows->magnify.id)
15283          {
15284            int
15285              factor;
15286
15287            static const char
15288              *MagnifyMenu[] =
15289              {
15290                "2",
15291                "4",
15292                "5",
15293                "6",
15294                "7",
15295                "8",
15296                "9",
15297                "3",
15298                (char *) NULL,
15299              };
15300
15301            static KeySym
15302              MagnifyCommands[] =
15303              {
15304                XK_2,
15305                XK_4,
15306                XK_5,
15307                XK_6,
15308                XK_7,
15309                XK_8,
15310                XK_9,
15311                XK_3
15312              };
15313
15314            /*
15315              Select a magnify factor from the pop-up menu.
15316            */
15317            factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15318            if (factor >= 0)
15319              XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor],
15320                exception);
15321            break;
15322          }
15323        if (event.xbutton.window == windows->pan.id)
15324          {
15325            switch (event.xbutton.button)
15326            {
15327              case Button4:
15328              {
15329                /*
15330                  Wheel up.
15331                */
15332                XTranslateImage(display,windows,*image,XK_Up);
15333                break;
15334              }
15335              case Button5:
15336              {
15337                /*
15338                  Wheel down.
15339                */
15340                XTranslateImage(display,windows,*image,XK_Down);
15341                break;
15342              }
15343              default:
15344              {
15345                XPanImage(display,windows,&event,exception);
15346                break;
15347              }
15348            }
15349            break;
15350          }
15351        delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15352          1L);
15353        timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15354        break;
15355      }
15356      case ButtonRelease:
15357      {
15358        if (IfMagickTrue(display_image->debug) )
15359          (void) LogMagickEvent(X11Event,GetMagickModule(),
15360            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15361            event.xbutton.button,event.xbutton.x,event.xbutton.y);
15362        break;
15363      }
15364      case ClientMessage:
15365      {
15366        if (IfMagickTrue(display_image->debug) )
15367          (void) LogMagickEvent(X11Event,GetMagickModule(),
15368            "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15369            event.xclient.message_type,event.xclient.format,(unsigned long)
15370            event.xclient.data.l[0]);
15371        if (event.xclient.message_type == windows->im_protocols)
15372          {
15373            if (*event.xclient.data.l == (long) windows->im_update_widget)
15374              {
15375                (void) CloneString(&windows->command.name,MagickTitle);
15376                windows->command.data=MagickMenus;
15377                (void) XCommandWidget(display,windows,CommandMenu,
15378                  (XEvent *) NULL);
15379                break;
15380              }
15381            if (*event.xclient.data.l == (long) windows->im_update_colormap)
15382              {
15383                /*
15384                  Update graphic context and window colormap.
15385                */
15386                for (i=0; i < (int) number_windows; i++)
15387                {
15388                  if (magick_windows[i]->id == windows->icon.id)
15389                    continue;
15390                  context_values.background=pixel->background_color.pixel;
15391                  context_values.foreground=pixel->foreground_color.pixel;
15392                  (void) XChangeGC(display,magick_windows[i]->annotate_context,
15393                    context_mask,&context_values);
15394                  (void) XChangeGC(display,magick_windows[i]->widget_context,
15395                    context_mask,&context_values);
15396                  context_values.background=pixel->foreground_color.pixel;
15397                  context_values.foreground=pixel->background_color.pixel;
15398                  context_values.plane_mask=context_values.background ^
15399                    context_values.foreground;
15400                  (void) XChangeGC(display,magick_windows[i]->highlight_context,
15401                    (size_t) (context_mask | GCPlaneMask),
15402                    &context_values);
15403                  magick_windows[i]->attributes.background_pixel=
15404                    pixel->background_color.pixel;
15405                  magick_windows[i]->attributes.border_pixel=
15406                    pixel->border_color.pixel;
15407                  magick_windows[i]->attributes.colormap=map_info->colormap;
15408                  (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15409                    (unsigned long) magick_windows[i]->mask,
15410                    &magick_windows[i]->attributes);
15411                }
15412                if (IfMagickTrue(windows->pan.mapped) )
15413                  {
15414                    (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15415                      windows->pan.pixmap);
15416                    (void) XClearWindow(display,windows->pan.id);
15417                    XDrawPanRectangle(display,windows);
15418                  }
15419                if (windows->backdrop.id != (Window) NULL)
15420                  (void) XInstallColormap(display,map_info->colormap);
15421                break;
15422              }
15423            if (*event.xclient.data.l == (long) windows->im_former_image)
15424              {
15425                *state|=FormerImageState | ExitState;
15426                break;
15427              }
15428            if (*event.xclient.data.l == (long) windows->im_next_image)
15429              {
15430                *state|=NextImageState | ExitState;
15431                break;
15432              }
15433            if (*event.xclient.data.l == (long) windows->im_retain_colors)
15434              {
15435                *state|=RetainColorsState;
15436                break;
15437              }
15438            if (*event.xclient.data.l == (long) windows->im_exit)
15439              {
15440                *state|=ExitState;
15441                break;
15442              }
15443            break;
15444          }
15445        if (event.xclient.message_type == windows->dnd_protocols)
15446          {
15447            Atom
15448              selection,
15449              type;
15450
15451            int
15452              format,
15453              status;
15454
15455            unsigned char
15456              *data;
15457
15458            unsigned long
15459              after,
15460              length;
15461
15462            /*
15463              Display image named by the Drag-and-Drop selection.
15464            */
15465            if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15466              break;
15467            selection=XInternAtom(display,"DndSelection",MagickFalse);
15468            status=XGetWindowProperty(display,root_window,selection,0L,(long)
15469              MagickPathExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15470              &length,&after,&data);
15471            if ((status != Success) || (length == 0))
15472              break;
15473            if (*event.xclient.data.l == 2)
15474              {
15475                /*
15476                  Offix DND.
15477                */
15478                (void) CopyMagickString(resource_info->image_info->filename,
15479                  (char *) data,MagickPathExtent);
15480              }
15481            else
15482              {
15483                /*
15484                  XDND.
15485                */
15486                if (strncmp((char *) data, "file:", 5) != 0)
15487                  {
15488                    (void) XFree((void *) data);
15489                    break;
15490                  }
15491                (void) CopyMagickString(resource_info->image_info->filename,
15492                  ((char *) data)+5,MagickPathExtent);
15493              }
15494            nexus=ReadImage(resource_info->image_info,exception);
15495            CatchException(exception);
15496            if (nexus != (Image *) NULL)
15497              *state|=NextImageState | ExitState;
15498            (void) XFree((void *) data);
15499            break;
15500          }
15501        /*
15502          If client window delete message, exit.
15503        */
15504        if (event.xclient.message_type != windows->wm_protocols)
15505          break;
15506        if (*event.xclient.data.l != (long) windows->wm_delete_window)
15507          break;
15508        (void) XWithdrawWindow(display,event.xclient.window,
15509          visual_info->screen);
15510        if (event.xclient.window == windows->image.id)
15511          {
15512            *state|=ExitState;
15513            break;
15514          }
15515        if (event.xclient.window == windows->pan.id)
15516          {
15517            /*
15518              Restore original image size when pan window is deleted.
15519            */
15520            windows->image.window_changes.width=windows->image.ximage->width;
15521            windows->image.window_changes.height=windows->image.ximage->height;
15522            (void) XConfigureImage(display,resource_info,windows,
15523              display_image,exception);
15524          }
15525        break;
15526      }
15527      case ConfigureNotify:
15528      {
15529        if (IfMagickTrue(display_image->debug) )
15530          (void) LogMagickEvent(X11Event,GetMagickModule(),
15531            "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15532            event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15533            event.xconfigure.y,event.xconfigure.send_event);
15534        if (event.xconfigure.window == windows->image.id)
15535          {
15536            /*
15537              Image window has a new configuration.
15538            */
15539            if (event.xconfigure.send_event != 0)
15540              {
15541                XWindowChanges
15542                  window_changes;
15543
15544                /*
15545                  Position the transient windows relative of the Image window.
15546                */
15547                if (windows->command.geometry == (char *) NULL)
15548                  if (IfMagickFalse(windows->command.mapped) )
15549                    {
15550                      windows->command.x=event.xconfigure.x-
15551                        windows->command.width-25;
15552                      windows->command.y=event.xconfigure.y;
15553                      XConstrainWindowPosition(display,&windows->command);
15554                      window_changes.x=windows->command.x;
15555                      window_changes.y=windows->command.y;
15556                      (void) XReconfigureWMWindow(display,windows->command.id,
15557                        windows->command.screen,(unsigned int) (CWX | CWY),
15558                        &window_changes);
15559                    }
15560                if (windows->widget.geometry == (char *) NULL)
15561                  if (IfMagickFalse(windows->widget.mapped) )
15562                    {
15563                      windows->widget.x=event.xconfigure.x+
15564                        event.xconfigure.width/10;
15565                      windows->widget.y=event.xconfigure.y+
15566                        event.xconfigure.height/10;
15567                      XConstrainWindowPosition(display,&windows->widget);
15568                      window_changes.x=windows->widget.x;
15569                      window_changes.y=windows->widget.y;
15570                      (void) XReconfigureWMWindow(display,windows->widget.id,
15571                        windows->widget.screen,(unsigned int) (CWX | CWY),
15572                        &window_changes);
15573                    }
15574                if (windows->magnify.geometry == (char *) NULL)
15575                  if (IfMagickFalse(windows->magnify.mapped) )
15576                    {
15577                      windows->magnify.x=event.xconfigure.x+
15578                        event.xconfigure.width+25;
15579                      windows->magnify.y=event.xconfigure.y;
15580                      XConstrainWindowPosition(display,&windows->magnify);
15581                      window_changes.x=windows->magnify.x;
15582                      window_changes.y=windows->magnify.y;
15583                      (void) XReconfigureWMWindow(display,windows->magnify.id,
15584                        windows->magnify.screen,(unsigned int) (CWX | CWY),
15585                        &window_changes);
15586                    }
15587                if (windows->pan.geometry == (char *) NULL)
15588                  if (IfMagickFalse(windows->pan.mapped) )
15589                    {
15590                      windows->pan.x=event.xconfigure.x+
15591                        event.xconfigure.width+25;
15592                      windows->pan.y=event.xconfigure.y+
15593                        windows->magnify.height+50;
15594                      XConstrainWindowPosition(display,&windows->pan);
15595                      window_changes.x=windows->pan.x;
15596                      window_changes.y=windows->pan.y;
15597                      (void) XReconfigureWMWindow(display,windows->pan.id,
15598                        windows->pan.screen,(unsigned int) (CWX | CWY),
15599                        &window_changes);
15600                    }
15601              }
15602            if ((event.xconfigure.width == (int) windows->image.width) &&
15603                (event.xconfigure.height == (int) windows->image.height))
15604              break;
15605            windows->image.width=(unsigned int) event.xconfigure.width;
15606            windows->image.height=(unsigned int) event.xconfigure.height;
15607            windows->image.x=0;
15608            windows->image.y=0;
15609            if (display_image->montage != (char *) NULL)
15610              {
15611                windows->image.x=vid_info.x;
15612                windows->image.y=vid_info.y;
15613              }
15614            if (IfMagickTrue(windows->image.mapped) &&
15615                IfMagickTrue(windows->image.stasis) )
15616              {
15617                /*
15618                  Update image window configuration.
15619                */
15620                windows->image.window_changes.width=event.xconfigure.width;
15621                windows->image.window_changes.height=event.xconfigure.height;
15622                (void) XConfigureImage(display,resource_info,windows,
15623                  display_image,exception);
15624              }
15625            /*
15626              Update pan window configuration.
15627            */
15628            if ((event.xconfigure.width < windows->image.ximage->width) ||
15629                (event.xconfigure.height < windows->image.ximage->height))
15630              {
15631                (void) XMapRaised(display,windows->pan.id);
15632                XDrawPanRectangle(display,windows);
15633              }
15634            else
15635              if (IfMagickTrue(windows->pan.mapped) )
15636                (void) XWithdrawWindow(display,windows->pan.id,
15637                  windows->pan.screen);
15638            break;
15639          }
15640        if (event.xconfigure.window == windows->magnify.id)
15641          {
15642            unsigned int
15643              magnify;
15644
15645            /*
15646              Magnify window has a new configuration.
15647            */
15648            windows->magnify.width=(unsigned int) event.xconfigure.width;
15649            windows->magnify.height=(unsigned int) event.xconfigure.height;
15650            if (IfMagickFalse(windows->magnify.mapped) )
15651              break;
15652            magnify=1;
15653            while ((int) magnify <= event.xconfigure.width)
15654              magnify<<=1;
15655            while ((int) magnify <= event.xconfigure.height)
15656              magnify<<=1;
15657            magnify>>=1;
15658            if (((int) magnify != event.xconfigure.width) ||
15659                ((int) magnify != event.xconfigure.height))
15660              {
15661                window_changes.width=(int) magnify;
15662                window_changes.height=(int) magnify;
15663                (void) XReconfigureWMWindow(display,windows->magnify.id,
15664                  windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15665                  &window_changes);
15666                break;
15667              }
15668            if (IfMagickTrue(windows->magnify.mapped) &&
15669                IfMagickTrue(windows->magnify.stasis) )
15670              {
15671                status=XMakeImage(display,resource_info,&windows->magnify,
15672                  display_image,windows->magnify.width,windows->magnify.height,
15673                  exception);
15674                XMakeMagnifyImage(display,windows,exception);
15675              }
15676            break;
15677          }
15678        if (IfMagickTrue(windows->magnify.mapped) &&
15679            (event.xconfigure.window == windows->pan.id))
15680          {
15681            /*
15682              Pan icon window has a new configuration.
15683            */
15684            if (event.xconfigure.send_event != 0)
15685              {
15686                windows->pan.x=event.xconfigure.x;
15687                windows->pan.y=event.xconfigure.y;
15688              }
15689            windows->pan.width=(unsigned int) event.xconfigure.width;
15690            windows->pan.height=(unsigned int) event.xconfigure.height;
15691            break;
15692          }
15693        if (event.xconfigure.window == windows->icon.id)
15694          {
15695            /*
15696              Icon window has a new configuration.
15697            */
15698            windows->icon.width=(unsigned int) event.xconfigure.width;
15699            windows->icon.height=(unsigned int) event.xconfigure.height;
15700            break;
15701          }
15702        break;
15703      }
15704      case DestroyNotify:
15705      {
15706        /*
15707          Group leader has exited.
15708        */
15709        if (IfMagickTrue(display_image->debug) )
15710          (void) LogMagickEvent(X11Event,GetMagickModule(),
15711            "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15712        if (event.xdestroywindow.window == windows->group_leader.id)
15713          {
15714            *state|=ExitState;
15715            break;
15716          }
15717        break;
15718      }
15719      case EnterNotify:
15720      {
15721        /*
15722          Selectively install colormap.
15723        */
15724        if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15725          if (event.xcrossing.mode != NotifyUngrab)
15726            XInstallColormap(display,map_info->colormap);
15727        break;
15728      }
15729      case Expose:
15730      {
15731        if (IfMagickTrue(display_image->debug) )
15732          (void) LogMagickEvent(X11Event,GetMagickModule(),
15733            "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15734            event.xexpose.width,event.xexpose.height,event.xexpose.x,
15735            event.xexpose.y);
15736        /*
15737          Refresh windows that are now exposed.
15738        */
15739        if ((event.xexpose.window == windows->image.id) &&
15740            IfMagickTrue(windows->image.mapped) )
15741          {
15742            XRefreshWindow(display,&windows->image,&event);
15743            delay=display_image->delay/MagickMax(
15744              display_image->ticks_per_second,1L);
15745            timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15746            break;
15747          }
15748        if ((event.xexpose.window == windows->magnify.id) &&
15749            IfMagickTrue(windows->magnify.mapped))
15750          {
15751            XMakeMagnifyImage(display,windows,exception);
15752            break;
15753          }
15754        if (event.xexpose.window == windows->pan.id)
15755          {
15756            XDrawPanRectangle(display,windows);
15757            break;
15758          }
15759        if (event.xexpose.window == windows->icon.id)
15760          {
15761            XRefreshWindow(display,&windows->icon,&event);
15762            break;
15763          }
15764        break;
15765      }
15766      case KeyPress:
15767      {
15768        int
15769          length;
15770
15771        /*
15772          Respond to a user key press.
15773        */
15774        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15775          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15776        *(command+length)='\0';
15777        if (IfMagickTrue(display_image->debug) )
15778          (void) LogMagickEvent(X11Event,GetMagickModule(),
15779            "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15780            key_symbol,command);
15781        if (event.xkey.window == windows->image.id)
15782          {
15783            command_type=XImageWindowCommand(display,resource_info,windows,
15784              event.xkey.state,key_symbol,&display_image,exception);
15785            if (command_type != NullCommand)
15786              nexus=XMagickCommand(display,resource_info,windows,command_type,
15787                &display_image,exception);
15788          }
15789        if (event.xkey.window == windows->magnify.id)
15790          XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol,
15791            exception);
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 (IfMagickTrue(display_image->debug) )
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 (IfMagickTrue(display_image->debug) )
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,exception);
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,exception);
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 (IfMagickTrue(display_image->debug) )
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) MagickPathExtent,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,MagickPathExtent);
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 (IfMagickTrue(display_image->debug) )
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 (IfMagickTrue(display_image->debug) )
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,exception);
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 (IfMagickTrue(display_image->debug) )
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 (IfMagickTrue(resource_info->confirm_edit) )
16056      {
16057        /*
16058          Query user if image has changed.
16059        */
16060        if (IfMagickFalse(resource_info->immutable) &&
16061            IfMagickTrue(display_image->taint))
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 (IfMagickTrue(windows->info.mapped) )
16084        (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16085      if (IfMagickTrue(windows->magnify.mapped) )
16086        (void) XWithdrawWindow(display,windows->magnify.id,
16087          windows->magnify.screen);
16088      if (IfMagickTrue(windows->command.mapped) )
16089        (void) XWithdrawWindow(display,windows->command.id,
16090          windows->command.screen);
16091    }
16092  if (IfMagickTrue(windows->pan.mapped) )
16093    (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16094  if (IfMagickFalse(resource_info->backdrop) )
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,MagickPathExtent);
16138  (void) directory;
16139  {
16140    int
16141      status;
16142
16143    if (*resource_info->home_directory == '\0')
16144      (void) CopyMagickString(resource_info->home_directory,".",MagickPathExtent);
16145    status=chdir(resource_info->home_directory);
16146    if (status == -1)
16147      (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
16148        "UnableToOpenFile","%s",resource_info->home_directory);
16149  }
16150  *image=display_image;
16151  return(nexus);
16152}
16153#else
16154
16155/*
16156%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16157%                                                                             %
16158%                                                                             %
16159%                                                                             %
16160+   D i s p l a y I m a g e s                                                 %
16161%                                                                             %
16162%                                                                             %
16163%                                                                             %
16164%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16165%
16166%  DisplayImages() displays an image sequence to any X window screen.  It
16167%  returns a value other than 0 if successful.  Check the exception member
16168%  of image to determine the reason for any failure.
16169%
16170%  The format of the DisplayImages method is:
16171%
16172%      MagickBooleanType DisplayImages(const ImageInfo *image_info,
16173%        Image *images,ExceptionInfo *exception)
16174%
16175%  A description of each parameter follows:
16176%
16177%    o image_info: the image info.
16178%
16179%    o image: the image.
16180%
16181%    o exception: return any errors or warnings in this structure.
16182%
16183*/
16184MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16185  Image *image,ExceptionInfo *exception)
16186{
16187  assert(image_info != (const ImageInfo *) NULL);
16188  assert(image_info->signature == MagickCoreSignature);
16189  assert(image != (Image *) NULL);
16190  assert(image->signature == MagickCoreSignature);
16191  (void) image_info;
16192  if (IfMagickTrue(image->debug) )
16193    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16194  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16195    "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image->filename);
16196  return(MagickFalse);
16197}
16198
16199/*
16200%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16201%                                                                             %
16202%                                                                             %
16203%                                                                             %
16204+   R e m o t e D i s p l a y C o m m a n d                                   %
16205%                                                                             %
16206%                                                                             %
16207%                                                                             %
16208%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16209%
16210%  RemoteDisplayCommand() encourages a remote display program to display the
16211%  specified image filename.
16212%
16213%  The format of the RemoteDisplayCommand method is:
16214%
16215%      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16216%        const char *window,const char *filename,ExceptionInfo *exception)
16217%
16218%  A description of each parameter follows:
16219%
16220%    o image_info: the image info.
16221%
16222%    o window: Specifies the name or id of an X window.
16223%
16224%    o filename: the name of the image filename to display.
16225%
16226%    o exception: return any errors or warnings in this structure.
16227%
16228*/
16229MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16230  const char *window,const char *filename,ExceptionInfo *exception)
16231{
16232  assert(image_info != (const ImageInfo *) NULL);
16233  assert(image_info->signature == MagickCoreSignature);
16234  assert(filename != (char *) NULL);
16235  (void) window;
16236  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16237  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16238    "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image_info->filename);
16239  return(MagickFalse);
16240}
16241#endif
16242